/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Micro//S ystems, Inc. Portions Copyright 1997-2006 Sun
* Micro//S ystems, Inc. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package org.netbeans.modules.scala.debugger;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.StackFrame;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.WeakHashMap;
import java.util.List;
import java.util.Set;
import org.netbeans.api.debugger.Properties;
import org.netbeans.spi.debugger.ContextProvider;
import org.netbeans.api.debugger.DebuggerManager;
import org.netbeans.api.debugger.Session;
import org.netbeans.api.debugger.jpda.CallStackFrame;
import org.netbeans.api.debugger.jpda.Field;
import org.netbeans.api.debugger.jpda.JPDADebugger;
import org.netbeans.api.debugger.jpda.JPDAThread;
import org.netbeans.api.debugger.jpda.LineBreakpoint;
import org.netbeans.api.debugger.jpda.LocalVariable;
import org.netbeans.api.debugger.jpda.Variable;
import org.netbeans.spi.debugger.jpda.EditorContext;
import org.netbeans.spi.debugger.jpda.EditorContext.Operation;
import org.netbeans.spi.debugger.jpda.SourcePathProvider;
import org.openide.ErrorManager;
/**
* Utility methods for sources.
*
* @see Similar class in debuggerjpda when modifying this.
*
* @author Jan Jancura
*/
public class SourcePath {
private ContextProvider contextProvider;
private SourcePathProvider sourcePathProvider;
private JPDADebugger debugger;
public SourcePath (ContextProvider contextProvider) {
this.contextProvider = contextProvider;
debugger = contextProvider.lookupFirst(null, JPDADebugger.class);
getContext();// To initialize the source path provider
}
private SourcePathProvider getContext () {
if (sourcePathProvider == null) {
List l = contextProvider.lookup (null, SourcePathProvider.class);
sourcePathProvider = (SourcePathProvider) l.get (0);
int i, k = l.size ();
for (i = 1; i < k; i++) {
sourcePathProvider = new CompoundContextProvider (
(SourcePathProvider) l.get (i),
sourcePathProvider
);
}
initSourcePaths ();
}
return sourcePathProvider;
}
static SourcePathProvider getDefaultContext() {
List providers = DebuggerManager.getDebuggerManager().
lookup("netbeans-JPDASession", SourcePathProvider.class);
for (Iterator it = providers.iterator(); it.hasNext(); ) {
Object provider = it.next();
// Hack - find our provider:
if (provider.getClass().getName().equals("org.netbeans.modules.debugger.jpda.projects.SourcePathProviderImpl")) {
return (SourcePathProvider) provider;
}
}
return null;
}
// ContextProvider methods .................................................
/**
* Returns relative path for given url.
*
* @param url a url of resource file
* @param directorySeparator a directory separator character
* @param includeExtension whether the file extension should be included
* in the result
*
* @return relative path
*/
public String getRelativePath (
String url,
char directorySeparator,
boolean includeExtension
) {
return getContext ().getRelativePath
(url, directorySeparator, includeExtension);
}
/**
* Translates a relative path ("java/lang/Thread.java") to url
* ("file:///C:/Sources/java/lang/Thread.java"). Uses GlobalPathRegistry
* if global == true.
*
* @param relativePath a relative path (java/lang/Thread.java)
* @param global true if global path should be used
* @return url
*/
public String getURL (String relativePath, boolean global) {
return getContext ().getURL (relativePath, global);
}
public String getURL (
StackFrame sf,
String stratumn
) {
try {
return getURL (
convertSlash (sf.location ().sourcePath (stratumn)),
true
);
} catch (AbsentInformationException e) {
return getURL (
convertClassNameToRelativePath (
sf.location ().declaringType ().name ()
),
true
);
}
}
/**
* Returns array of source roots.
*/
public String[] getSourceRoots () {
return getContext ().getSourceRoots ();
}
/**
* Sets array of source roots.
*
* @param sourceRoots a new array of sourceRoots
*/
public void setSourceRoots (String[] sourceRoots) {
getContext ().setSourceRoots (sourceRoots);
}
/**
* Returns set of original source roots.
*
* @return set of original source roots
*/
public String[] getOriginalSourceRoots () {
return getContext ().getOriginalSourceRoots ();
}
/**
* Adds property change listener.
*
* @param l new listener.
*/
public void addPropertyChangeListener (PropertyChangeListener l) {
getContext ().addPropertyChangeListener (l);
}
/**
* Removes property change listener.
*
* @param l removed listener.
*/
public void removePropertyChangeListener (
PropertyChangeListener l
) {
getContext ().removePropertyChangeListener (l);
}
// utility methods .........................................................
public boolean sourceAvailable (
String relativePath,
boolean global
) {
return getURL (relativePath, global) != null;
}
public boolean sourceAvailable (
JPDAThread t,
String stratumn,
boolean global
) {
try {
return sourceAvailable (
convertSlash (t.getSourcePath (stratumn)), global
);
} catch (AbsentInformationException e) {
return sourceAvailable (
convertClassNameToRelativePath (t.getClassName ()), global
);
}
}
public boolean sourceAvailable (
Field f
) {
String className = f.getClassName ();
return sourceAvailable (className, true);
}
public boolean sourceAvailable (
CallStackFrame csf,
String stratumn
) {
try {
return sourceAvailable (
convertSlash (csf.getSourcePath (stratumn)), true
);
} catch (AbsentInformationException e) {
return sourceAvailable (
convertClassNameToRelativePath (csf.getClassName ()), true
);
}
}
public String getURL (
CallStackFrame csf,
String stratumn
) {
try {
return getURL (convertSlash (csf.getSourcePath (stratumn)), true);
} catch (AbsentInformationException e) {
return getURL (
convertClassNameToRelativePath (csf.getClassName ()), true
);
}
}
public boolean showSource (
JPDAThread t,
String stratumn
) {
int lineNumber = t.getLineNumber (stratumn);
if (lineNumber < 1) lineNumber = 1;
try {
return EditorContextBridge.getContext().showSource (
getURL (convertSlash (t.getSourcePath (stratumn)), true),
lineNumber,
debugger
);
} catch (AbsentInformationException e) {
return EditorContextBridge.getContext().showSource (
getURL (
convertClassNameToRelativePath (t.getClassName ()), true
),
lineNumber,
debugger
);
}
}
public boolean showSource (CallStackFrame csf, String stratumn) {
try {
String url = getURL (
convertSlash (csf.getSourcePath (stratumn)), true
);
if (url == null) {
stratumn = csf.getDefaultStratum ();
url = getURL (
convertSlash (csf.getSourcePath (stratumn)), true
);
}
if (url == null) {
ErrorManager.getDefault().log(ErrorManager.WARNING,
"Show Source: No URL for source path "+csf.getSourcePath (stratumn)+
"\nThe reason is likely no opened project for this source file.");
return false;
}
int lineNumber = csf.getLineNumber (stratumn);
if (lineNumber < 1) lineNumber = 1;
return EditorContextBridge.getContext().showSource (
url,
lineNumber,
debugger
);
} catch (AbsentInformationException e) {
String url = getURL (
convertClassNameToRelativePath (csf.getClassName ()), true
);
if (url == null) {
ErrorManager.getDefault().log(ErrorManager.WARNING,
"Show Source: No source URL for class "+csf.getClassName()+
"\nThe reason is likely no opened project for the source file.");
return false;
}
return EditorContextBridge.getContext().showSource (
url,
1,
debugger
);
}
}
public boolean showSource (Field v) {
String fieldName = ((Field) v).getName ();
String className = className = ((Field) v).getClassName ();
String url = getURL (
EditorContextBridge.getRelativePath (className), true
);
if (url == null) return false;
int lineNumber = lineNumber = EditorContextBridge.getContext().getFieldLineNumber (
url,
className,
fieldName
);
if (lineNumber < 1) lineNumber = 1;
return EditorContextBridge.getContext().showSource (
url,
lineNumber,
debugger
);
}
private static String convertSlash (String original) {
return original.replace (File.separatorChar, '/');
}
public static String convertClassNameToRelativePath (
String className
) {
int i = className.indexOf ('$');
if (i > 0) className = className.substring (0, i);
String sourceName = className.replace
('.', '/') + ".scala";
return sourceName;
}
public Object annotate (
JPDAThread t,
String stratumn
) {
int lineNumber = t.getLineNumber (stratumn);
if (lineNumber < 1) return null;
//AST ast = t.getAST(stratumn);
Operation operation = t.getCurrentOperation();
String url;
try {
url = getURL (convertSlash (t.getSourcePath (stratumn)), true);
} catch (AbsentInformationException e) {
url = getURL (convertClassNameToRelativePath (t.getClassName ()), true);
}
List operationsAnn = annotateOperations(debugger, url, operation, t.getLastOperations(), lineNumber);
if (operation == null) {
if (operationsAnn.size() == 0) {
return EditorContextBridge.getContext().annotate (
url,
lineNumber,
EditorContext.CURRENT_LINE_ANNOTATION_TYPE,
debugger
);
} else {
/*
operationsAnn.add(EditorContextBridge.annotate (
url,
lineNumber,
EditorContext.CURRENT_LINE_ANNOTATION_TYPE,
debugger
));
*/
}
}
return operationsAnn;
}
public Object annotate (
CallStackFrame csf,
String stratumn
) {
int lineNumber = csf.getLineNumber (stratumn);
if (lineNumber < 1) return null;
Operation operation = csf.getCurrentOperation(stratumn);
try {
if (operation != null) {
int startOffset;
int endOffset;
if (operation.getMethodName() != null) {
startOffset = operation.getMethodStartPosition().getOffset();
endOffset = operation.getMethodEndPosition().getOffset();
} else {
startOffset = operation.getStartPosition().getOffset();
endOffset = operation.getEndPosition().getOffset();
}
return EditorContextBridge.getContext().annotate (
getURL (convertSlash (csf.getSourcePath (stratumn)), true),
startOffset,
endOffset,
EditorContext.CALL_STACK_FRAME_ANNOTATION_TYPE,
debugger
);
} else {
return EditorContextBridge.getContext().annotate (
getURL (convertSlash (csf.getSourcePath (stratumn)), true),
lineNumber,
EditorContext.CALL_STACK_FRAME_ANNOTATION_TYPE,
debugger
);
}
} catch (AbsentInformationException e) {
return EditorContextBridge.getContext().annotate (
getURL (
convertClassNameToRelativePath (csf.getClassName ()), true
),
lineNumber,
EditorContext.CALL_STACK_FRAME_ANNOTATION_TYPE,
debugger
);
}
}
private static List annotateOperations(JPDADebugger debugger, String url,
Operation currentOperation, List lastOperations,
int locLineNumber) {
List annotations = null;
if (currentOperation != null) {
annotations = new ArrayList();
annotations.add(createAnnotation(debugger, url, currentOperation,
EditorContext.CURRENT_LINE_ANNOTATION_TYPE,
true));
int lineNumber;
if (currentOperation.getMethodName() != null) {
lineNumber = currentOperation.getMethodStartPosition().getLine();
} else {
lineNumber = currentOperation.getStartPosition().getLine();
}
annotations.add(EditorContextBridge.getContext().annotate (
url,
lineNumber,
EditorContext.CURRENT_EXPRESSION_CURRENT_LINE_ANNOTATION_TYPE,
debugger
));
}
boolean isNewLineExp = false;
if (lastOperations != null && lastOperations.size() > 0) {
if (annotations == null) {
annotations = new ArrayList();
}
isNewLineExp = currentOperation == null;
for (int i = 0; i < lastOperations.size(); i++) {
Operation lastOperation = (Operation) lastOperations.get(i);
if (currentOperation == lastOperation && i == lastOperations.size() - 1) {
annotations.add(createAnnotation(debugger, url,
lastOperation,
EditorContext.CURRENT_OUT_OPERATION_ANNOTATION_TYPE,
false));
int lineNumber = lastOperation.getEndPosition().getLine();
annotations.add(EditorContextBridge.getContext().annotate (
url,
lineNumber,
EditorContext.CURRENT_EXPRESSION_CURRENT_LINE_ANNOTATION_TYPE,
debugger
));
isNewLineExp = false;
} else {
annotations.add(createAnnotation(debugger, url,
lastOperation,
EditorContext.CURRENT_LAST_OPERATION_ANNOTATION_TYPE,
true));
}
}
}
if (isNewLineExp) {
annotations.add(EditorContextBridge.getContext().annotate (
url,
locLineNumber,
EditorContext.CURRENT_LINE_ANNOTATION_TYPE,
debugger
));
}
if (annotations != null) {
return annotations;
} else {
return Collections.EMPTY_LIST;
}
}
private static Object createAnnotation(JPDADebugger debugger, String url,
Operation operation, String type,
boolean method) {
int startOffset;
int endOffset;
if (method && operation.getMethodName() != null) {
startOffset = operation.getMethodStartPosition().getOffset();
endOffset = operation.getMethodEndPosition().getOffset();
} else {
startOffset = operation.getStartPosition().getOffset();
endOffset = operation.getEndPosition().getOffset();
}
return EditorContextBridge.getContext().annotate (
url,
startOffset,
endOffset,
type,
debugger
);
}
// innerclasses ............................................................
private static class CompoundContextProvider extends SourcePathProvider {
private SourcePathProvider cp1, cp2;
CompoundContextProvider (
SourcePathProvider cp1,
SourcePathProvider cp2
) {
this.cp1 = cp1;
this.cp2 = cp2;
}
public String getURL (String relativePath, boolean global) {
String p1 = cp1.getURL (relativePath, global);
if (p1 != null) return p1;
return cp2.getURL (relativePath, global);
}
public String getRelativePath (
String url,
char directorySeparator,
boolean includeExtension
) {
String p1 = cp1.getRelativePath (
url,
directorySeparator,
includeExtension
);
if (p1 != null) return p1;
return cp2.getRelativePath (
url,
directorySeparator,
includeExtension
);
}
public String[] getSourceRoots () {
String[] fs1 = cp1.getSourceRoots ();
String[] fs2 = cp2.getSourceRoots ();
String[] fs = new String [fs1.length + fs2.length];
System.arraycopy (fs1, 0, fs, 0, fs1.length);
System.arraycopy (fs2, 0, fs, fs1.length, fs2.length);
return fs;
}
public String[] getOriginalSourceRoots () {
String[] fs1 = cp1.getOriginalSourceRoots ();
String[] fs2 = cp2.getOriginalSourceRoots ();
String[] fs = new String [fs1.length + fs2.length];
System.arraycopy (fs1, 0, fs, 0, fs1.length);
System.arraycopy (fs2, 0, fs, fs1.length, fs2.length);
return fs;
}
public void setSourceRoots (String[] sourceRoots) {
cp1.setSourceRoots (sourceRoots);
cp2.setSourceRoots (sourceRoots);
}
public void addPropertyChangeListener (PropertyChangeListener l) {
cp1.addPropertyChangeListener (l);
cp2.addPropertyChangeListener (l);
}
public void removePropertyChangeListener (PropertyChangeListener l) {
cp1.removePropertyChangeListener (l);
cp2.removePropertyChangeListener (l);
}
}
private void initSourcePaths () {
Properties properties = Properties.getDefault ().
getProperties ("debugger").getProperties ("sources");
Set originalSourceRoots = new HashSet (Arrays.asList (
sourcePathProvider.getOriginalSourceRoots ()
));
Set sourceRoots = new HashSet (Arrays.asList (
sourcePathProvider.getSourceRoots ()
));
Iterator enabledSourceRoots = properties.getProperties ("source_roots").
getCollection ("enabled", Collections.EMPTY_SET).iterator ();
while (enabledSourceRoots.hasNext ()) {
String root = (String) enabledSourceRoots.next ();
if (originalSourceRoots.contains (root))
sourceRoots.add (root);
}
Iterator disabledSourceRoots = properties.getProperties ("source_roots").
getCollection ("disabled", Collections.EMPTY_SET).iterator ();
while (disabledSourceRoots.hasNext ()) {
String root = (String) disabledSourceRoots.next ();
sourceRoots.remove (root);
}
String[] ss = new String [sourceRoots.size ()];
sourcePathProvider.setSourceRoots ((String[]) sourceRoots.toArray (ss));
}
private static class CompoundAnnotation {
Object annotation1;
Object annotation2;
}
}