0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.openide.nodes;
0043:
0044: import java.lang.ref.Reference;
0045: import java.lang.ref.WeakReference;
0046: import java.util.ArrayList;
0047: import java.util.Arrays;
0048: import java.util.Collection;
0049: import java.util.Collections;
0050: import java.util.Comparator;
0051: import java.util.Enumeration;
0052: import java.util.HashMap;
0053: import java.util.HashSet;
0054: import java.util.Iterator;
0055: import java.util.LinkedList;
0056: import java.util.List;
0057: import java.util.Set;
0058: import java.util.TreeSet;
0059: import java.util.logging.Level;
0060: import java.util.logging.Logger;
0061: import org.openide.util.Enumerations;
0062: import org.openide.util.Mutex;
0063:
0064: /**
0065: * Factory for the child Nodes of a Node. Every Node has a Children object.
0066: * Children are initially un-initialized, and child Nodes are created on
0067: * demand when, for example, the Node is expanded in an Explorer view.
0068: * If you know your Node has no child nodes, pass <code>Children.LEAF</code>.
0069: * Typically a Children object will create a Collection of objects from
0070: * some data model, and create one or more Nodes for each object on demand.
0071: *
0072: * If initializing the list of children of a Node is time-consuming (i.e. it
0073: * does I/O, parses a file or some other expensive operation), implement
0074: * ChildFactory and pass it to Children.create (theFactory, true) to have
0075: * the child nodes be computed asynchronously on a background thread.
0076: *
0077: * <p>In almost all cases you want to subclass ChildFactory and pass it to
0078: * Children.create(), or subclass {@link Children.Keys}.
0079: * Subclassing <code>Children</code> directly is not recommended.
0080: *
0081: * @author Jaroslav Tulach
0082: */
0083: public abstract class Children extends Object {
0084: /** A package internal accessor object to provide priviledged
0085: * access to children.
0086: */
0087: static final Mutex.Privileged PR = new Mutex.Privileged();
0088:
0089: /** Lock for access to hierarchy of all node lists.
0090: * Anyone who needs to ensure that there will not
0091: * be shared accesses to hierarchy nodes can use this
0092: * mutex.
0093: * <P>
0094: * All operations on the hierarchy of nodes (add, remove, etc.) are
0095: * done in the {@link Mutex#writeAccess} method of this lock, so if someone
0096: * needs for a certain amount of time to forbid modification,
0097: * he can execute his code in {@link Mutex#readAccess}.
0098: */
0099: public static final Mutex MUTEX = new Mutex(PR);
0100:
0101: /** The object representing an empty set of children. Should
0102: * be used to represent the children of leaf nodes. The same
0103: * object may be used by all such nodes.
0104: */
0105: public static final Children LEAF = new Empty();
0106: private static final Object LOCK = new Object();
0107: private static final Logger LOG_GET_ARRAY = Logger
0108: .getLogger("org.openide.nodes.Children.getArray"); // NOI18N
0109:
0110: /** parent node for all nodes in this list (can be null) */
0111: private Node parent;
0112:
0113: /** mapping from entries to info about them */
0114: private java.util.Map<Entry, Info> map;
0115:
0116: /** collection of all entries */
0117: private Collection<? extends Entry> entries = Collections
0118: .emptyList();
0119:
0120: private Reference<ChildrenArray> array = new WeakReference<ChildrenArray>(
0121: null);
0122:
0123: /** Obtains references to array holder. If it does not exist, it is
0124: * created.
0125: *
0126: * @param cannotWorkBetter array of size 1 or null, will contain true, if
0127: * the getArray cannot be initialized (we are under read access
0128: * and another thread is responsible for initialization, in such case
0129: * give up on computation of best result
0130: */
0131: private Thread initThread;
0132:
0133: /*
0134: private StringBuffer debug = new StringBuffer ();
0135:
0136: private void printStackTrace() {
0137: Exception e = new Exception ();
0138: java.io.StringWriter w1 = new java.io.StringWriter ();
0139: java.io.PrintWriter w = new java.io.PrintWriter (w1);
0140: e.printStackTrace(w);
0141: w.close ();
0142: debug.append (w1.toString ());
0143: debug.append ('\n');
0144: }
0145: */
0146:
0147: /** Constructor.
0148: */
0149: public Children() {
0150: }
0151:
0152: /** Setter of parent node for this list of children. Each children in the list
0153: * will have this node set as parent. The parent node will return nodes in
0154: * this list as its children.
0155: * <P>
0156: * This method is called from the Node constructor
0157: *
0158: * @param n node to attach to
0159: * @exception IllegalStateException when this object is already used with
0160: * different node
0161: */
0162: final void attachTo(final Node n) throws IllegalStateException {
0163: // special treatment for LEAF object.
0164: if (this == LEAF) {
0165: // do not attaches the node because the LEAF cannot have children
0166: // and that is why it need not set parent node for them
0167: return;
0168: }
0169:
0170: synchronized (this ) {
0171: if (parent != null) {
0172: // already used
0173: throw new IllegalStateException(
0174: "An instance of Children may not be used for more than one parent node."); // NOI18N
0175: }
0176:
0177: // attach itself as a node list for given node
0178: parent = n;
0179: }
0180:
0181: // this is the only place where parent is changed,
0182: // but only under readAccess => double check if
0183: // it happened correctly
0184: try {
0185: PR.enterReadAccess();
0186:
0187: Node[] nodes = testNodes();
0188:
0189: if (nodes == null) {
0190: return;
0191: }
0192:
0193: // fire the change
0194: for (int i = 0; i < nodes.length; i++) {
0195: Node node = nodes[i];
0196: node.assignTo(Children.this , i);
0197: node.fireParentNodeChange(null, parent);
0198: }
0199: } finally {
0200: PR.exitReadAccess();
0201: }
0202: }
0203:
0204: /** Called when the node changes it's children to different nodes.
0205: *
0206: * @param n node to detach from
0207: * @exception IllegalStateException if children were already detached
0208: */
0209: final void detachFrom() {
0210: // special treatment for LEAF object.
0211: if (this == LEAF) {
0212: // no need to do anything
0213: return;
0214: }
0215:
0216: Node oldParent = null;
0217:
0218: synchronized (this ) {
0219: if (parent == null) {
0220: // already detached
0221: throw new IllegalStateException(
0222: "Trying to detach children which do not have parent"); // NOI18N
0223: }
0224:
0225: // remember old parent
0226: oldParent = parent;
0227:
0228: // attach itself as a node list for given node
0229: parent = null;
0230: }
0231:
0232: // this is the only place where parent is changed,
0233: // but only under readAccess => double check if
0234: // it happened correctly
0235: try {
0236: PR.enterReadAccess();
0237:
0238: Node[] nodes = testNodes();
0239:
0240: if (nodes == null) {
0241: return;
0242: }
0243:
0244: // fire the change
0245: for (int i = 0; i < nodes.length; i++) {
0246: Node node = nodes[i];
0247: node.deassignFrom(Children.this );
0248: node.fireParentNodeChange(oldParent, null);
0249: }
0250: } finally {
0251: PR.exitReadAccess();
0252: }
0253: }
0254:
0255: /**
0256: * Create a <code>Children</code> object using the passed <code>ChildFactory</code>
0257: * object. The <code>ChildFactory</code> will be asked to create a list
0258: * of model objects that are the children; then for each object in the list,
0259: * {@link ChildFactory#createNodesFor} will be called to instantiate
0260: * one or more <code>Node</code>s for that object.
0261: * @param factory a factory which will provide child objects
0262: * @param asynchronous If true, the factory will always be called to
0263: * create the list of keys on
0264: * a background thread, displaying a "Please Wait" child node until
0265: * some or all child nodes have been computed. If so,
0266: * when it is expanded, the node that owns
0267: * the returned <code>Children</code> object will display a "Please Wait"
0268: * node while the children are computed in the background. Pass true
0269: * for any case where computing child nodes is expensive and should
0270: * not be done in the event thread.
0271: * @return a children object which
0272: * will invoke the factory instance as needed to supply model
0273: * objects and child nodes for it
0274: * @throws IllegalStateException if the passed factory has already
0275: * been used in a previous call to this method
0276: * @since org.openide.nodes 7.1
0277: */
0278: public static <T> Children create(ChildFactory<T> factory,
0279: boolean asynchronous) {
0280: if (factory == null)
0281: throw new NullPointerException("Null factory");
0282: return asynchronous ? new AsynchChildren<T>(factory)
0283: : new SynchChildren<T>(factory);
0284: }
0285:
0286: /** Get the parent node of these children.
0287: * @return the node attached to this children object, or <code>null</code> if there is none yet
0288: */
0289: protected final Node getNode() {
0290: return parent;
0291: }
0292:
0293: /** Allows access to the clone method for Node.
0294: * @return cloned hierarchy
0295: * @exception CloneNotSupportedException if not supported
0296: */
0297: final Object cloneHierarchy() throws CloneNotSupportedException {
0298: return clone();
0299: }
0300:
0301: /** Handles cloning in the right way, that can be later extended by
0302: * subclasses. Of course each subclass that wishes to support cloning
0303: * must implement the <code>Cloneable</code> interface, otherwise this method throws
0304: * <code>CloneNotSupportedException</code>.
0305: *
0306: * @return cloned version of this object, with the same class, uninitialized and without
0307: * a parent node
0308: * *exception CloneNotSupportedException if <code>Cloneable</code> interface is not implemented
0309: */
0310: protected Object clone() throws CloneNotSupportedException {
0311: Children ch = (Children) super .clone();
0312:
0313: ch.parent = null;
0314: ch.map = null;
0315: ch.entries = Collections.emptyList();
0316: ch.array = new WeakReference<ChildrenArray>(null);
0317:
0318: return ch;
0319: }
0320:
0321: /**
0322: * Add nodes to this container but <strong>do not call this method</strong>.
0323: * If you think you need to do this probably you really wanted to use
0324: * {@link Children.Keys#setKeys} instead.
0325: * The parent node of these nodes
0326: * is changed to the parent node of this list. Each node can be added
0327: * only once. If there is some reason a node cannot be added, for example
0328: * if the node expects only a special type of subnodes, the method should
0329: * do nothing and return <code>false</code> to signal that the addition has not been successful.
0330: * <P>
0331: * This method should be implemented by subclasses to filter some nodes, etc.
0332: *
0333: * @param nodes set of nodes to add to the list
0334: * @return <code>true</code> if successfully added
0335: */
0336: public abstract boolean add(final Node[] nodes);
0337:
0338: /** Remove nodes from the list. Only nodes that are present are
0339: * removed.
0340: *
0341: * @param nodes nodes to be removed
0342: * @return <code>true</code> if the nodes could be removed
0343: */
0344: public abstract boolean remove(final Node[] nodes);
0345:
0346: /** Get the nodes as an enumeration.
0347: * @return enumeration of nodes
0348: */
0349: public final Enumeration<Node> nodes() {
0350: return Enumerations.array(getNodes());
0351: }
0352:
0353: /** Find a child node by name.
0354: * This may be overridden in subclasses to provide a more advanced way of finding the
0355: * child, but the default implementation simply scans through the list of nodes
0356: * to find the first one with the requested name.
0357: * <p>Normally the list of nodes should have been computed by the time this returns,
0358: * but see {@link #getNodes()} for an important caveat as to why this may not
0359: * be doing what you want and what to do instead.
0360: * @param name (code) name of child node to find or <code>null</code> if any arbitrary child may
0361: * be returned
0362: * @return the node or <code>null</code> if it could not be found
0363: */
0364: public Node findChild(String name) {
0365: Node[] list = getNodes();
0366:
0367: if (list.length == 0) {
0368: return null;
0369: }
0370:
0371: if (name == null) {
0372: // return any node
0373: return list[0];
0374: }
0375:
0376: for (int i = 0; i < list.length; i++) {
0377: if (name.equals(list[i].getName())) {
0378: // ok, we have found it
0379: return list[i];
0380: }
0381: }
0382:
0383: return null;
0384: }
0385:
0386: /** Method that can be used to test whether the children content has
0387: * ever been used or it is still not initalized.
0388: * @return true if children has been used before
0389: * @see #addNotify
0390: */
0391: protected final boolean isInitialized() {
0392: ChildrenArray arr = array.get();
0393:
0394: return (arr != null) && arr.isInitialized();
0395: }
0396:
0397: /** Get's the node at given position. Package private right now
0398: * to allow tests to use it.
0399: */
0400: final Node getNodeAt(int i) {
0401: return getNodes()[i];
0402: }
0403:
0404: /** Get a (sorted) array of nodes in this list.
0405: * If the children object is not yet initialized,
0406: * it will be (using {@link #addNotify}) before
0407: * the nodes are returned.
0408: * <p><strong>Warning:</strong> not all children
0409: * implementations do a complete calculation at
0410: * this point, see {@link #getNodes(boolean)}
0411: * @return array of nodes
0412: */
0413:
0414: // private static String off = ""; // NOI18N
0415: public final Node[] getNodes() {
0416: //Thread.dumpStack();
0417: //System.err.println(off + "getNodes: " + getNode ());
0418: boolean[] results = new boolean[2];
0419:
0420: for (;;) {
0421: results[1] = isInitialized();
0422:
0423: // initializes the ChildrenArray possibly calls
0424: // addNotify if this is for the first time
0425: ChildrenArray array = getArray(results); // fils results[0]
0426:
0427: Node[] nodes;
0428:
0429: try {
0430: PR.enterReadAccess();
0431:
0432: nodes = array.nodes();
0433: } finally {
0434: PR.exitReadAccess();
0435: }
0436:
0437: final boolean IS_LOG_GET_ARRAY = LOG_GET_ARRAY
0438: .isLoggable(Level.FINE);
0439: if (IS_LOG_GET_ARRAY) {
0440: LOG_GET_ARRAY.fine(" length : " + nodes.length); // NOI18N
0441: LOG_GET_ARRAY.fine(" entries : " + entries); // NOI18N
0442: LOG_GET_ARRAY.fine(" init now : " + isInitialized()); // NOI18N
0443: }
0444: // if not initialized that means that after
0445: // we computed the nodes, somebody changed them (as a
0446: // result of addNotify) => we have to compute them
0447: // again
0448: if (results[1]) {
0449: // otherwise it is ok.
0450: return nodes;
0451: }
0452:
0453: if (results[0]) {
0454: // looks like the result cannot be computed, just give empty one
0455: return (nodes == null) ? new Node[0] : nodes;
0456: }
0457: }
0458: }
0459:
0460: /** Get a (sorted) array of nodes in this list.
0461: *
0462: * This method is usefull if you need a fully initialized array of nodes
0463: * for things like MenuView, node navigation from scripts/tests and so on.
0464: * But in general if you are trying to get useful data by calling
0465: * this method, you are probably doing something wrong.
0466: * Usually you should be asking some underlying model
0467: * for information, not the nodes for children. For example,
0468: * <code>DataFolder.getChildren()</code>
0469: * is a much more appropriate way to get what you want for the case of folder children.
0470: *
0471: * If you're extending children, you should make sure this method
0472: * will return a complete list of nodes. The default implementation will do
0473: * this correctly so long as your subclass implement findChild(null)
0474: * to initialize all subnodes.
0475: *
0476: * <p><strong>Note:</strong>You should not call this method from inside
0477: * <code>{@link org.openide.nodes.Children#MUTEX Children.MUTEX}.readAccess()</code>.
0478: * If you do so, the <code>Node</code> will be unable to update its state
0479: * before you leave the <code>readAccess()</code>.
0480: *
0481: * @since 2.17
0482: *
0483: * @param optimalResult whether to try to get a fully initialized array
0484: * or to simply delegate to {@link #getNodes()}
0485: * @return array of nodes
0486: */
0487: public Node[] getNodes(boolean optimalResult) {
0488: ChildrenArray hold;
0489: Node find;
0490: if (optimalResult) {
0491: final boolean IS_LOG_GET_ARRAY = LOG_GET_ARRAY
0492: .isLoggable(Level.FINE);
0493: if (IS_LOG_GET_ARRAY) {
0494: LOG_GET_ARRAY.fine("computing optimal result");// NOI18N
0495: }
0496: hold = getArray(null);
0497: if (IS_LOG_GET_ARRAY) {
0498: LOG_GET_ARRAY.fine("optimal result is here: " + hold);// NOI18N
0499: }
0500: find = findChild(null);
0501: if (IS_LOG_GET_ARRAY) {
0502: LOG_GET_ARRAY.fine("Find child got: " + find); // NOI18N
0503: }
0504: }
0505:
0506: return getNodes();
0507: }
0508:
0509: /** Get the number of nodes in the list.
0510: * @return the count
0511: */
0512: public final int getNodesCount() {
0513: return getNodes().length;
0514: }
0515:
0516: //
0517: // StateNotifications
0518: //
0519:
0520: /** Called when children are first asked for nodes.
0521: * Typical implementations at this time calculate
0522: * their node list (or keys for {@link Children.Keys} etc.).<BR>
0523: * Notice: call to getNodes() inside of this method will return
0524: * an empty array of nodes.
0525: * @see #isInitialized
0526: */
0527: protected void addNotify() {
0528: }
0529:
0530: /** Called when all the children Nodes are freed from memory.
0531: * Typical implementations at this time clear all the keys
0532: * (in case of {@link Children.Keys}) etc.
0533: *
0534: * Note that this is usually not the best place for unregistering
0535: * listeners, etc., as listeners usually keep the child nodes
0536: * in memory, preventing them from being collected, thus preventing
0537: * this method to be called in the first place.
0538: */
0539: protected void removeNotify() {
0540: }
0541:
0542: /** Method that can be overriden in subclasses to
0543: * do additional work and then call addNotify.
0544: */
0545: void callAddNotify() {
0546: addNotify();
0547: }
0548:
0549: //
0550: // ChildrenArray operations call only under lock
0551: //
0552:
0553: /** @return either nodes associated with this children or null if
0554: * they are not created
0555: */
0556: private Node[] testNodes() {
0557: ChildrenArray arr = array.get();
0558:
0559: return (arr == null) ? null : arr.nodes();
0560: }
0561:
0562: private ChildrenArray getArray(boolean[] cannotWorkBetter) {
0563: final boolean IS_LOG_GET_ARRAY = LOG_GET_ARRAY
0564: .isLoggable(Level.FINE);
0565:
0566: ChildrenArray arr;
0567: boolean doInitialize = false;
0568: synchronized (LOCK) {
0569: arr = array.get();
0570:
0571: if (arr == null) {
0572: arr = new ChildrenArray();
0573:
0574: // register the array with the children
0575: registerChildrenArray(arr, true);
0576: doInitialize = true;
0577: initThread = Thread.currentThread();
0578: }
0579: }
0580:
0581: if (doInitialize) {
0582: if (IS_LOG_GET_ARRAY) {
0583: LOG_GET_ARRAY.fine("Initialize " + this + " on "
0584: + Thread.currentThread()); // NOI18N
0585: }
0586:
0587: // this call can cause a lot of callbacks => be prepared
0588: // to handle them as clean as possible
0589: try {
0590: this .callAddNotify();
0591:
0592: if (IS_LOG_GET_ARRAY) {
0593: LOG_GET_ARRAY
0594: .fine("addNotify successfully called for "
0595: + this + " on "
0596: + Thread.currentThread()); // NOI18N
0597: }
0598: } finally {
0599: boolean notifyLater;
0600: notifyLater = MUTEX.isReadAccess();
0601:
0602: if (IS_LOG_GET_ARRAY) {
0603: LOG_GET_ARRAY.fine("notifyAll for " + this + " on "
0604: + Thread.currentThread()
0605: + " notifyLater: " + notifyLater); // NOI18N
0606: }
0607:
0608: // now attach to children, so when children == null => we are
0609: // not fully initialized!!!!
0610: arr.children = this ;
0611: class SetAndNotify implements Runnable {
0612: public ChildrenArray toSet;
0613: public Children whatSet;
0614:
0615: public void run() {
0616: synchronized (LOCK) {
0617: initThread = null;
0618: LOCK.notifyAll();
0619: }
0620:
0621: if (IS_LOG_GET_ARRAY) {
0622: LOG_GET_ARRAY.fine("notifyAll done"); // NOI18N
0623: }
0624:
0625: }
0626: }
0627:
0628: SetAndNotify setAndNotify = new SetAndNotify();
0629: setAndNotify.toSet = arr;
0630: setAndNotify.whatSet = this ;
0631:
0632: if (notifyLater) {
0633: // the notify to the lock has to be done later than
0634: // setKeys is executed, otherwise the result of addNotify
0635: // might not be visible to other threads
0636: // fix for issue 50308
0637: MUTEX.postWriteRequest(setAndNotify);
0638: } else {
0639: setAndNotify.run();
0640: }
0641: }
0642: } else {
0643: // otherwise, if not initialize yet (arr.children) wait
0644: // for the initialization to finish, but only if we can wait
0645: if (MUTEX.isReadAccess() || MUTEX.isWriteAccess()
0646: || (initThread == Thread.currentThread())) {
0647: // fail, we are in read access
0648: if (IS_LOG_GET_ARRAY) {
0649: LOG_GET_ARRAY.log(Level.FINE,
0650: "cannot initialize better " + this
0651: + // NOI18N
0652: " on " + Thread.currentThread()
0653: + // NOI18N
0654: " read access: "
0655: + MUTEX.isReadAccess() + // NOI18N
0656: " initThread: " + initThread, // NOI18N
0657: new Exception("StackTrace") // NOI18N
0658: );
0659: }
0660:
0661: if (cannotWorkBetter != null) {
0662: cannotWorkBetter[0] = true;
0663: }
0664:
0665: return arr;
0666: }
0667:
0668: // otherwise we can wait
0669: synchronized (LOCK) {
0670: while (initThread != null) {
0671: if (IS_LOG_GET_ARRAY) {
0672: LOG_GET_ARRAY.fine("waiting for children for "
0673: + this + // NOI18N
0674: " on " + Thread.currentThread() // NOI18N
0675: );
0676: }
0677:
0678: try {
0679: LOCK.wait();
0680: } catch (InterruptedException ex) {
0681: }
0682: }
0683: }
0684: if (IS_LOG_GET_ARRAY) {
0685: LOG_GET_ARRAY.fine(" children are here for " + this + // NOI18N
0686: " on " + Thread.currentThread() + // NOI18N
0687: " children " + arr.children);
0688: }
0689: }
0690:
0691: return arr;
0692: }
0693:
0694: /** Clears the nodes
0695: */
0696: private void clearNodes() {
0697: ChildrenArray arr = array.get();
0698:
0699: //System.err.println(off + " clearNodes: " + getNode ());
0700: if (arr != null) {
0701: // clear the array
0702: arr.clear();
0703: }
0704: }
0705:
0706: /** Forces finalization of nodes for given info.
0707: * Called from finalizer of Info.
0708: */
0709: final void finalizeNodes() {
0710: ChildrenArray arr = array.get();
0711:
0712: if (arr != null) {
0713: arr.finalizeNodes();
0714: }
0715: }
0716:
0717: /** Registration of ChildrenArray.
0718: * @param chArr the associated ChildrenArray
0719: * @param weak use weak or hard reference
0720: */
0721: final void registerChildrenArray(final ChildrenArray chArr,
0722: boolean weak) {
0723: final boolean IS_LOG_GET_ARRAY = LOG_GET_ARRAY
0724: .isLoggable(Level.FINE);
0725: if (IS_LOG_GET_ARRAY) {
0726: LOG_GET_ARRAY.fine("registerChildrenArray: " + chArr
0727: + " weak: " + weak); // NOI18N
0728: }
0729: if (weak) {
0730: this .array = new WeakReference<ChildrenArray>(chArr);
0731: } else {
0732: // hold the children hard
0733: this .array = new WeakReference<ChildrenArray>(chArr) {
0734: @Override
0735: public ChildrenArray get() {
0736: return chArr;
0737: }
0738: };
0739: }
0740:
0741: chArr.pointedBy(this .array);
0742: if (IS_LOG_GET_ARRAY) {
0743: LOG_GET_ARRAY.fine("pointed by: " + chArr + " to: "
0744: + this .array); // NOI18N
0745: }
0746: }
0747:
0748: /** Finalized.
0749: */
0750: final void finalizedChildrenArray(Reference caller) {
0751: final boolean IS_LOG_GET_ARRAY = LOG_GET_ARRAY
0752: .isLoggable(Level.FINE);
0753: // usually in removeNotify setKeys is called => better require write access
0754: try {
0755: PR.enterWriteAccess();
0756:
0757: if (IS_LOG_GET_ARRAY) {
0758: LOG_GET_ARRAY.fine("previous array: " + array
0759: + " caller: " + caller);
0760: }
0761: if (array == caller) {
0762: // really finalized and not reconstructed
0763: removeNotify();
0764: }
0765:
0766: /*
0767: else {
0768: System.out.println("Strange removeNotify " + caller + " : " + value );
0769: }
0770: */
0771: } finally {
0772: PR.exitWriteAccess();
0773: }
0774: }
0775:
0776: /** Computes the nodes now.
0777: */
0778: final Node[] justComputeNodes() {
0779: if (map == null) {
0780: map = Collections.synchronizedMap(new HashMap<Entry, Info>(
0781: 17));
0782:
0783: // debug.append ("Map initialized\n"); // NOI18N
0784: // printStackTrace();
0785: }
0786:
0787: List<Node> l = new LinkedList<Node>();
0788: for (Entry entry : entries) {
0789: Info info = findInfo(entry);
0790:
0791: try {
0792: l.addAll(info.nodes());
0793: } catch (RuntimeException ex) {
0794: NodeOp.warning(ex);
0795: }
0796: }
0797:
0798: Node[] arr = l.toArray(new Node[l.size()]);
0799:
0800: // initialize parent nodes
0801: for (int i = 0; i < arr.length; i++) {
0802: Node n = arr[i];
0803: n.assignTo(this , i);
0804: n.fireParentNodeChange(null, parent);
0805: }
0806:
0807: return arr;
0808: }
0809:
0810: /** Finds info for given entry, or registers
0811: * it, if not registered yet.
0812: */
0813: private Info findInfo(Entry entry) {
0814: synchronized (map) {
0815: Info info = map.get(entry);
0816:
0817: if (info == null) {
0818: info = new Info(entry);
0819: map.put(entry, info);
0820:
0821: // debug.append ("Put: " + entry + " info: " + info); // NOI18N
0822: // debug.append ('\n');
0823: // printStackTrace();
0824: }
0825: return info;
0826: }
0827: }
0828:
0829: //
0830: // Entries
0831: //
0832:
0833: /** Access to copy of current entries.
0834: * @return copy of entries in the objects
0835: */
0836: final List<Entry> getEntries() {
0837: return new ArrayList<Entry>(this .entries);
0838: }
0839:
0840: final void setEntries(Collection<? extends Entry> entries) {
0841: final boolean IS_LOG_GET_ARRAY = LOG_GET_ARRAY
0842: .isLoggable(Level.FINE);
0843: // current list of nodes
0844: ChildrenArray holder = array.get();
0845:
0846: if (IS_LOG_GET_ARRAY) {
0847: LOG_GET_ARRAY.fine("setEntries for " + this + " on "
0848: + Thread.currentThread()); // NOI18N
0849: LOG_GET_ARRAY.fine(" values: " + entries); // NOI18N
0850: LOG_GET_ARRAY.fine(" holder: " + holder); // NOI18N
0851: }
0852: if (holder == null) {
0853: // debug.append ("Set1: " + entries); // NOI18N
0854: // printStackTrace();
0855: this .entries = entries;
0856:
0857: if (map != null) {
0858: map.keySet().retainAll(new HashSet<Entry>(entries));
0859: }
0860:
0861: return;
0862: }
0863:
0864: Node[] current = holder.nodes();
0865:
0866: if (current == null) {
0867: // the initialization is not finished yet =>
0868: // debug.append ("Set2: " + entries); // NOI18N
0869: // printStackTrace();
0870: this .entries = entries;
0871:
0872: if (map != null) {
0873: map.keySet().retainAll(new HashSet<Entry>(entries));
0874: }
0875:
0876: return;
0877: }
0878:
0879: // if there are old items in the map, remove them to
0880: // reflect current state
0881: map.keySet().retainAll(new HashSet<Entry>(this .entries));
0882:
0883: // what should be removed
0884: Set<Entry> toRemove = new HashSet<Entry>(map.keySet());
0885: Set<Entry> entriesSet = new HashSet<Entry>(entries);
0886: toRemove.removeAll(entriesSet);
0887:
0888: if (!toRemove.isEmpty()) {
0889: // notify removing, the set must be ready for
0890: // callbacks with questions
0891: updateRemove(current, toRemove);
0892: current = holder.nodes();
0893: }
0894:
0895: // change the order of entries, notifies
0896: // it and again brings children to up-to-date state
0897: Collection<Info> toAdd = updateOrder(current, entries);
0898:
0899: if (!toAdd.isEmpty()) {
0900: // toAdd contains Info objects that should
0901: // be added
0902: updateAdd(toAdd, entries);
0903: }
0904: }
0905:
0906: private void checkInfo(Info info, Entry entry,
0907: Collection<? extends Entry> entries,
0908: java.util.Map<Entry, Info> map) {
0909: if (info == null) {
0910: throw new IllegalStateException(
0911: "Error in "
0912: + getClass().getName()
0913: + " with entry "
0914: + entry
0915: + " from among "
0916: + entries
0917: + " in "
0918: + map
0919: + // NOI18N
0920: " probably caused by faulty key implementation."
0921: + // NOI18N
0922: " The key hashCode() and equals() methods must behave as for an IMMUTABLE object"
0923: + // NOI18N
0924: " and the hashCode() must return the same value for equals() keys."); // NOI18N
0925: }
0926: }
0927:
0928: /** Removes the objects from the children.
0929: */
0930: private void updateRemove(Node[] current, Set<Entry> toRemove) {
0931: List<Node> nodes = new LinkedList<Node>();
0932:
0933: for (Entry en : toRemove) {
0934: Info info = map.remove(en);
0935:
0936: //debug.append ("Removed: " + en + " info: " + info); // NOI18N
0937: //debug.append ('\n');
0938: //printStackTrace();
0939: checkInfo(info, en, null, map);
0940:
0941: nodes.addAll(info.nodes());
0942: }
0943:
0944: // modify the current set of entries and empty the list of nodes
0945: // so it has to be recreated again
0946: //debug.append ("Current : " + this.entries + '\n'); // NOI18N
0947: this .entries.removeAll(toRemove);
0948:
0949: //debug.append ("Removing: " + toRemove + '\n'); // NOI18N
0950: //debug.append ("New : " + this.entries + '\n'); // NOI18N
0951: //printStackTrace();
0952: clearNodes();
0953:
0954: notifyRemove(nodes, current);
0955: }
0956:
0957: /** Notifies that a set of nodes has been removed from
0958: * children. It is necessary that the system is already
0959: * in consistent state, so any callbacks will return
0960: * valid values.
0961: *
0962: * @param nodes list of removed nodes
0963: * @param current state of nodes
0964: * @return array of nodes that were deleted
0965: */
0966: Node[] notifyRemove(Collection<Node> nodes, Node[] current) {
0967: //System.err.println("notifyRemove from: " + getNode ());
0968: //System.err.println("notifyRemove: " + nodes);
0969: //System.err.println("Current : " + Arrays.asList (current));
0970: //Thread.dumpStack();
0971: //Keys.last.printStackTrace();
0972: // [TODO] Children do not have always a parent
0973: // see Services->FIRST ($SubLevel.class)
0974: // during a deserialization it may have parent == null
0975: Node[] arr = nodes.toArray(new Node[nodes.size()]);
0976:
0977: if (parent == null) {
0978: return arr;
0979: }
0980:
0981: // fire change of nodes
0982: parent.fireSubNodesChange(false, // remove
0983: arr, current);
0984:
0985: // fire change of parent
0986: Iterator it = nodes.iterator();
0987:
0988: while (it.hasNext()) {
0989: Node n = (Node) it.next();
0990: n.deassignFrom(this );
0991: n.fireParentNodeChange(parent, null);
0992: }
0993:
0994: return arr;
0995: }
0996:
0997: /** Updates the order of entries.
0998: * @param current current state of nodes
0999: * @param entries new set of entries
1000: * @return list of infos that should be added
1001: */
1002: private List<Info> updateOrder(Node[] current,
1003: Collection<? extends Entry> newEntries) {
1004: List<Info> toAdd = new LinkedList<Info>();
1005:
1006: // that assignes entries their begining position in the array
1007: // of nodes
1008: java.util.Map<Info, Integer> offsets = new HashMap<Info, Integer>();
1009:
1010: {
1011: int previousPos = 0;
1012:
1013: for (Entry entry : entries) {
1014: Info info = map.get(entry);
1015: checkInfo(info, entry, entries, map);
1016:
1017: offsets.put(info, previousPos);
1018:
1019: previousPos += info.length();
1020: }
1021: }
1022:
1023: // because map can contain some additional items,
1024: // that has not been garbage collected yet,
1025: // retain only those that are in current list of
1026: // entries
1027: map.keySet().retainAll(new HashSet<Entry>(entries));
1028:
1029: int[] perm = new int[current.length];
1030: int currentPos = 0;
1031: int permSize = 0;
1032: List<Entry> reorderedEntries = null;
1033:
1034: for (Entry entry : newEntries) {
1035: Info info = map.get(entry);
1036:
1037: if (info == null) {
1038: // this info has to be added
1039: info = new Info(entry);
1040: toAdd.add(info);
1041: } else {
1042: int len = info.length();
1043:
1044: if (reorderedEntries == null) {
1045: reorderedEntries = new LinkedList<Entry>();
1046: }
1047:
1048: reorderedEntries.add(entry);
1049:
1050: // already there => test if it should not be reordered
1051: Integer previousInt = offsets.get(info);
1052:
1053: /*
1054: if (previousInt == null) {
1055: System.err.println("Offsets: " + offsets);
1056: System.err.println("Info: " + info);
1057: System.err.println("Entry: " + info.entry);
1058: System.err.println("This entries: " + this.entries);
1059: System.err.println("Entries: " + entries);
1060: System.err.println("Map: " + map);
1061:
1062: System.err.println("---------vvvvv");
1063: System.err.println(debug);
1064: System.err.println("---------^^^^^");
1065:
1066: }
1067: */
1068: int previousPos = previousInt;
1069:
1070: if (currentPos != previousPos) {
1071: for (int i = 0; i < len; i++) {
1072: perm[previousPos + i] = 1 + currentPos + i;
1073: }
1074:
1075: permSize += len;
1076: }
1077: }
1078:
1079: currentPos += info.length();
1080: }
1081:
1082: if (permSize > 0) {
1083: // now the perm array contains numbers 1 to ... and
1084: // 0 one places where no permutation occures =>
1085: // decrease numbers, replace zeros
1086: for (int i = 0; i < perm.length; i++) {
1087: if (perm[i] == 0) {
1088: // fixed point
1089: perm[i] = i;
1090: } else {
1091: // decrease
1092: perm[i]--;
1093: }
1094: }
1095:
1096: // reorderedEntries are not null
1097: this .entries = reorderedEntries;
1098:
1099: // debug.append ("Set3: " + this.entries); // NOI18N
1100: // printStackTrace();
1101: // notify the permutation to the parent
1102: clearNodes();
1103:
1104: //System.err.println("Paremutaiton! " + getNode ());
1105: Node p = parent;
1106:
1107: if (p != null) {
1108: p.fireReorderChange(perm);
1109: }
1110: }
1111:
1112: return toAdd;
1113: }
1114:
1115: /** Updates the state of children by adding given Infos.
1116: * @param infos list of Info objects to add
1117: * @param entries the final state of entries that should occur
1118: */
1119: private void updateAdd(Collection<Info> infos,
1120: Collection<? extends Entry> entries) {
1121: List<Node> nodes = new LinkedList<Node>();
1122: for (Info info : infos) {
1123: nodes.addAll(info.nodes());
1124: map.put(info.entry, info);
1125:
1126: // debug.append ("updateadd: " + info.entry + " info: " + info + '\n'); // NOI18N
1127: // printStackTrace();
1128: }
1129:
1130: this .entries = entries;
1131:
1132: // debug.append ("Set4: " + entries); // NOI18N
1133: // printStackTrace();
1134: clearNodes();
1135:
1136: notifyAdd(nodes);
1137: }
1138:
1139: /** Notifies that a set of nodes has been add to
1140: * children. It is necessary that the system is already
1141: * in consistent state, so any callbacks will return
1142: * valid values.
1143: *
1144: * @param nodes list of removed nodes
1145: */
1146: private void notifyAdd(Collection<Node> nodes) {
1147: // notify about parent change
1148: for (Node n : nodes) {
1149: n.assignTo(this , -1);
1150: n.fireParentNodeChange(null, parent);
1151: }
1152:
1153: Node[] arr = nodes.toArray(new Node[nodes.size()]);
1154:
1155: Node n = parent;
1156:
1157: if (n != null) {
1158: n.fireSubNodesChange(true, arr, null);
1159: }
1160: }
1161:
1162: /** Refreshes content of one entry. Updates the state of children
1163: * appropriately.
1164: */
1165: final void refreshEntry(Entry entry) {
1166: // current list of nodes
1167: ChildrenArray holder = array.get();
1168:
1169: if (holder == null) {
1170: return;
1171: }
1172:
1173: Node[] current = holder.nodes();
1174:
1175: if (current == null) {
1176: // the initialization is not finished yet =>
1177: return;
1178: }
1179:
1180: // because map can contain some additional items,
1181: // that has not been garbage collected yet,
1182: // retain only those that are in current list of
1183: // entries
1184: map.keySet().retainAll(new HashSet<Entry>(this .entries));
1185:
1186: Info info = map.get(entry);
1187:
1188: if (info == null) {
1189: // refresh of entry that is not present =>
1190: return;
1191: }
1192:
1193: Collection<Node> oldNodes = info.nodes();
1194: Collection<Node> newNodes = info.entry.nodes();
1195:
1196: if (oldNodes.equals(newNodes)) {
1197: // nodes are the same =>
1198: return;
1199: }
1200:
1201: Set<Node> toRemove = new HashSet<Node>(oldNodes);
1202: toRemove.removeAll(new HashSet<Node>(newNodes));
1203:
1204: if (!toRemove.isEmpty()) {
1205: // notify removing, the set must be ready for
1206: // callbacks with questions
1207: // modifies the list associated with the info
1208: oldNodes.removeAll(toRemove);
1209: clearNodes();
1210:
1211: // now everything should be consistent => notify the remove
1212: notifyRemove(toRemove, current);
1213:
1214: current = holder.nodes();
1215: }
1216:
1217: List<Node> toAdd = refreshOrder(entry, oldNodes, newNodes);
1218: info.useNodes(newNodes);
1219:
1220: if (!toAdd.isEmpty()) {
1221: // modifies the list associated with the info
1222: clearNodes();
1223: notifyAdd(toAdd);
1224: }
1225: }
1226:
1227: /** Updates the order of nodes after a refresh.
1228: * @param entry the refreshed entry
1229: * @param oldNodes nodes that are currently in the list
1230: * @param newNodes new nodes (defining the order of oldNodes and some more)
1231: * @return list of infos that should be added
1232: */
1233: private List<Node> refreshOrder(Entry entry,
1234: Collection<Node> oldNodes, Collection<Node> newNodes) {
1235: List<Node> toAdd = new LinkedList<Node>();
1236:
1237: int currentPos = 0;
1238:
1239: // cycle thru all entries to find index of the entry
1240: Iterator<? extends Entry> it1 = this .entries.iterator();
1241:
1242: for (;;) {
1243: Entry e = it1.next();
1244:
1245: if (e.equals(entry)) {
1246: break;
1247: }
1248:
1249: Info info = findInfo(e);
1250: currentPos += info.length();
1251: }
1252:
1253: Set<Node> oldNodesSet = new HashSet<Node>(oldNodes);
1254: Set<Node> toProcess = new HashSet<Node>(oldNodesSet);
1255:
1256: Node[] permArray = new Node[oldNodes.size()];
1257: Iterator<Node> it2 = newNodes.iterator();
1258:
1259: int pos = 0;
1260:
1261: while (it2.hasNext()) {
1262: Node n = it2.next();
1263:
1264: if (oldNodesSet.remove(n)) {
1265: // the node is in the old set => test for permuation
1266: permArray[pos++] = n;
1267: } else {
1268: if (!toProcess.contains(n)) {
1269: // if the node has not been processed yet
1270: toAdd.add(n);
1271: } else {
1272: it2.remove();
1273: }
1274: }
1275: }
1276:
1277: // JST: If you get IllegalArgumentException in following code
1278: // then it can be cause by wrong synchronization between
1279: // equals and hashCode methods. First of all check them!
1280: int[] perm = NodeOp.computePermutation(oldNodes
1281: .toArray(new Node[oldNodes.size()]), permArray);
1282:
1283: if (perm != null) {
1284: // apply the permutation
1285: clearNodes();
1286:
1287: // temporarily change the nodes the entry should use
1288: findInfo(entry).useNodes(Arrays.asList(permArray));
1289:
1290: Node p = parent;
1291:
1292: if (p != null) {
1293: p.fireReorderChange(perm);
1294: }
1295: }
1296:
1297: return toAdd;
1298: }
1299:
1300: /** Interface that provides a set of nodes.
1301: */
1302: static interface Entry {
1303: /** Set of nodes associated with this entry.
1304: */
1305: public Collection<Node> nodes();
1306: }
1307:
1308: /** Information about an entry. Contains number of nodes,
1309: * position in the array of nodes, etc.
1310: */
1311: final class Info extends Object {
1312: int length;
1313: final Entry entry;
1314:
1315: public Info(Entry entry) {
1316: this .entry = entry;
1317: }
1318:
1319: /** Finalizes the content of ChildrenArray.
1320: */
1321: protected void finalize() {
1322: finalizeNodes();
1323: }
1324:
1325: public Collection<Node> nodes() {
1326: // forces creation of the array
1327: ChildrenArray arr = getArray(null);
1328:
1329: return arr.nodesFor(this );
1330: }
1331:
1332: public void useNodes(Collection<Node> nodes) {
1333: // forces creation of the array
1334: ChildrenArray arr = getArray(null);
1335:
1336: arr.useNodes(this , nodes);
1337:
1338: // assign all there nodes the new children
1339: for (Node n : nodes) {
1340: n.assignTo(Children.this , -1);
1341: n.fireParentNodeChange(null, parent);
1342: }
1343: }
1344:
1345: public int length() {
1346: return length;
1347: }
1348:
1349: @Override
1350: public String toString() {
1351: return "Children.Info[" + entry + ",length=" + length + "]"; // NOI18N
1352: }
1353: }
1354:
1355: /** Empty list of children. Does not allow anybody to insert a node.
1356: * Treated especially in the attachTo method.
1357: */
1358: private static final class Empty extends Children {
1359: Empty() {
1360: }
1361:
1362: /** @return false, does no action */
1363: public boolean add(Node[] nodes) {
1364: return false;
1365: }
1366:
1367: /** @return false, does no action */
1368: public boolean remove(Node[] nodes) {
1369: return false;
1370: }
1371: }
1372:
1373: /** Implements the storage of node children by an array.
1374: * Each new child is added at the end of the array. The nodes are
1375: * returned in the order they were inserted.
1376: *
1377: * <p><strong>
1378: * Directly subclassing this class is discouraged.
1379: * {@link Children.Keys} is preferable.
1380: * </strong>
1381: */
1382: public static class Array extends Children implements Cloneable {
1383: /** the entry used for all nodes in the following collection
1384: * this object is used for synchronization of operations that
1385: * need to be synchronized on this instance of Children, but
1386: * we cannot synchronize on this instance because it is public
1387: * and somebody else could synchronize too.
1388: */
1389: private Entry nodesEntry;
1390:
1391: /** vector of added children */
1392: protected Collection<Node> nodes;
1393:
1394: /** Constructs a new list and allows a subclass to
1395: * provide its own implementation of <code>Collection</code> to store
1396: * data in. The collection should be empty and should not
1397: * be directly accessed in any way after creation.
1398: *
1399: * @param c collection to store data in
1400: */
1401: protected Array(Collection<Node> c) {
1402: this ();
1403: nodes = c;
1404: }
1405:
1406: /** Constructs a new array children without any assigned collection.
1407: * The collection will be created by a call to method initCollection the
1408: * first time, children will be used.
1409: */
1410: public Array() {
1411: nodesEntry = createNodesEntry();
1412: this .setEntries(Collections.singleton(getNodesEntry()));
1413: }
1414:
1415: /** Clones all nodes that are contained in the children list.
1416: *
1417: * @return the cloned array for this children
1418: */
1419: public Object clone() {
1420: try {
1421: final Children.Array ar = (Array) super .clone();
1422:
1423: try {
1424: PR.enterReadAccess();
1425:
1426: if (nodes != null) {
1427: // nodes already initilized
1428: // used to create the right type of collection
1429: // clears the content of the collection
1430: // JST: hack, but I have no better idea how to write this
1431: // pls. notice that in initCollection you can test
1432: // whether nodes == null => real initialization
1433: // nodes != null => only create new empty collection
1434: ar.nodes = ar.initCollection();
1435: ar.nodes.clear();
1436:
1437: // insert copies of the nodes
1438: for (Node n : nodes) {
1439: ar.nodes.add(n.cloneNode());
1440: }
1441: }
1442: } finally {
1443: PR.exitReadAccess();
1444: }
1445:
1446: return ar;
1447: } catch (CloneNotSupportedException e) {
1448: // this cannot happen
1449: throw new InternalError();
1450: }
1451: }
1452:
1453: /** Allow subclasses to create a collection, the first time the
1454: * children are used. It is called only if the collection has not
1455: * been passed in the constructor.
1456: * <P>
1457: * The current implementation returns ArrayList.
1458: *
1459: * @return empty or initialized collection to use
1460: */
1461: protected Collection<Node> initCollection() {
1462: return new ArrayList<Node>();
1463: }
1464:
1465: /** This method can be called by subclasses that
1466: * directly modify the nodes collection to update the
1467: * state of the nodes appropriatelly.
1468: * This method should be called under
1469: * MUTEX.writeAccess.
1470: */
1471: final void refreshImpl() {
1472: if (isInitialized()) {
1473: Array.this .refreshEntry(getNodesEntry());
1474: super .getArray(null).nodes();
1475: } else if (nodes != null) {
1476: for (Node n : nodes) {
1477: n.assignTo(this , -1);
1478: }
1479: }
1480: }
1481:
1482: /** This method can be called by subclasses that
1483: * directly modify the nodes collection to update the
1484: * state of the nodes appropriatelly.
1485: */
1486: protected final void refresh() {
1487: MUTEX.postWriteRequest(new Runnable() {
1488: public void run() {
1489: refreshImpl();
1490: }
1491: });
1492: }
1493:
1494: /** Getter for the entry.
1495: */
1496: final Entry getNodesEntry() {
1497: return nodesEntry;
1498: }
1499:
1500: /** This method allows subclasses (only in this package) to
1501: * provide own version of entry. Usefull for SortedArray.
1502: */
1503: Entry createNodesEntry() {
1504: return new AE();
1505: }
1506:
1507: /** Getter for nodes.
1508: */
1509: final Collection<Node> getCollection() {
1510: synchronized (getNodesEntry()) {
1511: if (nodes == null) {
1512: nodes = initCollection();
1513: }
1514: }
1515:
1516: return nodes;
1517: }
1518:
1519: /*
1520: * @param arr nodes to add
1521: * @return true if changed false if not
1522: */
1523: public boolean add(final Node[] arr) {
1524: synchronized (getNodesEntry()) {
1525: if (!getCollection().addAll(Arrays.asList(arr))) {
1526: // no change to the collection
1527: return false;
1528: }
1529:
1530: ;
1531: }
1532:
1533: refresh();
1534:
1535: return true;
1536: }
1537:
1538: /*
1539: * @param arr nodes to remove
1540: * @return true if changed false if not
1541: */
1542: public boolean remove(final Node[] arr) {
1543: synchronized (getNodesEntry()) {
1544: if (!getCollection().removeAll(Arrays.asList(arr))) {
1545: // the collection was not changed
1546: return false;
1547: }
1548: }
1549:
1550: refresh();
1551:
1552: return true;
1553: }
1554:
1555: /** One entry that holds all the nodes in the collection
1556: * member called nodes.
1557: */
1558: private final class AE extends Object implements Entry {
1559: AE() {
1560: }
1561:
1562: /** List of elements.
1563: */
1564: public Collection<Node> nodes() {
1565: Collection<Node> c = getCollection();
1566:
1567: if (c.isEmpty()) {
1568: return Collections.emptyList();
1569: } else {
1570: return new ArrayList<Node>(c);
1571: }
1572: }
1573:
1574: @Override
1575: public String toString() {
1576: return "Children.Array.AE" + getCollection(); // NOI18N
1577: }
1578:
1579: }
1580: }
1581:
1582: /** Implements the storage of node children by a map.
1583: * This class also permits
1584: * association of a key with any node and to remove nodes by key.
1585: * Subclasses should reasonably
1586: * implement {@link #add} and {@link #remove}.
1587: * @param T the key type
1588: */
1589: public static class Map<T> extends Children {
1590: /** A map to use to store children in.
1591: * Keys are <code>Object</code>s, values are {@link Node}s.
1592: * Do <em>not</em> modify elements in the map! Use it only for read access.
1593: */
1594: protected java.util.Map<T, Node> nodes;
1595:
1596: /** Constructs a new list with a supplied map object.
1597: * Should be used by subclasses desiring an alternate storage method.
1598: * The map must not be explicitly modified after creation.
1599: *
1600: * @param m the map to use for this list
1601: */
1602: protected Map(java.util.Map<T, Node> m) {
1603: nodes = m;
1604: }
1605:
1606: /** Constructs a new list using {@link HashMap}.
1607: */
1608: public Map() {
1609: }
1610:
1611: /** Getter for the map.
1612: * Ensures that the map has been initialized.
1613: */
1614: final java.util.Map<T, Node> getMap() {
1615: // package private only to simplify access from inner classes
1616: if (nodes == null) {
1617: nodes = initMap();
1618: }
1619:
1620: return nodes;
1621: }
1622:
1623: /** Called on first use.
1624: */
1625: final void callAddNotify() {
1626: this .setEntries(createEntries(getMap()));
1627:
1628: super .callAddNotify();
1629: }
1630:
1631: /** Method that allows subclasses (SortedMap) to redefine
1632: * order of entries.
1633: * @param map the map (Object, Node)
1634: * @return collection of (Entry)
1635: */
1636: Collection<? extends Entry> createEntries(
1637: java.util.Map<T, Node> map) {
1638: List<ME> l = new LinkedList<ME>();
1639: for (java.util.Map.Entry<T, Node> e : map.entrySet()) {
1640: l.add(new ME(e.getKey(), e.getValue()));
1641: }
1642: return l;
1643: }
1644:
1645: /** Allows subclasses that directly modifies the
1646: * map with nodes to synchronize the state of the children.
1647: * This method should be called under
1648: * MUTEX.writeAccess.
1649: */
1650: final void refreshImpl() {
1651: this .setEntries(createEntries(getMap()));
1652: }
1653:
1654: /** Allows subclasses that directly modifies the
1655: * map with nodes to synchronize the state of the children.
1656: */
1657: protected final void refresh() {
1658: try {
1659: PR.enterWriteAccess();
1660: refreshImpl();
1661: } finally {
1662: PR.exitWriteAccess();
1663: }
1664: }
1665:
1666: /** Allows subclasses that directly modifies the
1667: * map with nodes to synchronize the state of the children.
1668: * This method should be called under
1669: * MUTEX.writeAccess.
1670: *
1671: * @param key the key that should be refreshed
1672: */
1673: final void refreshKeyImpl(T key) {
1674: this .refreshEntry(new ME(key, null));
1675: }
1676:
1677: /** Allows subclasses that directly modifies the
1678: * map with nodes to synchronize the state of the children.
1679: *
1680: * @param key the key that should be refreshed
1681: */
1682: protected final void refreshKey(final T key) {
1683: try {
1684: PR.enterWriteAccess();
1685: refreshKeyImpl(key);
1686: } finally {
1687: PR.exitWriteAccess();
1688: }
1689: }
1690:
1691: /** Add a collection of new key/value pairs into the map.
1692: * The supplied map may contain any keys, but the values must be {@link Node}s.
1693: *
1694: * @param map the map with pairs to add
1695: */
1696: protected final void putAll(
1697: final java.util.Map<? extends T, ? extends Node> map) {
1698: try {
1699: PR.enterWriteAccess();
1700: nodes.putAll(map);
1701: refreshImpl();
1702:
1703: // PENDING sometime we should also call refreshKey...
1704: } finally {
1705: PR.exitWriteAccess();
1706: }
1707: }
1708:
1709: /** Add one key and one node to the list.
1710: * @param key the key
1711: * @param node the node
1712: */
1713: protected final void put(final T key, final Node node) {
1714: try {
1715: PR.enterWriteAccess();
1716:
1717: if (nodes.put(key, node) != null) {
1718: refreshKeyImpl(key);
1719: } else {
1720: refreshImpl();
1721: }
1722: } finally {
1723: PR.exitWriteAccess();
1724: }
1725: }
1726:
1727: /** Remove some children from the list by key.
1728: * @param keys collection of keys to remove
1729: */
1730: protected final void removeAll(
1731: final Collection<? extends T> keys) {
1732: try {
1733: PR.enterWriteAccess();
1734: nodes.keySet().removeAll(keys);
1735: refreshImpl();
1736: } finally {
1737: PR.exitWriteAccess();
1738: }
1739: }
1740:
1741: /** Remove a given child node from the list by its key.
1742: * @param key key to remove
1743: */
1744: protected void remove(final T key) {
1745: try {
1746: PR.enterWriteAccess();
1747:
1748: if (nodes.remove(key) != null) {
1749: refreshImpl();
1750: }
1751: } finally {
1752: PR.exitWriteAccess();
1753: }
1754: }
1755:
1756: /** Initialize some nodes. Allows a subclass to
1757: * provide a default map to initialize the map with.
1758: * Called only if the map has not been provided in the constructor.
1759: *
1760: * <P>
1761: * The default implementation returns <code>new HashMap (7)</code>.
1762: *
1763: * @return a map from keys to nodes
1764: */
1765: protected java.util.Map<T, Node> initMap() {
1766: return new HashMap<T, Node>(7);
1767: }
1768:
1769: /** Does nothing. Should be reimplemented in a subclass wishing
1770: * to support external addition of nodes.
1771: *
1772: * @param arr nodes to add
1773: * @return <code>false</code> in the default implementation
1774: */
1775: public boolean add(Node[] arr) {
1776: return false;
1777: }
1778:
1779: /** Does nothing. Should be reimplemented in a subclass wishing
1780: * to support external removal of nodes.
1781: * @param arr nodes to remove
1782: * @return <code>false</code> in the default implementation
1783: */
1784: public boolean remove(Node[] arr) {
1785: return false;
1786: }
1787:
1788: /** Entry mapping one key to the node.
1789: */
1790: final static class ME extends Object implements Entry {
1791: /** key */
1792: public Object key;
1793:
1794: /** node set */
1795: public Node node;
1796:
1797: /** Constructor.
1798: */
1799: public ME(Object key, Node node) {
1800: this .key = key;
1801: this .node = node;
1802: }
1803:
1804: /** Nodes */
1805: public Collection<Node> nodes() {
1806: return Collections.singleton(node);
1807: }
1808:
1809: /** Hash code.
1810: */
1811: public int hashCode() {
1812: return key.hashCode();
1813: }
1814:
1815: /** Equals.
1816: */
1817: public boolean equals(Object o) {
1818: if (o instanceof ME) {
1819: ME me = (ME) o;
1820:
1821: return key.equals(me.key);
1822: }
1823:
1824: return false;
1825: }
1826:
1827: public String toString() {
1828: return "Key (" + key + ")"; // NOI18N
1829: }
1830: }
1831: }
1832:
1833: /** Maintains a list of children sorted by the provided comparator in an array.
1834: * The comparator can change during the lifetime of the children, in which case
1835: * the children are resorted.
1836: */
1837: public static class SortedArray extends Children.Array {
1838: /** comparator to use */
1839: private Comparator<? super Node> comp;
1840:
1841: /** Create an empty list of children. */
1842: public SortedArray() {
1843: }
1844:
1845: /** Create an empty list with a specified storage method.
1846: *
1847: * @param c collection to store data in
1848: * @see Children.Array#Children.Array(Collection)
1849: */
1850: protected SortedArray(Collection<Node> c) {
1851: super (c);
1852: }
1853:
1854: /** Set the comparator. The children will be resorted.
1855: * The comparator is used to compare Nodes, if no
1856: * comparator is used then nodes will be compared by
1857: * the use of natural ordering.
1858: *
1859: * @param c the new comparator
1860: */
1861: public void setComparator(final Comparator<? super Node> c) {
1862: try {
1863: PR.enterWriteAccess();
1864: comp = c;
1865: refresh();
1866: } finally {
1867: PR.exitWriteAccess();
1868: }
1869: }
1870:
1871: /** Get the current comparator.
1872: * @return the comparator
1873: */
1874: public Comparator<? super Node> getComparator() {
1875: return comp;
1876: }
1877:
1878: /** This method allows subclasses (only in this package) to
1879: * provide own version of entry. Useful for SortedArray.
1880: */
1881: Entry createNodesEntry() {
1882: return new SAE();
1883: }
1884:
1885: /** One entry that holds all the nodes in the collection
1886: * member called nodes.
1887: */
1888: private final class SAE extends Object implements Entry {
1889: /** Constructor that provides the original comparator.
1890: */
1891: public SAE() {
1892: }
1893:
1894: /** List of elements.
1895: */
1896: public Collection<Node> nodes() {
1897: List<Node> al = new ArrayList<Node>(getCollection());
1898: Collections.sort(al, comp);
1899:
1900: return al;
1901: }
1902: }
1903: }
1904:
1905: // end of SortedArray
1906:
1907: /** Maintains a list of children sorted by the provided comparator in a map.
1908: * Similar to {@link Children.SortedArray}.
1909: */
1910: public static class SortedMap<T> extends Children.Map<T> {
1911: /** comparator to use */
1912: private Comparator<? super Node> comp;
1913:
1914: /** Create an empty list. */
1915: public SortedMap() {
1916: }
1917:
1918: /** Create an empty list with a specific storage method.
1919: *
1920: * @param map the map to use with this object
1921: * @see Children.Map#Children.Map(java.util.Map)
1922: */
1923: protected SortedMap(java.util.Map<T, Node> map) {
1924: super (map);
1925: }
1926:
1927: /** Set the comparator. The children will be resorted.
1928: * The comparator is used to compare Nodes, if no
1929: * comparator is used then values will be compared by
1930: * the use of natural ordering.
1931: *
1932: * @param c the new comparator that should compare nodes
1933: */
1934: public void setComparator(final Comparator<? super Node> c) {
1935: try {
1936: PR.enterWriteAccess();
1937: comp = c;
1938: refresh();
1939: } finally {
1940: PR.exitWriteAccess();
1941: }
1942: }
1943:
1944: /** Get the current comparator.
1945: * @return the comparator
1946: */
1947: public Comparator<? super Node> getComparator() {
1948: return comp;
1949: }
1950:
1951: /** Method that allows subclasses (SortedMap) to redefine
1952: * order of entries.
1953: * @param map the map (Object, Node)
1954: * @return collection of (Entry)
1955: */
1956: Collection<? extends Entry> createEntries(
1957: java.util.Map<T, Node> map) {
1958: // SME objects use natural ordering
1959: Set<ME> l = new TreeSet<ME>(new SMComparator());
1960:
1961: for (java.util.Map.Entry<T, Node> e : map.entrySet()) {
1962: l.add(new ME(e.getKey(), e.getValue()));
1963: }
1964:
1965: return l;
1966: }
1967:
1968: /** Sorted map entry can be used for comparing.
1969: */
1970: final class SMComparator implements Comparator<ME> {
1971: public int compare(ME me1, ME me2) {
1972: Comparator<? super Node> c = comp;
1973:
1974: if (c == null) {
1975: // compare keys
1976: @SuppressWarnings("unchecked")
1977: // we just assume that it is comparable, not statically checked
1978: int r = ((Comparable) me1.key).compareTo(me2.key);
1979: return r;
1980: } else {
1981: return c.compare(me1.node, me2.node);
1982: }
1983: }
1984: }
1985: }
1986:
1987: // end of SortedMap
1988:
1989: /** Implements an array of child nodes associated nonuniquely with keys and sorted by these keys.
1990: * There is a {@link #createNodes(Object) method} that should for each
1991: * key create an array of nodes that represents the key.
1992: *
1993: * <p>This class is preferable to {@link Children.Array} because
1994: * <ol>
1995: * <li>It more clearly separates model from view and encourages use of a discrete model.
1996: * <li>It correctly handles adding, removing, and reordering children while preserving
1997: * existing node selections in a tree (or other) view where possible.
1998: * </ol>
1999: *
2000: * <p>Typical usage:
2001: * <ol>
2002: * <li>Subclass.
2003: * <li>Decide what type your key should be.
2004: * <li>Implement {@link #createNodes} to create some nodes
2005: * (usually exactly one) per key.
2006: * <li>Override {@link Children#addNotify} to compute a set of keys
2007: * and set it using {@link #setKeys(Collection)}.
2008: * The collection may be ordered.
2009: * <li>Override {@link Children#removeNotify} to just call
2010: * <code>setKeys</code> on {@link Collections#EMPTY_SET}.
2011: * <li>When your model changes, call <code>setKeys</code>
2012: * with the new set of keys. <code>Children.Keys</code> will
2013: * be smart and calculate exactly what it needs to do effficiently.
2014: * <li><i>(Optional)</i> if your notion of what the node for a
2015: * given key changes (but the key stays the same), you can
2016: * call {@link #refreshKey}. Usually this is not necessary.
2017: * </ol>
2018: * Note that for simple cases, it may be preferable to subclass
2019: * <a href="ChildFactory.html">ChildFactory</a> and pass the result to
2020: * <a href="Children.html#create(org.openide.nodes.ChildFactory, boolean)">
2021: * create()</a>; doing so makes it easy to switch to using child
2022: * nodes computed on a background thread if necessary for performance
2023: * reasons.
2024: * @param T the type of the key
2025: */
2026: public static abstract class Keys<T> extends Children.Array {
2027: /** the last runnable (created in method setKeys) for each children object.
2028: */
2029: private static java.util.Map<Keys<?>, Runnable> lastRuns = new HashMap<Keys<?>, Runnable>(
2030: 11);
2031:
2032: /** add array children before or after keys ones */
2033: private boolean before;
2034:
2035: public Keys() {
2036: super ();
2037: }
2038:
2039: /** Special handling for clonning.
2040: */
2041: public Object clone() {
2042: Keys<?> k = (Keys<?>) super .clone();
2043:
2044: return k;
2045: }
2046:
2047: /**
2048: * @deprecated Do not use! Just call {@link #setKeys(Collection)} with a larger set.
2049: */
2050: @Deprecated
2051: public boolean add(Node[] arr) {
2052: return super .add(arr);
2053: }
2054:
2055: /**
2056: * @deprecated Do not use! Just call {@link #setKeys(Collection)} with a smaller set.
2057: */
2058: @Deprecated
2059: public boolean remove(final Node[] arr) {
2060: try {
2061: PR.enterWriteAccess();
2062:
2063: if (nodes != null) {
2064: // removing from array, just if the array nodes are
2065: // really created
2066: // expecting arr.length == 1, which is the usual case
2067: for (int i = 0; i < arr.length; i++) {
2068: if (!nodes.contains(arr[i])) {
2069: arr[i] = null;
2070: }
2071: }
2072:
2073: super .remove(arr);
2074: }
2075: } finally {
2076: PR.exitWriteAccess();
2077: }
2078:
2079: return true;
2080: }
2081:
2082: /** Refresh the child nodes for a given key.
2083: *
2084: * @param key the key to refresh
2085: */
2086: protected final void refreshKey(final T key) {
2087: MUTEX.postWriteRequest(new Runnable() {
2088: public void run() {
2089: Keys.this .refreshEntry(new KE(key));
2090: }
2091: });
2092: }
2093:
2094: /** Set new keys for this children object. Setting of keys
2095: * does not necessarily lead to the creation of nodes. It happens only
2096: * when the list has already been initialized.
2097: *
2098: * @param keysSet the keys for the nodes (collection of any objects)
2099: */
2100: protected final void setKeys(Collection<? extends T> keysSet) {
2101: boolean asserts = false;
2102: assert asserts = true;
2103: int sz = keysSet.size();
2104: if (asserts && sz < 10) {
2105: List<? extends T> keys = new ArrayList<T>(keysSet);
2106: for (int i = 0; i < sz - 1; i++) {
2107: T a = keys.get(i);
2108: for (int j = i + 1; j < sz; j++) {
2109: T b = keys.get(j);
2110: assert !(a.equals(b) && a.hashCode() != b
2111: .hashCode()) : "bad equals/hashCode in "
2112: + a + " vs. " + b;
2113: }
2114: }
2115: }
2116:
2117: final List<Entry> l = new ArrayList<Entry>(
2118: keysSet.size() + 1);
2119:
2120: if (before) {
2121: l.add(getNodesEntry());
2122: }
2123:
2124: KE updator = new KE();
2125: updator.updateList(keysSet, l);
2126:
2127: if (!before) {
2128: l.add(getNodesEntry());
2129: }
2130:
2131: applyKeys(l);
2132: }
2133:
2134: /** Set keys for this list.
2135: *
2136: * @param keys the keys for the nodes
2137: * @see #setKeys(Collection)
2138: */
2139: protected final void setKeys(final T[] keys) {
2140: boolean asserts = false;
2141: assert asserts = true;
2142: int sz = keys.length;
2143: if (asserts && sz < 10) {
2144: for (int i = 0; i < sz - 1; i++) {
2145: T a = keys[i];
2146: for (int j = i + 1; j < sz; j++) {
2147: T b = keys[j];
2148: assert !(a.equals(b) && a.hashCode() != b
2149: .hashCode()) : "bad equals/hashCode in "
2150: + a + " vs. " + b;
2151: }
2152: }
2153: }
2154:
2155: final List<Entry> l = new ArrayList<Entry>(keys.length + 1);
2156:
2157: KE updator = new KE();
2158:
2159: if (before) {
2160: l.add(getNodesEntry());
2161: }
2162:
2163: updator.updateList(keys, l);
2164:
2165: if (!before) {
2166: l.add(getNodesEntry());
2167: }
2168:
2169: applyKeys(l);
2170: }
2171:
2172: /** Applies the keys.
2173: */
2174: private void applyKeys(final List<? extends Entry> l) {
2175: Runnable invoke = new Runnable() {
2176: public void run() {
2177: if (keysCheck(Keys.this , this )) {
2178: // no next request after me
2179: Keys.this .setEntries(l);
2180:
2181: // clear this runnable
2182: keysExit(Keys.this , this );
2183: }
2184: }
2185: };
2186:
2187: keysEnter(this , invoke);
2188: MUTEX.postWriteRequest(invoke);
2189: }
2190:
2191: /** Set whether new nodes should be added to the beginning or end of sublists for a given key.
2192: * Generally should not be used.
2193: * @param b <code>true</code> if the children should be added before
2194: */
2195: protected final void setBefore(final boolean b) {
2196: try {
2197: PR.enterWriteAccess();
2198:
2199: if (before != b) {
2200: List<Entry> l = Keys.this .getEntries();
2201: l.remove(getNodesEntry());
2202: before = b;
2203:
2204: if (b) {
2205: l.add(0, getNodesEntry());
2206: } else {
2207: l.add(getNodesEntry());
2208: }
2209:
2210: Keys.this .setEntries(l);
2211: }
2212: } finally {
2213: PR.exitWriteAccess();
2214: }
2215: }
2216:
2217: /** Create nodes for a given key.
2218: * @param key the key
2219: * @return child nodes for this key or null if there should be no
2220: * nodes for this key
2221: */
2222: protected abstract Node[] createNodes(T key);
2223:
2224: /** Called when the nodes have been removed from the children.
2225: * This method should allow subclasses to clean the nodes somehow.
2226: * <p>
2227: * Current implementation notifies all listeners on the nodes
2228: * that nodes have been deleted.
2229: *
2230: * @param arr array of deleted nodes
2231: */
2232: protected void destroyNodes(Node[] arr) {
2233: for (int i = 0; i < arr.length; i++) {
2234: arr[i].fireNodeDestroyed();
2235: }
2236: }
2237:
2238: /** Notifies the children class that nodes has been released.
2239: */
2240: Node[] notifyRemove(Collection<Node> nodes, Node[] current) {
2241: Node[] arr = super .notifyRemove(nodes, current);
2242: destroyNodes(arr);
2243:
2244: return arr;
2245: }
2246:
2247: /** Enter of setKeys.
2248: * @param ch the children
2249: * @param run runnable
2250: */
2251: private static synchronized void keysEnter(Keys<?> ch,
2252: Runnable run) {
2253: lastRuns.put(ch, run);
2254: }
2255:
2256: /** Clears the entry for the children
2257: * @param ch children
2258: */
2259: private static synchronized void keysExit(Keys ch, Runnable r) {
2260: Runnable reg = lastRuns.remove(ch);
2261:
2262: if ((reg != null) && !reg.equals(r)) {
2263: lastRuns.put(ch, reg);
2264: }
2265: }
2266:
2267: /** Check whether the runnable is "the current" for given children.
2268: * @param ch children
2269: * @param run runnable
2270: * @return true if the runnable shoul run
2271: */
2272: private static synchronized boolean keysCheck(Keys ch,
2273: Runnable run) {
2274: return run == lastRuns.get(ch);
2275: }
2276:
2277: /** Entry for a key
2278: */
2279: private final class KE extends Dupl<T> {
2280: /** Has default constructor that allows to create a factory
2281: * of KE objects for use with updateList
2282: */
2283: public KE() {
2284: }
2285:
2286: /** Creates directly an instance of the KE object.
2287: */
2288: public KE(T key) {
2289: this .key = key;
2290: }
2291:
2292: /** Nodes are taken from the create nodes.
2293: */
2294: public Collection<Node> nodes() {
2295: Node[] arr = createNodes(getKey());
2296:
2297: if (arr == null) {
2298: return Collections.emptyList();
2299: } else {
2300: return new LinkedList<Node>(Arrays.asList(arr));
2301: }
2302: }
2303:
2304: @Override
2305: public String toString() {
2306: String s = getKey().toString();
2307: if (s.length() > 80) {
2308: s = s.substring(0, 80);
2309: }
2310: return "Children.Keys.KE[" + s + "," + getCnt() + "]"; // NOI18N
2311: }
2312: }
2313: }
2314:
2315: // end of Keys
2316:
2317: /** Supporting class that provides support for duplicated
2318: * objects that still should not be equal.
2319: * <P>
2320: * It counts the number of times an object has been
2321: * added to the collection and if the same object is added
2322: * more than once it is indexed by a number.
2323: */
2324: private static abstract class Dupl<T> implements Cloneable, Entry {
2325: /** the key either real value or Dupl (Dupl (Dupl (... value ...)))*/
2326: protected Object key;
2327:
2328: Dupl() {
2329: }
2330:
2331: /** Updates the second collection with values from the first one.
2332: * If there is a multiple occurrence of an object in the first collection
2333: * a Dupl for the object is created to encapsulate it.
2334: */
2335: public final void updateList(Collection<? extends T> src,
2336: Collection<? super Dupl<T>> target) {
2337: java.util.Map<T, Object> map = new HashMap<T, Object>(src
2338: .size() * 2);
2339: for (T o : src) {
2340: updateListAndMap(o, target, map);
2341: }
2342: }
2343:
2344: /** Updates the second collection with values from the first array.
2345: * If there is a multiple occurrence of an object in the first array
2346: * a Dupl for the object is created to encapsulate it.
2347: */
2348: public final void updateList(T[] arr,
2349: Collection<? super Dupl<T>> target) {
2350: java.util.Map<T, Object> map = new HashMap<T, Object>(
2351: arr.length * 2);
2352: for (T o : arr) {
2353: updateListAndMap(o, target, map);
2354: }
2355: }
2356:
2357: /** Updates the linked list and the map with right
2358: * values. The map is used to count the number of times
2359: * a element occurs in the list.
2360: *
2361: * @param obj object to add
2362: * @param list the list to add obj to
2363: * @param map to track number of occurrences in the array
2364: */
2365: public final void updateListAndMap(T obj,
2366: Collection<? super Dupl<T>> list,
2367: java.util.Map<T, Object> map) {
2368: // optimized for first occurrence
2369: // of each object because often occurrences should be rare
2370: Object prev = map.put(obj, this );
2371:
2372: if (prev == null) {
2373: // first occurrence of object obj
2374: list.add(createInstance(obj, 0));
2375:
2376: return;
2377: } else {
2378: if (prev == this ) {
2379: // second occurrence of the object
2380: map.put(obj, 1);
2381: list.add(createInstance(obj, 1));
2382:
2383: return;
2384: } else {
2385: int cnt = ((Integer) prev) + 1;
2386: map.put(obj, cnt);
2387: list.add(createInstance(obj, cnt));
2388:
2389: return;
2390: }
2391: }
2392: }
2393:
2394: /** Gets the key represented by this object.
2395: * @return the key
2396: */
2397: @SuppressWarnings("unchecked")
2398: // data structure really weird
2399: public final T getKey() {
2400: if (key instanceof Dupl) {
2401: return (T) ((Dupl) key).getKey();
2402: } else {
2403: return (T) key;
2404: }
2405: }
2406:
2407: /** Counts the index of this key.
2408: * @return integer
2409: */
2410: public final int getCnt() {
2411: int cnt = 0;
2412: Dupl d = this ;
2413:
2414: while (d.key instanceof Dupl) {
2415: d = (Dupl) d.key;
2416: cnt++;
2417: }
2418:
2419: return cnt;
2420: }
2421:
2422: /** Create instance of Dupl (uses cloning of the class)
2423: */
2424: @SuppressWarnings("unchecked")
2425: // data structure really weird
2426: private final Dupl<T> createInstance(Object obj, int cnt) {
2427: try {
2428: // creates the chain of Dupl (Dupl (Dupl (obj))) where
2429: // for cnt = 0 the it would look like Dupl (obj)
2430: // for cnt = 1 like Dupl (Dupl (obj))
2431: Dupl d = (Dupl) this .clone();
2432: Dupl first = d;
2433:
2434: while (cnt-- > 0) {
2435: Dupl n = (Dupl) this .clone();
2436: d.key = n;
2437: d = n;
2438: }
2439:
2440: d.key = obj;
2441:
2442: return first;
2443: } catch (CloneNotSupportedException ex) {
2444: throw new InternalError();
2445: }
2446: }
2447:
2448: @Override
2449: public int hashCode() {
2450: return getKey().hashCode();
2451: }
2452:
2453: @Override
2454: public boolean equals(Object o) {
2455: if (o instanceof Dupl) {
2456: Dupl d = (Dupl) o;
2457:
2458: return getKey().equals(d.getKey())
2459: && (getCnt() == d.getCnt());
2460: }
2461:
2462: return false;
2463: }
2464: }
2465:
2466: /*
2467: static void printNodes (Node[] n) {
2468: for (int i = 0; i < n.length; i++) {
2469: System.out.println (" " + i + ". " + n[i].getName () + " number: " + System.identityHashCode (n[i]));
2470: }
2471: }
2472: */
2473: /* JST: Useful test routine ;-) *
2474: static {
2475: final TopComponent.Registry r = TopComponent.getRegistry ();
2476: r.addPropertyChangeListener (new PropertyChangeListener () {
2477: Node last = new AbstractNode (LEAF);
2478:
2479: public void propertyChange (PropertyChangeEvent ev) {
2480: Node[] arr = r.getCurrentNodes ();
2481: if (arr != null && arr.length == 1) {
2482: last = arr[0];
2483: }
2484: System.out.println (
2485: "Activated node: " + last + " \nparent: " + last.getParentNode ()
2486: );
2487: }
2488: });
2489: }
2490: */
2491: }
|