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:
042: package org.netbeans.modules.cnd.debugger.gdb.breakpoints;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.io.IOException;
047: import java.net.MalformedURLException;
048: import java.net.URL;
049: import java.util.HashMap;
050: import java.util.HashSet;
051: import java.util.Map;
052: import java.util.Set;
053: import java.util.WeakHashMap;
054: import javax.swing.event.ChangeEvent;
055: import javax.swing.event.ChangeListener;
056: import org.openide.cookies.LineCookie;
057: import org.openide.filesystems.FileObject;
058: import org.openide.filesystems.URLMapper;
059: import org.openide.loaders.DataObject;
060: import org.openide.loaders.DataObjectNotFoundException;
061: import org.openide.text.Line;
062: import org.openide.util.WeakListeners;
063:
064: /**
065: * Translation utility for handling of lines that are shifted during source modifications.
066: *
067: * @author Martin Entlicher
068: */
069: class LineTranslations {
070:
071: private static LineTranslations translations;
072:
073: private ChangeListener changedFilesListener;
074: private Map<Object, Registry> timeStampToRegistry = new WeakHashMap<Object, Registry>();
075: private Map<LineBreakpoint, BreakpointLineUpdater> lineUpdaters = new HashMap<LineBreakpoint, BreakpointLineUpdater>();
076: private Map<Object, Map<LineBreakpoint, Integer>> originalBreakpointLines = new WeakHashMap<Object, Map<LineBreakpoint, Integer>>();
077: private Map<Object, PropertyChangeListener> breakpointListeners = new WeakHashMap<Object, PropertyChangeListener>();
078:
079: private LineTranslations() {
080: }
081:
082: static synchronized LineTranslations getTranslations() {
083: if (translations == null) {
084: translations = new LineTranslations();
085: }
086: return translations;
087: }
088:
089: /**
090: * Creates a new time stamp.
091: *
092: * @param timeStamp a new time stamp
093: */
094: synchronized void createTimeStamp(Object timeStamp) {
095: Set<DataObject> modifiedDataObjects = DataObject.getRegistry()
096: .getModifiedSet();
097: Registry r = new Registry();
098: timeStampToRegistry.put(timeStamp, r);
099: for (DataObject dobj : modifiedDataObjects) {
100: r.register(dobj);
101: }
102:
103: if (changedFilesListener == null) {
104: changedFilesListener = new ChangedFilesListener();
105: DataObject.getRegistry().addChangeListener(
106: changedFilesListener);
107: }
108: }
109:
110: /**
111: * Disposes given time stamp.
112: *
113: * @param timeStamp a time stamp to be disposed
114: */
115: synchronized void disposeTimeStamp(Object timeStamp) {
116: timeStampToRegistry.remove(timeStamp);
117: if (timeStampToRegistry.isEmpty()) {
118: DataObject.getRegistry().removeChangeListener(
119: changedFilesListener);
120: changedFilesListener = null;
121: }
122: originalBreakpointLines.remove(timeStamp);
123: breakpointListeners.remove(timeStamp);
124: }
125:
126: /**
127: * Returns the original line number of a breakpoint.
128: *
129: * @param url The URL
130: * @param currentLineNumber The current line number
131: * @param timeStamp a time stamp to be used
132: *
133: * @return The original line number
134: */
135: synchronized int getOriginalLineNumber(LineBreakpoint lb,
136: final Object timeStamp) {
137: Map<LineBreakpoint, Integer> bpLines = originalBreakpointLines
138: .get(timeStamp);
139: if (bpLines != null) {
140: Integer line = bpLines.get(lb);
141: if (line != null) {
142: //System.err.println("Original line of "+lb+" IS "+line);
143: return line.intValue();
144: }
145: } else {
146: bpLines = new WeakHashMap<LineBreakpoint, Integer>();
147: originalBreakpointLines.put(timeStamp, bpLines);
148: }
149: int line = getOriginalLineNumber(lb.getURL(), lb
150: .getLineNumber(), timeStamp);
151: bpLines.put(lb, line);
152: PropertyChangeListener lineNumberListener = new PropertyChangeListener() {
153: public void propertyChange(PropertyChangeEvent evt) {
154: if (LineBreakpoint.PROP_LINE_NUMBER.equals(evt
155: .getPropertyName())) {
156: synchronized (LineTranslations.this ) {
157: Map<LineBreakpoint, Integer> bpLines = originalBreakpointLines
158: .get(timeStamp);
159: if (bpLines != null) {
160: LineBreakpoint lb = (LineBreakpoint) evt
161: .getSource();
162: int line = getOriginalLineNumber(lb
163: .getURL(), lb.getLineNumber(),
164: timeStamp);
165: bpLines.put(lb, line);
166: }
167: }
168: }
169: }
170: };
171: breakpointListeners.put(timeStamp, lineNumberListener);
172: lb.addPropertyChangeListener(WeakListeners.propertyChange(
173: lineNumberListener, lb));
174: return line;
175: }
176:
177: /**
178: * Returns the original line number.
179: *
180: * @param url The URL
181: * @param currentLineNumber The current line number
182: * @param timeStamp a time stamp to be used
183: *
184: * @return The original line number
185: */
186: int getOriginalLineNumber(String url, int currentLineNumber,
187: Object timeStamp) {
188: //System.err.println("getOriginalLineNumber("+url+", "+currentLineNumber+", "+timeStamp+")");
189: if (timeStamp == null) {
190: return currentLineNumber;
191: } else {
192: Line.Set lineSet = getLineSet(url, timeStamp);
193: if (lineSet == null) {
194: return currentLineNumber;
195: }
196: try {
197: //Line line = lineSet.getCurrent(currentLineNumber);
198: //System.err.println(" current line = "+line);
199: //System.err.println(" original line = "+lineSet.getOriginalLineNumber(line));
200: //System.err.println(" original line2 = "+lineSet.getOriginal(currentLineNumber));
201: //System.err.println("Original line of "+currentLineNumber+" IS "+lineSet.getOriginalLineNumber(lineSet.getCurrent(currentLineNumber)));
202: return lineSet.getOriginalLineNumber(lineSet
203: .getCurrent(currentLineNumber));
204: } catch (IndexOutOfBoundsException ioobex) {
205: //ioobex.printStackTrace();
206: //System.err.println(" getOriginalLineNumber.return "+currentLineNumber);
207: return currentLineNumber;
208: }
209: }
210: }
211:
212: /**
213: * Updates timeStamp for gived url.
214: *
215: * @param timeStamp time stamp to be updated
216: * @param url an url
217: */
218: synchronized void updateTimeStamp(Object timeStamp, String url) {
219: //System.err.println("LineTranslations.updateTimeStamp("+timeStamp+", "+url+")");
220: Registry registry = timeStampToRegistry.get(timeStamp);
221: registry.register(getDataObject(url));
222: Map<LineBreakpoint, Integer> bpLines = originalBreakpointLines
223: .get(timeStamp);
224: if (bpLines != null) {
225: Set<LineBreakpoint> bpts = new HashSet<LineBreakpoint>(
226: bpLines.keySet());
227: for (LineBreakpoint bp : bpts) {
228: if (url.equals(bp.getURL())) {
229: bpLines.remove(bp);
230: }
231: }
232: }
233: }
234:
235: Line.Set getLineSet(String url, Object timeStamp) {
236: DataObject dataObject = getDataObject(url);
237: if (dataObject == null) {
238: return null;
239: }
240:
241: if (timeStamp != null) {
242: // get original
243: synchronized (this ) {
244: Registry registry = timeStampToRegistry.get(timeStamp);
245: if (registry != null) {
246: Line.Set ls = registry.getLineSet(dataObject);
247: if (ls != null) {
248: return ls;
249: }
250: }
251: }
252: }
253:
254: // get current
255: LineCookie lineCookie = dataObject.getCookie(LineCookie.class);
256: if (lineCookie == null) {
257: return null;
258: }
259: return lineCookie.getLineSet();
260: }
261:
262: Line getLine(String url, int lineNumber, Object timeStamp) {
263: //System.err.println("LineTranslations.getLine("+lineNumber+", "+timeStamp+")");
264: Line.Set ls = getLineSet(url, timeStamp);
265: //System.err.println(" Line.Set = "+ls+", date = "+ls.getDate());
266: //System.err.println(" current("+(lineNumber-1)+") = "+ls.getCurrent (lineNumber - 1));
267: //System.err.println(" originl("+(lineNumber-1)+") = "+ls.getOriginal (lineNumber - 1));
268: if (ls == null) {
269: return null;
270: }
271: try {
272: if (timeStamp == null) {
273: return ls.getCurrent(lineNumber - 1);
274: } else {
275: return ls.getOriginal(lineNumber - 1);
276: }
277: } catch (IndexOutOfBoundsException e) {
278: } catch (IllegalArgumentException e) {
279: }
280: return null;
281: }
282:
283: synchronized void registerForLineUpdates(LineBreakpoint lb) {
284: //translatedBreakpoints.add(lb);
285: DataObject dobj = getDataObject(lb.getURL());
286: if (dobj != null) {
287: BreakpointLineUpdater blu = new BreakpointLineUpdater(lb,
288: dobj);
289: try {
290: blu.attach();
291: lineUpdaters.put(lb, blu);
292: } catch (IOException ioex) {
293: // Ignore
294: }
295: }
296: }
297:
298: synchronized void unregisterFromLineUpdates(LineBreakpoint lb) {
299: //translatedBreakpoints.remove(lb);
300: BreakpointLineUpdater blu = lineUpdaters.remove(lb);
301: if (blu != null) {
302: blu.detach();
303: }
304: //if (timeStampToRegistry.isEmpty () && translatedBreakpoints.isEmpty()) {
305: // DataObject.getRegistry ().removeChangeListener (changedFilesListener);
306: // changedFilesListener = null;
307: //}
308: }
309:
310: private static DataObject getDataObject(String url) {
311: FileObject file;
312: try {
313: file = URLMapper.findFileObject(new URL(url));
314: } catch (MalformedURLException e) {
315: return null;
316: }
317:
318: if (file == null) {
319: return null;
320: }
321: try {
322: return DataObject.find(file);
323: } catch (DataObjectNotFoundException ex) {
324: return null;
325: }
326: }
327:
328: private static class Registry {
329:
330: private Map<DataObject, Line.Set> dataObjectToLineSet = new HashMap<DataObject, Line.Set>();
331:
332: synchronized void register(DataObject dataObject) {
333: LineCookie lc = dataObject.getCookie(LineCookie.class);
334: if (lc == null) {
335: return;
336: }
337: dataObjectToLineSet.put(dataObject, lc.getLineSet());
338: }
339:
340: synchronized void registerIfNotThere(DataObject dataObject) {
341: if (!dataObjectToLineSet.containsKey(dataObject)) {
342: register(dataObject);
343: }
344: }
345:
346: synchronized Line.Set getLineSet(DataObject dataObject) {
347: return dataObjectToLineSet.get(dataObject);
348: }
349:
350: }
351:
352: private class ChangedFilesListener implements ChangeListener {
353: public void stateChanged(ChangeEvent e) {
354: Set<DataObject> newDOs = new HashSet<DataObject>(DataObject
355: .getRegistry().getModifiedSet());
356: synchronized (LineTranslations.this ) {
357: //newDOs.removeAll (modifiedDataObjects);
358: for (Registry r : timeStampToRegistry.values()) {
359: for (DataObject dobj : newDOs) {
360: r.registerIfNotThere(dobj);
361: }
362: }
363: //modifiedDataObjects = DataObject.getRegistry().getModifiedSet();
364: }
365: }
366: }
367:
368: private class BreakpointLineUpdater implements
369: PropertyChangeListener {
370:
371: private LineBreakpoint lb;
372: private DataObject dataObject;
373: private LineCookie lc;
374: private Line line;
375: private boolean updatingLine = false;
376:
377: public BreakpointLineUpdater(LineBreakpoint lb,
378: DataObject dataObject) {
379: this .lb = lb;
380: this .dataObject = dataObject;
381: }
382:
383: public synchronized void attach() throws IOException {
384: this .lc = dataObject.getCookie(LineCookie.class);
385: if (lc == null) {
386: return;
387: }
388: lb.addPropertyChangeListener(this );
389: try {
390: this .line = lc.getLineSet().getCurrent(
391: lb.getLineNumber() - 1);
392: line.addPropertyChangeListener(this );
393: } catch (IndexOutOfBoundsException ioobex) {
394: // ignore document changes for BP with bad line number
395: }
396: }
397:
398: public synchronized void detach() {
399: lb.removePropertyChangeListener(this );
400: if (line != null) {
401: line.removePropertyChangeListener(this );
402: }
403: }
404:
405: private synchronized void update() {
406: updatingLine = true;
407: try {
408: lb.setLineNumber(line.getLineNumber() + 1);
409: } finally {
410: updatingLine = false;
411: }
412: }
413:
414: public synchronized void propertyChange(PropertyChangeEvent evt) {
415: if (Line.PROP_LINE_NUMBER.equals(evt.getPropertyName())
416: && line == evt.getSource()) {
417: update();
418: return;
419: }
420: if (!updatingLine
421: && LineBreakpoint.PROP_LINE_NUMBER.equals(evt
422: .getPropertyName())) {
423: boolean haveDocL = line != null;
424: try {
425: line = lc.getLineSet().getCurrent(
426: lb.getLineNumber() - 1);
427: if (!haveDocL) {
428: line.addPropertyChangeListener(this );
429: }
430: } catch (IndexOutOfBoundsException ioobex) {
431: // ignore document changes for BP with bad line number
432: if (haveDocL) {
433: line.removePropertyChangeListener(this );
434: line = null;
435: }
436: }
437: }
438: if (LineBreakpoint.PROP_URL.equals(evt.getPropertyName())) {
439: // detach
440: line.removePropertyChangeListener(this );
441:
442: // update DataObject
443: this .dataObject = getDataObject(lb.getURL());
444:
445: // attach
446: this .lc = dataObject.getCookie(LineCookie.class);
447: try {
448: this .line = lc.getLineSet().getCurrent(
449: lb.getLineNumber() - 1);
450: line.addPropertyChangeListener(this );
451: } catch (IndexOutOfBoundsException ioobex) {
452: // ignore document changes for BP with bad line number
453: this.line = null;
454: }
455: }
456: }
457:
458: }
459:
460: }
|