001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2015 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 029package edu.wisc.ssec.mcidasv.control; 030 031import java.awt.Color; 032import java.awt.Container; 033import java.awt.Dimension; 034import java.rmi.RemoteException; 035import java.util.ArrayList; 036import java.util.Collection; 037import java.util.HashMap; 038import java.util.HashSet; 039import java.util.Hashtable; 040import java.util.Iterator; 041import java.util.LinkedHashSet; 042import java.util.List; 043import java.util.Map; 044import java.util.Map.Entry; 045import java.util.Set; 046 047import javax.swing.JComponent; 048import javax.swing.JPanel; 049import javax.swing.JTabbedPane; 050 051import org.python.core.PyDictionary; 052import org.python.core.PyFloat; 053import org.python.core.PyInteger; 054import org.slf4j.Logger; 055import org.slf4j.LoggerFactory; 056 057import visad.ConstantMap; 058import visad.Data; 059import visad.Real; 060import visad.VisADException; 061import visad.georef.MapProjection; 062 063import ucar.unidata.data.DataChoice; 064import ucar.unidata.data.DataSource; 065import ucar.unidata.data.DirectDataChoice; 066import ucar.unidata.idv.MapViewManager; 067import ucar.unidata.util.GuiUtils; 068import ucar.unidata.util.LogUtil; 069import ucar.unidata.view.geoloc.MapProjectionDisplay; 070import ucar.visad.display.DisplayMaster; 071 072import edu.wisc.ssec.mcidasv.Constants; 073import edu.wisc.ssec.mcidasv.McIDASV; 074import edu.wisc.ssec.mcidasv.data.ComboDataChoice; 075import edu.wisc.ssec.mcidasv.data.hydra.MultiDimensionSubset; 076import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData; 077import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralDataSource; 078import edu.wisc.ssec.mcidasv.display.hydra.MultiSpectralDisplay; 079import edu.wisc.ssec.mcidasv.display.hydra.MultiSpectralDisplay.DragLine; 080import edu.wisc.ssec.mcidasv.jython.Console; 081import edu.wisc.ssec.mcidasv.jython.ConsoleCallback; 082 083public 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 * Return a mapping of names to their {@link edu.wisc.ssec.mcidasv.control.LinearCombo.Selector Selectors}. 301 * 302 * @param objMap {@code Map} of objects. 303 * 304 * @return Map of name to {@code Selector}. 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 * @return {@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 * {@code Selector}. 638 * 639 * @return Wave number currently selected by this {@code Selector}. 640 */ 641 public float getWaveNumber() { 642 return waveNumber; 643 } 644 645 /** 646 * Returns the color associated with this {@code Selector}. 647 * 648 * @return {@literal "Color"} for this {@code Selector}. 649 */ 650 public ConstantMap[] getColor() { 651 return color; 652 } 653 654 /** 655 * Returns the data selected by the location of this {@code Selector}. 656 * 657 * @return Data selected by this {@code Selector}. 658 */ 659 public Data getData() { 660 return control.getMultiSpectralDisplay().getImageDataFrom(waveNumber); 661 } 662 663 /** 664 * Returns an identifier for this {@code Selector}. 665 * 666 * @return ID for this {@code Selector}. 667 */ 668 public String getId() { 669 return ID; 670 } 671 672 /** 673 * Returns a {@code String} representation of the relevant information 674 * {@literal "stored"} by this {@code Selector}. 675 * 676 * @return {@code String} representation of this {@code Selector}. 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}