// // BasicSSCell.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad.ss; import javax.imageio.*; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*; import java.net.*; import java.rmi.*; import java.util.*; import javax.swing.*; import visad.*; import visad.bom.ImageRendererJ3D; import visad.collab.DisplayMonitor; import visad.data.*; import visad.formula.*; import visad.java2d.*; import visad.java3d.*; import visad.util.*; /** * BasicSSCell represents a single spreadsheet display cell. * BasicSSCells can be added to a VisAD user interface to provide some of the * capabilities presented in the VisAD SpreadSheet. Other capabilities, like * the file loader and data mapping dialog boxes, are available only with a * FancySSCell. */ public class BasicSSCell extends JPanel implements DisplayListener, MessageListener { /** * Debugging flag. */ public static boolean DEBUG = false; /** * Debugging level. * * <li>1 = Normal. * <li>2 = Collaboration messages. * <li>3 = All exceptions. */ public static int DEBUG_LEVEL = 2; // --- STATIC UTILITY METHODS --- /** * List of SSCells on this JVM. */ protected static final Vector SSCellVector = new Vector(); /** * The number of SSCells currently saving data. */ protected static int Saving = 0; /** * Whether Java3D is possible for this JVM. */ protected static boolean Possible3D; /** * Whether Java3D is enabled for this JVM. */ protected static boolean CanDo3D = enable3D(); /** * Gets the SSCell with the specified name. */ public static BasicSSCell getSSCellByName(String name) { synchronized (SSCellVector) { int len = SSCellVector.size(); for (int i=0; i<len; i++) { BasicSSCell cell = (BasicSSCell) SSCellVector.elementAt(i); if (name.equalsIgnoreCase(cell.Name)) return cell; } } return null; } /** * Returns true if any SSCell is currently saving data. */ public static boolean isSaving() { return Saving > 0; } // -- Detect, enable & disable Java3D -- /** * Returns true if Java3D is possible for this JVM. */ public static boolean possible3D() { return Possible3D; } /** * Returns true if Java3D is enabled for this JVM. */ public static boolean canDo3D() { return CanDo3D; } /** * Attempts to enable Java3D for this JVM, returning true if successful. */ public static boolean enable3D() { if (Possible3D) { // Java3D test has already succeeded CanDo3D = true; } else { // test for Java3D availability Possible3D = CanDo3D = Util.canDoJava3D(); if (DEBUG && !Possible3D) { if (DEBUG) System.err.println("Warning: Java3D library not found"); } } return CanDo3D; } /** * Disables Java3D for this JVM. */ public static void disable3D() { CanDo3D = false; } // --- CONSTRUCTORS --- /** * Default FormulaManager object used by SSCells. */ protected static final FormulaManager defaultFM = FormulaUtil.createStandardManager(); /** * Name of this cell. */ protected String Name; /** * Formula manager for this cell. */ protected FormulaManager fm; /** * Constructs a new BasicSSCell with the given name. */ public BasicSSCell(String name) throws VisADException, RemoteException { this(name, null, null, false, null); } /** * Constructs a new BasicSSCell with the given name and non-default * formula manager, to allow for custom formulas. */ public BasicSSCell(String name, FormulaManager fman) throws VisADException, RemoteException { this(name, fman, null, false, null); } /** * Constructs a new BasicSSCell with the given name, that gets its * information from the given RemoteServer. The associated SSCell on the * server end must have already invoked its addToRemoteServer method. */ public BasicSSCell(String name, RemoteServer rs) throws VisADException, RemoteException { this(name, null, rs, false, null); } /** * Constructs a new BasicSSCell with the given name and save string, * used to reconstruct this cell's configuration. */ public BasicSSCell(String name, String save) throws VisADException, RemoteException { this(name, null, null, false, save); } /** * Constructs a new BasicSSCell with the given name, formula manager, * and remote server. */ public BasicSSCell(String name, FormulaManager fman, RemoteServer rs, String save) throws VisADException, RemoteException { this(name, fman, rs, false, save); } /** * Constructs a new, possibly slaved, BasicSSCell with the given name, * formula manager, and remote server. */ public BasicSSCell(String name, FormulaManager fman, RemoteServer rs, boolean slave, String save) throws VisADException, RemoteException { // set name if (name == null) { throw new VisADException("BasicSSCell: name cannot be null"); } synchronized (SSCellVector) { int len = SSCellVector.size(); for (int i=0; i<len; i++) { BasicSSCell cell = (BasicSSCell) SSCellVector.elementAt(i); if (name.equalsIgnoreCase(cell.Name)) { throw new VisADException("BasicSSCell: name already used"); } } Name = name; SSCellVector.add(this); } // set formula manager fm = (fman == null ? defaultFM : fman); // set remote server if (rs != null) { RemoteVServer = rs; RemoteVDisplay = rs.getDisplay(Name); } IsRemote = (RemoteVDisplay != null); IsSlave = slave; // collaboration setup if (IsRemote) { // CLIENT: initialize setupClient(); } else { // SERVER: initialize setupServer(); } // set save string if (save != null) setSaveString(save); // finish GUI setup initDisplayPanel(); setPreferredSize(new Dimension(0, 0)); setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); setBackground(IsSlave ? Color.darkGray : Color.black); setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); } // --- COLLABORATION --- // Command Message ID Message Str Message Data // ------- ---------- ----------- ------------ // Add file ADD_DATA src str loaded data // Add URL ADD_DATA src str loaded data // Add RMI addr ADD_SOURCE src str Real(src type) // Add formula ADD_SOURCE src str Real(src type) // Add direct ADD_DATA src str("") direct data // Remove data REMOVE_DATA varName null // Set maps SET_MAPS map str null // Clear maps SET_MAPS null null // Request dim switch SET_DIM "" Real(newDim) // Switch dim SET_DIM null Real(newDim) // Set errors for cell SET_ERRORS null Tuple(Text[]) // Set errors for data SET_ERRORS varName Tuple(Text[]) // Update data UPDATE_DATA varName new data // Update dependencies UPDATE_DEPENDENCIES varName true or false // Request status STATUS "" null // Update status STATUS null status info // Relevant methods: sendMessage(), receiveMessage() // Most collaboration messages are handled by calling the appropriate // methods with the notify bit set to false, so that additional messages // are not sent in response, thus avoiding feedback loops. // However, there are four exceptions to this model: // 1) Clients send SET_MAPS commands but do not alter maps themselves. // Servers receive SET_MAPS commands and alter mappings, but do not // send SET_MAPS commands, thus avoiding feedback loops. // 2) Both servers and clients send SET_DIM commands when setDimension() // is called. However, only servers actually alter the dimension. // Clients switch dimensions using setDimClone() when a SET_DIM command // from the server is received. A server receiving a SET_DIM command // will switch the dimension using setDimension(), generating another // SET_DIM command to which the clients respond. Thus, whenever a client // changes the dimension, two SET_DIM commands actually get sent: the // first by that client (msg="") and the second by the server (msg=null). // 3) Only servers send UPDATE_DEPENDENCIES commands. Both servers and // clients receive them and set their dependencies to match the message. // The reason for this behavior is that only the server computes formulas, // and consequently only servers can compute whether a given data object // is dependent on other data objects. // 4) New clients call STATUS to request the current status of the cell. // Servers answer with a cell status report by calling STATUS with the // current status of the cell as the message data. // CTR: TODO: If a cell has a formula depending on another data object in // that same cell, and user wants to clear the whole cell, he'll get a // message to confirm saying "other data objects depend"... This weird // message should be avoided somehow, but it's not easy without extending // the current implementation of formulas. /** * Message ID indicating a data object has been added. */ public static final int ADD_DATA = 0; /** * Message ID indicating a source has been added. */ public static final int ADD_SOURCE = 1; /** * Message ID indicating a data object has been removed. */ public static final int REMOVE_DATA = 2; /** * Message ID indicating mappings have changed. */ public static final int SET_MAPS = 3; /** * Message ID indicating dimension has changed. */ public static final int SET_DIM = 4; /** * Message ID indicating errors have changed. */ public static final int SET_ERRORS = 5; /** * Message ID indicating a data object has changed. */ public static final int UPDATE_DATA = 6; /** * Message ID indicating a data object's dependencies have changed. */ public static final int UPDATE_DEPENDENCIES = 7; /** * Message ID indicating a cell's status information * is being requested or reported. */ public static final int STATUS = 8; /** * No message ID should have a value greater than or equal to this number. */ public static final int MAX_ID = 9; /** * Message ID strings, for debugging. */ public static final String[] messages = {"ADD_DATA", "ADD_SOURCE", "REMOVE_DATA", "SET_MAPS", "SET_DIM", "SET_ERRORS", "UPDATE_DATA", "UPDATE_DEPENDENCIES", "STATUS"}; /** * List of servers to which this cell has been added. */ protected Vector Servers = new Vector(); /** * Associated DisplayImpl for sending and receiving messages. */ protected DisplayImpl MDisplay = null; /** * Associated RemoteDisplay for sending and receiving messages. */ protected RemoteDisplay RemoteMDisplay = null; /** * Associated VisAD RemoteDisplay. */ protected RemoteDisplay RemoteVDisplay = null; /** * Associated VisAD RemoteSlaveDisplay, if any. */ protected RemoteSlaveDisplayImpl RemoteVSlave = null; /** * Associated VisAD RemoteServer, if any. */ protected RemoteServer RemoteVServer = null; /** * ID number for this collaborative cell. */ protected int CollabID = DisplayMonitor.UNKNOWN_LISTENER_ID; /** * Whether this display is remote. */ protected boolean IsRemote; /** * Whether this display is slaved. */ protected boolean IsSlave; /** * Whether this display is still a new client (hasn't been initialized). */ protected boolean NewClient; // -- Add & remove cells to remote servers -- /** * Adds this cell to the given RemoteServer. SSCell servers must call this * method for each cell before clients can clone the cells with the * BasicSSCell(String name, RemoteServer rs) constructor, and before the * cells can be exported as RMI addresses. */ public void addToRemoteServer(RemoteServerImpl rs) throws RemoteException { if (rs == null) return; if (IsRemote) { // CLIENT: illegal operation throw new RemoteException("Cannot add a cloned cell to a server"); } synchronized (Servers) { if (!Servers.contains(rs)) { rs.addDisplay((RemoteDisplayImpl) RemoteMDisplay); rs.addDisplay((RemoteDisplayImpl) RemoteVDisplay); synchronized (CellData) { int len = CellData.size(); for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); rs.addDataReference(cellData.getRemoteReference()); } } Servers.add(rs); } } } /** * Removes this cell from the given RemoteServer. */ public void removeFromRemoteServer(RemoteServerImpl rs) throws RemoteException { if (rs == null) return; if (IsRemote) { // CLIENT: illegal operation throw new RemoteException("Cannot remove a cloned cell from a server"); } synchronized (Servers) { if (Servers.contains(rs)) { rs.removeDisplay((RemoteDisplayImpl) RemoteMDisplay); rs.removeDisplay((RemoteDisplayImpl) RemoteVDisplay); synchronized (CellData) { int len = CellData.size(); for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); rs.removeDataReference(cellData.getRemoteReference()); } } Servers.remove(rs); } } } // -- Send & receive cell update messages -- /** * Sends a message of type <tt>id</tt> to server and all clients * of this cell. */ void sendMessage(int id, String msg, Data data) throws RemoteException { // convert data to remote data RemoteData d; if (data instanceof RemoteData) d = (RemoteData) data; else d = new RemoteDataImpl((DataImpl) data); MDisplay.sendMessage(new MessageEvent(MAX_ID * CollabID + id, msg, d)); if (DEBUG && DEBUG_LEVEL >= 2) { System.out.println(Name + "[" + CollabID + "]: sent " + messages[id] + ": msg=" + msg + ", data=" + (data == null ? "null" : data.getClass().getName())); } } /** * Handles VisAD messages. This method is the heart of BasicSSCell's * collaboration support. */ public void receiveMessage(MessageEvent msg) throws RemoteException { int id = msg.getId(); int mid = id % MAX_ID; int oid = id / MAX_ID; if (oid == CollabID && mid != UPDATE_DEPENDENCIES) { // cells ignore their own updates, except for UPDATE_DEPENDENCIES return; } String m = msg.getString(); RemoteData data = msg.getData(); if (DEBUG && DEBUG_LEVEL >= 2) { DataImpl ld = DataUtility.makeLocal(data, DEBUG); System.out.println(Name + "[" + CollabID + "]: received " + messages[mid] + " from " + oid + ": msg=" + m + ", data=" + (ld == null ? "null" : ld.getClass().getName())); } try { if (mid == ADD_DATA) { // add data from remote URL_SOURCE or DIRECT_SOURCE synchronized (CellData) { SSCellData cellData = addReferenceImpl(0, null, null, m, m.equals("") ? DIRECT_SOURCE : URL_SOURCE, false, false); cellData.setData(data, false); } } else if (mid == ADD_SOURCE) { // add data from remote RMI_SOURCE or FORMULA_SOURCE synchronized (CellData) { int type = (int) ((Real) DataUtility.makeLocal(data, DEBUG)).getValue(); addDataSource(0, m, type, false); } } else if (mid == REMOVE_DATA) { // remove data synchronized (CellData) { SSCellData cellData = getCellDataByName(m); removeDataImpl(cellData, false, true); } } else if (mid == SET_MAPS) { // set maps if (!IsRemote) { // SERVER: respond to client's SET_MAPS command if (m == null) clearMaps(); else setMaps(DataUtility.convertStringToMaps(m, getData(), true)); } } else if (mid == SET_DIM) { // set dimension int dim = (int) ((Real) DataUtility.makeLocal(data, DEBUG)).getValue(); if (m != null) { // SET_DIM command originates from client if (IsRemote) { // CLIENT: turn on wait dialog beginWait(true); } else { // SERVER: respond to client's SET_DIM command setDimension(dim); } } else if (IsRemote) { // CLIENT: respond to server's SET_DIM command endWait(false); setDimClone(); } } else if (mid == SET_ERRORS) { // set errors Tuple tuple = (Tuple) DataUtility.makeLocal(data, DEBUG); String[] errors = DataUtility.tupleToStrings(tuple, DEBUG); SSCellData cellData; synchronized (CellData) { cellData = getCellDataByName(m); } if (cellData != null) cellData.setErrors(errors, false); } else if (mid == UPDATE_DATA) { // update local data to match data from message SSCellData cellData; synchronized (CellData) { cellData = getCellDataByName(m); } cellData.cell.skipNextErrors(); cellData.setData(data, false); } else if (mid == UPDATE_DEPENDENCIES) { // update local data dependencies to match dependencies from message SSCellData cellData; synchronized (CellData) { cellData = getCellDataByName(m); } if (cellData != null) { cellData.setDependencies( (Real) DataUtility.makeLocal(data, DEBUG)); } } else if (mid == STATUS) { if (m == null) { // status report from server if (IsRemote && NewClient) { Tuple tuple = (Tuple) DataUtility.makeLocal(data, DEBUG); if (tuple != null) { synchronized (CellData) { // add Data objects to cell try { int len = tuple.getDimension(); for (int i=0; i<len; i++) { Tuple t = (Tuple) DataUtility.makeLocal(tuple.getComponent(i), DEBUG); Real rid = (Real) DataUtility.makeLocal(t.getComponent(0), DEBUG); Data d = t.getComponent(1); DataReferenceImpl ref = new DataReferenceImpl(Name); ref.setData(d); Text source = (Text) DataUtility.makeLocal(t.getComponent(2), DEBUG); Real type = (Real) DataUtility.makeLocal(t.getComponent(3), DEBUG); addReferenceImpl((int) rid.getValue(), ref, null, source.getValue(), (int) type.getValue(), false, false); } } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); } } } NewClient = false; } } else { // status request from new client if (!IsRemote) { // SERVER: send out status report synchronized (CellData) { try { int len = CellData.size(); Data[] d = new Data[len]; for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); Data[] status = new Data[4]; status[0] = new Real(cellData.getId()); status[1] = cellData.getData(); status[2] = new Text(cellData.getSource()); status[3] = new Real(cellData.getSourceType()); d[i] = new Tuple(status); } sendMessage(STATUS, null, len == 0 ? null : new Tuple(d)); } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); } } } } } else if (DEBUG) { warn("unknown message id (" + mid + ") received."); } } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); } } // -- Set up server & client -- /** * Sets up data needed for this cell to be a server. */ protected void setupServer() throws VisADException, RemoteException { MDisplay = new DisplayImplJ2D(Name + "_Messenger", null); RemoteMDisplay = new RemoteDisplayImpl(MDisplay); MDisplay.addMessageListener(this); CollabID = 0; setDimension(JAVA2D_2D); } /** * Sets up data needed for this cell to be a client. */ protected void setupClient() throws VisADException, RemoteException { RemoteMDisplay = RemoteVServer.getDisplay(Name + "_Messenger"); MDisplay = new DisplayImplJ2D(RemoteMDisplay, null); MDisplay.addMessageListener(this); try { CollabID = MDisplay.getConnectionID(RemoteMDisplay); } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); } setDimClone(); addDisplayListener(this); NewClient = true; sendMessage(STATUS, "", null); } // -- Manage dependencies -- /** * Broadcasts updated cell dependencies to all clients. */ static void updateDependencies() { // check every data object of each cell for changes in dependency status synchronized (SSCellVector) { int len = SSCellVector.size(); for (int i=0; i<len; i++) { BasicSSCell cell = (BasicSSCell) SSCellVector.elementAt(i); if (!cell.isRemote()) { synchronized (cell.CellData) { int len2 = cell.CellData.size(); for (int j=0; j<len2; j++) { SSCellData cellData = (SSCellData) cell.CellData.elementAt(j); if (!cellData.ssCell.isRemote()) { String varName = cellData.getVariableName(); boolean canBeRemoved = true; try { canBeRemoved = cell.fm.canBeRemoved(varName); } catch (FormulaException exc) { if (DEBUG) exc.printStackTrace(); } if (canBeRemoved == cellData.othersDepend) { try { // notify linked cells of dependency status change cellData.ssCell.sendMessage(UPDATE_DEPENDENCIES, varName, canBeRemoved ? SSCellImpl.FALSE : SSCellImpl.TRUE); } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); } } // if canBeRemoved == cellData.othersDepend } // if cellData.ssCell.isRemote } // for j } // synchronized cell.CellData } // if cell.isRemote } // for i } // synchronized SSCellVector } // --- DATA MANAGEMENT --- /** * Interval at which to check for status changes while waiting. */ protected static final int POLLING_INTERVAL = 100; /** * Indicates that the source of the data is unknown. */ public static final int UNKNOWN_SOURCE = -1; /** * Indicates that the data was added to this cell directly * using addData() or addReference(). */ public static final int DIRECT_SOURCE = 0; /** * Indicates that the data came from a file or URL. */ public static final int URL_SOURCE = 1; /** * Indicates that the data was computed from a formula. */ public static final int FORMULA_SOURCE = 2; /** * Indicates that the data came from an RMI server. */ public static final int RMI_SOURCE = 3; /** * Indicates that the data came from a remotely linked cell. */ public static final int REMOTE_SOURCE = 4; /** * The number of data objects this cell is currently loading. */ protected int Loading = 0; /** * List of this cell's data. */ protected Vector CellData = new Vector(); // -- Add data -- /** * Adds a Data object to this cell, creating * an associated DataReference for it. * * @return Variable name of the newly added data. */ public String addData(Data data) throws VisADException, RemoteException { return addData(0, data, null, "", DIRECT_SOURCE, true); } /** * Adds a Data object to this cell, creating an associated * DataReference with the specified ConstantMaps for it. * * @return Variable name of the newly added data. */ public String addData(Data data, ConstantMap[] cmaps) throws VisADException, RemoteException { return addData(0, data, cmaps, "", DIRECT_SOURCE, true); } /** * Adds a Data object to this cell from the given source of the * specified type, creating an associated DataReference for it. * * @return Variable name of the newly added data. */ protected String addData(int id, Data data, ConstantMap[] cmaps, String source, int type, boolean notify) throws VisADException, RemoteException { // add Data object to cell DataReferenceImpl ref = new DataReferenceImpl(Name); ref.setData(data); SSCellData cellData; synchronized (CellData) { cellData = addReferenceImpl(id, ref, cmaps, source, type, notify, true); } return cellData.getVariableName(); } /** * Adds the given DataReference to this cell. * * @return Variable name of the newly added reference. */ public String addReference(DataReferenceImpl ref) throws VisADException, RemoteException { SSCellData cellData; synchronized (CellData) { cellData = addReferenceImpl(0, ref, null, "", DIRECT_SOURCE, true, true); } return cellData.getVariableName(); } /** * Adds the given DataReference to this cell with the specified ConstantMaps. * * @return Variable name of the newly added reference. */ public String addReference(DataReferenceImpl ref, ConstantMap[] cmaps) throws VisADException, RemoteException { SSCellData cellData; synchronized (CellData) { cellData = addReferenceImpl(0, ref, cmaps, "", DIRECT_SOURCE, true, true); } return cellData.getVariableName(); } /** * Obtains a Data object from the given source of unknown type, * and adds it to this cell. * * @return Variable name of the newly added data. */ public String addDataSource(String source) throws VisADException, RemoteException { return addDataSource(0, source, UNKNOWN_SOURCE, true); } /** * Obtains a Data object from the given source of the specified type, * and adds it to this cell. * * @return Variable name of the newly added data. */ public String addDataSource(String source, int type) throws VisADException, RemoteException { return addDataSource(0, source, type, true); } /** * Obtains a Data object from the given source of the specified type, * and adds it to this cell, assigning it the specified id. * * @return Variable name of the newly added data. */ String addDataSource(int id, String source, int type, boolean notify) throws VisADException, RemoteException { String varName = null; if (type == UNKNOWN_SOURCE) { // determine source type if (source.startsWith("rmi://")) type = RMI_SOURCE; else if (source.startsWith("adde://")) type = URL_SOURCE; else { File f = new File(source); if (f.exists()) type = URL_SOURCE; else { URL url = null; try { url = new URL(source); } catch (MalformedURLException exc) { if (DEBUG && DEBUG_LEVEL >= 3) exc.printStackTrace(); } if (url != null) type = URL_SOURCE; else type = FORMULA_SOURCE; } } } if (type == DIRECT_SOURCE || type == REMOTE_SOURCE) { // direct and remote source types are invalid throw new VisADException("Invalid source type"); } if (type == URL_SOURCE) { // obtain data from filename or URL beginWait(true); Data data = null; boolean success = true; try { // load file or URL DefaultFamily loader = new DefaultFamily("loader", true); if (source.startsWith("file:")) { source = source.substring(5); // if source looks like it starts with a Windows drive spec... if (source.length() > 2 && source.charAt(2) == ':' && source.charAt(0) == '/') { source = source.substring(1); } } data = loader.open(source); // check if source is a local file File file = new File(source); if (file.exists()) { String path = file.getAbsolutePath(); char[] p = path.toCharArray(); for (int i=0; i<p.length; i++) if (p[i] == '\\') p[i] = '/'; path = new String(p); source = "file:" + (path.startsWith("/") ? "" : "/") + path; } } catch (OutOfMemoryError err) { if (DEBUG) err.printStackTrace(); success = false; throw new VisADException("Not enough memory to import the data."); } catch (BadFormException exc) { if (DEBUG) exc.printStackTrace(); success = false; throw new VisADException( "The source could not be converted to VisAD data."); } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); success = false; throw exc; } finally { endWait(!success); } if (data == null) { throw new VisADException("Could not load data from source " + source); } varName = addData(id, data, null, source, URL_SOURCE, notify); } else if (type == FORMULA_SOURCE) { synchronized (CellData) { SSCellData cellData = addReferenceImpl(id, null, null, source, FORMULA_SOURCE, false, false); varName = cellData.getVariableName(); if (!IsRemote) { // SERVER: link data to formula computation fm.assignFormula(varName, source); } } if (notify) { // notify linked cells of source addition sendMessage(ADD_SOURCE, source, new Real(type)); } } else if (type == RMI_SOURCE) { // obtain data from an RMI server // example of RMI address: rmi://www.ssec.wisc.edu/MyServer/A1 if (!source.startsWith("rmi://")) { throw new VisADException("RMI address must begin with rmi://"); } final DataReferenceImpl lref = new DataReferenceImpl(Name); SSCellData cellData; synchronized (CellData) { cellData = addReferenceImpl(id, lref, null, source, RMI_SOURCE, false, false); varName = cellData.getVariableName(); } if (!IsRemote) { // SERVER: obtain data from RMI address beginWait(true); boolean success = true; try { // attempt to obtain data from RMI server int len = source.length(); int end = source.lastIndexOf("/"); if (end < 6) end = len; String server = source.substring(4, end); String object = (end < len - 1) ? source.substring(end + 1) : ""; RemoteServer rs = null; rs = (RemoteServer) Naming.lookup(server); RemoteDataReference ref = rs.getDataReference(object); if (ref == null) throw new VisADException("The remote object " + "called \"" + object + "\" does not exist"); // set up cell to update local data when remote data changes final SSCellData fcd = cellData; final RemoteDataReference rref = ref; final BasicSSCell cell = this; CellImpl lcell = new CellImpl() { public void doAction() { try { lref.setData(DataUtility.makeLocal(rref.getData(), DEBUG)); } catch (NullPointerException exc) { if (DEBUG) exc.printStackTrace(); fcd.setError("Remote data is null"); } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); fcd.setError("Could not update remote data"); } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); fcd.setError("Unable to import updated remote data"); } } }; RemoteCellImpl rcell = new RemoteCellImpl(lcell); rcell.addReference(ref); } catch (ClassCastException exc) { if (DEBUG) exc.printStackTrace(); success = false; throw new VisADException("The name of the RMI server is not valid."); } catch (MalformedURLException exc) { if (DEBUG) exc.printStackTrace(); success = false; throw new VisADException("The name of the RMI server is not valid."); } catch (NotBoundException exc) { if (DEBUG) exc.printStackTrace(); success = false; throw new VisADException( "The remote data specified does not exist."); } catch (AccessException exc) { if (DEBUG) exc.printStackTrace(); success = false; throw new VisADException( "Could not gain access to the remote data."); } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); success = false; throw new VisADException("Could not connect to the RMI server."); } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); success = false; throw exc; } finally { endWait(!success); } } if (notify) { // notify linked cells of source addition sendMessage(ADD_SOURCE, source, new Real(type)); } } return varName; } /** * Does the work of adding the given DataReference, * from the given source of the specified type. * * @return The newly created SSCellData object. */ protected SSCellData addReferenceImpl(int id, DataReferenceImpl ref, ConstantMap[] cmaps, String source, int type, boolean notify, boolean checkErrors) throws VisADException, RemoteException { // ensure that id is valid if (id == 0) id = getFirstFreeId(); // ensure that ref is valid if (ref == null) ref = new DataReferenceImpl(Name); // notify linked cells of data addition (ADD_DATA message must come first) if (notify) sendMessage(ADD_DATA, source, ref.getData()); // add data reference to cell SSCellData cellData = new SSCellData(id, this, ref, cmaps, source, type, checkErrors); CellData.add(cellData); if (!IsRemote) { // SERVER: add data reference to display if (HasMappings) VDisplay.addReference(ref, cmaps); // add remote data reference to servers synchronized (Servers) { RemoteDataReferenceImpl remoteRef = (RemoteDataReferenceImpl) cellData.getRemoteReference(); int len = Servers.size(); for (int i=0; i<len; i++) { RemoteServerImpl rs = (RemoteServerImpl) Servers.elementAt(i); rs.addDataReference(remoteRef); } } } return cellData; } // -- Remove data -- /** * Removes the given Data object from this cell. */ public void removeData(Data data) throws VisADException, RemoteException { boolean found = false; synchronized (CellData) { int len = CellData.size(); for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); if (cellData.getData() == data) { removeDataImpl(cellData, true, true); found = true; break; } } } if (!found) { throw new VisADException("The given Data object does not exist"); } } /** * Removes the Data object corresponding to the * given variable name from this cell. */ public void removeData(String varName) throws VisADException, RemoteException { synchronized (CellData) { SSCellData cellData = getCellDataByName(varName); if (cellData == null) { throw new VisADException( "Data object called " + varName + " does not exist"); } removeDataImpl(cellData, true, true); } } /** * Removes the given DataReference's associated Data object from this cell. */ public void removeReference(DataReferenceImpl ref) throws VisADException, RemoteException { boolean found = false; synchronized (CellData) { int len = CellData.size(); for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); if (cellData.getReference() == ref) { removeDataImpl(cellData, true, true); found = true; break; } } } if (!found) { throw new VisADException("The given DataReference does not exist"); } } /** * Removes all Data objects from this cell. */ public void removeAllReferences() throws VisADException, RemoteException { removeAllReferences(true, true); } /** * Removes all Data objects from this cell, notifying listeners if the * notify flag is set. */ protected void removeAllReferences(boolean notify) throws VisADException, RemoteException { removeAllReferences(notify, true); } /** * Removes all Data objects from this cell, notifying listeners if the * notify flag is set, and updating the display if the display flag is set. */ protected void removeAllReferences(boolean notify, boolean display) throws VisADException, RemoteException { synchronized (CellData) { int len = CellData.size(); for (int i=0; i<len; i++) { removeDataImpl((SSCellData) CellData.firstElement(), notify, display); } } } /** * Does the work of removing the Data object at the specified index. */ protected void removeDataImpl(SSCellData cellData, boolean notify, boolean display) throws VisADException, RemoteException { String varName = cellData.getVariableName(); // notify linked cells of data removal if (notify) sendMessage(REMOVE_DATA, varName, null); if (!IsRemote) { // remove data reference from display if (HasMappings) VDisplay.removeReference(cellData.getReference()); // remove data reference from all servers synchronized (Servers) { RemoteDataReferenceImpl ref = (RemoteDataReferenceImpl) cellData.getRemoteReference(); int len = Servers.size(); for (int i=0; i<len; i++) { RemoteServerImpl rs = (RemoteServerImpl) Servers.elementAt(i); rs.removeDataReference(ref); } } } // purge cell data CellData.remove(cellData); cellData.destroy(); cellData = null; // clear cell if no data objects are left if (display) { if (hasData()) updateDisplay(); else clearDisplay(); } } // -- Wait for data to load -- /** * Blocks until the Data object with the given variable name * finishes loading. */ public void waitForData(String varName) throws VisADException { SSCellData cellData; synchronized (CellData) { cellData = getCellDataByName(varName); } // wait for formula computation to finish fm.waitForFormula(varName); // wait for cell data to initialize while (!cellData.isInited()) { try { Thread.sleep(POLLING_INTERVAL); } catch (InterruptedException exc) { if (DEBUG && DEBUG_LEVEL >= 3) exc.printStackTrace(); } } } /** * Blocks until all of this cell's Data objects finish loading. */ public void waitForData() throws VisADException { // compile list of data variable names String[] varNames; int len; synchronized (CellData) { len = CellData.size(); varNames = new String[len]; for (int i=0; i<len; i++) { // get data's variable name SSCellData cellData = (SSCellData) CellData.elementAt(i); varNames[i] = cellData.getVariableName(); } } // wait for file, URL and RMI loads to finish while (Loading > 0) { try { Thread.sleep(POLLING_INTERVAL); } catch (InterruptedException exc) { if (DEBUG && DEBUG_LEVEL >= 3) exc.printStackTrace(); } } // wait for each data object for (int i=0; i<len; i++) waitForData(varNames[i]); } // -- Save data -- /** * Exports a Data object to the given location, using the given Data form. */ public void saveData(String varName, String location, Form form) throws BadFormException, IOException, VisADException, RemoteException { if (IsSlave) { // SLAVE: saveData not supported throw new VisADException("Cannot saveData on a slaved cell"); } Data data = getData(varName); Saving++; try { form.save(location, data, true); } finally { Saving--; } } // -- Utility -- /** * Obtains the cell data entry corresponding to the given variable name. */ protected SSCellData getCellDataByName(String varName) { int len = CellData.size(); SSCellData cellData = null; for (int i=0; i<len; i++) { SSCellData cd = (SSCellData) CellData.elementAt(i); if (cd.getVariableName().equals(varName)) { cellData = cd; break; } } return cellData; } /** * Gets the first free cell data ID number. */ protected int getFirstFreeId() { synchronized (CellData) { if (CellData.size() == 0) return 1; SSCellData cellData = (SSCellData) CellData.lastElement(); return cellData.getId() + 1; } } // --- DISPLAY MANAGEMENT --- /** * Constant for 3-D (Java3D) dimensionality. */ public static final int JAVA3D_3D = 1; /** * Constant for 2-D (Java2D) dimensionality. */ public static final int JAVA2D_2D = 2; /** * Constant for 2-D (Java3D) dimensionality. */ public static final int JAVA3D_2D = 3; /** * Class name of the 3-D (Java3D) display renderer. */ private static final String j33 = "visad.java3d.DefaultDisplayRendererJ3D"; /** * Class name of the 2-D (Java2D) display renderer. */ private static final String j22 = "visad.java2d.DefaultDisplayRendererJ2D"; /** * Class name of the 2-D (Java3D) display renderer. */ private static final String j32 = "visad.java3d.TwoDDisplayRendererJ3D"; /** * Class name of the image 3-D (Java3D) display renderer. */ private static final String jir = "visad.bom.ImageRendererJ3D"; /** * Associated VisAD Display. */ protected DisplayImpl VDisplay; /** * The dimensionality of the display: JAVA3D_3D, JAVA2D_2D, or JAVA3D_2D. */ protected int Dim = -1; /** * Whether this cell has mappings from Data to Display. */ protected boolean HasMappings = false; // -- Build display -- /** * Reconstructs this cell's display. */ public synchronized boolean constructDisplay() { boolean success = true; DisplayImpl newDisplay = VDisplay; RemoteDisplay rmtDisplay = RemoteVDisplay; if (IsSlave) { // SLAVE: construct dummy 2-D display try { newDisplay = new DisplayImplJ2D("DUMMY"); } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); success = false; } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); success = false; } } else if (!CanDo3D && Dim != JAVA2D_2D) { // dimension requires Java3D, but Java3D is disabled for this JVM success = false; } else { // construct display of the proper dimension try { if (IsRemote) { // CLIENT: construct new display from server's remote copy if (Dim == JAVA3D_3D) newDisplay = new DisplayImplJ3D(rmtDisplay); else if (Dim == JAVA2D_2D) { newDisplay = new DisplayImplJ2D(rmtDisplay); } else { // Dim == JAVA3D_2D TwoDDisplayRendererJ3D tdr = new TwoDDisplayRendererJ3D(); newDisplay = new DisplayImplJ3D(rmtDisplay, tdr); } } else { // SERVER: construct new display and make a remote copy if (Dim == JAVA3D_3D) newDisplay = new DisplayImplJ3D(Name); else if (Dim == JAVA2D_2D) newDisplay = new DisplayImplJ2D(Name); else { // Dim == JAVA3D_2D TwoDDisplayRendererJ3D tdr = new TwoDDisplayRendererJ3D(); newDisplay = new DisplayImplJ3D(Name, tdr); } rmtDisplay = new RemoteDisplayImpl(newDisplay); } } catch (NoClassDefFoundError err) { if (DEBUG) err.printStackTrace(); success = false; } catch (UnsatisfiedLinkError err) { if (DEBUG) err.printStackTrace(); success = false; } catch (Exception exc) { if (DEBUG) exc.printStackTrace(); success = false; } } if (success) { if (VDisplay != null) { try { VDisplay.destroy(); } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); } } VDisplay = newDisplay; RemoteVDisplay = rmtDisplay; } return success; } // -- Handle display changes -- /** * Handles display changes. */ public void displayChanged(DisplayEvent e) { int id = e.getId(); if (id == DisplayEvent.TRANSFORM_DONE || (id == DisplayEvent.FRAME_DONE && IsSlave && !hasDisplay())) { if (!hasDisplay()) { initDisplayPanel(); updateDisplay(true); } // display has changed; notify listeners notifySSCellListeners(SSCellChangeEvent.DISPLAY_CHANGE); } else if (id == DisplayEvent.MAPS_CLEARED) updateDisplay(false); } // -- Set & clear mappings -- /** * Maps RealTypes to the display according to the specified ScalarMaps. */ public synchronized void setMaps(ScalarMap[] maps) throws VisADException, RemoteException { if (maps == null) return; VisADException vexc = null; RemoteException rexc = null; if (IsRemote) { // CLIENT: send new mappings to server sendMessage(SET_MAPS, DataUtility.convertMapsToString(maps), null); } else { // SERVER: set up mappings DataReference[] dr; ConstantMap[][] cmaps; synchronized (CellData) { int len = CellData.size(); dr = new DataReference[len]; cmaps = new ConstantMap[len][]; for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); dr[i] = cellData.getReference(); cmaps[i] = cellData.getConstantMaps(); } } String save = getPartialSaveString(); VDisplay.disableAction(); clearMaps(); for (int i=0; i<maps.length; i++) { if (maps[i] != null) { try { VDisplay.addMap(maps[i]); } catch (VisADException exc) { vexc = exc; } catch (RemoteException exc) { rexc = exc; } } } for (int i=0; i<dr.length; i++) { // determine if ImageRendererJ3D can be used boolean ok = false; Data data = dr[i].getData(); if (data == null) { if (DEBUG) warn("data #" + i + " is null; cannot analyze MathType"); } else if (Possible3D) { if (data instanceof FieldImpl) { visad.Set set = ((FieldImpl) data).getDomainSet(); if (set instanceof GriddedSet && set.getManifoldDimension() == 2) { MathType type = data.getType(); try { ok = ImageRendererJ3D.isRendererUsable(type, maps); } catch (VisADException exc) { if (DEBUG && DEBUG_LEVEL >= 3) exc.printStackTrace(); } } } } // add reference if (ok && Dim != JAVA2D_2D) { VDisplay.addReferences(new ImageRendererJ3D(), dr[i], cmaps[i]); } else { if (DEBUG) warn("data #" + i + " cannot use ImageRendererJ3D"); VDisplay.addReference(dr[i], cmaps[i]); } } setPartialSaveString(save, true); VDisplay.enableAction(); } HasMappings = true; if (vexc != null) throw vexc; if (rexc != null) throw rexc; } /** * Clears this cell's mappings. */ public void clearMaps() throws VisADException, RemoteException { if (IsRemote) clearMapsClone(true); else if (hasMappings()) { VDisplay.removeAllReferences(); VDisplay.clearMaps(); HasMappings = false; } } /** * Clears this cloned cell's mappings. */ private void clearMapsClone(boolean display) throws VisADException, RemoteException { if (hasMappings()) { RemoteVDisplay.removeAllReferences(); RemoteVDisplay.clearMaps(); if (display) { clearDisplay(); constructDisplay(); initDisplayPanel(); updateDisplay(true); } HasMappings = false; } } // -- Clear cell -- /** * Clears this cell's display. */ public void clearDisplay() throws VisADException, RemoteException { if (!DisplayEnabled) return; HasDisplay = false; Util.invoke(false, DEBUG, new Runnable() { public void run() { removeAll(); refresh(); } }); } /** * Clears this cell completely. */ public void clearCell() throws VisADException, RemoteException { removeAllReferences(); } /** * Clears this cell completely and destroys it, * removing it from the list of created cells. */ public void destroyCell() throws VisADException, RemoteException { RemoteException problem = null; setDisplayEnabled(false); // remove all data objects from this cell removeAllReferences(false, !IsRemote); if (!IsRemote) { // SERVER: stop serving this cell clearCell(); int slen = Servers.size(); for (int i=0; i<slen; i++) { RemoteServerImpl rs = (RemoteServerImpl) Servers.elementAt(i); try { removeFromRemoteServer(rs); } catch (RemoteException exc) { problem = exc; } } } else if (IsSlave && RemoteVSlave != null) { // SLAVE: disconnect cleanly try { RemoteVSlave.unlink(); } catch (RemoteException exc) { problem = exc; } } // remove cell from static list synchronized (SSCellVector) { SSCellVector.remove(this); } if (problem != null) throw problem; } // -- Set dimension -- /** * Sets this cell's dimensionality. */ public void setDimension(int dim) throws VisADException, RemoteException { if (dim == Dim) return; if (dim != JAVA3D_3D && dim != JAVA2D_2D && dim != JAVA3D_2D) { throw new VisADException("Invalid dimension"); } if (!IsRemote) { // SERVER: do dimension switch Dim = dim; synchronized (DListen) { // remove listeners temporarily detachListeners(); // save current mappings for restoration after dimension switch ScalarMap[] maps = null; if (VDisplay != null) { Vector mapVector = VDisplay.getMapVector(); int mvs = mapVector.size(); if (mvs > 0) { maps = new ScalarMap[mvs]; for (int i=0; i<mvs; i++) { maps[i] = (ScalarMap) mapVector.elementAt(i); } } } synchronized (Servers) { // remove old display from all RemoteServers int slen = Servers.size(); for (int i=0; i<slen; i++) { RemoteServerImpl rsi = (RemoteServerImpl) Servers.elementAt(i); rsi.removeDisplay((RemoteDisplayImpl) RemoteVDisplay); } // switch display dimension constructDisplay(); for (int i=0; i<slen; i++) { RemoteServerImpl rsi = (RemoteServerImpl) Servers.elementAt(i); rsi.addDisplay((RemoteDisplayImpl) RemoteVDisplay); } } // put mappings back if (maps != null) { try { setMaps(maps); } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); } } // reinitialize display initDisplayPanel(); updateDisplay(hasData()); // put listeners back attachListeners(); } // broadcast dimension change event notifySSCellListeners(SSCellChangeEvent.DIMENSION_CHANGE); } // notify linked cells of dimension change sendMessage(SET_DIM, IsRemote ? "" : null, new Real(dim)); } /** * Updates the dimension of this cloned cell to match that of the server. */ private void setDimClone() throws VisADException, RemoteException { synchronized (DListen) { // remove listeners temporarily detachListeners(); // remove old display panel from cell clearDisplay(); // get updated display from server RemoteVDisplay = RemoteVServer.getDisplay(Name); // update remote slave display if (IsSlave) { if (RemoteVSlave != null) RemoteVSlave.unlink(); RemoteVSlave = new RemoteSlaveDisplayImpl(RemoteVDisplay); } // autodetect new dimension String s = RemoteVDisplay.getDisplayRendererClassName(); if (s.equals(j33) || s.equals(jir)) Dim = JAVA3D_3D; else if (s.equals(j22)) Dim = JAVA2D_2D; else if (s.equals(j32)) Dim = JAVA3D_2D; // construct new display from server's display boolean success = constructDisplay(); if (!success) { // set up error message canvas JComponent errorCanvas; if (Dim == JAVA2D_2D) { errorCanvas = new JComponent() { public void paint(Graphics g) { g.setColor(Color.white); g.drawString("A serious error occurred while " + "constructing this display.", 8, 20); } }; } else { errorCanvas = new JComponent() { public void paint(Graphics g) { g.setColor(Color.white); g.drawString("This machine does not support Java3D.", 8, 20); g.drawString("Switch the dimension to 2-D (Java2D) to " + "view this display.", 8, 35); } }; } // set up dummy display VDisplay = new DisplayImplJ2D("DUMMY"); // redraw cell final JComponent ec = errorCanvas; Util.invoke(false, DEBUG, new Runnable() { public void run() { removeAll(); add(ec); refresh(); } }); } // reinitialize display initDisplayPanel(); if (success && hasData()) updateDisplay(true); // put all listeners back attachListeners(); } // broadcast dimension change event notifySSCellListeners(SSCellChangeEvent.DIMENSION_CHANGE); } // -- Capture display image -- /** * Captures an image and saves it to a given file name, in JPEG format. */ public void captureImage(File f) throws VisADException, IOException { BufferedImage image = IsSlave ? RemoteVSlave.getImage() : VDisplay.getImage(); try { Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpeg"); ImageWriter writer = iter.next(); ImageWriteParam param = writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(1.0f); FileOutputStream fout = new FileOutputStream(f); writer.setOutput(fout); IIOImage iio = new IIOImage(image, null, null); writer.write(null, iio, param); ImageIO.write(image, "image/jpeg", fout); fout.close(); } catch (NoClassDefFoundError err) { throw new VisADException("JPEG codec not found"); } } // --- SAVE STRINGS --- /** * Gets the save string necessary to reconstruct this cell. */ public String getSaveString() { StringBuffer sb = new StringBuffer(); // append data information synchronized (CellData) { int len = CellData.size(); sb.append("# "); sb.append(Name); sb.append(": data information\n"); for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); sb.append("id = "); sb.append(cellData.getId()); sb.append('\n'); sb.append("source = "); sb.append(cellData.getSource()); sb.append('\n'); sb.append("source type = "); sb.append(cellData.getSourceType()); sb.append('\n'); } } // append display information sb.append("\n# "); sb.append(Name); sb.append(": display information\n"); // add dimension to save string sb.append("dim = "); sb.append(Dim); sb.append('\n'); // add mapping and control information to save string sb.append(getPartialSaveString()); return sb.toString(); } /** * Reconstructs this cell using the specified save string. */ public void setSaveString(String save) throws VisADException, RemoteException { setPartialSaveString(save, false); } /** * Gets a string for reconstructing ScalarMap range * and Control information. */ public String getPartialSaveString() { StringBuffer sb = new StringBuffer(); Vector mapVector = null; if (hasMappings()) { mapVector = VDisplay.getMapVector(); int mvs = mapVector.size(); if (mvs > 0) { // add mappings to save string sb.append("maps ="); sb.append(DataUtility.convertMapsToString(mapVector)); sb.append('\n'); // add map ranges to save string sb.append("map ranges ="); for (int i=0; i<mvs; i++) { ScalarMap m = (ScalarMap) mapVector.elementAt(i); double[] range = new double[2]; boolean scale = m.getScale(new double[2], range, new double[2]); if (scale) { sb.append(' '); sb.append(range[0]); sb.append(' '); sb.append(range[1]); } } sb.append('\n'); } } if (hasDisplay()) { // add projection control state to save string ProjectionControl pc = VDisplay.getProjectionControl(); if (pc != null) { sb.append("projection = "); sb.append(pc.getSaveString()); } // add graphics mode control settings to save string GraphicsModeControl gmc = VDisplay.getGraphicsModeControl(); if (gmc != null) { sb.append("graphics mode = "); sb.append(gmc.getSaveString()); sb.append('\n'); } // add color control state(s) to save string Vector cv = VDisplay.getControls(ColorControl.class); if (cv != null) { int cvlen = cv.size(); for (int i=0; i<cvlen; i++) { ColorControl cc = (ColorControl) cv.elementAt(i); if (cc != null) { sb.append("color = "); sb.append(cc.getSaveString()); } } } // add contour control state(s) to save string cv = VDisplay.getControls(ContourControl.class); if (cv != null) { int cvlen = cv.size(); for (int i=0; i<cvlen; i++) { ContourControl cc = (ContourControl) cv.elementAt(i); if (cc != null) { sb.append("contour = "); sb.append(cc.getSaveString()); sb.append('\n'); } } } // add range control state(s) to save string cv = VDisplay.getControls(RangeControl.class); if (cv != null) { int cvlen = cv.size(); for (int i=0; i<cvlen; i++) { RangeControl rc = (RangeControl) cv.elementAt(i); if (rc != null) { sb.append("range = "); sb.append(rc.getSaveString()); sb.append('\n'); } } } // add animation control state(s) to save string cv = VDisplay.getControls(AnimationControl.class); if (cv != null) { int cvlen = cv.size(); for (int i=0; i<cvlen; i++) { AnimationControl ac = (AnimationControl) cv.elementAt(i); if (ac != null) { sb.append("anim = "); sb.append(ac.getSaveString()); sb.append('\n'); } } } // add value control state(s) to save string cv = VDisplay.getControls(ValueControl.class); if (cv != null) { int cvlen = cv.size(); for (int i=0; i<cvlen; i++) { ValueControl vc = (ValueControl) cv.elementAt(i); if (vc != null) { sb.append("value = "); sb.append(vc.getSaveString()); sb.append('\n'); } } } } return sb.toString(); } /** * Reconstructs parts of this cell using the specified save string. */ public void setPartialSaveString(String save, boolean preserveMaps) throws VisADException, RemoteException { // make sure cell is not remote if (IsRemote) { throw new VisADException("Cannot setSaveString on a remote cell"); } // data variables Vector ids = new Vector(); Vector sources = new Vector(); Vector types = new Vector(); // display variables int dim = -1; String mapString = null; ScalarMap[] maps = null; Vector mapMins = null; Vector mapMaxs = null; String proj = null; String mode = null; Vector color = new Vector(); Vector contour = new Vector(); Vector range = new Vector(); Vector anim = new Vector(); Vector selectVal = new Vector(); // parse the save string into "keyword = value" tokens SaveStringTokenizer sst = new SaveStringTokenizer(save); for (int i=0; i<sst.keywords.length; i++) { String keyword = sst.keywords[i]; String value = sst.values[i]; // id if (keyword.equalsIgnoreCase("id") || keyword.equalsIgnoreCase("data id") || keyword.equalsIgnoreCase("data_id") || keyword.equalsIgnoreCase("dataid")) { try { ids.add(new Integer(value)); } catch (NumberFormatException exc) { // invalid id value if (DEBUG) exc.printStackTrace(); warn("data id value " + value + " is not valid and will be ignored"); } } // source else if (keyword.equalsIgnoreCase("source") || keyword.equalsIgnoreCase("data source") || keyword.equalsIgnoreCase("data_source") || keyword.equalsIgnoreCase("datasource")) { sources.add(value); } // source type else if (keyword.equalsIgnoreCase("source type") || keyword.equalsIgnoreCase("source_type") || keyword.equalsIgnoreCase("sourcetype") || keyword.equalsIgnoreCase("data source type") || keyword.equalsIgnoreCase("data_source_type") || keyword.equalsIgnoreCase("datasourcetype")) { try { types.add(new Integer(value)); } catch (NumberFormatException exc) { // invalid source type value if (DEBUG) exc.printStackTrace(); warn("source type value " + value + " is not valid and will be ignored"); } } // filename (old keyword) else if (keyword.equalsIgnoreCase("filename") || keyword.equalsIgnoreCase("file name") || keyword.equalsIgnoreCase("file_name") || keyword.equalsIgnoreCase("file")) { ids.add(new Integer(0)); sources.add(value); types.add(new Integer(URL_SOURCE)); } // rmi address (old keyword) else if (keyword.equalsIgnoreCase("rmi") || keyword.equalsIgnoreCase("rmi address") || keyword.equalsIgnoreCase("rmi_address") || keyword.equalsIgnoreCase("rmiaddress")) { ids.add(new Integer(0)); sources.add(value); types.add(new Integer(RMI_SOURCE)); } // formula (old keyword) else if (keyword.equalsIgnoreCase("formula") || keyword.equalsIgnoreCase("equation")) { ids.add(new Integer(0)); sources.add(value); types.add(new Integer(FORMULA_SOURCE)); } // dimension else if (keyword.equalsIgnoreCase("dim") || keyword.equalsIgnoreCase("dimension")) { int d = -1; try { d = Integer.parseInt(value); } catch (NumberFormatException exc) { if (DEBUG) exc.printStackTrace(); } if (d > 0 && d < 4) dim = d; else { // invalid dimension value warn("dimension value " + value + " is not valid and will be ignored"); } } // mappings else if (keyword.equalsIgnoreCase("maps") || keyword.equalsIgnoreCase("mappings")) { mapString = value; } // mapping ranges else if (keyword.equalsIgnoreCase("map ranges") || keyword.equalsIgnoreCase("map_ranges") || keyword.equalsIgnoreCase("mapranges")) { StringTokenizer st = new StringTokenizer(value); mapMins = new Vector(); mapMaxs = new Vector(); while (true) { if (!st.hasMoreTokens()) break; String s1 = st.nextToken(); if (!st.hasMoreTokens()) { warn("trailing map range min value " + s1 + " has no corresponding max value and will be ignored"); break; } String s2 = st.nextToken(); Double d1 = null, d2 = null; try { d1 = new Double(s1.equals("NaN") ? Double.NaN : Double.parseDouble(s1)); d2 = new Double(s2.equals("NaN") ? Double.NaN : Double.parseDouble(s2)); } catch (NumberFormatException exc) { if (DEBUG) exc.printStackTrace(); } if (d1 == null || d2 == null) { warn("map range min/max pair (" + s1 + ", " + s2 + ") is not valid and will be ignored"); } else { mapMins.add(d1); mapMaxs.add(d2); } } } // projection matrix else if (keyword.equalsIgnoreCase("projection") || keyword.equalsIgnoreCase("proj")) { proj = value; } // graphics mode settings else if (keyword.equalsIgnoreCase("graphics mode") || keyword.equalsIgnoreCase("graphics_mode") || keyword.equalsIgnoreCase("graphicsmode") || keyword.equalsIgnoreCase("graphics") || keyword.equalsIgnoreCase("mode")) { mode = value; } // color table else if (keyword.equalsIgnoreCase("color") || keyword.equalsIgnoreCase("color table") || keyword.equalsIgnoreCase("color_table") || keyword.equalsIgnoreCase("colortable")) { color.add(value); } // contour data else if (keyword.equalsIgnoreCase("contour") || keyword.equalsIgnoreCase("contours") || keyword.equalsIgnoreCase("iso contour") || keyword.equalsIgnoreCase("iso_contour") || keyword.equalsIgnoreCase("isocontour") || keyword.equalsIgnoreCase("iso contours") || keyword.equalsIgnoreCase("iso_contours") || keyword.equalsIgnoreCase("isocontours")) { contour.add(value); } // range else if (keyword.equalsIgnoreCase("range") || keyword.equalsIgnoreCase("select range") || keyword.equalsIgnoreCase("select_range") || keyword.equalsIgnoreCase("selectrange")) { range.add(value); } // animation else if (keyword.equalsIgnoreCase("anim") || keyword.equalsIgnoreCase("animation")) { anim.add(value); } // select value else if (keyword.equalsIgnoreCase("value") || keyword.equalsIgnoreCase("select value") || keyword.equalsIgnoreCase("select_value") || keyword.equalsIgnoreCase("selectvalue")) { selectVal.add(value); } // unknown keyword else { warn("keyword " + keyword + " is unknown and will be ignored"); } } if (preserveMaps) { // detect which maps are the same and set appropriate ranges maps = DataUtility.convertStringToMaps(mapString, getData(), true); if (maps != null) { int lmin = mapMins == null ? -1 : mapMins.size(); int lmax = mapMaxs == null ? -1 : mapMaxs.size(); int cmin = 0, cmax = 0; Vector mapVector = VDisplay.getMapVector(); for (int j=0; j<maps.length; j++) { if (maps[j] != null) { // detect whether map needs a range boolean scale = maps[j].getScale( new double[2], new double[2], new double[2]); if (scale && cmin < lmin && cmax < lmax) { // find map in current display vector int mapIndex = mapVector.indexOf(maps[j]); if (mapIndex >= 0) { // set map's minimum and maximum range values ScalarMap map = (ScalarMap) mapVector.elementAt(mapIndex); map.setRange( ((Double) mapMins.elementAt(cmin++)).doubleValue(), ((Double) mapMaxs.elementAt(cmax++)).doubleValue()); } else { // skip current minimum and maximum range values cmin++; cmax++; } } } } } } else { // clear old stuff from cell clearCell(); // set up dimension setDimension(dim); // set up data objects int ilen = ids.size(); int slen = sources.size(); int tlen = types.size(); if (ilen != slen || ilen != tlen) { warn("some data object entries are corrupt and will be ignored"); } int len = ilen < slen && ilen < tlen ? ilen : (slen < tlen ? slen : tlen); setDisplayEnabled(false); for (int i=0; i<len; i++) { int id = ((Integer) ids.elementAt(i)).intValue(); String source = (String) sources.elementAt(i); int type = ((Integer) types.elementAt(i)).intValue(); addDataSource(id, source, type, true); } waitForData(); // set up map ranges; then set maps maps = DataUtility.convertStringToMaps(mapString, getData(), true); if (maps != null) { int lmin = mapMins == null ? -1 : mapMins.size(); int lmax = mapMaxs == null ? -1 : mapMaxs.size(); int cmin = 0, cmax = 0; for (int j=0; j<maps.length; j++) { if (maps[j] != null) { // set map's minimum and maximum range value, if applicable ScalarMap sm = maps[j]; boolean scale = sm.getScale( new double[2], new double[2], new double[2]); if (scale && cmin < lmin && cmax < lmax) { sm.setRange(((Double) mapMins.elementAt(cmin++)).doubleValue(), ((Double) mapMaxs.elementAt(cmax++)).doubleValue()); } } } setMaps(maps); } setDisplayEnabled(true); } // set up projection control if (proj != null) { ProjectionControl pc = VDisplay.getProjectionControl(); if (pc != null) pc.setSaveString(proj); else if (!preserveMaps) warn("display has no ProjectionControl; " + "the provided projection matrix will be ignored"); } // set up graphics mode control if (mode != null) { GraphicsModeControl gmc = VDisplay.getGraphicsModeControl(); if (gmc != null) { try { gmc.setSaveString(mode); } catch (VisADException exc) { if (DEBUG && DEBUG_LEVEL >= 3) exc.printStackTrace(); } } else if (!preserveMaps) warn("display has no GraphicsModeControl; " + "the provided graphics mode settings will be ignored"); } // set up color control(s) int len = color.size(); if (len > 0) { for (int i=0; i<len; i++) { String s = (String) color.elementAt(i); ColorControl cc = (ColorControl) VDisplay.getControl(ColorControl.class, i); if (cc != null) cc.setSaveString(s); else if (!preserveMaps) warn("display has no ColorControl #" + (i + 1) + "; " + "the provided color table will be ignored"); } } // set up contour control(s) len = contour.size(); if (len > 0) { for (int i=0; i<len; i++) { String s = (String) contour.elementAt(i); ContourControl cc = (ContourControl) VDisplay.getControl(ContourControl.class, i); if (cc != null) cc.setSaveString(s); else if (!preserveMaps) warn("display has no ContourControl #" + (i + 1) + "; " + "the provided contour settings will be ignored"); } } // set up range control(s) len = range.size(); if (len > 0) { for (int i=0; i<len; i++) { String s = (String) range.elementAt(i); RangeControl rc = (RangeControl) VDisplay.getControl(RangeControl.class, i); if (rc != null) rc.setSaveString(s); else if (!preserveMaps) warn("display has no RangeControl #" + (i + 1) + "; " + "the provided range will be ignored"); } } // set up animation control(s) len = anim.size(); if (len > 0) { for (int i=0; i<len; i++) { String s = (String) anim.elementAt(i); AnimationControl ac = (AnimationControl) VDisplay.getControl(AnimationControl.class, i); if (ac != null) { // Note: There is a race condition that prevents the AnimationControl // from correctly setting the current step and step delays. // The AnimationControl gets its parameters reset back to default // values when its ScalarMap's range is set above. // The one-second delay here should solve the problem in most cases. try { Thread.sleep(1000); } catch (InterruptedException exc) { if (DEBUG && DEBUG_LEVEL >= 3) exc.printStackTrace(); } ac.setSaveString(s); } else if (!preserveMaps) warn("display has no AnimationControl #" + (i + 1) + "; " + "the provided animation settings will be ignored"); } } // set up value control(s) len = selectVal.size(); if (len > 0) { for (int i=0; i<len; i++) { String s = (String) selectVal.elementAt(i); ValueControl vc = (ValueControl) VDisplay.getControl(ValueControl.class, i); if (vc != null) vc.setSaveString(s); else if (!preserveMaps) warn("display has no ValueControl #" + (i + 1) + "; " + "the provided value will be ignored"); } } } // --- UTILITY --- /** * Adds a variable to this cell's formula manager. */ public void addVar(String name, ThingReference tr) throws VisADException { fm.createVar(name, tr); } /** * Prints a warning message. */ private void warn(String s) { System.err.println(Name + ": Warning: " + s); } // --- GUI MANAGEMENT --- /** * Prevents simultaneous GUI manipulation. */ protected Object Lock = new Object(); /** * Associated VisAD Display component. */ protected Component VDPanel; /** * Global errors currently being displayed in this cell, if any. */ protected String[] Errors; /** * Whether a valid VisAD display currently exists. */ protected boolean HasDisplay = false; /** * Whether display updates are enabled. */ protected boolean DisplayEnabled = true; /** * A panel that displays the words "Please wait." */ private JPanel WaitPanel = null; // -- GUI refresh -- /** * Refreshes this cell's display. */ void refresh() { validate(); repaint(); } // -- Set errors -- /** * Displays global errors in this cell, notifying * linked cells if notify flag is set. */ protected void setErrors(String[] errors, boolean notify) { if (Util.arraysEqual(Errors, errors)) return; Errors = errors; updateDisplay(); if (notify) { try { sendMessage(SET_ERRORS, null, stringsToTuple(errors)); } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); } } } // -- Init & toggle display panel -- /** * Initializes this cell's display panel. */ private void initDisplayPanel() { if (IsSlave) VDPanel = RemoteVSlave.getComponent(); else VDPanel = VDisplay.getComponent(); } /** * Display the data for this cell, or all relevant errors if there are any. */ void updateDisplay(boolean hasDisplay) { HasDisplay = hasDisplay; updateDisplay(); } /** * Display the data for this cell if hasDisplay flag is set. */ void updateDisplay() { if (!DisplayEnabled) return; if (WaitPanel == null) { // initialize "Please wait" panel WaitPanel = new JPanel(); WaitPanel.setBackground(Color.black); WaitPanel.setLayout(new BoxLayout(WaitPanel, BoxLayout.X_AXIS)); WaitPanel.add(Box.createHorizontalGlue()); WaitPanel.add(new JLabel("Please wait...")); WaitPanel.add(Box.createHorizontalGlue()); } // compile list of errors final Vector e = new Vector(); if (Errors == null) { // no global errors; compile list of data-related errors synchronized (CellData) { int len = CellData.size(); for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); String varName = cellData.getVariableName(); String[] errors = cellData.getErrors(); if (errors != null) { for (int j=0; j<errors.length; j++) { e.add(varName + ": " + errors[j]); } } } } } else { // global errors exist; they take precedence for (int i=0; i<Errors.length; i++) e.add(Errors[i]); } // set up error canvas final int len = e.size(); JComponent errorCanvas; if (len == 0) errorCanvas = null; else { errorCanvas = new JComponent() { public void paint(Graphics g) { g.setColor(Color.white); String s = (len == 1 ? "An error" : "Errors") + " occurred while computing this cell:"; g.drawString(s, 8, 20); for (int i=0; i<len; i++) { s = (String) e.elementAt(i); g.drawString(s, 8, 15*i + 50); } } }; } final JComponent ec = errorCanvas; Util.invoke(true, DEBUG, new Runnable() { public void run() { // determine whether VDPanel is already present onscreen Component[] c = getComponents(); boolean hasPanel = c.length > 0 && c[0] == VDPanel; // redraw cell if (Loading > 0) { removeAll(); add(WaitPanel); } else if (ec != null) { removeAll(); add(ec); } else if (HasDisplay) { if (!hasPanel) { // no need to re-add VDPanel if already present removeAll(); add(VDPanel); } } else removeAll(); refresh(); } }); } /** * Enables or disables display updates. */ private void setDisplayEnabled(boolean value) { if (value == DisplayEnabled) return; DisplayEnabled = value; if (DisplayEnabled) updateDisplay(); } // -- Toggle waiting mode -- /** * Increments the loading counter. */ private void beginWait(boolean update) { Loading++; if (update) updateDisplay(); } /** * Decrements the loading counter. */ private void endWait(boolean update) { Loading--; if (update) updateDisplay(); } // --- EVENT HANDLING --- /** * List of SSCellListeners. */ protected Vector SListen = new Vector(); /** * List of DisplayListeners. */ protected Vector DListen = new Vector(); // -- Add & remove DisplayListeners -- /** * Adds a DisplayListener. */ public void addDisplayListener(DisplayListener d) { synchronized (DListen) { if (!DListen.contains(d)) { if (IsSlave) RemoteVSlave.addDisplayListener(d); else VDisplay.addDisplayListener(d); DListen.add(d); } } } /** * Removes a DisplayListener from this cell. */ public void removeDisplayListener(DisplayListener d) { synchronized (DListen) { if (DListen.contains(d)) { if (IsSlave) RemoteVSlave.removeDisplayListener(d); else VDisplay.removeDisplayListener(d); DListen.remove(d); } } } /** * Re-attaches all display listeners after they have been detached. */ private void attachListeners() { int len = DListen.size(); if (IsSlave) { for (int i=0; i<len; i++) { DisplayListener l = (DisplayListener) DListen.elementAt(i); RemoteVSlave.addDisplayListener(l); } } else { for (int i=0; i<len; i++) { DisplayListener l = (DisplayListener) DListen.elementAt(i); VDisplay.addDisplayListener(l); } } } /** * Temporarily detaches all display listeners. */ private void detachListeners() { int len = DListen.size(); if (IsSlave) { for (int i=0; i<len; i++) { DisplayListener l = (DisplayListener) DListen.elementAt(i); RemoteVSlave.removeDisplayListener(l); } } else { for (int i=0; i<len; i++) { DisplayListener l = (DisplayListener) DListen.elementAt(i); VDisplay.removeDisplayListener(l); } } } // -- Add, remove & notify SSCellListeners -- /** * Adds an SSCellListener. */ public void addSSCellListener(SSCellListener l) { synchronized (SListen) { if (!SListen.contains(l)) SListen.add(l); } } /** * Removes an SSCellListener. */ public void removeSSCellListener(SSCellListener l) { synchronized (SListen) { SListen.remove(l); } } /** * Removes all SSCellListeners. */ public void removeAllSSCellListeners() { synchronized (SListen) { SListen.removeAllElements(); } } /** * Informs all SSCellListeners of cell change. */ void notifySSCellListeners(int changeType) { notifySSCellListeners(changeType, null); } /** * Informs all SSCellListeners of a cell change. */ void notifySSCellListeners(int changeType, String varName) { SSCellChangeEvent e = new SSCellChangeEvent(this, changeType, varName); int len; SSCellListener[] l; synchronized (SListen) { len = SListen.size(); l = new SSCellListener[len]; for (int i=0; i<len; i++) l[i] = (SSCellListener) SListen.elementAt(i); } for (int i=0; i<len; i++) l[i].ssCellChanged(e); } // --- ACCESSORS --- /** * Gets this cell's name. */ public String getName() { return Name; } /** * Gets whether this cell is a cloned display cell. */ public boolean isRemote() { return IsRemote; } /** * Gets whether this cell is a slaved display cell. */ public boolean isSlave() { return IsSlave; } /** * Gets the id number this cell uses for remote collaboration. */ public int getRemoteId() { return CollabID; } /** * Gets this cell's formula manager. */ public FormulaManager getFormulaManager() { return fm; } /** * Gets this cell's VisAD Display. */ public DisplayImpl getDisplay() { return VDisplay; } /** * Gets this cell's VisAD RemoteDisplay. */ public RemoteDisplay getRemoteDisplay() { return RemoteVDisplay; } /** * Gets this cell's mappings. */ public ScalarMap[] getMaps() { Vector mapVector = null; if (IsSlave) { // SLAVE: get mappings from remote display try { mapVector = RemoteVDisplay.getMapVector(); } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); } } else if (VDisplay != null) { // get mappings from local display mapVector = VDisplay.getMapVector(); } int len = (mapVector == null ? 0 : mapVector.size()); ScalarMap[] maps = (len > 0 ? new ScalarMap[len] : null); for (int i=0; i<len; i++) maps[i] = (ScalarMap) mapVector.elementAt(i); return maps; } /** * Gets this cell's dimension. * @return Dimension type. Valid types are: * <UL> * <LI>BasicSSCell.JAVA3D_3D * <LI>BasicSSCell.JAVA2D_2D * <LI>BasicSSCell.JAVA3D_2D * </UL> */ public int getDimension() { return Dim; } /** * Gets this cell's Data object with the specified variable name. */ public Data getData(String varName) { SSCellData cellData; synchronized (CellData) { cellData = getCellDataByName(varName); } return cellData == null ? null : cellData.getData(); } /** * Gets this cell's Data objects. */ public Data[] getData() { synchronized (CellData) { int len = CellData.size(); Data[] data = new Data[len]; for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); data[i] = cellData.getData(); } return data; } } /** * Gets this cell's DataReference with the specified variable name. */ public DataReference getReference(String varName) { SSCellData cellData; synchronized (CellData) { cellData = getCellDataByName(varName); } return cellData == null ? null : cellData.getReference(); } /** * Gets this cell's DataReferences. */ public DataReferenceImpl[] getReferences() { synchronized (CellData) { int len = CellData.size(); DataReferenceImpl[] refs = new DataReferenceImpl[len]; for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); refs[i] = cellData.getReference(); } return refs; } } /** * Gets this cell's remote DataReference for data * with the specified variable name. */ public RemoteDataReference getRemoteReference(String varName) { SSCellData cellData; synchronized (CellData) { cellData = getCellDataByName(varName); } return cellData == null ? null : cellData.getRemoteReference(); } /** * Gets this cell's remote DataReferences. */ public RemoteDataReference[] getRemoteReferences() { synchronized (CellData) { int len = CellData.size(); RemoteDataReference[] remoteRefs = new RemoteDataReference[len]; for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); remoteRefs[i] = cellData.getRemoteReference(); } return remoteRefs; } } /** * Gets this cell's data source type for data * with the specified variable name. */ public int getDataSourceType(String varName) { SSCellData cellData; synchronized (CellData) { cellData = getCellDataByName(varName); } return cellData == null ? UNKNOWN_SOURCE : cellData.getSourceType(); } /** * Gets this cell's data source types. */ public int[] getDataSourceTypes() { synchronized (CellData) { int len = CellData.size(); int[] types = new int[len]; for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); types[i] = cellData.getSourceType(); } return types; } } /** * Gets this cell's data source string for data * with the specified variable name. */ public String getDataSource(String varName) { SSCellData cellData; synchronized (CellData) { cellData = getCellDataByName(varName); } return cellData == null ? null : cellData.getSource(); } /** * Gets this cell's data source strings. */ public String[] getDataSources() { synchronized (CellData) { int len = CellData.size(); String[] sources = new String[len]; for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); sources[i] = cellData.getSource(); } return sources; } } /** * Gets the variable name of this cell's first Data object. */ public String getFirstVariableName() { String varName = null; synchronized (CellData) { if (CellData.size() > 0) { SSCellData cellData = (SSCellData) CellData.firstElement(); varName = cellData.getVariableName(); } } return varName; } /** * Gets the variable name of this cell's last Data object. */ public String getLastVariableName() { String varName = null; synchronized (CellData) { if (CellData.size() > 0) { SSCellData cellData = (SSCellData) CellData.lastElement(); varName = cellData.getVariableName(); } } return varName; } /** * Gets the variable names of this cell's Data objects. */ public String[] getVariableNames() { synchronized (CellData) { int len = CellData.size(); String[] varNames = new String[len]; for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); varNames[i] = cellData.getVariableName(); } return varNames; } } /** * Gets the number of Data object this cell has. */ public int getDataCount() { return CellData.size(); } /** * Whether this cell has any data. */ public boolean hasData() { return CellData.size() > 0; } /** * Whether this cell has a valid display on-screen. */ public boolean hasDisplay() { return HasDisplay; } /** * Whether this cell has any mappings. */ public boolean hasMappings() { if (IsRemote) { // CLIENT: check mappings from remote display Vector v = null; try { v = RemoteVDisplay.getMapVector(); } catch (VisADException exc) { if (DEBUG) exc.printStackTrace(); } catch (RemoteException exc) { if (DEBUG) exc.printStackTrace(); } return v != null && !v.isEmpty(); } else return HasMappings; } /** * Whether other cells are dependent on this cell's Data object * with the specified variable name. */ public boolean othersDepend(String varName) { SSCellData cellData; synchronized (CellData) { cellData = getCellDataByName(varName); } return cellData.othersDepend(); } /** * Whether other cells are dependent on any of this cell's Data objects. */ public boolean othersDepend() { synchronized (CellData) { int len = CellData.size(); for (int i=0; i<len; i++) { SSCellData cellData = (SSCellData) CellData.elementAt(i); if (cellData.othersDepend()) return true; } return false; } } // --- DEPRECATED METHODS --- /** * @deprecated Use addVar(String, ThingReference) instead. */ public static void createVar(String name, ThingReference tr) throws VisADException { defaultFM.createVar(name, tr); } /** * @deprecated Use addSSCellListener(SSCellListener) instead. */ public void addSSCellChangeListener(SSCellListener l) { addSSCellListener(l); } /** * @deprecated Use removeSSCellListener(SSCellListener) instead. */ public void removeListener(SSCellListener l) { removeSSCellListener(l); } /** * @deprecated Use removeAllSSCellListeners() instead. */ public void removeAllListeners() { removeAllSSCellListeners(); } /** * @deprecated Use setSaveString(String) instead. */ public void setSSCellString(String save) throws VisADException, RemoteException { setSaveString(save); } /** * @deprecated Use getSaveString() instead. */ public String getSSCellString() { return getSaveString(); } /** * @deprecated Use saveData(String, Form) instead. */ public void saveData(File f, boolean netcdf) throws BadFormException, IOException, VisADException, RemoteException { Form form; if (netcdf) form = new visad.data.netcdf.Plain(); else form = new visad.data.visad.VisADForm(); saveData(getFirstVariableName(), f.getPath(), form); } /** * @deprecated Use saveData(String, Form) instead. */ public void saveData(File f, Form form) throws BadFormException, IOException, VisADException, RemoteException { saveData(getFirstVariableName(), f.getPath(), form); } /** * @deprecated Use addData(Data) instead. */ public void setData(Data data) throws VisADException, RemoteException { removeAllReferences(); addData(data); } /** * @deprecated Use addDataSource(String, FORMULA_SOURCE) instead. */ public void setFormula(String f) throws VisADException, RemoteException { removeAllReferences(); addDataSource(f, FORMULA_SOURCE); } /** * @deprecated Use waitForData(String) instead. */ public void waitForFormula() throws VisADException, RemoteException { waitForData(); } /** * @deprecated Use getReference(String) instead. */ public DataReferenceImpl getDataRef() { return (getDataCount() > 0 ? (DataReferenceImpl) getReference(getFirstVariableName()) : null); } /** * @deprecated Use getRemoteReference(String) instead. */ public RemoteDataReferenceImpl getRemoteDataRef() { return (getDataCount() > 0 ? (RemoteDataReferenceImpl) getRemoteReference(getFirstVariableName()) : null); } /** * @deprecated Use getDataSource(String) instead. */ public URL getFileURL() { URL url = null; String varName = getFirstVariableName(); if (getDataCount() > 0 && getDataSourceType(varName) == URL_SOURCE) { try { url = new URL(getDataSource(varName)); } catch (MalformedURLException exc) { if (DEBUG && DEBUG_LEVEL >= 3) exc.printStackTrace(); } } return url; } /** * @deprecated Use getDataSource(String) instead. */ public String getFilename() { String filename = ""; String varName = getFirstVariableName(); if (getDataCount() > 0 && getDataSourceType(varName) == URL_SOURCE) { filename = getDataSource(varName); } return filename; } /** * @deprecated Use getDataSource(String) instead. */ public String getRMIAddress() { String rmi = null; String varName = getFirstVariableName(); if (getDataCount() > 0 && getDataSourceType(varName) == RMI_SOURCE) { rmi = getDataSource(varName); } return rmi; } /** * @deprecated Use getDataSource(String) instead. */ public String getFormula() { String formula = ""; String varName = getFirstVariableName(); if (getDataCount() > 0 && getDataSourceType(varName) == FORMULA_SOURCE) { formula = getDataSource(varName); } return formula; } /** * @deprecated Use addDataSource(String, URL_SOURCE) instead. */ public void loadData(URL u) throws VisADException, RemoteException { addDataSource(u.toString(), URL_SOURCE); } /** * @deprecated Use addDataSource(String, URL_SOURCE) instead. */ public void loadData(String s) throws VisADException, RemoteException { addDataSource(s, URL_SOURCE); } /** * @deprecated Use addDataSource(String, RMI_SOURCE) instead. */ public void loadRMI(String s) throws VisADException, RemoteException { addDataSource(s, RMI_SOURCE); } /** * @deprecated Use setDimension(int) instead. */ public void setDimension(boolean twoD, boolean java2d) throws VisADException, RemoteException { int dim; if (!twoD && java2d) return; if (!twoD && !java2d) dim = JAVA3D_3D; else if (twoD && java2d) dim = JAVA2D_2D; else dim = JAVA3D_2D; // twoD && !java2d setDimension(dim); } /** * @deprecated Use getDataSourceType(String) instead. */ public boolean hasFormula() { return (getDataCount() > 0 && getDataSourceType(getFirstVariableName()) == FORMULA_SOURCE); } /** * @deprecated Use getReference(String) instead. */ public DataReference getReference() { return (getDataCount() > 0 ? getReference(getFirstVariableName()) : null); } /** * @deprecated Use visad.DataUtility.stringsToTuple(String[]) instead. */ public static Tuple stringsToTuple(String[] s) { return DataUtility.stringsToTuple(s, DEBUG); } /** * @deprecated Use visad.DataUtility.tupleToStrings(Tuple) instead. */ public static String[] tupleToStrings(Tuple t) { return DataUtility.tupleToStrings(t, DEBUG); } /** * @deprecated Use visad.DataUtility.makeLocal(data) instead. */ public static DataImpl makeLocal(Data data) { return DataUtility.makeLocal(data, DEBUG && DEBUG_LEVEL >= 3); } /** * @deprecated Use visad.Util.arraysEqual(Object[], Object[]) instead. */ public static boolean arraysEqual(Object[] o1, Object[] o2) { return Util.arraysEqual(o1, o2); } /** * @deprecated Use visad.Util.invoke(boolean, Runnable) instead. */ public static void invoke(boolean wait, Runnable r) { Util.invoke(wait, DEBUG, r); } }