001: /*
002: * uDig - User Friendly Desktop Internet GIS client
003: * http://udig.refractions.net
004: * (C) 2004, Refractions Research Inc.
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: */
017: package net.refractions.udig.catalog.util;
018:
019: import java.lang.ref.WeakReference;
020:
021: import net.refractions.udig.catalog.IResolve;
022: import net.refractions.udig.catalog.IResolveChangeEvent;
023: import net.refractions.udig.catalog.IResolveChangeListener;
024: import net.refractions.udig.catalog.IResolveDelta;
025:
026: /**
027: * Easy example that listens to a specific IResolve.
028: * <p>
029: * <p>
030: * Will do its best to reduce the IResolveChangeEvent to:
031: * <ul>
032: * <li>start() - called after create with valid handle
033: * <li>stop() - called before remove while handle is still valid
034: * <li>dispose() - called after remove while handle is invalid
035: * <li>refresh() - called when handle has changed
036: * <li>replace() - called when handle is replaced
037: * <li>reset() - called when handle is replaced, and replacement is unknown
038: * </ul>
039: * </p>
040: * <p>
041: * Note 1: The IResolveDelta does not always include information about *your* handle. If your parent
042: * is being replaced - your direct replacement may not be available until you ask for it.
043: * </p>
044: * <p>
045: * Translation repalce with a null newResolve = reset. This indicates you need to find your handle
046: * again from first principles.
047: * </p>
048: * <p>
049: * Note 2: HandleListener only holds onto a IResolve with a weak reference. Although this protects
050: * you a little bit, you still need to remove zombie listeners from the catalog (they cannot do it
051: * themselves).
052: * </p>
053: * <p>
054: * However when the weak reference is cleaned up, we will notice and call dispose for you.
055: * </p>
056: *
057: * @author jgarnett
058: * @since 0.9.0
059: */
060: public abstract class HandleListener implements IResolveChangeListener {
061: private WeakReference<IResolve> reference;
062:
063: public HandleListener(IResolve handle) {
064: reference = new WeakReference<IResolve>(handle);
065: }
066:
067: /**
068: * Resolve the handle - will call dispose if handle has passed out of scope.
069: */
070: public IResolve getHandle() {
071: if (reference == null)
072: return null;
073: if (reference.enqueue()) {
074: dispose();
075: return null;
076: }
077: IResolve handle = reference.get();
078: if (handle == null) {
079: dispose();
080: }
081: return null;
082: }
083:
084: /**
085: * Can be called during a repalce, or reset to switch this listener over to the new handle.
086: *
087: * @param handle
088: * @return
089: */
090: public void setHandle(IResolve newHandle) {
091: reference.clear();
092: reference = null;
093: reference = new WeakReference<IResolve>(newHandle);
094: }
095:
096: /** Called after create with valid handle */
097: public abstract void start(IResolve handle);
098:
099: /** Called before remove while handle is still valid */
100: public abstract void stop(IResolve handle);
101:
102: /** Called after remove while handle is invalid */
103: public abstract void dispose();
104:
105: /** Called when handle has changed */
106: public abstract void refresh(IResolve handle);
107:
108: /**
109: * Handle is being replaced with newHandle.
110: * <p>
111: * Note: This method is called post change, the newHandle already exists in the catalog.
112: * </p>
113: * <p>
114: * You have two options:
115: * <ul>
116: * <li>use setHandle( newHandle ) during your implemenation to <b>switch</b> over to the
117: * newHandle
118: * <li>remove this listener and set up a new one watching newHandle
119: * </ul>
120: * </p>
121: */
122: public abstract void replace(IResolve handle, IResolve newHandle);
123:
124: /**
125: * This is a replace where the replacement is unknown.
126: * <p>
127: * This occurs when:
128: * <ul>
129: * <li>an IResolveChangeDelta indicates your is replaced
130: * <li>an IResolveChangeDelta indicates you replaced
131: * </ul>
132: * </p>
133: * <p>
134: * In both cases you have two options:
135: * <ul>
136: * <li>Easy: find your replacement handle from first principles (ie look it up in the catalog)
137: * <li>Harder: as an optimization you can try and look through the event information and locate
138: * the delta for your parent. This delta may indicate your new parent, allowing you to locate
139: * your "new" handle in a more timely fashion.
140: * </ul>
141: * </p>
142: *
143: * @param handle
144: */
145: public abstract void reset(IResolve handle,
146: IResolveChangeEvent event);
147:
148: /** Actual listener implemtnation */
149: final public void changed(IResolveChangeEvent event) {
150: IResolve handle = getHandle();
151: if (handle == null) {
152: dispose();
153: return;
154: }
155: if (event.getResolve() == handle) {
156: // simple case event mentions handle
157: switch (event.getType()) {
158: case PRE_DELETE:
159: stop(handle);
160: return;
161: case PRE_CLOSE:
162: dispose();
163: return;
164: case POST_CHANGE:
165: default:
166: refresh(handle);
167: return;
168: }
169: }
170: IResolveDelta match = SearchResolveDeltaVisitor.search(handle,
171: event);
172: if (match == null) {
173: return; // this event does not effect us
174: }
175: if (match.getResolve() == handle) {
176: // simple case delta mentions handle
177: switch (match.getKind()) {
178: case NO_CHANGE:
179: return; // one of our childs must of changed ...
180: case ADDED:
181: start(handle);
182: return;
183: case REMOVED:
184: dispose();
185: return;
186: case REPLACED:
187: if (match.getNewResolve() != null) {
188: replace(handle, match.getNewResolve());
189: } else {
190: reset(handle, event);
191: }
192: return;
193: case CHANGED:
194: default:
195: refresh(handle);
196: return;
197: }
198: }
199: // our parent has changed
200: switch (match.getKind()) {
201: case NO_CHANGE:
202: case ADDED:
203: return; // these do not make sense in this context
204: case REMOVED:
205: dispose(); // parent is being removed
206: return;
207: case REPLACED: // parent is replaced, reset handle
208: reset(handle, event);
209: return;
210: case CHANGED:
211: default:
212: refresh(handle);
213: return;
214: }
215: }
216: }
|