001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.model;
038:
039: import java.util.List;
040: import java.util.LinkedList;
041: import java.util.Hashtable;
042: import javax.swing.text.*;
043:
044: import edu.rice.cs.drjava.DrJava;
045: import edu.rice.cs.drjava.config.OptionConstants;
046: import edu.rice.cs.drjava.model.definitions.*;
047: import edu.rice.cs.util.Log;
048: import edu.rice.cs.util.swing.Utilities;
049:
050: /** Default light-weight parsing control.
051: * @version $Id$
052: */
053: public class DefaultLightWeightParsingControl implements
054: LightWeightParsingControl {
055: /** The model. */
056: private AbstractGlobalModel _model;
057:
058: /** The time at which updates may be performed. */
059: private long _beginUpdates;
060:
061: /** The time at which the last delay operation was performed. */
062: private long _lastDelay = System.currentTimeMillis();
063:
064: /** Last updates for the documents. */
065: private Hashtable<OpenDefinitionsDocument, Long> _lastUpdates = new Hashtable<OpenDefinitionsDocument, Long>();
066:
067: /** Enclosing class names for the documents. */
068: private Hashtable<OpenDefinitionsDocument, String> _enclosingClassNames = new Hashtable<OpenDefinitionsDocument, String>();
069:
070: /** Flag to stop automatic updates. */
071: private volatile boolean _running = false;
072:
073: /** Monitor to restart automatic updates. */
074: private Object _restart = new Object();
075:
076: /** List of listeners. */
077: private LinkedList<LightWeightParsingListener> _listeners = new LinkedList<LightWeightParsingListener>();
078:
079: /** Log file. */
080: private static final Log _log = new Log("LightWeightParsing", false);
081:
082: /** Thread group for the updater. */
083: private ThreadGroup _updaterThreadGroup = new ThreadGroup(
084: "Light-weight parsing updater thread group") {
085: public void uncaughtException(Thread t, Throwable e) {
086: _log
087: .log(
088: "Uncaught exception in updater; disabled for rest of session",
089: e);
090: new edu.rice.cs.drjava.ui.DrJavaErrorHandler().handle(e);
091: }
092: };
093:
094: /** Thread to perform automatic updates. */
095: private Thread _updater = new Thread(_updaterThreadGroup,
096: new Runnable() {
097: public void run() {
098: while (true) { // this is ok, it's a daemon thread and will die when all other threads have died
099: while (!_running) {
100: _log.log("Waiting...");
101: try {
102: synchronized (_restart) {
103: if (!_running) {
104: _restart.wait();
105: }
106: }
107: } catch (InterruptedException e) {
108: }
109: }
110: long current = System.currentTimeMillis();
111: long delta = (_beginUpdates - current);
112: // _log.logTime("Begin updates at "+_beginUpdates+" (delta="+delta+")");
113: if (current >= _beginUpdates) {
114: OpenDefinitionsDocument doc = _model
115: .getActiveDocument();
116: Long last = _lastUpdates.get(doc);
117: if ((last == null) || (last < _lastDelay)) {
118: update(doc);
119: // _log.logTime("Update done.");
120: } else {
121: // _log.logTime("Not updating, last update was at "+last);
122: }
123: delta = DrJava
124: .getConfig()
125: .getSetting(
126: OptionConstants.DIALOG_LIGHTWEIGHT_PARSING_DELAY)
127: .intValue();
128: }
129: // _log.logTime("Not updating, sleeping for "+delta);
130: try {
131: Thread.sleep(delta);
132: } catch (InterruptedException e) { /* ignore, just wake up earlier and retry */
133: }
134: }
135: }
136: });
137:
138: /** Create the default light-weight parsing control.
139: * @param model the model */
140: public DefaultLightWeightParsingControl(AbstractGlobalModel model) {
141: _model = model;
142: _updater.setDaemon(true);
143: _updater.start();
144: }
145:
146: /** Perform light-weight parsing. */
147: public synchronized void update(final OpenDefinitionsDocument doc) {
148: _log.log("Update for " + doc);
149: try {
150: _lastUpdates.put(doc, System.currentTimeMillis());
151: final String old = _enclosingClassNames.get(doc);
152: final String updated = doc.getEnclosingClassName(doc
153: .getCaretPosition(), true);
154: if ((old == null) || (!old.equals(updated))) {
155: _enclosingClassNames.put(doc, updated);
156: Utilities.invokeLater(new Runnable() {
157: public void run() {
158: List<LightWeightParsingListener> listeners = getListeners();
159: for (LightWeightParsingListener l : listeners) {
160: l.enclosingClassNameUpdated(doc, old,
161: updated);
162: }
163: }
164: });
165: }
166: } catch (BadLocationException e) { /* ignore */
167: } catch (ClassNameNotFoundException e) { /* ignore */
168: }
169: }
170:
171: /** Start or stop automatic updates.
172: * @param b {@code true} to start or {@code false} to stop automatic updates
173: */
174: public void setAutomaticUpdates(boolean b) {
175: _log.log("setAutomaticUpdates(" + b + ")");
176: _running = b;
177: if (b) {
178: delay();
179: synchronized (_restart) {
180: _restart.notify();
181: }
182: }
183: }
184:
185: /** Delay the next update. */
186: public void delay() {
187: _lastDelay = System.currentTimeMillis();
188: _beginUpdates = _lastDelay
189: + (DrJava
190: .getConfig()
191: .getSetting(
192: OptionConstants.DIALOG_LIGHTWEIGHT_PARSING_DELAY)
193: .intValue());
194: }
195:
196: /** Reset light-weight parsing. Forget everything. */
197: public synchronized void reset() {
198: for (final OpenDefinitionsDocument doc : _enclosingClassNames
199: .keySet()) {
200: final String old = _enclosingClassNames.get(doc);
201: Utilities.invokeLater(new Runnable() {
202: public void run() {
203: List<LightWeightParsingListener> listeners = getListeners();
204: for (LightWeightParsingListener l : listeners) {
205: l.enclosingClassNameUpdated(doc, old, null);
206: }
207: }
208: });
209: }
210: _enclosingClassNames.clear();
211: _lastUpdates.clear();
212: }
213:
214: /** Return the last enclosing class name for the specified document, "" if not inside a class, or
215: * null if unknown.
216: * WARNING: In long source files and when contained in anonymous inner classes, this function might take a LONG time.
217: * @param doc the document for which we want the information
218: * @return the enclosing class name
219: */
220: public synchronized String getEnclosingClassName(
221: OpenDefinitionsDocument doc) {
222: return _enclosingClassNames.get(doc);
223: }
224:
225: /** Add the listener to this controller.
226: * @param l listener to add */
227: public synchronized void addListener(LightWeightParsingListener l) {
228: _listeners.add(l);
229: }
230:
231: /** Remove the listener from this controller. */
232: public synchronized void removeListener(LightWeightParsingListener l) {
233: _listeners.remove(l);
234: }
235:
236: /** Remove all listeners from this controller. */
237: public synchronized void removeAllListeners() {
238: _listeners.clear();
239: }
240:
241: /** @return a copy of the list of listeners. */
242: public synchronized List<LightWeightParsingListener> getListeners() {
243: return new LinkedList<LightWeightParsingListener>(_listeners);
244: }
245: }
|