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: package org.netbeans.modules.debugger.jpda;
042:
043: import com.sun.jdi.AbsentInformationException;
044: import com.sun.jdi.Location;
045: import com.sun.jdi.StackFrame;
046: import java.beans.PropertyChangeListener;
047: import java.io.File;
048: import java.util.List;
049: import java.util.logging.Level;
050: import java.util.logging.Logger;
051: import org.netbeans.spi.debugger.ContextProvider;
052:
053: import org.netbeans.api.debugger.jpda.CallStackFrame;
054: import org.netbeans.api.debugger.jpda.Field;
055: import org.netbeans.api.debugger.jpda.JPDADebugger;
056: import org.netbeans.api.debugger.jpda.JPDAThread;
057: import org.netbeans.spi.debugger.jpda.SourcePathProvider;
058: import org.netbeans.spi.debugger.jpda.EditorContext;
059: import org.openide.ErrorManager;
060: import org.openide.util.Exceptions;
061:
062: /**
063: * Utility methods for sources.
064: *
065: * @see Similar class in debuggerjpda/ui when modifying this.
066: *
067: * @author Jan Jancura
068: */
069: public class SourcePath {
070:
071: private ContextProvider lookupProvider;
072: private SourcePathProvider contextProvider;
073: private JPDADebugger debugger;
074:
075: public SourcePath(ContextProvider lookupProvider) {
076: this .lookupProvider = lookupProvider;
077: debugger = lookupProvider.lookupFirst(null, JPDADebugger.class);
078: }
079:
080: public SourcePathProvider getContext() {
081: if (contextProvider == null) {
082: List l = lookupProvider.lookup(null,
083: SourcePathProvider.class);
084: contextProvider = (SourcePathProvider) l.get(0);
085: int i, k = l.size();
086: for (i = 1; i < k; i++) {
087: contextProvider = new CompoundContextProvider(
088: (SourcePathProvider) l.get(i), contextProvider);
089: }
090: }
091: return contextProvider;
092: }
093:
094: // ContextProvider methods .................................................
095:
096: /**
097: * Returns relative path for given url.
098: *
099: * @param url a url of resource file
100: * @param directorySeparator a directory separator character
101: * @param includeExtension whether the file extension should be included
102: * in the result
103: *
104: * @return relative path
105: */
106: public String getRelativePath(String url, char directorySeparator,
107: boolean includeExtension) {
108: return getContext().getRelativePath(url, directorySeparator,
109: includeExtension);
110: }
111:
112: /**
113: * Returns the source root (if any) for given url.
114: *
115: * @param url a url of resource file
116: *
117: * @return the source root or <code>null</code> when no source root was found.
118: */
119: public String getSourceRoot(String url) {
120: return getContext().getSourceRoot(url);
121: }
122:
123: /**
124: * Translates a relative path ("java/lang/Thread.java") to url
125: * ("file:///C:/Sources/java/lang/Thread.java"). Uses GlobalPathRegistry
126: * if global == true.
127: *
128: * @param relativePath a relative path (java/lang/Thread.java)
129: * @param global true if global path should be used
130: * @return url
131: */
132: public String getURL(String relativePath, boolean global) {
133: String url = getContext().getURL(relativePath, global);
134: if (url != null) {
135: try {
136: new java.net.URL(url);
137: } catch (java.net.MalformedURLException muex) {
138: Logger.getLogger(SourcePath.class.getName()).log(
139: Level.WARNING,
140: "Malformed URL '" + url + "' produced by "
141: + getContext(), muex);
142: return null;
143: }
144: }
145: return url;
146: }
147:
148: public String getURL(StackFrame sf, String stratumn) {
149: try {
150: return getURL(convertSlash(sf.location().sourcePath(
151: stratumn)), true);
152: } catch (AbsentInformationException e) {
153: return getURL(convertClassNameToRelativePath(sf.location()
154: .declaringType().name()), true);
155: }
156: }
157:
158: public String getURL(Location loc, String stratumn) {
159: try {
160: return getURL(convertSlash(loc.sourcePath(stratumn)), true);
161: } catch (AbsentInformationException e) {
162: return getURL(convertClassNameToRelativePath(loc
163: .declaringType().name()), true);
164: }
165: }
166:
167: /**
168: * Returns array of source roots.
169: */
170: public String[] getSourceRoots() {
171: return getContext().getSourceRoots();
172: }
173:
174: /**
175: * Sets array of source roots.
176: *
177: * @param sourceRoots a new array of sourceRoots
178: */
179: public void setSourceRoots(String[] sourceRoots) {
180: getContext().setSourceRoots(sourceRoots);
181: }
182:
183: /**
184: * Returns set of original source roots.
185: *
186: * @return set of original source roots
187: */
188: public String[] getOriginalSourceRoots() {
189: return getContext().getOriginalSourceRoots();
190: }
191:
192: /**
193: * Returns the project's source roots.
194: *
195: * @return array of source roots belonging to the project
196: */
197: public String[] getProjectSourceRoots() {
198: try {
199: java.lang.reflect.Method getProjectSourceRootsMethod = getContext()
200: .getClass().getMethod("getProjectSourceRoots",
201: new Class[] {}); // NOI18N
202: String[] projectSourceRoots = (String[]) getProjectSourceRootsMethod
203: .invoke(getContext(), new Object[] {});
204: return projectSourceRoots;
205: } catch (Exception ex) {
206: Exceptions.printStackTrace(ex);
207: return new String[] {};
208: }
209: }
210:
211: /**
212: * Adds property change listener.
213: *
214: * @param l new listener.
215: */
216: public void addPropertyChangeListener(PropertyChangeListener l) {
217: getContext().addPropertyChangeListener(l);
218: }
219:
220: /**
221: * Removes property change listener.
222: *
223: * @param l removed listener.
224: */
225: public void removePropertyChangeListener(PropertyChangeListener l) {
226: getContext().removePropertyChangeListener(l);
227: }
228:
229: // utility methods .........................................................
230:
231: public boolean sourceAvailable(String relativePath) {
232: return getURL(relativePath, true) != null;
233: }
234:
235: public boolean sourceAvailable(JPDAThread t, String stratumn) {
236: try {
237: return sourceAvailable(convertSlash(t
238: .getSourcePath(stratumn)));
239: } catch (AbsentInformationException e) {
240: return sourceAvailable(convertClassNameToRelativePath(t
241: .getClassName()));
242: }
243: }
244:
245: public boolean sourceAvailable(Field f) {
246: String className = f.getClassName();
247: return sourceAvailable(className);
248: }
249:
250: public boolean sourceAvailable(CallStackFrame csf, String stratumn) {
251: try {
252: return sourceAvailable(convertSlash(csf
253: .getSourcePath(stratumn)));
254: } catch (AbsentInformationException e) {
255: return sourceAvailable(convertClassNameToRelativePath(csf
256: .getClassName()));
257: }
258: }
259:
260: public String getURL(CallStackFrame csf, String stratumn) {
261: try {
262: return getURL(convertSlash(csf.getSourcePath(stratumn)),
263: true);
264: } catch (AbsentInformationException e) {
265: return getURL(convertClassNameToRelativePath(csf
266: .getClassName()), true);
267: }
268: }
269:
270: public boolean showSource(JPDAThread t, String stratumn) {
271: int lineNumber = t.getLineNumber(stratumn);
272: if (lineNumber < 1)
273: lineNumber = 1;
274: try {
275: return EditorContextBridge.getContext().showSource(
276: getURL(convertSlash(t.getSourcePath(stratumn)),
277: true), lineNumber, debugger);
278: } catch (AbsentInformationException e) {
279: return EditorContextBridge.getContext().showSource(
280: getURL(convertClassNameToRelativePath(t
281: .getClassName()), true), lineNumber,
282: debugger);
283: }
284: }
285:
286: public boolean showSource(Field v) {
287: String fieldName = ((Field) v).getName();
288: String className = className = ((Field) v).getClassName();
289: String url = getURL(EditorContextBridge
290: .getRelativePath(className), true);
291: if (url == null)
292: return false;
293: int lineNumber = lineNumber = EditorContextBridge.getContext()
294: .getFieldLineNumber(url, className, fieldName);
295: if (lineNumber < 1)
296: lineNumber = 1;
297: return EditorContextBridge.getContext().showSource(url,
298: lineNumber, debugger);
299: }
300:
301: private static String convertSlash(String original) {
302: return original.replace(File.separatorChar, '/');
303: }
304:
305: public static String convertClassNameToRelativePath(String className) {
306: int i = className.indexOf('$');
307: if (i > 0)
308: className = className.substring(0, i);
309: String sourceName = className.replace('.', '/') + ".java";
310: return sourceName;
311: }
312:
313: public Object annotate(JPDAThread t, String stratumn) {
314: int lineNumber = t.getLineNumber(stratumn);
315: if (lineNumber < 1)
316: return null;
317: try {
318: return EditorContextBridge.getContext().annotate(
319: getURL(convertSlash(t.getSourcePath(stratumn)),
320: true), lineNumber,
321: EditorContext.CURRENT_LINE_ANNOTATION_TYPE,
322: debugger);
323: } catch (AbsentInformationException e) {
324: return EditorContextBridge.getContext().annotate(
325: getURL(convertClassNameToRelativePath(t
326: .getClassName()), true), lineNumber,
327: EditorContext.CURRENT_LINE_ANNOTATION_TYPE,
328: debugger);
329: }
330: }
331:
332: public Object annotate(CallStackFrame csf, String stratumn) {
333: int lineNumber = csf.getLineNumber(stratumn);
334: if (lineNumber < 1)
335: return null;
336: try {
337: return EditorContextBridge.getContext().annotate(
338: getURL(convertSlash(csf.getSourcePath(stratumn)),
339: true), lineNumber,
340: EditorContext.CALL_STACK_FRAME_ANNOTATION_TYPE,
341: debugger);
342: } catch (AbsentInformationException e) {
343: return EditorContextBridge.getContext().annotate(
344: getURL(convertClassNameToRelativePath(csf
345: .getClassName()), true), lineNumber,
346: EditorContext.CALL_STACK_FRAME_ANNOTATION_TYPE,
347: debugger);
348: }
349: }
350:
351: // innerclasses ............................................................
352:
353: private static class CompoundContextProvider extends
354: SourcePathProvider {
355:
356: private SourcePathProvider cp1, cp2;
357:
358: CompoundContextProvider(SourcePathProvider cp1,
359: SourcePathProvider cp2) {
360: this .cp1 = cp1;
361: this .cp2 = cp2;
362: }
363:
364: public String getURL(String relativePath, boolean global) {
365: String p1 = cp1.getURL(relativePath, global);
366: if (p1 != null) {
367: try {
368: new java.net.URL(p1);
369: return p1;
370: } catch (java.net.MalformedURLException muex) {
371: Logger.getLogger(SourcePath.class.getName()).log(
372: Level.WARNING,
373: "Malformed URL '" + p1 + "' produced by "
374: + cp1, muex);
375: }
376: }
377: p1 = cp2.getURL(relativePath, global);
378: if (p1 != null) {
379: try {
380: new java.net.URL(p1);
381: } catch (java.net.MalformedURLException muex) {
382: Logger.getLogger(SourcePath.class.getName()).log(
383: Level.WARNING,
384: "Malformed URL '" + p1 + "' produced by "
385: + cp2, muex);
386: p1 = null;
387: }
388: }
389: return p1;
390: }
391:
392: public String getRelativePath(String url,
393: char directorySeparator, boolean includeExtension) {
394: String p1 = cp1.getRelativePath(url, directorySeparator,
395: includeExtension);
396: if (p1 != null)
397: return p1;
398: return cp2.getRelativePath(url, directorySeparator,
399: includeExtension);
400: }
401:
402: public String getSourceRoot(String url) {
403: String sourceRoot = cp1.getSourceRoot(url);
404: if (sourceRoot == null) {
405: sourceRoot = cp2.getSourceRoot(url);
406: }
407: return sourceRoot;
408: }
409:
410: public String[] getSourceRoots() {
411: String[] fs1 = cp1.getSourceRoots();
412: String[] fs2 = cp2.getSourceRoots();
413: String[] fs = new String[fs1.length + fs2.length];
414: System.arraycopy(fs1, 0, fs, 0, fs1.length);
415: System.arraycopy(fs2, 0, fs, fs1.length, fs2.length);
416: return fs;
417: }
418:
419: public String[] getOriginalSourceRoots() {
420: String[] fs1 = cp1.getOriginalSourceRoots();
421: String[] fs2 = cp2.getOriginalSourceRoots();
422: String[] fs = new String[fs1.length + fs2.length];
423: System.arraycopy(fs1, 0, fs, 0, fs1.length);
424: System.arraycopy(fs2, 0, fs, fs1.length, fs2.length);
425: return fs;
426: }
427:
428: public String[] getProjectSourceRoots() {
429: String[] projectSourceRoots1;
430: String[] projectSourceRoots2;
431: //System.err.println("\nCompoundContextProvider["+toString()+"].getProjectSourceRoots()...\n");
432: try {
433: java.lang.reflect.Method getProjectSourceRootsMethod = cp1
434: .getClass().getMethod("getProjectSourceRoots",
435: new Class[] {}); // NOI18N
436: projectSourceRoots1 = (String[]) getProjectSourceRootsMethod
437: .invoke(cp1, new Object[] {});
438: } catch (Exception ex) {
439: projectSourceRoots1 = new String[0];
440: }
441: try {
442: java.lang.reflect.Method getProjectSourceRootsMethod = cp2
443: .getClass().getMethod("getProjectSourceRoots",
444: new Class[] {}); // NOI18N
445: projectSourceRoots2 = (String[]) getProjectSourceRootsMethod
446: .invoke(cp2, new Object[] {});
447: } catch (Exception ex) {
448: projectSourceRoots2 = new String[0];
449: }
450: if (projectSourceRoots1.length == 0) {
451: //System.err.println("\nCompoundContextProvider.getProjectSourceRoots() = "+java.util.Arrays.toString(projectSourceRoots2)+"\n");
452: return projectSourceRoots2;
453: }
454: if (projectSourceRoots2.length == 0) {
455: //System.err.println("\nCompoundContextProvider.getProjectSourceRoots() = "+java.util.Arrays.toString(projectSourceRoots1)+"\n");
456: return projectSourceRoots1;
457: }
458: String[] projectSourceRoots = new String[projectSourceRoots1.length
459: + projectSourceRoots2.length];
460: System.arraycopy(projectSourceRoots1, 0,
461: projectSourceRoots, 0, projectSourceRoots1.length);
462: System.arraycopy(projectSourceRoots2, 0,
463: projectSourceRoots, projectSourceRoots1.length,
464: projectSourceRoots2.length);
465: //System.err.println("\nCompoundContextProvider.getProjectSourceRoots() = "+java.util.Arrays.toString(projectSourceRoots)+"\n");
466: return projectSourceRoots;
467: }
468:
469: public void setSourceRoots(String[] sourceRoots) {
470: cp1.setSourceRoots(sourceRoots);
471: cp2.setSourceRoots(sourceRoots);
472: }
473:
474: public void addPropertyChangeListener(PropertyChangeListener l) {
475: cp1.addPropertyChangeListener(l);
476: cp2.addPropertyChangeListener(l);
477: }
478:
479: public void removePropertyChangeListener(
480: PropertyChangeListener l) {
481: cp1.removePropertyChangeListener(l);
482: cp2.removePropertyChangeListener(l);
483: }
484:
485: @Override
486: public String toString() {
487: return "CompoundContextProvider[" + cp1.toString() + ", "
488: + cp2.toString() + "]";
489: }
490:
491: }
492:
493: private static class CompoundAnnotation {
494: CompoundAnnotation() {
495: }
496:
497: Object annotation1;
498: Object annotation2;
499: }
500: }
|