001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 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 Map<String, double[]> 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 * Selectors are objects that allow users to select a given wavenumber/band 498 * by simply dragging within the GUI. 499 */ 500 public static class Selector extends JythonThing { 501 502 /** */ 503 private final String ID; 504 505 /** */ 506 private float waveNumber; 507 508 /** */ 509 private ConstantMap[] color; 510 511 /** */ 512 private Console console; 513 514 /** */ 515 private HydraControl control; 516 517 /** */ 518 private Data data; 519 520 /** */ 521 private MultiSpectralDisplay display; 522 523 /** 524 * Create a new Selector. 525 * 526 * @param waveNumber Initial {@literal "wave number (or band)"} of the Selector. 527 * @param color RGB triple that will be the color of the Selector. 528 * @param control Control that created the Selector. 529 * @param console Console that created the Selector. 530 */ 531 public Selector(final float waveNumber, final ConstantMap[] color, final HydraControl control, final Console console) { 532 super(); 533 this.ID = hashCode() + "_jython"; 534 this.waveNumber = waveNumber; 535 this.control = control; 536 this.console = console; 537 this.display = control.getMultiSpectralDisplay(); 538 539 this.color = new ConstantMap[color.length]; 540 for (int i = 0; i < this.color.length; i++) { 541 ConstantMap mappedColor = color[i]; 542 this.color[i] = (ConstantMap)mappedColor.clone(); 543 } 544 545 if (control instanceof LinearCombo) { 546 LinearCombo lc = (LinearCombo)control; 547 try { 548 lc.addSelector(this); 549 } catch (Exception e) { 550 logger.error("Could not create selector", e); 551 } 552 } 553 } 554 555 /** 556 * Attempts removal of a known name for the current Selector. 557 * 558 * @param name Name (within Jython namespace) to remove. 559 * 560 * @return {@code true} if removal was successful, {@code false} 561 * otherwise. 562 */ 563 public boolean removeName(final String name) { 564 return jythonNames.remove(name); 565 } 566 567 /** 568 * Returns the known Jython names associated with this Selector. 569 * 570 * @return {@literal "Names"} (aka variables) in a Jython namespace that 571 * refer to this Selector. Collection may be empty, but never 572 * {@code null}. 573 */ 574 public Collection<String> getNames() { 575 return new LinkedHashSet<String>(jythonNames); 576 } 577 578 /** 579 * Resets the known names of a Selector. 580 */ 581 public void clearNames() { 582 jythonNames.clear(); 583 } 584 585 /** 586 * Attempts to associate a Jython {@literal "variable"/"name"} with 587 * this Selector. 588 * 589 * @param name Name used within the Jython namespace. Cannot be 590 * {@code null}. 591 * 592 * @return {@code true} if {@code name} was successfully added, 593 * {@code false} otherwise. 594 */ 595 public boolean addName(final String name) { 596 return jythonNames.add(name); 597 } 598 599 /** 600 * Returns a Jython name associated with this Selector. Consider using 601 * {@link #getNames()} instead. 602 * 603 * @return Either a blank {@code String} if there are no associated 604 * names, or the {@literal "first"} (iteration-order) name. 605 * 606 * @see #getNames() 607 */ 608 public String getName() { 609 if (jythonNames.isEmpty()) { 610 return ""; 611 } else { 612 return jythonNames.iterator().next(); 613 } 614 } 615 616 /** 617 * Changes the {@literal "selected"} wave number to the given value. 618 * 619 * <p><b>WARNING:</b>no bounds-checking is currently being performed, 620 * but this is expected to change in the near future.</p> 621 * 622 * @param newWaveNumber New wave number to associate with the current 623 * Selector. 624 */ 625 public void setWaveNumber(final float newWaveNumber) { 626 waveNumber = newWaveNumber; 627 try { 628 display.setSelectorValue(ID, waveNumber); 629 } catch (Exception e) { 630 LogUtil.logException("Selector.setWaveNumber", e); 631 } 632 } 633 634 /** 635 * Returns the {@literal "selected"} wave number associated with this 636 * {@code Selector}. 637 * 638 * @return Wave number currently selected by this {@code Selector}. 639 */ 640 public float getWaveNumber() { 641 return waveNumber; 642 } 643 644 /** 645 * Returns the color associated with this {@code Selector}. 646 * 647 * @return {@literal "Color"} for this {@code Selector}. 648 */ 649 public ConstantMap[] getColor() { 650 return color; 651 } 652 653 /** 654 * Returns the data selected by the location of this {@code Selector}. 655 * 656 * @return Data selected by this {@code Selector}. 657 */ 658 public Data getData() { 659 return control.getMultiSpectralDisplay().getImageDataFrom(waveNumber); 660 } 661 662 /** 663 * Returns an identifier for this {@code Selector}. 664 * 665 * @return ID for this {@code Selector}. 666 */ 667 public String getId() { 668 return ID; 669 } 670 671 /** 672 * Returns a {@code String} representation of the relevant information 673 * {@literal "stored"} by this {@code Selector}. 674 * 675 * @return {@code String} representation of this {@code Selector}. 676 */ 677 @Override public String toString() { 678 int hashLen = 0; 679 int idLen = 0; 680 int waveLen = 0; 681 int colorLen = 0; 682 int namesLen = 0; 683 return String.format("[Selector@%x: id=%s, waveNumber=%f, color=%s, jythonNames=%s]", 684 hashCode(), ID, waveNumber, colorString(color), jythonNames); 685 686 } 687 } 688 689 public static abstract class Combination extends JythonThing { 690 private final Object left; 691 private final Object right; 692 693 private final String leftName; 694 private final String rightName; 695 696 private final Data leftData; 697 private final Data rightData; 698 699 private Data operationData; 700 701 public Combination(final Object lhs, final Object rhs) throws VisADException, RemoteException { 702 left = lhs; 703 right = rhs; 704 705 leftName = extractName(left); 706 rightName = extractName(right); 707 708 leftData = extractData(left); 709 rightData = extractData(right); 710 } 711 712 private static Data extractData(final Object obj) throws VisADException, RemoteException { 713 if (obj instanceof JythonThing) { 714 return ((JythonThing)obj).getData(); 715 } 716 if (obj instanceof PyFloat) { 717 return new Real(((PyFloat)obj).getValue()); 718 } 719 if (obj instanceof PyInteger) { 720 return new Real(((PyInteger)obj).getValue()); 721 } 722 if (obj instanceof Double) { 723 return new Real((Double)obj); 724 } 725 if (obj instanceof Integer) { 726 return new Real((Integer)obj); 727 } 728 if (obj instanceof Data) { 729 return (Data)obj; 730 } 731 throw new IllegalArgumentException("Can't figure out what to do with " + obj); 732 } 733 734 protected static String extractName(final Object obj) { 735 if (obj instanceof JythonThing) { 736 return ((JythonThing)obj).getName(); 737 } 738 if (obj instanceof PyFloat) { 739 return ((PyFloat)obj).toString(); 740 } 741 if (obj instanceof PyInteger) { 742 return ((PyInteger)obj).toString(); 743 } 744 if (obj instanceof Double) { 745 return ((Double)obj).toString(); 746 } 747 if (obj instanceof Integer) { 748 return ((Integer)obj).toString(); 749 } 750 throw new IllegalArgumentException("UGH: "+obj); 751 } 752 753 protected void setOperationData(final Data opData) { 754 operationData = opData; 755 } 756 757 protected Data getOperationData() { 758 return operationData; 759 } 760 761 //public Data 762 763 public Object getLeft() { 764 return left; 765 } 766 767 public Object getRight() { 768 return right; 769 } 770 771 public String getLeftName() { 772 return leftName; 773 } 774 775 public String getRightName() { 776 return rightName; 777 } 778 779 public Data getLeftData() { 780 return leftData; 781 } 782 783 public Data getRightData() { 784 return rightData; 785 } 786 787 public boolean removeName(final String name) { 788 return true; 789 } 790 791 public boolean addName(final String name) { 792 return true; 793 } 794 795 public String getName() { 796 return getFriendlyString(); 797 } 798 799 public Data getData() { 800 return operationData; 801 } 802 803 public Collection<String> getNames() { 804 Set<String> set = new LinkedHashSet<String>(1); 805 set.add(getFriendlyString()); 806 return set; 807 } 808 809 public abstract String getFriendlyString(); 810 public abstract String getPersistableString(); 811 public abstract String toString(); 812 } 813 814 private static class AddCombination extends Combination { 815 public AddCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException { 816 super(lhs, rhs); 817 setOperationData(getLeftData().add(getRightData())); 818 } 819 public String getFriendlyString() { 820 return String.format("(%s + %s)", getLeftName(), getRightName()); 821 } 822 public String getPersistableString() { 823 return getFriendlyString(); 824 } 825 public String toString() { 826 return String.format("[AddCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]", hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString()); 827 } 828 } 829 private static class SubtractCombination extends Combination { 830 public SubtractCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException { 831 super(lhs, rhs); 832 setOperationData(getLeftData().subtract(getRightData())); 833 } 834 public String getFriendlyString() { 835 return String.format("(%s - %s)", getLeftName(), getRightName()); 836 } 837 public String getPersistableString() { 838 return getFriendlyString(); 839 } 840 public String toString() { 841 return String.format("[SubtractCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]", 842 hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString()); 843 } 844 } 845 private static class MultiplyCombination extends Combination { 846 public MultiplyCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException { 847 super(lhs, rhs); 848 setOperationData(getLeftData().multiply(getRightData())); 849 } 850 public String getFriendlyString() { 851 return String.format("(%s * %s)", getLeftName(), getRightName()); 852 } 853 public String getPersistableString() { 854 return getFriendlyString(); 855 } 856 public String toString() { 857 return String.format("[MultiplyCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]", 858 hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString()); 859 } 860 } 861 private static class DivideCombination extends Combination { 862 public DivideCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException { 863 super(lhs, rhs); 864 setOperationData(getLeftData().divide(getRightData())); 865 } 866 public String getFriendlyString() { 867 return String.format("(%s / %s)", getLeftName(), getRightName()); 868 } 869 public String getPersistableString() { 870 return getFriendlyString(); 871 } 872 public String toString() { 873 return String.format("[DivideCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]", 874 hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString()); 875 } 876 } 877 private static class ExponentCombination extends Combination { 878 public ExponentCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException { 879 super(lhs, rhs); 880 setOperationData(getLeftData().pow(getRightData())); 881 } 882 public String getFriendlyString() { 883 return String.format("(%s**%s)", getLeftName(), getRightName()); 884 } 885 public String getPersistableString() { 886 return getFriendlyString(); 887 } 888 public String toString() { 889 return String.format("[ExponentCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]", 890 hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString()); 891 } 892 } 893 private static class ModuloCombination extends Combination { 894 public ModuloCombination(final Object lhs, final Object rhs) throws VisADException, RemoteException { 895 super(lhs, rhs); 896 setOperationData(getLeftData().remainder(getRightData())); 897 } 898 public String getFriendlyString() { 899 return String.format("(%s %% %s)", getLeftName(), getRightName()); 900 } 901 public String getPersistableString() { 902 return getFriendlyString(); 903 } 904 public String toString() { 905 return String.format("[ModuloCombo@%x: leftName=%s, rightName=%s, friendlyString=%s, persistableString=%s]", 906 hashCode(), getLeftName(), getRightName(), getFriendlyString(), getPersistableString()); 907 } 908 } 909 private static class NegateCombination extends Combination { 910 public NegateCombination(final Object lhs) throws VisADException, RemoteException { 911 super(lhs, null); 912 setOperationData(getLeftData().negate()); 913 } 914 public String getFriendlyString() { 915 return String.format("(-%s)", getLeftName()); 916 } 917 public String getPersistableString() { 918 return getFriendlyString(); 919 } 920 public String toString() { 921 return String.format("[NegateCombo@%x: leftName=%s, friendlyString=%s, persistableString=%s]", 922 hashCode(), getLeftName(), getFriendlyString(), getPersistableString()); 923 } 924 } 925}