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.io.IOException;
046: import java.io.InputStream;
047: import java.io.OutputStream;
048: import java.lang.ref.Reference;
049: import java.lang.ref.WeakReference;
050: import java.util.AbstractList;
051: import java.util.ArrayList;
052: import java.util.Collections;
053: import java.util.List;
054: import java.util.Map;
055: import java.util.WeakHashMap;
056: import java.util.logging.Level;
057: import java.util.logging.Logger;
058: import javax.swing.SwingUtilities;
059: import threaddemo.locking.RWLock;
060: import threaddemo.locking.LockAction;
061: import threaddemo.locking.LockExceptionAction;
062: import threaddemo.locking.Locks;
063: import threaddemo.locking.Worker;
064:
065: /**
066: * Phadhail model impl using a technique like SwingWorker.
067: * Fairly complicated. Methods are broken down into several categories:
068: * 1. Simple methods like delete() will just block on the work thread.
069: * 2. Ditto hasChildren(), but the result is cached.
070: * 3. name + path return a dummy initial value and later fire a change.
071: * 4. Ditto children, but then the results must be wrapped too.
072: * 5. create* also wraps results.
073: * 6. Listeners are added asynch and their callbacks must be posted back to AWT too.
074: * For a more complex model, you could use Proxy to do this stuff, if there some kind
075: * of map giving the desired thread behavior of each method.
076: * @author Jesse Glick
077: */
078: final class SwungPhadhail implements Phadhail, PhadhailListener {
079:
080: private static final Logger logger = Logger
081: .getLogger(SwungPhadhail.class.getName());
082:
083: private static final Map<Phadhail, Reference<Phadhail>> instances = new WeakHashMap<Phadhail, Reference<Phadhail>>();
084:
085: /** factory */
086: public static Phadhail forPhadhail(Phadhail _ph) {
087: assert EventQueue.isDispatchThread();
088: Reference<Phadhail> r = instances.get(_ph);
089: Phadhail ph = (r != null) ? r.get() : null;
090: if (ph == null) {
091: ph = new SwungPhadhail(_ph);
092: instances.put(_ph, new WeakReference<Phadhail>(ph));
093: }
094: return ph;
095: }
096:
097: private final Phadhail ph;
098: private String name = null;
099: private String path = null;
100: private boolean computingName = false;
101: private List<Phadhail> children = null;
102: private boolean computingChildren = false;
103: private Boolean leaf = null;
104: private List<PhadhailListener> listeners = null;
105:
106: private SwungPhadhail(Phadhail ph) {
107: this .ph = ph;
108: }
109:
110: private void fireNameChanged() {
111: assert EventQueue.isDispatchThread();
112: // XXX synch on listeners to get them, then release
113: if (listeners != null) {
114: PhadhailNameEvent ev = PhadhailNameEvent.create(this , null,
115: null);
116: for (PhadhailListener l : listeners) {
117: logger.log(Level.FINER,
118: "fireNameChanged for {0} to {1}", new Object[] {
119: this , l });
120: l.nameChanged(ev);
121: }
122: }
123: }
124:
125: private String getNameOrPath(boolean p) {
126: assert EventQueue.isDispatchThread();
127: if ((p ? path : name) != null) {
128: logger.log(Level.FINER, "cached name for {0}", this );
129: return (p ? path : name);
130: } else {
131: if (!computingName) {
132: computingName = true;
133: logger.log(Level.FINER, "calculating name for {0}",
134: this );
135: Worker.start(new Runnable() {
136: public void run() {
137: final String n = ph.getName();
138: final String p = ph.getPath();
139: SwingUtilities.invokeLater(new Runnable() {
140: public void run() {
141: name = n;
142: path = p;
143: computingName = false;
144: logger.log(Level.FINER,
145: "fireNameChanged for {0}",
146: SwungPhadhail.this );
147: fireNameChanged();
148: }
149: });
150: }
151: });
152: }
153: logger.log(Level.FINER, "dummy name for {0}", this );
154: return (p ? "Please wait..." : "computingName");
155: }
156: }
157:
158: public String getName() {
159: return getNameOrPath(false);
160: }
161:
162: public String getPath() {
163: return getNameOrPath(true);
164: }
165:
166: private Phadhail createPhadhail(final String name,
167: final boolean container) throws IOException {
168: assert EventQueue.isDispatchThread();
169: return forPhadhail(Worker
170: .block(new LockExceptionAction<Phadhail, IOException>() {
171: public Phadhail run() throws IOException {
172: if (container) {
173: return ph.createContainerPhadhail(name);
174: } else {
175: return ph.createLeafPhadhail(name);
176: }
177: }
178: }));
179: }
180:
181: public Phadhail createContainerPhadhail(String name)
182: throws IOException {
183: return createPhadhail(name, true);
184: }
185:
186: public Phadhail createLeafPhadhail(String name) throws IOException {
187: return createPhadhail(name, false);
188: }
189:
190: public void rename(final String nue) throws IOException {
191: assert EventQueue.isDispatchThread();
192: Worker.block(new LockExceptionAction<Void, IOException>() {
193: public Void run() throws IOException {
194: ph.rename(nue);
195: return null;
196: }
197: });
198: }
199:
200: public void delete() throws IOException {
201: assert EventQueue.isDispatchThread();
202: Worker.block(new LockExceptionAction<Void, IOException>() {
203: public Void run() throws IOException {
204: ph.delete();
205: return null;
206: }
207: });
208: }
209:
210: private void fireChildrenChanged() {
211: assert EventQueue.isDispatchThread();
212: // XXX synch on listeners to get them, then release
213: if (listeners != null) {
214: logger.finer("fireChildrenChanged");
215: PhadhailEvent ev = PhadhailEvent.create(this );
216: for (PhadhailListener l : listeners) {
217: l.childrenChanged(ev);
218: }
219: }
220: }
221:
222: public List<Phadhail> getChildren() {
223: assert EventQueue.isDispatchThread();
224: if (children != null) {
225: return children;
226: } else {
227: if (!computingChildren) {
228: computingChildren = true;
229: Worker.start(new Runnable() {
230: public void run() {
231: final List<Phadhail> ch = ph.getChildren();
232: SwingUtilities.invokeLater(new Runnable() {
233: public void run() {
234: children = new SwungChildrenList(ch);
235: computingChildren = false;
236: fireChildrenChanged();
237: }
238: });
239: }
240: });
241: }
242: return Collections.emptyList();
243: }
244: }
245:
246: private static final class SwungChildrenList extends
247: AbstractList<Phadhail> {
248: private final List<Phadhail> orig;
249: private final Phadhail[] kids;
250:
251: public SwungChildrenList(List<Phadhail> orig) {
252: this .orig = orig;
253: kids = new Phadhail[orig.size()];
254: }
255:
256: public Phadhail get(int i) {
257: assert EventQueue.isDispatchThread();
258: if (kids[i] == null) {
259: kids[i] = forPhadhail(orig.get(i));
260: }
261: return kids[i];
262: }
263:
264: public int size() {
265: assert EventQueue.isDispatchThread();
266: return kids.length;
267: }
268: }
269:
270: public InputStream getInputStream() throws IOException {
271: assert EventQueue.isDispatchThread();
272: return Worker
273: .block(new LockExceptionAction<InputStream, IOException>() {
274: public InputStream run() throws IOException {
275: return ph.getInputStream();
276: }
277: });
278: }
279:
280: public OutputStream getOutputStream() throws IOException {
281: assert EventQueue.isDispatchThread();
282: return Worker
283: .block(new LockExceptionAction<OutputStream, IOException>() {
284: public OutputStream run() throws IOException {
285: return ph.getOutputStream();
286: }
287: });
288: }
289:
290: public boolean hasChildren() {
291: assert EventQueue.isDispatchThread();
292: logger.log(Level.FINER, "hasChildren on {0}", this );
293: if (leaf == null) {
294: logger.finer("not cached");
295: leaf = Worker.block(new LockAction<Boolean>() {
296: public Boolean run() {
297: logger.finer("hasChildren: working...");
298: return ph.hasChildren();
299: }
300: });
301: logger.log(Level.FINER, "leaf={0}", leaf);
302: }
303: return !leaf.booleanValue();
304: }
305:
306: public synchronized void addPhadhailListener(PhadhailListener l) {
307: if (listeners == null) {
308: listeners = new ArrayList<PhadhailListener>();
309: ph.addPhadhailListener(SwungPhadhail.this );
310: }
311: listeners.add(l);
312: }
313:
314: public synchronized void removePhadhailListener(PhadhailListener l) {
315: if (listeners != null && listeners.remove(l)
316: && listeners.isEmpty()) {
317: listeners = null;
318: ph.removePhadhailListener(SwungPhadhail.this );
319: }
320: }
321:
322: public String toString() {
323: return "SwungPhadhail<" + ph + ">";
324: }
325:
326: public void childrenChanged(PhadhailEvent ev) {
327: // XXX should this go ahead and compute them now?
328: SwingUtilities.invokeLater(new Runnable() {
329: public void run() {
330: children = null;
331: computingChildren = false; // XXX right?
332: fireChildrenChanged();
333: }
334: });
335: }
336:
337: public void nameChanged(PhadhailNameEvent ev) {
338: SwingUtilities.invokeLater(new Runnable() {
339: public void run() {
340: name = null;
341: path = null;
342: computingName = false;
343: fireNameChanged();
344: }
345: });
346: }
347:
348: public RWLock lock() {
349: return Locks.event();
350: }
351:
352: }
|