package org.autoplot.dom; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; import org.das2.datum.DatumRange; import org.autoplot.datasource.DataSourceUtil; /** * Represents a state of the application as a whole, with its one canvas and * multiple plots, axes, and bindings. * @author jbf */ public class Application extends DomNode { public Application() { } /** * default time range indicates when the range is not being used. This should never been seen by the user. */ public static final DatumRange DEFAULT_TIME_RANGE= DataSourceUtil.DEFAULT_TIME_RANGE; protected CopyOnWriteArrayList dataSourceFilters= new CopyOnWriteArrayList( new DataSourceFilter[0] ); public static final String PROP_DATASOURCEFILTERS = "dataSourceFilters"; public DataSourceFilter[] getDataSourceFilters() { return (DataSourceFilter[]) dataSourceFilters.toArray( new DataSourceFilter[dataSourceFilters.size()] ); } public void setDataSourceFilters(DataSourceFilter[] dataSourceFilters) { DataSourceFilter[] oldDataSourceFilters = (DataSourceFilter[]) this.dataSourceFilters.toArray( new DataSourceFilter[this.dataSourceFilters.size()] ); this.dataSourceFilters = new CopyOnWriteArrayList( dataSourceFilters ); propertyChangeSupport.firePropertyChange(PROP_DATASOURCEFILTERS, oldDataSourceFilters, dataSourceFilters); } public DataSourceFilter getDataSourceFilters(int index) { return this.dataSourceFilters.get(index); } public void setDataSourceFilters(int index, DataSourceFilter newDataSourceFilter) { DataSourceFilter oldDataSourceFilters = this.dataSourceFilters.set(index, newDataSourceFilter ); propertyChangeSupport.fireIndexedPropertyChange(PROP_DATASOURCEFILTERS, index, oldDataSourceFilters, newDataSourceFilter); } public static final String PROP_PLOT_ELEMENTS = "plotElements"; protected CopyOnWriteArrayList plotElements = new CopyOnWriteArrayList(); public PlotElement[] getPlotElements() { return plotElements.toArray(new PlotElement[plotElements.size()]); } public void setPlotElements(PlotElement[] pele) { PlotElement[] old = (PlotElement[]) this.plotElements.toArray( new PlotElement[this.plotElements.size()] ); this.plotElements = new CopyOnWriteArrayList( pele ); propertyChangeSupport.firePropertyChange(PROP_PLOT_ELEMENTS, old, pele); } public PlotElement getPlotElements(int index) { return this.plotElements.get(index); } public void setPlotElements(int index, PlotElement pele) { PlotElement old = this.plotElements.set(index,pele); propertyChangeSupport.fireIndexedPropertyChange(PROP_PLOT_ELEMENTS, index, old, pele); } public static final String PROP_PLOTS = "plots"; protected CopyOnWriteArrayList plots = new CopyOnWriteArrayList(); public Plot[] getPlots() { return plots.toArray(new Plot[plots.size()]); } public void setPlots(Plot[] plots) { Plot[] oldPlots = this.plots.toArray(new Plot[this.plots.size()]); this.plots = new CopyOnWriteArrayList(plots); propertyChangeSupport.firePropertyChange(PROP_PLOTS, oldPlots, plots); } public Plot getPlots(int index) { return this.plots.get(index); } public void setPlots(int index, Plot newPlots) { Plot oldPlots = this.plots.set(index, newPlots); propertyChangeSupport.fireIndexedPropertyChange(PROP_PLOTS, index, oldPlots, newPlots); } public static final String PROP_CANVASES = "canvases"; protected CopyOnWriteArrayList canvases = new CopyOnWriteArrayList(); public Canvas[] getCanvases() { return canvases.toArray(new Canvas[canvases.size()]); } public void setCanvases(Canvas[] canvases) { Canvas[] old = this.canvases.toArray(new Canvas[this.canvases.size()]); this.canvases = new CopyOnWriteArrayList(canvases); propertyChangeSupport.firePropertyChange(PROP_CANVASES, old, canvases); } public Canvas getCanvases(int index) { return this.canvases.get(index); } public void setCanvases(int index, Canvas newCanvas ) { Canvas old = this.canvases.set(index, newCanvas ); propertyChangeSupport.fireIndexedPropertyChange(PROP_PLOTS, index, old, newCanvas ); } public static final String PROP_ANNOTATIONS = "annotations"; protected CopyOnWriteArrayList annotations= new CopyOnWriteArrayList(); public Annotation[] getAnnotations() { return annotations.toArray(new Annotation[annotations.size()]); } public void setAnnotations(Annotation[] annotations) { Annotation[] oldAnnotations = this.annotations.toArray(new Annotation[this.annotations.size()]); this.annotations = new CopyOnWriteArrayList(annotations); propertyChangeSupport.firePropertyChange(PROP_ANNOTATIONS, oldAnnotations, annotations); } public Annotation getAnnotations(int index) { return this.annotations.get(index); } public void setAnnotations(int index, Annotation annotation) { Annotation old = this.annotations.set(index, annotation ); propertyChangeSupport.fireIndexedPropertyChange(PROP_ANNOTATIONS, index, old, annotation ); } ApplicationController controller; public ApplicationController getController() { return controller; } protected Options options = new Options(); public Options getOptions() { return options; } public void setOptions(Options options) { this.options = options; } protected DatumRange timeRange = DEFAULT_TIME_RANGE; /** * all time axes should hang off of this. */ public static final String PROP_TIMERANGE = "timeRange"; public DatumRange getTimeRange() { return timeRange; } public void setTimeRange(DatumRange timeRange) { if ( timeRange==null ) { throw new IllegalArgumentException("timeRange set to null"); } if ( timeRange.width().value()==0 ) { throw new IllegalArgumentException("timeRange.width().value()==0"); } DatumRange oldTimeRange = this.timeRange; this.timeRange = timeRange; // if ( timeRange.width().value()>0 && timeRange.getUnits()==oldTimeRange.getUnits() && !timeRange.equals(oldTimeRange) ) { // int dmin= (int)( DatumRangeUtil.normalize(timeRange,oldTimeRange.min())*10000 + 0.5 ); // int dmax= (int)( DatumRangeUtil.normalize(timeRange,oldTimeRange.max())*10000 + 0.5 ); // if ( dmin==0 && dmax==10000 ) { // logger.severe("strange ringing where events are tiny changes"); // } // } propertyChangeSupport.firePropertyChange(PROP_TIMERANGE, oldTimeRange, timeRange); } /** * URI pointing to events list, or empty String. */ private String eventsListUri = ""; public static final String PROP_EVENTSLISTURI = "eventsListUri"; public String getEventsListUri() { return eventsListUri; } public void setEventsListUri(String eventsListUri) { String oldEventsListUri = this.eventsListUri; this.eventsListUri = eventsListUri; propertyChangeSupport.firePropertyChange(PROP_EVENTSLISTURI, oldEventsListUri, eventsListUri); } public static final String PROP_BINDINGS = "bindings"; protected CopyOnWriteArrayList bindings= new CopyOnWriteArrayList(); public BindingModel[] getBindings() { BindingModel[] result= bindings.toArray(new BindingModel[bindings.size()]); return result; } public void setBindings(BindingModel[] bindings) { BindingModel[] oldBindings = getBindings(); this.bindings = new CopyOnWriteArrayList(bindings); try { propertyChangeSupport.firePropertyChange(PROP_BINDINGS, oldBindings, bindings); } catch ( NullPointerException ex ) { try { logger.fine("strange case where script creates NullPointerException"); Thread.sleep(100); } catch (InterruptedException ex1) { Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex1); } propertyChangeSupport.firePropertyChange(PROP_BINDINGS, oldBindings, bindings); // thread safety, presumably. Kludge around this... logger.log(Level.WARNING, null, ex ); } } public BindingModel getBindings(int index) { return this.bindings.get(index); } public void setBindings(int index, BindingModel newBinding) { BindingModel oldBinding = this.bindings.set(index, newBinding); propertyChangeSupport.fireIndexedPropertyChange(PROP_BINDINGS, index, oldBinding, newBinding); } public static final String PROP_CONNECTORS = "connectors"; protected CopyOnWriteArrayList connectors= new CopyOnWriteArrayList(); public Connector[] getConnectors() { Connector[] result= connectors.toArray(new Connector[connectors.size()]); return result; } public void setConnectors(Connector[] connectors) { Connector[] oldConnectors = getConnectors(); this.connectors = new CopyOnWriteArrayList(connectors); propertyChangeSupport.firePropertyChange(PROP_CONNECTORS, oldConnectors, connectors); } public Connector getConnectors(int index) { return this.connectors.get(index); } public void setConnectors(int index, Connector newConnector) { Connector oldConnector = this.connectors.set(index, newConnector); propertyChangeSupport.fireIndexedPropertyChange(PROP_CONNECTORS, index, oldConnector, newConnector); } /***** end properties *********************/ /** * return a copy of this application state. * @return a copy of this application state. */ @Override public DomNode copy() { Application result = (Application) super.copy(); result.controller= null; result.options = (Options) this.getOptions().copy(); DataSourceFilter[] DataSourceFiltersCopy= this.getDataSourceFilters(); for ( int i=0; i childNodes() { ArrayList result = new ArrayList(); result.addAll(plots); result.addAll(plotElements); result.addAll(dataSourceFilters); result.addAll(canvases); result.addAll(connectors); result.addAll(annotations); result.add(options); return result; } @Override public void syncTo(DomNode n) { syncTo(n,new ArrayList() ); } @Override public void syncTo(DomNode n,List exclude) { super.syncTo(n,exclude); if ( !( n instanceof Application ) ) throw new IllegalArgumentException("node should be an Application"); if ( this.controller!=null ) { //TODO: what if there's no controller, shouldn't we sync that? this.controller.syncTo( (Application)n, exclude ); } } private void addArrayDiffs( String property, Object[] thata, Object[] thisa, List result ) { try { if ( thata.length > thisa.length ) { for ( int i=thata.length-1; i>=thisa.length; i-- ) { result.add( new ArrayNodeDiff( property, ArrayNodeDiff.Action.Delete, thata[i], i ) ); } } if ( thata.length < thisa.length ) { for ( int i=thisa.length-1; i diffs(DomNode node) { if ( !( node instanceof Application ) ) throw new IllegalArgumentException("node should be an Application"); Application that = (Application) node; List result = new ArrayList<>(); addArrayDiffs( "dataSourceFilters", this.getDataSourceFilters(), that.getDataSourceFilters(), result ); addArrayDiffs( "plotElements", this.getPlotElements(), that.getPlotElements(), result ); addArrayDiffs( "plots", this.getPlots(), that.getPlots(), result ); addArrayDiffs( "canvases", this.getCanvases(), that.getCanvases(), result ); addArrayDiffs( "bindings", this.getBindings(), that.getBindings(), result ); addArrayDiffs( "connectors", this.getConnectors(), that.getConnectors(), result ); addArrayDiffs( "annotations", this.getAnnotations(), that.getAnnotations(), result ); for ( int i=0; i