001 /*
002 * $Id: LinearCombo.java,v 1.54 2012/02/19 17:35:38 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2012
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 *
012 * All Rights Reserved
013 *
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.
016 *
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 *
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
025 * GNU Lesser Public License for more details.
026 *
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program. If not, see http://www.gnu.org/licenses.
029 */
030
031 package edu.wisc.ssec.mcidasv.control;
032
033 import java.awt.Color;
034 import java.awt.Container;
035 import java.awt.Dimension;
036 import java.rmi.RemoteException;
037 import java.util.ArrayList;
038 import java.util.Collection;
039 import java.util.HashMap;
040 import java.util.HashSet;
041 import java.util.Hashtable;
042 import java.util.Iterator;
043 import java.util.LinkedHashSet;
044 import java.util.List;
045 import java.util.Map;
046 import java.util.Map.Entry;
047 import java.util.Set;
048
049 import javax.swing.JComponent;
050 import javax.swing.JPanel;
051 import javax.swing.JTabbedPane;
052
053 import org.python.core.PyDictionary;
054 import org.python.core.PyFloat;
055 import org.python.core.PyInteger;
056 import org.slf4j.Logger;
057 import org.slf4j.LoggerFactory;
058
059 import visad.ConstantMap;
060 import visad.Data;
061 import visad.Real;
062 import visad.VisADException;
063 import visad.georef.MapProjection;
064
065 import ucar.unidata.data.DataChoice;
066 import ucar.unidata.data.DataSource;
067 import ucar.unidata.data.DirectDataChoice;
068 import ucar.unidata.idv.MapViewManager;
069 import ucar.unidata.util.GuiUtils;
070 import ucar.unidata.util.LogUtil;
071 import ucar.unidata.view.geoloc.MapProjectionDisplay;
072 import ucar.visad.display.DisplayMaster;
073
074 import edu.wisc.ssec.mcidasv.Constants;
075 import edu.wisc.ssec.mcidasv.McIDASV;
076 import edu.wisc.ssec.mcidasv.data.ComboDataChoice;
077 import edu.wisc.ssec.mcidasv.data.hydra.MultiDimensionSubset;
078 import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData;
079 import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralDataSource;
080 import edu.wisc.ssec.mcidasv.display.hydra.MultiSpectralDisplay;
081 import edu.wisc.ssec.mcidasv.display.hydra.MultiSpectralDisplay.DragLine;
082 import edu.wisc.ssec.mcidasv.jython.Console;
083 import edu.wisc.ssec.mcidasv.jython.ConsoleCallback;
084
085 public class LinearCombo extends HydraControl implements ConsoleCallback {
086
087 /** Trusty logging object. */
088 private static final Logger logger = LoggerFactory.getLogger(LinearCombo.class);
089
090 /** Help topic identifier. */
091 public static final String HYDRA_HELP_ID =
092 "idv.controls.hydra.linearcombinationcontrol";
093
094 /**
095 * Path to the Jython source code that allows for interaction with a
096 * linear combination display control.
097 */
098 public static final String HYDRA_SRC =
099 "/edu/wisc/ssec/mcidasv/resources/python/linearcombo/hydra.py";
100
101 /** Name used in Jython namespace to refer to the {@literal "IDV god object"}. */
102 public static final String CONSOLE_IDV_OBJECT = "idv";
103
104 /**
105 * Name used in Jython namespace to refer back to an instantiation of a
106 * linear combination control.
107 */
108 public static final String CONSOLE_CONTROL_OBJECT = "_linearCombo";
109
110 public static final String CONSOLE_OBJECT = "_jythonConsole";
111
112 public static final String CONSOLE_DATA_OBJECT = "_data";
113
114 private Console console;
115
116 private MultiSpectralDisplay display;
117
118 private DisplayMaster displayMaster;
119
120 private String sourceFile = "";
121
122 private ComboDataChoice comboChoice;
123
124 private MultiSpectralDataSource source;
125
126 private List<String> jythonHistory;
127
128 private Map<String, Selector> selectorMap;
129
130 private Map<String, Selector> jythonMap;
131
132 private DataChoice dataChoice = null;
133
134 /**
135 *
136 */
137 public LinearCombo() {
138 super();
139 setHelpUrl(HYDRA_HELP_ID);
140 jythonHistory = new ArrayList<String>();
141 selectorMap = new HashMap<String, Selector>();
142 jythonMap = new HashMap<String, Selector>();
143 }
144
145 @Override public boolean init(final DataChoice choice) throws VisADException, RemoteException {
146 List<DataSource> sources = new ArrayList<DataSource>();
147 choice.getDataSources(sources);
148 dataChoice = choice;
149
150 ((McIDASV)getIdv()).getMcvDataManager().setHydraControl(choice, this);
151
152 source = ((MultiSpectralDataSource)sources.get(0));
153 sourceFile = source.getDatasetName();
154
155 MultiSpectralData data = source.getMultiSpectralData(choice);
156
157 Float fieldSelectorChannel = (Float)getDataSelection().getProperty(Constants.PROP_CHAN);
158 if (fieldSelectorChannel == null)
159 fieldSelectorChannel = data.init_wavenumber;
160
161 console = new Console();
162 console.setCallbackHandler(this);
163
164 console.injectObject(CONSOLE_IDV_OBJECT, getIdv());
165 console.injectObject(CONSOLE_CONTROL_OBJECT, this);
166 console.injectObject(CONSOLE_OBJECT, console);
167 console.injectObject(CONSOLE_DATA_OBJECT, source.getMultiSpectralData(choice));
168
169 console.runFile("__main__", "/edu/wisc/ssec/mcidasv/resources/python/console_init.py");
170 console.runFile("__main__", HYDRA_SRC);
171
172 display = new MultiSpectralDisplay((DirectDataChoice)choice);
173 display.setWaveNumber(fieldSelectorChannel);
174 display.setDisplayControl(this);
175 ((McIDASV)getIdv()).getMcvDataManager().setHydraDisplay(choice, display);
176 return true;
177 }
178
179 @Override public void initDone() {
180 MapViewManager viewManager = (MapViewManager)getViewManager();
181 MapProjectionDisplay dispMaster =
182 (MapProjectionDisplay)viewManager.getMaster();
183
184 try {
185 dispMaster.setMapProjection(getDataProjection());
186 } catch (Exception e) {
187 logException("problem setting MapProjection", e);
188 }
189
190 getIdv().getIdvUIManager().showDashboard();
191 console.queueBatch("history", jythonHistory);
192 jythonHistory.clear();
193 }
194
195 public List<String> getJythonHistory() {
196 return console.getHistory();
197 }
198
199 public void setJythonHistory(final List<String> persistedHistory) {
200 jythonHistory = persistedHistory;
201 }
202
203 @Override public MapProjection getDataProjection() {
204 MapProjection mp = null;
205 HashMap subset = null;
206 Hashtable table = dataChoice.getProperties();
207 MultiDimensionSubset dataSel =
208 (MultiDimensionSubset)table.get(MultiDimensionSubset.key);
209
210 if (dataSel != null) {
211 subset = dataSel.getSubset();
212 }
213 mp = source.getDataProjection(subset);
214 return mp;
215 }
216
217 @Override public Container doMakeContents() {
218 JTabbedPane pane = new JTabbedPane();
219 pane.add("Console", GuiUtils.inset(getConsoleTab(), 5));
220 GuiUtils.handleHeavyWeightComponentsInTabs(pane);
221 return pane;
222 }
223
224 private JComponent getConsoleTab() {
225 JPanel consolePanel = console.getPanel();
226 consolePanel.setPreferredSize(new Dimension(500, 150));
227 return GuiUtils.topCenter(display.getDisplayComponent(), consolePanel);
228 }
229
230 @Override public void doRemove() throws VisADException, RemoteException {
231 super.doRemove();
232 }
233
234 @Override public String toString() {
235 return "[LinearCombo@" + Integer.toHexString(hashCode()) +
236 ": sourceFile=" + sourceFile + ']';
237 }
238
239 public void moveSelector(final String id, final float wavenum) {
240 if (!selectorMap.containsKey(id)) {
241 return;
242 }
243 display.updateControlSelector(id, wavenum);
244 }
245
246 public void updateSelector(final String id, final float wavenum) {
247 if (!selectorMap.containsKey(id)) {
248 return;
249 }
250
251 selectorMap.get(id).setWaveNumber(wavenum);
252 String cmd = new StringBuilder("_linearCombo.moveSelector('")
253 .append(id)
254 .append("', ")
255 .append(wavenum)
256 .append(')')
257 .toString();
258
259 console.addPretendHistory(cmd);
260 }
261
262 protected void addSelector(final Selector selector) throws Exception {
263 ConstantMap[] mapping = selector.getColor();
264 float r = Double.valueOf(mapping[0].getConstant()).floatValue();
265 float g = Double.valueOf(mapping[1].getConstant()).floatValue();
266 float b = Double.valueOf(mapping[2].getConstant()).floatValue();
267 Color javaColor = new Color(r, g, b);
268 display.createSelector(selector.getId(), javaColor);
269 display.setSelectorValue(selector.getId(), selector.getWaveNumber());
270 selectorMap.put(selector.getId(), selector);
271 logger.trace("added selector={}", selector);
272 }
273
274 protected MultiSpectralDisplay getMultiSpectralDisplay() {
275 return display;
276 }
277
278 protected int getSelectorCount() {
279 return selectorMap.size();
280 }
281
282 private Set<String> getSelectorIds(final Map<String, Object> objMap) {
283 assert objMap != null : objMap;
284
285 Set<String> ids = new HashSet<String>();
286 Collection<Object> jython = objMap.values();
287
288 for (Iterator<Object> i = jython.iterator(); i.hasNext();) {
289 Object obj = i.next();
290 if (!(obj instanceof Selector)) {
291 continue;
292 }
293
294 String selectorId = ((Selector)obj).getId();
295 ids.add(selectorId);
296 }
297
298 return ids;
299 }
300
301 /**
302 *
303 *
304 * @param objMap
305 *
306 * @return
307 */
308 private Map<String, Selector> mapNamesToThings(final Map<String, Object> objMap) {
309 assert objMap != null : objMap;
310
311 Map<String, Selector> nameMap = new HashMap<String, Selector>(objMap.size());
312 Set<Selector> seen = new LinkedHashSet<Selector>();
313 for (Map.Entry<String, Object> entry : objMap.entrySet()) {
314 Object obj = entry.getValue();
315 if (!(obj instanceof Selector)) {
316 continue;
317 }
318
319 String name = entry.getKey();
320 Selector selector = (Selector)obj;
321 if (!seen.contains(selector)) {
322 seen.add(selector);
323 selector.clearNames();
324 }
325 nameMap.put(name, selector);
326 selector.addName(name);
327 }
328 return nameMap;
329 }
330
331 public float getInitialWavenumber() {
332 return display.getMultiSpectralData().init_wavenumber;
333 }
334
335 public PyDictionary getBandNameMappings() {
336 PyDictionary map = new PyDictionary();
337 MultiSpectralData data = display.getMultiSpectralData();
338 if (!data.hasBandNames())
339 return map;
340
341 for (Entry<String, Float> entry : data.getBandNameMap().entrySet()) {
342 map.__setitem__(entry.getKey(), new PyFloat(entry.getValue()));
343 }
344
345 return map;
346 }
347
348 public void addCombination(final String name, final Data combo) {
349 source.addChoice(name, combo);
350 }
351
352 // public void addRealCombination(final String name, final Combination combo) {
353 // source.addRealCombo(name, combo, console);
354 // }
355 //
356 // public Console getConsole() {
357 // return console;
358 // }
359
360 /**
361 * Called after Jython's internals have finished processing {@code line}
362 * (and before control is given back to the user).
363 *
364 * <p>This is where {@code LinearCombo} controls map Jython names to Java
365 * objects.
366 */
367 public void ranBlock(final String line) {
368 List<DragLine> dragLines = display.getSelectors();
369 Map<String, Object> javaObjects = console.getJavaInstances();
370 Set<String> ids = getSelectorIds(javaObjects);
371 for (DragLine dragLine : dragLines) {
372 String lineId = dragLine.getControlId();
373 if (!ids.contains(lineId)) {
374 display.removeSelector(lineId);
375 selectorMap.remove(lineId);
376 }
377 }
378
379 jythonMap = mapNamesToThings(javaObjects);
380 logger.trace("ranBlock: javaObjs={}", javaObjects);
381 }
382
383 // public void saveJythonThings() {
384 // // well, only selectors so far...
385 // for (Map.Entry<String, Selector> entry : jythonMap.entrySet()) {
386 // String cmd = String.format("%s.setWaveNumber(%f)", entry.getKey(), entry.getValue().getWaveNumber());
387 // System.err.println("saving: "+cmd);
388 // console.addMetaCommand(cmd);
389 // }
390 // }
391
392 public static abstract class JythonThing {
393 protected Set<String> jythonNames = new LinkedHashSet<String>();
394 public JythonThing() { }
395 public abstract Data getData();
396
397 public static String colorString(final ConstantMap[] color) {
398 if (color == null) {
399 return "[null]";
400 }
401 if (color.length != 3) {
402 return "[invalid color string]";
403 }
404
405 double r = color[0].getConstant();
406 double g = color[1].getConstant();
407 double b = color[2].getConstant();
408 return String.format("[r=%.3f; g=%.3f; b=%.3f]", r, g, b);
409 }
410
411 private static Data extractData(final Object other) throws VisADException, RemoteException {
412 if (other instanceof JythonThing) {
413 return ((JythonThing)other).getData();
414 }
415 if (other instanceof PyFloat) {
416 return new Real(((PyFloat)other).getValue());
417 }
418 if (other instanceof PyInteger) {
419 return new Real(((PyInteger)other).getValue());
420 }
421 if (other instanceof Double) {
422 return new Real((Double)other);
423 }
424 if (other instanceof Integer) {
425 return new Real((Integer)other);
426 }
427 if (other instanceof Data) {
428 return (Data)other;
429 }
430 throw new IllegalArgumentException("Can't figure out what to do with " + other);
431 }
432
433 private static String extractName(final Object other) {
434 if (other instanceof JythonThing) {
435 return ((JythonThing)other).getName();
436 }
437 if (other instanceof PyInteger) {
438 return ((PyInteger)other).toString();
439 }
440 if (other instanceof PyFloat) {
441 return ((PyFloat)other).toString();
442 }
443 if (other instanceof Double) {
444 return ((Double)other).toString();
445 }
446 if (other instanceof Integer) {
447 return ((Integer)other).toString();
448 }
449 throw new IllegalArgumentException("UGH: "+other);
450 }
451
452 public abstract boolean removeName(final String name);
453 public abstract boolean addName(final String name);
454 public abstract String getName();
455 public abstract Collection<String> getNames();
456
457 public Combination __add__(final Object other) throws VisADException, RemoteException {
458 return new AddCombination(this, other);
459 }
460 public Combination __sub__(final Object other) throws VisADException, RemoteException {
461 return new SubtractCombination(this, other);
462 }
463 public Combination __mul__(final Object other) throws VisADException, RemoteException {
464 return new MultiplyCombination(this, other);
465 }
466 public Combination __div__(final Object other) throws VisADException, RemoteException {
467 return new DivideCombination(this, other);
468 }
469 public Combination __pow__(final Object other) throws VisADException, RemoteException {
470 return new ExponentCombination(this, other);
471 }
472 public Combination __mod__(final Object other) throws VisADException, RemoteException {
473 return new ModuloCombination(this, other);
474 }
475 public Combination __radd__(final Object other) throws VisADException, RemoteException {
476 return new AddCombination(other, this);
477 }
478 public Combination __rsub__(final Object other) throws VisADException, RemoteException {
479 return new SubtractCombination(other, this);
480 }
481 public Combination __rmul__(final Object other) throws VisADException, RemoteException {
482 return new MultiplyCombination(other, this);
483 }
484 public Combination __rdiv__(final Object other) throws VisADException, RemoteException {
485 return new DivideCombination(other, this);
486 }
487 public Combination __rpow__(final Object other) throws VisADException, RemoteException {
488 return new ExponentCombination(other, this);
489 }
490 public Combination __rmod__(final Object other) throws VisADException, RemoteException {
491 return new ModuloCombination(other, this);
492 }
493 public Combination __neg__() throws VisADException, RemoteException {
494 return new NegateCombination(this);
495 }
496 }
497
498 /**
499 *
500 */
501 public static class Selector extends JythonThing {
502
503 /** */
504 private final String ID;
505
506 /** */
507 private float waveNumber;
508
509 /** */
510 private ConstantMap[] color;
511
512 /** */
513 private Console console;
514
515 /** */
516 private HydraControl control;
517
518 /** */
519 private Data data;
520
521 /** */
522 private MultiSpectralDisplay display;
523
524 /**
525 *
526 *
527 * @param waveNumber
528 * @param color
529 * @param control
530 * @param console
531 */
532 public Selector(final float waveNumber, final ConstantMap[] color, final HydraControl control, final Console console) {
533 super();
534 this.ID = hashCode() + "_jython";
535 this.waveNumber = waveNumber;
536 this.control = control;
537 this.console = console;
538 this.display = control.getMultiSpectralDisplay();
539
540 this.color = new ConstantMap[color.length];
541 for (int i = 0; i < this.color.length; i++) {
542 ConstantMap mappedColor = (ConstantMap)color[i];
543 this.color[i] = (ConstantMap)mappedColor.clone();
544 }
545
546 if (control instanceof LinearCombo) {
547 LinearCombo lc = (LinearCombo)control;
548 try {
549 lc.addSelector(this);
550 } catch (Exception e) {
551 // TODO(jon): no way jose
552 System.err.println("Could not create selector: "+e.getMessage());
553 e.printStackTrace();
554 }
555 }
556 }
557
558 /**
559 * Attempts removal of a known name for the current Selector.
560 *
561 * @param name Name (within Jython namespace) to remove.
562 *
563 * @return {@code true} if removal was successful, {@code false}
564 * otherwise.
565 */
566 public boolean removeName(final String name) {
567 return jythonNames.remove(name);
568 }
569
570 /**
571 * Returns the known Jython names associated with this Selector.
572 *
573 * @param {@literal "Names"} (aka variables) in a Jython namespace that
574 * refer to this Selector. Collection may be empty, but never
575 * {@code null}.
576 */
577 public Collection<String> getNames() {
578 return new LinkedHashSet<String>(jythonNames);
579 }
580
581 /**
582 * Resets the known names of a Selector.
583 */
584 public void clearNames() {
585 jythonNames.clear();
586 }
587
588 /**
589 * Attempts to associate a Jython {@literal "variable"/"name"} with
590 * this Selector.
591 *
592 * @param name Name used within the Jython namespace. Cannot be
593 * {@code null}.
594 *
595 * @return {@code true} if {@code name} was successfully added,
596 * {@code false} otherwise.
597 */
598 public boolean addName(final String name) {
599 return jythonNames.add(name);
600 }
601
602 /**
603 * Returns a Jython name associated with this Selector. Consider using
604 * {@link #getNames()} instead.
605 *
606 * @return Either a blank {@code String} if there are no associated
607 * names, or the {@literal "first"} (iteration-order) name.
608 *
609 * @see #getNames()
610 */
611 public String getName() {
612 if (jythonNames.isEmpty()) {
613 return "";
614 } else {
615 return jythonNames.iterator().next();
616 }
617 }
618
619 /**
620 * Changes the {@literal "selected"} wave number to the given value.
621 *
622 * <p><b>WARNING:</b>no bounds-checking is currently being performed,
623 * but this is expected to change in the near future.</p>
624 *
625 * @param newWaveNumber New wave number to associate with the current
626 * Selector.
627 */
628 public void setWaveNumber(final float newWaveNumber) {
629 waveNumber = newWaveNumber;
630 try {
631 display.setSelectorValue(ID, waveNumber);
632 } catch (Exception e) {
633 LogUtil.logException("Selector.setWaveNumber", e);
634 }
635 }
636
637 /**
638 * Returns the {@literal "selected"} wave number associated with this
639 * Selector.
640 *
641 * @return Wave number currently selected by this Selector.
642 */
643 public float getWaveNumber() {
644 return waveNumber;
645 }
646
647 /**
648 *
649 *
650 * @return
651 */
652 public ConstantMap[] getColor() {
653 return color;
654 }
655
656 /**
657 *
658 *
659 * @return
660 */
661 public Data getData() {
662 return control.getMultiSpectralDisplay().getImageDataFrom(waveNumber);
663 }
664
665 /**
666 *
667 *
668 * @return
669 */
670 public String getId() {
671 return ID;
672 }
673
674 /**
675 *
676 * @return
677 */
678 @Override public String toString() {
679 int hashLen = 0;
680 int idLen = 0;
681 int waveLen = 0;
682 int colorLen = 0;
683 int namesLen = 0;
684 return String.format("[Selector@%x: id=%s, waveNumber=%f, color=%s, jythonNames=%s]",
685 hashCode(), ID, waveNumber, colorString(color), jythonNames);
686
687 }
688 }
689
690 public static abstract class Combination extends JythonThing {
691 private final Object left;
692 private final Object right;
693
694 private final String leftName;
695 private final String rightName;
696
697 private final Data leftData;
698 private final Data rightData;
699
700 private Data operationData;
701
702 public Combination(final Object lhs, final Object rhs) throws VisADException, RemoteException {
703 left = lhs;
704 right = rhs;
705
706 leftName = extractName(left);
707 rightName = extractName(right);
708
709 leftData = extractData(left);
710 rightData = extractData(right);
711 }
712
713 private static Data extractData(final Object obj) throws VisADException, RemoteException {
714 if (obj instanceof JythonThing) {
715 return ((JythonThing)obj).getData();
716 }
717 if (obj instanceof PyFloat) {
718 return new Real(((PyFloat)obj).getValue());
719 }
720 if (obj instanceof PyInteger) {
721 return new Real(((PyInteger)obj).getValue());
722 }
723 if (obj instanceof Double) {
724 return new Real((Double)obj);
725 }
726 if (obj instanceof Integer) {
727 return new Real((Integer)obj);
728 }
729 if (obj instanceof Data) {
730 return (Data)obj;
731 }
732 throw new IllegalArgumentException("Can't figure out what to do with " + obj);
733 }
734
735 protected static String extractName(final Object obj) {
736 if (obj instanceof JythonThing) {
737 return ((JythonThing)obj).getName();
738 }
739 if (obj instanceof PyFloat) {
740 return ((PyFloat)obj).toString();
741 }
742 if (obj instanceof PyInteger) {
743 return ((PyInteger)obj).toString();
744 }
745 if (obj instanceof Double) {
746 return ((Double)obj).toString();
747 }
748 if (obj instanceof Integer) {
749 return ((Integer)obj).toString();
750 }
751 throw new IllegalArgumentException("UGH: "+obj);
752 }
753
754 protected void setOperationData(final Data opData) {
755 operationData = opData;
756 }
757
758 protected Data getOperationData() {
759 return operationData;
760 }
761
762 //public Data
763
764 public Object getLeft() {
765 return left;
766 }
767
768 public Object getRight() {
769 return right;
770 }
771
772 public String getLeftName() {
773 return leftName;
774 }
775
776 public String getRightName() {
777 return rightName;
778 }
779
780 public Data getLeftData() {
781 return leftData;
782 }
783
784 public Data getRightData() {
785 return rightData;
786 }
787
788 public boolean removeName(final String name) {
789 return true;
790 }
791
792 public boolean addName(final String name) {
793 return true;
794 }
795
796 public String getName() {
797 return getFriendlyString();
798 }
799
800 public Data getData() {
801 return operationData;
802 }
803
804 public Collection<String> getNames() {
805 Set<String> set = new LinkedHashSet<String>(1);
806 set.add(getFriendlyString());
807 return set;
808 }
809
810 public abstract String getFriendlyString();
811 public abstract String getPersistableString();
812 public abstract String toString();
813 }
814
815 private static class AddCombination extends Combination {
816 public AddCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException {
817 super(lhs, rhs);
818 setOperationData(getLeftData().add(getRightData()));
819 }
820 public String getFriendlyString() {
821 return String.format("(%s + %s)", getLeftName(), getRightName());
822 }
823 public String getPersistableString() {
824 return getFriendlyString();
825 }
826 public String toString() {
827 return String.format("[AddCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]", hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString());
828 }
829 }
830 private static class SubtractCombination extends Combination {
831 public SubtractCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException {
832 super(lhs, rhs);
833 setOperationData(getLeftData().subtract(getRightData()));
834 }
835 public String getFriendlyString() {
836 return String.format("(%s - %s)", getLeftName(), getRightName());
837 }
838 public String getPersistableString() {
839 return getFriendlyString();
840 }
841 public String toString() {
842 return String.format("[SubtractCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]",
843 hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString());
844 }
845 }
846 private static class MultiplyCombination extends Combination {
847 public MultiplyCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException {
848 super(lhs, rhs);
849 setOperationData(getLeftData().multiply(getRightData()));
850 }
851 public String getFriendlyString() {
852 return String.format("(%s * %s)", getLeftName(), getRightName());
853 }
854 public String getPersistableString() {
855 return getFriendlyString();
856 }
857 public String toString() {
858 return String.format("[MultiplyCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]",
859 hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString());
860 }
861 }
862 private static class DivideCombination extends Combination {
863 public DivideCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException {
864 super(lhs, rhs);
865 setOperationData(getLeftData().divide(getRightData()));
866 }
867 public String getFriendlyString() {
868 return String.format("(%s / %s)", getLeftName(), getRightName());
869 }
870 public String getPersistableString() {
871 return getFriendlyString();
872 }
873 public String toString() {
874 return String.format("[DivideCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]",
875 hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString());
876 }
877 }
878 private static class ExponentCombination extends Combination {
879 public ExponentCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException {
880 super(lhs, rhs);
881 setOperationData(getLeftData().pow(getRightData()));
882 }
883 public String getFriendlyString() {
884 return String.format("(%s**%s)", getLeftName(), getRightName());
885 }
886 public String getPersistableString() {
887 return getFriendlyString();
888 }
889 public String toString() {
890 return String.format("[ExponentCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]",
891 hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString());
892 }
893 }
894 private static class ModuloCombination extends Combination {
895 public ModuloCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException {
896 super(lhs, rhs);
897 setOperationData(getLeftData().remainder(getRightData()));
898 }
899 public String getFriendlyString() {
900 return String.format("(%s %% %s)", getLeftName(), getRightName());
901 }
902 public String getPersistableString() {
903 return getFriendlyString();
904 }
905 public String toString() {
906 return String.format("[ModuloCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]",
907 hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString());
908 }
909 }
910 private static class NegateCombination extends Combination {
911 public NegateCombination(final Object lhs) throws VisADException, RemoteException {
912 super(lhs, null);
913 setOperationData(getLeftData().negate());
914 }
915 public String getFriendlyString() {
916 return String.format("(-%s)", getLeftName());
917 }
918 public String getPersistableString() {
919 return getFriendlyString();
920 }
921 public String toString() {
922 return String.format("[NegateCombo@%x: leftName=%s, friendlyString=%s, persistableString=%s]",
923 hashCode(), getLeftName(), getFriendlyString(), getPersistableString());
924 }
925 }
926 }