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 threaddemo.model;
043:
044: import java.awt.EventQueue;
045: import java.lang.ref.Reference;
046: import java.lang.ref.WeakReference;
047: import java.lang.reflect.Method;
048: import java.util.AbstractList;
049: import java.util.List;
050: import java.util.Map;
051: import java.util.WeakHashMap;
052: import spin.Spin;
053: import spin.Starter;
054: import threaddemo.locking.Locks;
055: import threaddemo.locking.Worker;
056:
057: /**
058: * An asynchronous Phadhail impl using Spin.
059: * Spin does not appear to handle nested beans so we do that part manually.
060: * We keep a dedicated single thread for running stuff in
061: * (Spin's default behavior is to spawn a thread for every method call).
062: * @author Jesse Glick
063: */
064: final class SpunPhadhail extends Spin {
065:
066: private static final Starter starter = new Starter() {
067: public void start(Runnable r) {
068: Worker.start(r);
069: }
070: };
071:
072: private static final Map<Phadhail, Reference<Phadhail>> instances = new WeakHashMap<Phadhail, Reference<Phadhail>>();
073:
074: /** factory */
075: public static Phadhail forPhadhail(Phadhail _ph) {
076: assert EventQueue.isDispatchThread();
077: Reference<Phadhail> r = instances.get(_ph);
078: Phadhail ph = (r != null) ? r.get() : null;
079: if (ph == null) {
080: Spin spin = new SpunPhadhail(_ph);
081: ph = BufferedPhadhail.forPhadhail((Phadhail) spin
082: .getProxy());
083: instances.put(_ph, new WeakReference<Phadhail>(ph));
084: }
085: return ph;
086: }
087:
088: private final Phadhail ph;
089:
090: private SpunPhadhail(Phadhail ph) {
091: super (ph, Spin.SPIN_OFF, starter);
092: this .ph = ph;
093: }
094:
095: /** overridden to recursively wrap phadhails */
096: public Object invoke(Object proxy, Method method, Object[] args)
097: throws Throwable {
098: String mname = method.getName();
099: if (mname.equals("toString")) {
100: return "SpunPhadhail<" + ph + ">";
101: } else if (mname.equals("lock")) {
102: return Locks.event();
103: } else if (mname.equals("equals")) {
104: return args[0] == ph ? Boolean.TRUE : Boolean.FALSE;
105: } else if (mname.equals("hashCode")) {
106: return new Integer(ph.hashCode());
107: } else if (mname.endsWith("PhadhailListener")) {
108: // Can do this synch - it's thread-safe and fast.
109: assert args != null;
110: assert args.length == 1;
111: // Need to wrap this too!
112: Spin spin = new SpunPhadhailListener(
113: (PhadhailListener) args[0], (Phadhail) proxy);
114: PhadhailListener l = (PhadhailListener) spin.getProxy();
115: if (mname.equals("addPhadhailListener")) {
116: ph.addPhadhailListener(l);
117: } else {
118: assert mname.equals("removePhadhailListener") : mname;
119: ph.removePhadhailListener(l);
120: }
121: return null;
122: } else {
123: assert EventQueue.isDispatchThread() : mname;
124: Object result = super .invoke(proxy, method, args);
125: if (result instanceof Phadhail) {
126: return forPhadhail((Phadhail) result);
127: } else if (result instanceof List) {
128: // I.e. from getChildren(). Need to wrap result phadhails.
129: @SuppressWarnings("unchecked")
130: List<Phadhail> l = (List<Phadhail>) result;
131: return new SpunChildrenList(l);
132: } else {
133: // Just pass on the call.
134: return result;
135: }
136: }
137: }
138:
139: private static final class SpunChildrenList extends
140: AbstractList<Phadhail> {
141: private final List<Phadhail> orig;
142: private final Phadhail[] kids;
143:
144: public SpunChildrenList(List<Phadhail> orig) {
145: this .orig = orig;
146: kids = new Phadhail[orig.size()];
147: }
148:
149: public Phadhail get(int i) {
150: assert EventQueue.isDispatchThread();
151: if (kids[i] == null) {
152: kids[i] = forPhadhail(orig.get(i));
153: }
154: return kids[i];
155: }
156:
157: public int size() {
158: assert EventQueue.isDispatchThread();
159: return kids.length;
160: }
161: }
162:
163: private static final class SpunPhadhailListener extends Spin {
164:
165: private final PhadhailListener l;
166: private final Phadhail ph;
167:
168: public SpunPhadhailListener(PhadhailListener l, Phadhail ph) {
169: super (l, Spin.SPIN_OVER, starter);
170: this .l = l;
171: this .ph = ph;
172: }
173:
174: /** overridden to translate PhadhailEvent's */
175: public Object invoke(Object proxy, Method method, Object[] args)
176: throws Throwable {
177: String mname = method.getName();
178: if (mname.equals("toString")) {
179: return "SpunPhadhailListener<" + l + ">";
180: } else if (mname.equals("equals")) {
181: return args[0] == l ? Boolean.TRUE : Boolean.FALSE;
182: } else if (mname.equals("hashCode")) {
183: return new Integer(l.hashCode());
184: } else {
185: assert mname.endsWith("Changed") : mname;
186: assert EventQueue.isDispatchThread() : mname;
187: assert args != null;
188: assert args.length == 1;
189: Object arg2;
190: // Need to translate the original Phadhail event source to the proxy.
191: if (mname.equals("childrenChanged")) {
192: arg2 = PhadhailEvent.create(ph);
193: } else {
194: assert mname.equals("nameChanged");
195: PhadhailNameEvent orig = (PhadhailNameEvent) args[0];
196: arg2 = PhadhailNameEvent.create(ph, orig
197: .getOldName(), orig.getNewName());
198: }
199: return super .invoke(proxy, method,
200: new Object[] { arg2 });
201: }
202: }
203:
204: }
205:
206: }
|