/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ package org.opensourcephysics.tools; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.TreeSet; import org.opensourcephysics.controls.XMLControlElement; import org.opensourcephysics.display.Data; import org.opensourcephysics.display.Dataset; import org.opensourcephysics.display.DatasetManager; /** * This tool sends data to any tool that requests it. * * @author Douglas Brown * @version 1.0 */ public class DataRefreshTool implements Tool { private static Map<Data, DataRefreshTool> tools = new HashMap<Data, DataRefreshTool>(); private Data data; // data source protected HashSet<Data> moreData = new HashSet<Data>(); private HashMap<Integer, Dataset> ids = new HashMap<Integer, Dataset>(); /** * Returns a DataRefreshTool for the specified data object. * * @param data the data * @return the tool */ public static DataRefreshTool getTool(Data data) { DataRefreshTool tool = tools.get(data); if(tool==null) { tool = new DataRefreshTool(data); tools.put(data, tool); } return tool; } /** * Constructs a DataRefreshTool for the specified data object. * * @param data the data */ private DataRefreshTool(Data data) { this.data = data; } /** * Sends a job to this tool and specifies a tool to reply to. * The job xml defines the Data object requesting a refresh ("requestData"). * The requestData is compared with this tool's Data ("localData") as follows: * 1. If the requestData ID matches the localData ID, then the localData * is sent back to the requester. * 2. If not, then the requestData ID is compared with the IDs of all Data objects * in the list returned by DataTool.getSelfContainedData(localData). If a match is found, * the matching Data is sent back to the requester. * 3. If not, then the requestData ID is compared with the IDs of Datasets * in the list returned by DataTool.getDatasets(localData). If a match is found, * the matching Dataset is sent back to the requester. * 4. If not, then every Dataset ID in the list returned by * DataTool.getDatasets(requestData) is compared with the IDs of Datasets * in the list returned by DataTool.getDatasets(localData). * All matching Datasets that are found are sent back to the requester. * * @param job the Job * @param replyTo the tool requesting refreshed data * @throws RemoteException */ public void send(Job job, Tool replyTo) throws RemoteException { XMLControlElement control = new XMLControlElement(job.getXML()); if(control.failedToRead()||(replyTo==null)||!Data.class.isAssignableFrom(control.getObjectClass())) { return; } Data request = (Data) control.loadObject(null, true, true); // check for matching ID with localData if(request.getID()==data.getID()) { control = new XMLControlElement(data); job.setXML(control.toXML()); replyTo.send(job, this); return; } // check for matching ID with DataTool.getSelfContainedData(localData) for(Data next : DataTool.getSelfContainedData(data)) { if(request.getID()==next.getID()) { control = new XMLControlElement(next); job.setXML(control.toXML()); replyTo.send(job, this); return; } } // check for matching ID with local datasets ArrayList<Dataset> localDatasets = DataTool.getDatasets(data); for(Dataset next : localDatasets) { if(request.getID()==next.getID()) { control = new XMLControlElement(next); job.setXML(control.toXML()); replyTo.send(job, this); return; } } // collect all request datasets that match a local dataset DatasetManager reply = new DatasetManager(); // set name of reply to that of request reply.setName(request.getName()); // find datasets that match requested ids ids.clear(); ArrayList<Dataset> requestedDatasets = DataTool.getDatasets(request); findDatasets(requestedDatasets, localDatasets, reply, false); if (!moreData.isEmpty()) { for (Data more: moreData) { localDatasets = DataTool.getDatasets(more); findDatasets(requestedDatasets, localDatasets, reply, true); } padDatasets(reply); } // send datasets to requesting tool if(!reply.getDatasets().isEmpty()) { control = new XMLControlElement(reply); job.setXML(control.toXML()); replyTo.send(job, this); } } /** * Adds a Data object. Note: added Data objects must use the same * independent variable as the original Data. * * @param data the Data object to add */ public void addData(Data data) { if (data==this.data) return; moreData.add(data); } /** * Removes a Data object. * * @param data the Data object to remove */ public void removeData(Data data) { moreData.remove(data); } private void padDatasets(DatasetManager datasets) { // first gather all values of x TreeSet<Double> tSet = new TreeSet<Double>(); for (Dataset dataset: datasets.getDatasets()) { for (double t: dataset.getXPoints()) { tSet.add(t); } } // put x values into an array Double[] temp = tSet.toArray(new Double[tSet.size()]); double[] array = new double[tSet.size()]; for (int i=0; i< array.length; i++) { array[i] = temp[i]; } // now pad each dataset so all share same values of x for (Dataset dataset: datasets.getDatasets()) { padDataset(dataset, array); } } private void findDatasets(ArrayList<Dataset> requestedDatasets, ArrayList<Dataset> datasetsToSearch, DatasetManager reply, boolean isMore) { for(Dataset next : requestedDatasets) { if (next==null) continue; Dataset match = getMatch(next.getID(), datasetsToSearch); if (match!=null) { Dataset toSend = ids.get(match.getID()); if (toSend==null) { toSend = DataTool.copyDataset(match, null, true); if (isMore) { toSend.setXYColumnNames(match.getXColumnName(), next.getYColumnName()); } toSend.setXColumnVisible(toSend.getXColumnName().equals(next.getYColumnName())); toSend.setYColumnVisible(toSend.getYColumnName().equals(next.getYColumnName())); ids.put(match.getID(), toSend); } else { if (toSend.getXColumnName().equals(next.getYColumnName())) toSend.setXColumnVisible(true); if (toSend.getYColumnName().equals(next.getYColumnName())) toSend.setYColumnVisible(true); } reply.addDataset(toSend); } } } private Dataset getMatch(int id, ArrayList<Dataset> datasets) { for(Dataset next : datasets) { if(next==null) { continue; } if(id==next.getID()) { return next; } } return null; } /** * Pads a dataset with NaN values where needed. * * @param dataset the dataset * @param newXArray expanded array of independent variable values */ private void padDataset(Dataset dataset, double[] newXArray) { double[] xArray = dataset.getXPoints(); double[] yArray = dataset.getYPoints(); Map<Double, Double> valueMap = new HashMap<Double, Double>(); for (int k=0; k<xArray.length; k++) { valueMap.put(xArray[k], yArray[k]); } // pad y-values of nextOut with NaN where needed double[] newYArray = new double[newXArray.length]; for (int k=0; k<newXArray.length; k++) { double x = newXArray[k]; newYArray[k] = valueMap.keySet().contains(x)? valueMap.get(x): Double.NaN; } dataset.clear(); dataset.append(newXArray, newYArray); } } /* * Open Source Physics software is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be released * under the GNU GPL license. * * This software 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */