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: package org.openide.util.lookup;
042:
043: import java.lang.ref.Reference;
044: import java.lang.ref.WeakReference;
045: import org.openide.util.Lookup;
046: import org.openide.util.LookupEvent;
047: import org.openide.util.LookupListener;
048:
049: import java.util.*;
050:
051: /**
052: * Simple proxy lookup. Keeps reference to a lookup it delegates to and
053: * forwards all requests.
054: *
055: * @author Jaroslav Tulach
056: */
057: final class SimpleProxyLookup extends org.openide.util.Lookup {
058: /** the provider to check for the status */
059: private Provider provider;
060:
061: /** the lookup we currently delegate to */
062: private Lookup delegate;
063:
064: /** map of all templates to Reference (results) associated to this lookup */
065: private WeakHashMap<Template<?>, Reference<ProxyResult<?>>> results;
066:
067: /**
068: * @param provider provider to delegate to
069: */
070: SimpleProxyLookup(Provider provider) {
071: this .provider = provider;
072: }
073:
074: /** Checks whether we still delegate to the same lookup */
075: private Lookup checkLookup() {
076: Lookup l = provider.getLookup();
077:
078: // iterator over Reference (ProxyResult)
079: Iterator<Reference<ProxyResult<?>>> toCheck = null;
080:
081: synchronized (this ) {
082: if (l != delegate) {
083: this .delegate = l;
084:
085: if (results != null) {
086: toCheck = new ArrayList<Reference<ProxyResult<?>>>(
087: results.values()).iterator();
088: }
089: }
090: }
091:
092: if (toCheck != null) {
093: // update
094: ArrayList<Object> evAndListeners = new ArrayList<Object>();
095: for (Iterator<Reference<ProxyResult<?>>> it = toCheck; it
096: .hasNext();) {
097: java.lang.ref.Reference<ProxyResult<?>> ref = it.next();
098: if (ref == null) {
099: continue;
100: }
101:
102: ProxyResult<?> p = ref.get();
103:
104: if (p != null && p.updateLookup(l)) {
105: p.collectFires(evAndListeners);
106: }
107: }
108:
109: for (Iterator it = evAndListeners.iterator(); it.hasNext();) {
110: LookupEvent ev = (LookupEvent) it.next();
111: LookupListener ll = (LookupListener) it.next();
112: ll.resultChanged(ev);
113: }
114: }
115:
116: return delegate;
117: }
118:
119: @SuppressWarnings("unchecked")
120: private static <T> ProxyResult<T> cast(ProxyResult<?> p) {
121: return (ProxyResult<T>) p;
122: }
123:
124: public <T> Result<T> lookup(Template<T> template) {
125: synchronized (this ) {
126: if (results == null) {
127: results = new WeakHashMap<Template<?>, Reference<ProxyResult<?>>>();
128: } else {
129: Reference<ProxyResult<?>> ref = results.get(template);
130:
131: if (ref != null) {
132: ProxyResult<?> p = ref.get();
133:
134: if (p != null) {
135: return cast(p);
136: }
137: }
138: }
139:
140: ProxyResult<T> p = new ProxyResult<T>(template);
141: Reference<ProxyResult<?>> ref = new WeakReference<ProxyResult<?>>(
142: p);
143: results.put(template, ref);
144:
145: return p;
146: }
147: }
148:
149: public <T> T lookup(Class<T> clazz) {
150: if (clazz == null) {
151: checkLookup();
152: return null;
153: }
154: return checkLookup().lookup(clazz);
155: }
156:
157: public <T> Item<T> lookupItem(Template<T> template) {
158: return checkLookup().lookupItem(template);
159: }
160:
161: /**
162: * Result used in SimpleLookup. It holds a reference to the collection
163: * passed in constructor. As the contents of this lookup result never
164: * changes the addLookupListener and removeLookupListener are empty.
165: */
166: private final class ProxyResult<T> extends WaitableResult<T>
167: implements LookupListener {
168: /** Template used for this result. It is never null.*/
169: private Template<T> template;
170:
171: /** result to delegate to */
172: private Lookup.Result<T> delegate;
173:
174: /** listeners set */
175: private javax.swing.event.EventListenerList listeners;
176: private LookupListener lastListener;
177:
178: /** Just remembers the supplied argument in variable template.*/
179: ProxyResult(Template<T> template) {
180: this .template = template;
181: }
182:
183: /** Checks state of the result
184: */
185: private Result<T> checkResult() {
186: updateLookup(checkLookup());
187:
188: return this .delegate;
189: }
190:
191: /** Updates the state of the lookup.
192: * @return true if the lookup really changed
193: */
194: public boolean updateLookup(Lookup l) {
195: Collection<? extends Item<T>> oldPairs = (delegate != null) ? delegate
196: .allItems()
197: : null;
198:
199: LookupListener removedListener;
200:
201: synchronized (this ) {
202: if ((delegate != null) && (lastListener != null)) {
203: removedListener = lastListener;
204: delegate.removeLookupListener(lastListener);
205: } else {
206: removedListener = null;
207: }
208: }
209:
210: // cannot call to foreign code
211: Lookup.Result<T> res = l.lookup(template);
212:
213: synchronized (this ) {
214: if (removedListener == lastListener) {
215: delegate = res;
216: lastListener = new WeakResult<T>(this , delegate);
217: delegate.addLookupListener(lastListener);
218: }
219: }
220:
221: if (oldPairs == null) {
222: // nobody knows about a change
223: return false;
224: }
225:
226: Collection<? extends Item<T>> newPairs = delegate
227: .allItems();
228:
229: // See #34961 for explanation.
230: if (!(oldPairs instanceof List)) {
231: if (oldPairs == Collections.EMPTY_SET) {
232: // avoid allocation
233: oldPairs = Collections.emptyList();
234: } else {
235: oldPairs = new ArrayList<Item<T>>(oldPairs);
236: }
237: }
238:
239: if (!(newPairs instanceof List)) {
240: newPairs = new ArrayList<Item<T>>(newPairs);
241: }
242:
243: return !oldPairs.equals(newPairs);
244: }
245:
246: public synchronized void addLookupListener(LookupListener l) {
247: if (listeners == null) {
248: listeners = new javax.swing.event.EventListenerList();
249: }
250:
251: listeners.add(LookupListener.class, l);
252: }
253:
254: public synchronized void removeLookupListener(LookupListener l) {
255: if (listeners != null) {
256: listeners.remove(LookupListener.class, l);
257: }
258: }
259:
260: public java.util.Collection<? extends T> allInstances() {
261: return checkResult().allInstances();
262: }
263:
264: public Set<Class<? extends T>> allClasses() {
265: return checkResult().allClasses();
266: }
267:
268: public Collection<? extends Item<T>> allItems() {
269: return checkResult().allItems();
270: }
271:
272: protected void beforeLookup(Lookup.Template t) {
273: Lookup.Result r = checkResult();
274:
275: if (r instanceof WaitableResult) {
276: ((WaitableResult) r).beforeLookup(t);
277: }
278: }
279:
280: /** A change in lookup occured.
281: * @param ev event describing the change
282: *
283: */
284: public void resultChanged(LookupEvent anEvent) {
285: collectFires(null);
286: }
287:
288: protected void collectFires(Collection<Object> evAndListeners) {
289: javax.swing.event.EventListenerList l = this .listeners;
290:
291: if (l == null) {
292: return;
293: }
294:
295: Object[] listeners = l.getListenerList();
296:
297: if (listeners.length == 0) {
298: return;
299: }
300:
301: LookupEvent ev = new LookupEvent(this );
302: AbstractLookup.notifyListeners(listeners, ev,
303: evAndListeners);
304: }
305: }
306:
307: // end of ProxyResult
308: private final class WeakResult<T> extends WaitableResult<T>
309: implements LookupListener {
310: private Lookup.Result source;
311: private Reference<ProxyResult<T>> result;
312:
313: public WeakResult(ProxyResult<T> r, Lookup.Result<T> s) {
314: this .result = new WeakReference<ProxyResult<T>>(r);
315: this .source = s;
316: }
317:
318: protected void beforeLookup(Lookup.Template t) {
319: ProxyResult r = (ProxyResult) result.get();
320: if (r != null) {
321: r.beforeLookup(t);
322: } else {
323: source.removeLookupListener(this );
324: }
325: }
326:
327: protected void collectFires(Collection<Object> evAndListeners) {
328: ProxyResult<T> r = result.get();
329: if (r != null) {
330: r.collectFires(evAndListeners);
331: } else {
332: source.removeLookupListener(this );
333: }
334: }
335:
336: public void addLookupListener(LookupListener l) {
337: assert false;
338: }
339:
340: public void removeLookupListener(LookupListener l) {
341: assert false;
342: }
343:
344: public Collection<T> allInstances() {
345: assert false;
346: return null;
347: }
348:
349: public void resultChanged(LookupEvent ev) {
350: ProxyResult r = (ProxyResult) result.get();
351: if (r != null) {
352: r.resultChanged(ev);
353: } else {
354: source.removeLookupListener(this );
355: }
356: }
357:
358: public Collection<? extends Item<T>> allItems() {
359: assert false;
360: return null;
361: }
362:
363: public Set<Class<? extends T>> allClasses() {
364: assert false;
365: return null;
366: }
367: } // end of WeakResult
368: }
|