001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.nodes;
043:
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.Collections;
047: import java.util.Iterator;
048: import org.openide.util.Lookup.Template;
049: import org.openide.util.lookup.AbstractLookup;
050: import org.openide.util.lookup.AbstractLookup.Pair;
051:
052: /** A lookup that represents content of a Node.getCookie and the node itself.
053: *
054: *
055: * @author Jaroslav Tulach
056: */
057: final class NodeLookup extends AbstractLookup {
058: /** See #40734 and NodeLookupTest and CookieActionIsTooSlowTest.
059: * When finding action state for FilterNode, the action might been
060: * triggered way to many times, due to initialization in beforeLookup
061: * that triggered LookupListener and PROP_COOKIE change.
062: */
063: static final ThreadLocal<Node> NO_COOKIE_CHANGE = new ThreadLocal<Node>();
064:
065: /** Set of Classes that we have already queried <type>Class</type> */
066: private java.util.Collection<Class> queriedCookieClasses = new ArrayList<Class>();
067:
068: /** node we are associated with
069: */
070: private Node node;
071:
072: /** New flat lookup.
073: */
074: public NodeLookup(Node n) {
075: super ();
076:
077: this .node = n;
078: addPair(new LookupItem(n));
079: }
080:
081: /** Calls into Node to find out if it has a cookie of given class.
082: * It does special tricks to make CookieSet.Entry work.
083: *
084: * @param node node to ask
085: * @param c class to query
086: * @param colleciton to put Pair into if found
087: */
088: private static void addCookie(Node node, Class<?> c,
089: Collection<AbstractLookup.Pair> collection,
090: java.util.Map<AbstractLookup.Pair, Class> fromPairToClass) {
091: Object res;
092: Collection<AbstractLookup.Pair> pairs;
093: Object prev = CookieSet.entryQueryMode(c);
094:
095: try {
096: @SuppressWarnings("unchecked")
097: Class<? extends Node.Cookie> fake = (Class<? extends Node.Cookie>) c;
098: res = node.getCookie(fake);
099: } finally {
100: pairs = CookieSet.exitQueryMode(prev);
101: }
102:
103: if (pairs == null) {
104: if (res == null) {
105: return;
106: }
107:
108: pairs = Collections
109: .singleton((AbstractLookup.Pair) new LookupItem(res));
110: }
111:
112: collection.addAll(pairs);
113: for (AbstractLookup.Pair p : pairs) {
114: Class<?> oldClazz = fromPairToClass.get(p);
115: if (oldClazz == null || c.isAssignableFrom(oldClazz)) {
116: fromPairToClass.put(p, c);
117: }
118: }
119: }
120:
121: /** Notifies subclasses that a query is about to be processed.
122: * @param template the template
123: */
124: protected final void beforeLookup(Template template) {
125: Class type = template.getType();
126:
127: if (type == Object.class) {
128: // ok, this is likely query for everything
129: java.util.Set all;
130: Object prev = null;
131:
132: try {
133: prev = CookieSet.entryAllClassesMode();
134:
135: Object ignoreResult = node.getCookie(Node.Cookie.class);
136: } finally {
137: all = CookieSet.exitAllClassesMode(prev);
138: }
139:
140: Iterator it = all.iterator();
141:
142: while (it.hasNext()) {
143: Class c = (Class) it.next();
144: updateLookupAsCookiesAreChanged(c);
145: }
146:
147: // update Node.Cookie if not yet
148: if (!queriedCookieClasses.contains(Node.Cookie.class)) {
149: updateLookupAsCookiesAreChanged(Node.Cookie.class);
150: }
151: }
152:
153: if (!queriedCookieClasses.contains(type)) {
154: updateLookupAsCookiesAreChanged(type);
155: }
156: }
157:
158: public void updateLookupAsCookiesAreChanged(Class toAdd) {
159: java.util.Collection<AbstractLookup.Pair> instances;
160: java.util.Map<AbstractLookup.Pair, Class> fromPairToQueryClass;
161:
162: // if it is cookie change, do the rescan, try to keep order
163: synchronized (this ) {
164: if (toAdd != null) {
165: if (queriedCookieClasses.contains(toAdd)) {
166: // if this class has already been added, go away
167: return;
168: }
169:
170: queriedCookieClasses.add(toAdd);
171: }
172:
173: instances = new java.util.LinkedHashSet<AbstractLookup.Pair>(
174: queriedCookieClasses.size());
175: fromPairToQueryClass = new java.util.LinkedHashMap<AbstractLookup.Pair, Class>();
176:
177: java.util.Iterator<Class> it = /* #74334 */new ArrayList<Class>(
178: queriedCookieClasses).iterator();
179: LookupItem nodePair = new LookupItem(node);
180: instances.add(nodePair);
181: fromPairToQueryClass.put(nodePair, Node.class);
182:
183: while (it.hasNext()) {
184: Class c = it.next();
185: addCookie(node, c, instances, fromPairToQueryClass);
186: }
187: }
188:
189: final java.util.Map<AbstractLookup.Pair, Class> m = fromPairToQueryClass;
190:
191: class Cmp implements java.util.Comparator<AbstractLookup.Pair> {
192: public int compare(AbstractLookup.Pair p1,
193: AbstractLookup.Pair p2) {
194: Class<?> c1 = m.get(p1);
195: Class<?> c2 = m.get(p2);
196:
197: if (c1 == c2) {
198: return 0;
199: }
200:
201: if (c1.isAssignableFrom(c2)) {
202: return -1;
203: }
204:
205: if (c2.isAssignableFrom(c1)) {
206: return 1;
207: }
208:
209: if (c1.isAssignableFrom(p2.getType())) {
210: return -1;
211: }
212:
213: if (c2.isAssignableFrom(p1.getType())) {
214: return 1;
215: }
216:
217: return 0;
218: }
219: }
220:
221: java.util.ArrayList<AbstractLookup.Pair> list = new java.util.ArrayList<AbstractLookup.Pair>(
222: instances);
223: java.util.Collections.sort(list, new Cmp());
224:
225: if (toAdd == null) {
226: setPairs(list);
227: } else {
228: Node prev = NO_COOKIE_CHANGE.get();
229:
230: try {
231: NO_COOKIE_CHANGE.set(node);
232:
233: // doing the setPairs under entryQueryMode guarantees that
234: // FilterNode will ignore the change
235: setPairs(list);
236: } finally {
237: NO_COOKIE_CHANGE.set(prev);
238: }
239: }
240: }
241:
242: /** Simple Pair to hold cookies and nodes */
243: private static class LookupItem extends AbstractLookup.Pair {
244: private Object instance;
245:
246: public LookupItem(Object instance) {
247: this .instance = instance;
248: }
249:
250: public String getDisplayName() {
251: return getId();
252: }
253:
254: public String getId() {
255: return instance.toString();
256: }
257:
258: public Object getInstance() {
259: return instance;
260: }
261:
262: public Class getType() {
263: return instance.getClass();
264: }
265:
266: public boolean equals(Object object) {
267: if (object instanceof LookupItem) {
268: return instance == ((LookupItem) object).getInstance();
269: }
270:
271: return false;
272: }
273:
274: public int hashCode() {
275: return instance.hashCode();
276: }
277:
278: protected boolean creatorOf(Object obj) {
279: return instance == obj;
280: }
281:
282: protected boolean instanceOf(Class c) {
283: return c.isInstance(instance);
284: }
285: }
286: // End of LookupItem class
287: }
|