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-2007 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: package org.netbeans.modules.visualweb.insync.live;
042:
043: import java.lang.reflect.InvocationTargetException;
044: import java.lang.reflect.Method;
045: import java.util.Set;
046: import org.openide.nodes.*;
047: import org.openide.util.lookup.AbstractLookup;
048: import org.openide.util.lookup.InstanceContent;
049:
050: import java.beans.PropertyChangeEvent;
051:
052: import java.util.ArrayList;
053: import java.util.Collection;
054: import java.util.Iterator;
055:
056: /** XXX Copied from NB openide/nodes, in order to get the lookup impl.
057: * See also NB issue #81540.
058: *
059: * A lookup that represents content of a Node.getCookie and the node itself.
060: *
061: *
062: * @author Jaroslav Tulach
063: */
064: final class DesignBeanNodeLookup extends AbstractLookup {
065: /** See #40734 and NodeLookupTest and CookieActionIsTooSlowTest.
066: * When finding action state for FilterNode, the action might been
067: * triggered way to many times, due to initialization in beforeLookup
068: * that triggered LookupListener and PROP_COOKIE change.
069: */
070: static final ThreadLocal NO_COOKIE_CHANGE = new ThreadLocal();
071:
072: /** Set of Classes that we have already queried <type>Class</type> */
073: private java.util.Collection queriedCookieClasses = new ArrayList();
074:
075: /** node we are associated with
076: */
077: private Node node;
078:
079: /** New flat lookup.
080: */
081: public DesignBeanNodeLookup() {
082: super ();
083: }
084:
085: public void setNode(Node n) {
086: this .node = n;
087: addPair(new LookupItem(n));
088: }
089:
090: /** Calls into Node to find out if it has a cookie of given class.
091: * It does special tricks to make CookieSet.Entry work.
092: *
093: * @param node node to ask
094: * @param c class to query
095: * @param colleciton to put Pair into if found
096: */
097: private static void addCookie(Node node, Class c,
098: Collection collection, java.util.Map fromPairToClass) {
099: Object res;
100: AbstractLookup.Pair pair;
101: // Object prev = CookieSet.entryQueryMode(c);
102: Object prev = cookieSetEntryQueryMode(c);
103:
104: try {
105: res = node.getCookie(c);
106: } finally {
107: // pair = CookieSet.exitQueryMode(prev);
108: pair = cookieSetExitQueryMode(prev);
109: }
110:
111: if (pair == null) {
112: if (res == null) {
113: return;
114: }
115:
116: pair = new LookupItem(res);
117: }
118:
119: collection.add(pair);
120: fromPairToClass.put(pair, c);
121: }
122:
123: /** Notifies subclasses that a query is about to be processed.
124: * @param template the template
125: */
126: protected final void beforeLookup(Template template) {
127: Class type = template.getType();
128:
129: if (type == Object.class) {
130: // ok, this is likely query for everything
131: java.util.Set all;
132: Object prev = null;
133:
134: try {
135: // prev = CookieSet.entryAllClassesMode();
136: prev = cookieSetEntryAllClassesMode();
137:
138: Object ignoreResult = node.getCookie(Node.Cookie.class);
139: } finally {
140: // all = CookieSet.exitAllClassesMode(prev);
141: all = cookieSetExitAllClassesMode(prev);
142: }
143:
144: Iterator it = all.iterator();
145:
146: while (it.hasNext()) {
147: Class c = (Class) it.next();
148: updateLookupAsCookiesAreChanged(c);
149: }
150:
151: // fallthru and update Node.Cookie if not yet
152: type = Node.Cookie.class;
153: }
154:
155: if (Node.Cookie.class.isAssignableFrom(type)) {
156: if (!queriedCookieClasses.contains(type)) {
157: updateLookupAsCookiesAreChanged(type);
158: }
159: }
160: }
161:
162: public void updateLookupAsCookiesAreChanged(Class toAdd) {
163: java.util.Collection instances;
164: java.util.Map fromPairToQueryClass;
165:
166: // if it is cookie change, do the rescan, try to keep order
167: synchronized (this ) {
168: if (toAdd != null) {
169: if (queriedCookieClasses.contains(toAdd)) {
170: // if this class has already been added, go away
171: return;
172: }
173:
174: queriedCookieClasses.add(toAdd);
175: }
176:
177: instances = new java.util.LinkedHashSet(
178: queriedCookieClasses.size());
179: fromPairToQueryClass = new java.util.HashMap();
180:
181: java.util.Iterator it = queriedCookieClasses.iterator();
182: LookupItem nodePair = new LookupItem(node);
183: instances.add(nodePair);
184: fromPairToQueryClass.put(nodePair, Node.class);
185:
186: while (it.hasNext()) {
187: Class c = (Class) it.next();
188: addCookie(node, c, instances, fromPairToQueryClass);
189: }
190: }
191:
192: final java.util.Map m = fromPairToQueryClass;
193:
194: class Cmp implements java.util.Comparator {
195: public int compare(Object o1, Object o2) {
196: Pair p1 = (Pair) o1;
197: Pair p2 = (Pair) o2;
198: Class c1 = (Class) m.get(p1);
199: Class c2 = (Class) m.get(p2);
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 list = new java.util.ArrayList(instances);
222: java.util.Collections.sort(list, new Cmp());
223:
224: if (toAdd == null) {
225: setPairs(list);
226: } else {
227: Object prev = NO_COOKIE_CHANGE.get();
228:
229: try {
230: NO_COOKIE_CHANGE.set(node);
231:
232: // doing the setPairs under entryQueryMode guarantees that
233: // FilterNode will ignore the change
234: setPairs(list);
235: } finally {
236: NO_COOKIE_CHANGE.set(prev);
237: }
238: }
239: }
240:
241: private static Object cookieSetEntryQueryMode(Class c) {
242: return invokeOnCookieSet("entryQueryMode",
243: new Class[] { Class.class }, new Object[] { c });
244: }
245:
246: private static Pair cookieSetExitQueryMode(Object prev) {
247: Collection col = (Collection) invokeOnCookieSet(
248: "exitQueryMode", new Class[] { Object.class },
249: new Object[] { prev });
250: return col != null ? (Pair) col.iterator().next() : null;
251: }
252:
253: private Object cookieSetEntryAllClassesMode() {
254: return invokeOnCookieSet("entryAllClassesMode", new Class[0],
255: new Object[0]);
256: }
257:
258: private Set cookieSetExitAllClassesMode(Object prev) {
259: return (Set) invokeOnCookieSet("exitAllClassesMode",
260: new Class[] { Object.class }, new Object[] { prev });
261: }
262:
263: // XXX There seems to be no other way how to fake the same behaviour, so calling the methods via reflection.
264: private static Object invokeOnCookieSet(String methodName,
265: Class[] parameterTypes, Object[] parameters) {
266: try {
267: Method method = CookieSet.class.getDeclaredMethod(
268: methodName, parameterTypes);
269: method.setAccessible(true);
270: try {
271: return method.invoke(null, parameters);
272: } catch (IllegalArgumentException ex) {
273: ex.printStackTrace();
274: } catch (InvocationTargetException ex) {
275: ex.printStackTrace();
276: } catch (IllegalAccessException ex) {
277: ex.printStackTrace();
278: }
279: } catch (SecurityException ex) {
280: ex.printStackTrace();
281: } catch (NoSuchMethodException ex) {
282: ex.printStackTrace();
283: }
284: return null;
285: }
286:
287: /** Simple Pair to hold cookies and nodes */
288: private static class LookupItem extends AbstractLookup.Pair {
289: private Object instance;
290:
291: public LookupItem(Object instance) {
292: this .instance = instance;
293: }
294:
295: public String getDisplayName() {
296: return getId();
297: }
298:
299: public String getId() {
300: return instance.toString();
301: }
302:
303: public Object getInstance() {
304: return instance;
305: }
306:
307: public Class getType() {
308: return instance.getClass();
309: }
310:
311: public boolean equals(Object object) {
312: if (object instanceof LookupItem) {
313: return instance == ((LookupItem) object).getInstance();
314: }
315:
316: return false;
317: }
318:
319: public int hashCode() {
320: return instance.hashCode();
321: }
322:
323: protected boolean creatorOf(Object obj) {
324: return instance == obj;
325: }
326:
327: protected boolean instanceOf(Class c) {
328: return c.isInstance(instance);
329: }
330: }
331: // End of LookupItem class
332: }
|