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;
042:
043: import java.io.PrintWriter;
044:
045: /**
046: * An abstract representation of a logical compilation unit. The implementation may be directly
047: * associated with a source file/buffer, or may be an abstraction on top of one or more other units.
048: *
049: * @author Carl Quinn
050: */
051: public interface Unit {
052:
053: //---------------------------------------------------------------------------------------- State
054:
055: /**
056: * Tracks the state of the unit.<p/>
057: *
058: * When everything is Insync, we're in the CLEAN state.<p/>
059: *
060: * A programmatic modification of the unit (e.g. setting a DOM attribute, or java property)
061: * within a writeLock() takes us into the MODELDIRTY state temporarily, and then to a
062: * SOURCEDIRTY state on writeUnlock().<p/>
063: *
064: * A buffer edit takes us from the CLEAN state to the SOURCEDIRTY state. <p/>
065: *
066: * A sync() takes us back from SOURCEDIRTY to CLEAN, but if there is an error during sync(), we
067: * go to the BUSTED state.<p/>
068: *
069: * An edit in the BUSTED state takes us back to the SOURCEDIRTY state from which a new sync()
070: * can be attempted.
071: */
072: public static class State {
073: private final String name;
074:
075: private State(String name) {
076: this .name = name;
077: }
078:
079: public String toString() {
080: return name;
081: }
082:
083: /**
084: * Report whether the source is in an invalid state. This typically means
085: * that the source file cannot be parsed due to some source error.
086: * You can get the errors causing the invalid state by calling
087: * @see{getErrors}.
088: * @return true iff the source is invalid
089: */
090: public boolean isBusted() {
091: return this == BUSTED;
092: }
093:
094: /**
095: * Report whether the model is available to be written to
096: * @return true iff the state is clean or modeldirty
097: */
098: public boolean isModelAvailable() {
099: return this == CLEAN || this == MODELDIRTY;
100: }
101:
102: public static final State CLEAN = new State("Clean"); //NOI18N
103: public static final State MODELDIRTY = new State("ModelDirty"); //NOI18N
104: public static final State SOURCEDIRTY = new State("SourceDirty"); //NOI18N
105: public static final State BUSTED = new State("Busted"); //NOI18N
106: }
107:
108: /**
109: * Get the current state of this unit.
110: *
111: * @return This unit's current state.
112: */
113: public abstract State getState();
114:
115: /**
116: * Return the list of errors if this unit does not compile. If there are no errors it returns an
117: * empty array - never null.
118: *
119: * @return An array of ParserAnnotations.
120: */
121: public abstract ParserAnnotation[] getErrors();
122:
123: //---------------------------------------------------------------------------------------- Input
124:
125: /**
126: * Acquires a lock to begin reading some state from the unit. There can be multiple readers at
127: * the same time. Writing blocks the readers until notification of the change to the listeners
128: * has been completed. This method should be used very carefully to avoid unintended compromise
129: * of the unit. It should always be balanced with a <code>readUnlock</code>.
130: *
131: * @see #readUnlock
132: */
133: public abstract void readLock();
134:
135: /**
136: * Does a read unlock. This signals that one of the readers is done. If there are no more
137: * readers then writing can begin again. This should be balanced with a readLock, and should
138: * occur in a finally statement so that the balance is guaranteed. The following is an example.
139: *
140: * <pre><code>
141: * readLock();
142: * try { // do something }
143: * finally { readUnlock(); }
144: * </code></pre>
145: *
146: * @see #readLock
147: */
148: public abstract void readUnlock();
149:
150: /**
151: * Sync this unit's contents to its document or underlying unit, reading changes as needed and
152: * updating flags.
153: *
154: * @see #writeLock
155: * @return true iff the model was modified by the operation
156: */
157: public abstract boolean sync();
158:
159: //--------------------------------------------------------------------------------------- Output
160:
161: /**
162: * Acquires a lock to begin mutating the unit this lock protects. There can be no writing,
163: * notification of changes, or reading going on in order to gain the lock. Additionally a thread
164: * is allowed to gain more than one <code>writeLock</code>, as long as it doesn't attempt to
165: * gain additional <code>writeLock</code> s from within unit notification. Attempting to gain
166: * a *<code>writeLock</code> from within a UnitListener notification will result in an
167: * <code>IllegalStateException</code>. The ability to obtain more than one
168: * <code>writeLock</code> per thread allows subclasses to gain a writeLock, perform a number
169: * of operations, then release the lock.
170: * <p>
171: * Calls to <code>writeLock</code> must be balanced with calls to <code>writeUnlock</code>,
172: * else the <code>Unit</code> will be left in a locked state so that no reading or writing can
173: * be done.
174: *
175: * @exception IllegalStateException thrown on illegal lock attempt. If the unit is implemented
176: * properly, this can only happen if a unit listener attempts to mutate the unit.
177: * This situation violates the bean event model where order of delivery is not
178: * guaranteed and all listeners should be notified before further mutations are
179: * allowed.
180: */
181: public abstract void writeLock(UndoEvent event);
182:
183: /**
184: * Releases a write lock previously obtained via <code>writeLock</code>. After decrementing
185: * the lock count if there are no oustanding locks this will allow a new writer, or readers.
186: *
187: * @return true iff the last pending write lock was completely release.
188: * @see #writeLock
189: */
190: public abstract boolean writeUnlock(UndoEvent event);
191:
192: /**
193: * Return true if and only if the unit is currently locked by a write lock
194: *
195: * @see #writeLock
196: */
197: public abstract boolean isWriteLocked();
198:
199: /**
200: * Destroy this unit & cleanup any resources or registrations that this unit may have.
201: */
202: public abstract void destroy();
203:
204: //--------------------------------------------------------------------------------------- Events
205: /*
206:
207: protected ArrayList listeners = new ArrayList();
208: boolean notifyingListeners;
209:
210: public void addNodeChangeListener(Node node, NodeChangeListener l) {
211: listeners.add(new NodeChangeListener.Pair(node, l));
212: }
213:
214: public void removeNodeChangeListener(NodeChangeListener l) {
215: for (Iterator i = listeners.iterator(); i.hasNext(); ) {
216: NodeChangeListener.Pair p = (NodeChangeListener.Pair)i.next();
217: if (p.listener == l)
218: i.remove();
219: }
220: }
221:
222: public NodeChangeListener.Pair[] getNodeChangeListeners() {
223: return (NodeChangeListener.Pair[])listeners.toArray(NodeChangeListener.Pair.EMPTY_ARRAY);
224: }
225:
226: protected synchronized void fireChangeEvent(NodeChangeEvent e) {
227: notifyingListeners = true;
228: // fire e to every listener associated with owner or changed node...
229: for (Iterator i = listeners.iterator(); i.hasNext(); ) {
230: NodeChangeListener.Pair p = (NodeChangeListener.Pair)i.next();
231: if (p.node == null || p.node == e.owner || p.node == e.target) {
232: switch (e.change) {
233: case NodeChangeEvent.MODIFIED:
234: p.listener.nodeModified(e);
235: break;
236: case NodeChangeEvent.ADDED:
237: p.listener.nodeAdded(e);
238: break;
239: case NodeChangeEvent.REMOVED:
240: p.listener.nodeRemoved(e);
241: break;
242: }
243: }
244: }
245: notifyingListeners = false;
246: }
247:
248: protected synchronized void fireChangeEvent(Node owner, Node changed, int change) {
249: if (!listeners.isEmpty())
250: fireChangeEvent(new NodeChangeEvent(this, owner, changed, change));
251: }
252: */
253:
254: //---------------------------------------------------------------------------------------- Debug
255: /**
256: * Debug method to dump diagnostic info of this unit to a PrintWriter
257: *
258: * @param w The PrintWriter to dump debug info to
259: */
260: public abstract void dumpTo(PrintWriter w);
261: }
|