/***************************************************************************** * Limpet - the Lightweight InforMation ProcEssing Toolkit * http://limpet.info * * (C) 2015-2016, Deep Blue C Technologies Ltd * * This library is free software; you can redistribute it and/or * modify it under the terms of the Eclipse Public License v1.0 * (http://www.eclipse.org/legal/epl-v10.html) * * 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. *****************************************************************************/ package info.limpet.data.commands; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.UUID; import info.limpet.IChangeListener; import info.limpet.ICommand; import info.limpet.IContext; import info.limpet.IQuantityCollection; import info.limpet.IStore; import info.limpet.IStoreGroup; import info.limpet.IStoreItem; import info.limpet.UIProperty; public abstract class AbstractCommand<T extends IStoreItem> implements ICommand<T> { private final String title; private final String description; private final boolean canUndo; private final boolean canRedo; private final IStore store; private final List<T> inputs; private final List<T> outputs; private IStoreGroup _parent; /** * whether the command should recalculate if its children change * */ private boolean dynamic = true; private transient UUID uuid; private final transient IContext context; public AbstractCommand(String title, String description, IStore store, boolean canUndo, boolean canRedo, List<T> inputs, IContext context) { this.title = title; this.description = description; this.store = store; this.canUndo = canUndo; this.canRedo = canRedo; this.context = context; this.inputs = new ArrayList<T>(); this.outputs = new ArrayList<T>(); // store any inputs, if we have any if (inputs != null) { this.getInputs().addAll(inputs); } } /** * provide access to the context object * * @return the context object */ protected final IContext getContext() { return context; } @Override public UUID getUUID() { if (uuid == null) { uuid = UUID.randomUUID(); } return uuid; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getUUID().hashCode(); return result; } @Override public final boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } AbstractCommand<?> other = (AbstractCommand<?>) obj; if (!getUUID().equals(other.getUUID())) { return false; } return true; } /** * provide a name for the single output dataset * * @return a string to use, or null to cancel the operation */ protected abstract String getOutputName(); /** * convenience function, to return the datasets as a comma separated list * * @return */ protected String getSubjectList() { StringBuffer res = new StringBuffer(); @SuppressWarnings("unchecked") Iterator<IStoreItem> iter = (Iterator<IStoreItem>) getInputs().iterator(); int ctr = 0; while (iter.hasNext()) { IStoreItem storeItem = (IStoreItem) iter.next(); if (ctr++ > 0) { res.append(", "); } res.append(storeItem.getName()); } return res.toString(); } protected int getNonSingletonArrayLength(List<IStoreItem> inputs) { int size = 0; Iterator<IStoreItem> iter = inputs.iterator(); while (iter.hasNext()) { IQuantityCollection<?> thisC = (IQuantityCollection<?>) iter.next(); if (thisC.getValuesCount() >= 1) { size = thisC.getValuesCount(); break; } } return size; } @UIProperty(name = "Dynamic updates", category = UIProperty.CATEGORY_LABEL) @Override public boolean getDynamic() { return dynamic; } @Override public void setDynamic(boolean dynamic) { this.dynamic = dynamic; } @Override public void metadataChanged(IStoreItem subject) { // TODO: do a more intelligent/informed processing of metadata changed dataChanged(subject); } @Override public IStoreGroup getParent() { return _parent; } @Override public final void setParent(IStoreGroup parent) { _parent = parent; } @Override public final void dataChanged(IStoreItem subject) { // are we doing live updates? if (dynamic) { // do the recalc recalculate(subject); // // now tell the outputs they have changed // TODO: fire updates from the performCalc event, not here // - then we can minimise the number of updates we send // Iterator<T> iter = getOutputs().iterator(); // while (iter.hasNext()) // { // T t = (T) iter.next(); // t.fireDataChanged(); // } } } protected abstract void recalculate(IStoreItem subject); @Override public void collectionDeleted(IStoreItem subject) { } public final IStore getStore() { return store; } @UIProperty(name = "Description", category = UIProperty.CATEGORY_LABEL) @Override public final String getDescription() { return description; } @Override public void execute() { // ok, register as a listener with the input files Iterator<T> iter = getInputs().iterator(); while (iter.hasNext()) { T t = (T) iter.next(); t.addChangeListener(this); } } @Override public void undo() { throw new UnsupportedOperationException( "Should not be called, undo not provided"); } @Override public void redo() { throw new UnsupportedOperationException( "Should not be called, redo not provided"); } @Override public final boolean canUndo() { return canUndo; } @Override public final boolean canRedo() { return canRedo; } @Override public final List<T> getInputs() { return inputs; } @Override public final List<T> getOutputs() { return outputs; } public final void addOutput(T output) { getOutputs().add(output); } @UIProperty(name = "Name", category = UIProperty.CATEGORY_LABEL) @Override public String getName() { return title; } @Override public boolean hasChildren() { return true; } @Override public final void addChangeListener(IChangeListener listener) { // TODO we should add change listener support } @Override public final void removeChangeListener(IChangeListener listener) { // TODO we should add change listener support } @Override public void fireDataChanged() { // hmm, we don't really implement this, because apps listen to the // results collections, not the command. // TODO Auto-generated method stub } }