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