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 }