// Copyright 2000-2006, FreeHEP. package hep.graphics.heprep.ref; import hep.graphics.heprep.HepRepAttValue; import hep.graphics.heprep.HepRepAttributeListener; import hep.graphics.heprep.HepRepFrameListener; import hep.graphics.heprep.HepRepInstance; import hep.graphics.heprep.HepRepInstanceTree; import hep.graphics.heprep.HepRepIterator; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; /** * Fast iterator, which allows for iteration of all HepRepInstances * in a HepRepInstanceTree or for iteration of a specific layer. * It also features a callback to a HepRepListener to signal * changes in attributes. * * @author M.Donszelmann * @version $Id: DefaultHepRepIterator.java 8584 2006-08-10 23:06:37Z duns $ */ public class DefaultHepRepIterator implements HepRepIterator { // state private List/*<HepRepInstanceTree>*/ instanceTrees; private Iterator treeIterator; private HepRepInstanceTree currentTree; private Iterator layerIterator; private String currentLayer; private boolean iterateFrames; private boolean inFrameLayer; private boolean inFrameLayerChanged; private Set types = null; // for the iteration private HepRepInstance nextInstance = null; // next Instance private HepRepInstance currentInstance = null; // current Instance // Fast Instance Stack private HepRepInstance[] instanceStack = new HepRepInstance[1000]; // HepRepInstances to be processed private int instanceStackTop = -1; // for the listeners private Map/*<LowerCaseName, List<HepRepAttributeListener>>*/ attListeners; private List/*<HepRepFrameListener>*/ frameListeners; // for the attributes private Map/*<LowerCaseName, HepRepAttValue>*/ attributes; // current attributes subscribed to /** * Creates a HepRepIterator for the given list of InstanceTrees. * Layer changes are reported but ignored. * * @param instanceTrees to be iterated over. */ public DefaultHepRepIterator(List/*<HepRepInstanceTree>*/ instanceTrees) { this(instanceTrees, null); } /** * Creates a HepRepIterator for the given list of InstanceTrees and set of layers. * * @param instanceTrees to be iterated over. * @param layers to be used in iteration. */ public DefaultHepRepIterator(List/*<HepRepInstanceTree>*/ instanceTrees, List/*<String>*/ layers) { this(instanceTrees, layers, false); } /** * Creates a HepRepIterator for the given list of InstanceTrees and set of layers. * * @param instanceTrees to be iterated over. * @param layers to be used in iteration. * @param iterateFrames iterate separately over a frame layer for each layer. */ public DefaultHepRepIterator(List/*<HepRepInstanceTree>*/ instanceTrees, List/*<String>*/ layers, boolean iterateFrames) { this(instanceTrees, layers, null, iterateFrames); } /** * Creates a HepRepIterator for the given list of InstanceTrees and set of layers. * * @param instanceTrees to be iterated over. * @param layers to be used in iteration. * @param types to be used in iteration. * @param iterateFrames iterate separately over a frame layer for each layer. */ public DefaultHepRepIterator(List/*<HepRepInstanceTree>*/ instanceTrees, List/*<String>*/ layers, Set/*<Object>*/ types, boolean iterateFrames) { if ((layers == null) || (layers.size() == 0)) { layers = new ArrayList(); layers.add(null); } layerIterator = layers.iterator(); currentLayer = (String)layerIterator.next(); if (currentLayer != null) currentLayer = currentLayer.intern(); this.types = types; this.instanceTrees = instanceTrees; treeIterator = instanceTrees.iterator(); currentTree = null; this.iterateFrames = iterateFrames; inFrameLayer = false; inFrameLayerChanged = true; //setup attribute tables attListeners = new HashMap(); frameListeners = new ArrayList(); attributes = new HashMap(); } private void fillInstanceStack(Collection/*<HepRepInstance>*/ instances) { // put instances of this instancetree on stack for processing Iterator iterator = instances.iterator(); while (iterator.hasNext()) { instanceStackTop++; if (instanceStackTop >= instanceStack.length) { // increment stack size Object oldInstanceStack[] = instanceStack; int newCapacity = (oldInstanceStack.length * 3)/2 + 1; instanceStack = new HepRepInstance[newCapacity]; System.arraycopy(oldInstanceStack, 0, instanceStack, 0, oldInstanceStack.length); } instanceStack[instanceStackTop] = (HepRepInstance)iterator.next(); } } /** * Add a listener to be informed about a certain attribute's changes * while the iteration is ongoing. * * @param name attribute name, null listens to all attributes * @param l listener to be added. */ public void addHepRepAttributeListener(String name, HepRepAttributeListener listener) { String lowerCaseName = name != null ? name.toLowerCase().intern() : name; List list = (List)attListeners.get(lowerCaseName); if (list == null) { list = new ArrayList(); attListeners.put(lowerCaseName, list); } list.add(listener); } /** * Remove a listener for a certain attribute. * * @param name attribute name, null removes all attribute listener * @param l listener to be removed. */ public void removeHepRepAttributeListener(String name, HepRepAttributeListener listener) { String lowerCaseName = (name != null) ? name.toLowerCase() : null; List list = (List)attListeners.get(lowerCaseName); if (list != null) { list.remove(listener); if (list.isEmpty()) { attListeners.remove(lowerCaseName); } } } public void addHepRepFrameListener(HepRepFrameListener l) { frameListeners.add(l); } public void removeHepRepFrameListener(HepRepFrameListener l) { frameListeners.remove(l); } private void informFrameListeners() { if (inFrameLayerChanged) { inFrameLayerChanged = false; for (Iterator i = frameListeners.iterator(); i.hasNext(); ) { HepRepFrameListener l = (HepRepFrameListener)i.next(); l.setFrameLayer(inFrameLayer); } } } /** * Informs listeners of any attribute changes. */ private void informAttributeListeners() { for (Iterator i = attListeners.keySet().iterator(); i.hasNext(); ) { String lowerCaseName = (String)i.next(); List list = (List)attListeners.get(lowerCaseName); if (list == null) continue; for (Iterator j = list.iterator(); j.hasNext(); ) { HepRepAttributeListener listener = (HepRepAttributeListener)j.next(); if (listener == null) continue; if (lowerCaseName == null) { informAttributeListener(listener); } else { informAttributeListener(lowerCaseName, listener); } } } } /** * Inform the "all attribute" listener of all changed attributes. * @param listener listener for "all atribute" */ private void informAttributeListener(HepRepAttributeListener listener) { Set/*<String>*/ attNames = new HashSet(); if (currentInstance != null) { for (Iterator i = currentInstance.getAttValuesFromNode().iterator(); i.hasNext(); ) { attNames.add(((HepRepAttValue)i.next()).getLowerCaseName()); } } for (Iterator i = nextInstance.getAttValuesFromNode().iterator(); i.hasNext(); ) { attNames.add(((HepRepAttValue)i.next()).getLowerCaseName()); } // System.err.println("***** atts to report on:"); // for (Iterator i=attNames.iterator(); i.hasNext(); ) { // System.err.println(" "+i.next()); // } // System.err.println("-----"); for (Iterator i=attNames.iterator(); i.hasNext(); ) { informAttributeListener((String)i.next(), listener); } } /** * Informs specific attribute listener * @param lowerCaseName name of the attribute listened to. * @param listener listener for attribuye */ private void informAttributeListener(String lowerCaseName, HepRepAttributeListener listener) { HepRepAttValue nextAttValue = null; if (currentInstance != null) { nextAttValue = nextInstance.getAttValueFromNode(lowerCaseName); if (nextAttValue == null) { if (currentInstance.getAttValueFromNode(lowerCaseName) == null) { if (currentInstance.getType() == nextInstance.getType()) { // notdef -> notdef, types equal: special DO NOTHING case. return; } } // * -> notdef: lookup nextAttValue = nextInstance.getAttValue(lowerCaseName); } } else { // first time: lookup nextAttValue = nextInstance.getAttValue(lowerCaseName); } informAttributeListener(lowerCaseName, listener, nextAttValue); } /** * Informs listener in case of a change in the given AttributeValue and cache the value. * * @param listener listener * @param lowerCaseName name of the attribute * @param attValue the changed attributeValue. */ private void informAttributeListener(String lowerCaseName, HepRepAttributeListener listener, HepRepAttValue attValue) { // look if changed Object oldAttValue = attributes.get(lowerCaseName); if (oldAttValue == attValue) return; if ((attValue != null) && (attValue.equals(oldAttValue))) return; // cache attributes.put(lowerCaseName, attValue); // new value removed ? if (attValue == null) { listener.removeAttribute(nextInstance, lowerCaseName); return; } // System.err.println("--> "+attValue.getName()+" "+attValue.getAsString()); // inform // NOTE, should take type specified on definition (if there was). Now a change in int will not be notified // to the double. See JHEPREP-57. // we forward the call from int to double in HepRepAttributeAdapter switch (attValue.getType()) { case HepRepAttValue.TYPE_STRING: listener.setAttribute(nextInstance, lowerCaseName, attValue.getString(), attValue.getLowerCaseString(), attValue.showLabel()); break; case HepRepAttValue.TYPE_COLOR: listener.setAttribute(nextInstance, lowerCaseName, attValue.getColor(), attValue.showLabel()); break; case HepRepAttValue.TYPE_LONG: listener.setAttribute(nextInstance, lowerCaseName, attValue.getLong(), attValue.showLabel()); break; case HepRepAttValue.TYPE_INT: listener.setAttribute(nextInstance, lowerCaseName, attValue.getInteger(), attValue.showLabel()); break; case HepRepAttValue.TYPE_DOUBLE: listener.setAttribute(nextInstance, lowerCaseName, attValue.getDouble(), attValue.showLabel()); break; case HepRepAttValue.TYPE_BOOLEAN: listener.setAttribute(nextInstance, lowerCaseName, attValue.getBoolean(), attValue.showLabel()); break; default: System.err.println("Unknown type in DefaultHepRepIterator: '"+attValue.getType()+"'"); listener.setAttribute(nextInstance, lowerCaseName, attValue.toString(), attValue.toString().toLowerCase(), attValue.showLabel()); break; } } /** * @return the next HepRepInstance */ public HepRepInstance nextInstance() { return (HepRepInstance)next(); } /** * @return the next HepRepInstance */ public Object next() { if (!hasNext()) { throw new NoSuchElementException(); } // inform about in/out frame changes informFrameListeners(); // report all attribute changes informAttributeListeners(); // move on currentInstance = nextInstance; nextInstance = null; return currentInstance; } /** * Prepares the next instance and returns true if exists. Successive * calls to hasNext() without calling next() or nextInstance() are ignored. * * The instances are walked through the tree in in-order traversal. * * @return true if there exists a next HepRepInstance */ public boolean hasNext() { // no need if nextInstance is prepared if (nextInstance != null) return true; do { if (instanceStackTop < 0) { // stack is empty, what next... if (treeIterator.hasNext()) { // get next tree currentTree = (HepRepInstanceTree)treeIterator.next(); } else if (inFrameLayer) { // change to normal layer and re-fill for same layer inFrameLayer = false; inFrameLayerChanged = true; // run over all trees again... treeIterator = instanceTrees.iterator(); currentTree = (HepRepInstanceTree)treeIterator.next(); } else { // select next layer if exists... if (!layerIterator.hasNext()) { nextInstance = null; return false; } currentLayer = (String)layerIterator.next(); if (currentLayer != null) { currentLayer = currentLayer.intern(); } // run over all trees again... treeIterator = instanceTrees.iterator(); currentTree = (HepRepInstanceTree)treeIterator.next(); if (iterateFrames) { // change to frame layer inFrameLayer = true; inFrameLayerChanged = true; } } // fill stack from current tree fillInstanceStack(currentTree.getInstances()); } // get stacked instance nextInstance = instanceStack[instanceStackTop]; instanceStackTop--; // add children of this instance to the instanceStack fillInstanceStack(nextInstance.getInstances()); // skip if not in layer, unless layer is null; // skip if not in types, unless types is null; // skip if iterating frames and inFrameLayer and instance has no frame } while (((currentLayer != null) && (nextInstance.getLayer() != currentLayer)) || ((types != null) && !types.contains(nextInstance.getType())) || (iterateFrames && inFrameLayer && !nextInstance.hasFrame())); // nextInstance is valid. return true; } /** * Returns true if the current instance, just delivered by nextInstance(), is to be drawn as a frame. */ public boolean drawAsFrame() { return inFrameLayer; } /** * Removes the current instance, however this is not permitted. * * @throws UnsupportedOperationException in all cases. */ public void remove() throws UnsupportedOperationException { throw new UnsupportedOperationException(); } }