blob: 6e329a797b26138dc3972de4d36e324cf769c7de [file] [log] [blame]
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2012 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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]"
*
* 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.
*
* Contributor(s):
*
* Portions Copyrighted 2012 Sun Microsystems, Inc.
*/
package org.netbeans.modules.jackpot30.resolve.api;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import org.netbeans.api.java.lexer.JavaTokenId;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.modules.jackpot30.backend.base.CategoryStorage;
import org.netbeans.modules.jackpot30.backend.base.SourceRoot;
import org.netbeans.modules.java.source.indexing.JavaIndex;
import org.openide.filesystems.FileObject;
/**
*
* @author lahvac
*/
public class ResolveService {
static SourceRoot sourceRoot(CategoryStorage category, String relativePath) {
for (SourceRoot sr : category.getSourceRoots()) {
if (relativePath.startsWith(sr.getRelativePath())) {
return sr;
}
}
throw new IllegalStateException();
}
Javac javacFor(String segment, String relative) throws IOException, InterruptedException {
CategoryStorage category = CategoryStorage.forId(segment);
return Javac.get(sourceRoot(category, relative));
}
public static CompilationInfo parse(String segment, String relative) throws IOException, InterruptedException {
CategoryStorage category = CategoryStorage.forId(segment);
Javac javac = Javac.get(sourceRoot(category, relative));
return javac.parse(relative);
}
public static String resolveSource(String segment, String relative, String signature) throws IOException, InterruptedException {
String fqn = topLevelClassFromSignature(signature);
SourceRoot sourceRoot = sourceRoot(CategoryStorage.forId(segment), relative);
List<String> classpathElements = new ArrayList<String>();
classpathElements.add(sourceRoot.getCode());
String classpath = sourceRoot.getClassPathString();
if (classpath != null) classpathElements.addAll(Arrays.asList(classpath.split(":")));
for (String element : classpathElements) {
if (element.endsWith(".jar")) continue;
String file = fileForFQN(sourceRoot.getCategory(), element, fqn);
if (file != null) {
return file;
}
}
return null;
}
public static Map<? extends CategoryStorage, ? extends Iterable<? extends String>> findSourcesContaining(String signature) throws IOException, InterruptedException {
Map<CategoryStorage, Iterable<? extends String>> result = new HashMap<CategoryStorage, Iterable<? extends String>>();
String fqn = topLevelClassFromSignature(signature);
for (CategoryStorage category : CategoryStorage.listCategories()) {
//would it be faster to check if the given class is in the current category?
List<String> files = new ArrayList<String>();
for (SourceRoot sourceRoot : category.getSourceRoots()) {
String file = fileForFQN(category, sourceRoot.getCode(), fqn);
if (file != null) {
files.add(file);
}
}
if (!files.isEmpty()) {
result.put(category, files);
}
}
return result;
}
private static String fileForFQN(CategoryStorage category, String code, String fqn) throws IOException {
FileObject root = category.getEmbeddedJarRoot(code);
FileObject fqn2files = root != null ? root.getFileObject("java/" + JavaIndex.VERSION + "/fqn2files.properties") : null;
if (fqn2files == null) return null;
Properties props = new Properties();
InputStream in = fqn2files.getInputStream();
try {
props.load(in);
} finally {
in.close();
}
String file = props.getProperty(fqn);
if (file != null) {
return file.substring("rel:/".length());
}
return null;
}
private static String topLevelClassFromSignature(String signature) {
String fqn = signature.split(":")[1];
if (fqn.indexOf('$') != (-1)) {//not fully correct
return fqn.substring(0, fqn.indexOf('$'));
} else {
return fqn;
}
}
public static long[] nameSpan(CompilationInfo info, TreePath forTree) {
JCTree jcTree = (JCTree) forTree.getLeaf(); //XXX
Name name = null;
int pos = jcTree.pos;
switch (forTree.getLeaf().getKind()) {
case IDENTIFIER: name = ((IdentifierTree) forTree.getLeaf()).getName(); break;
case MEMBER_SELECT: name = ((MemberSelectTree) forTree.getLeaf()).getIdentifier(); pos++; break;
case ANNOTATION_TYPE: case CLASS:
case ENUM: case INTERFACE:
name = ((ClassTree) forTree.getLeaf()).getSimpleName();
TokenSequence<JavaTokenId> ts = info.getTokenHierarchy().tokenSequence(JavaTokenId.language());
ts.move(pos);
while (ts.moveNext()) {
if (ts.token().id() == JavaTokenId.IDENTIFIER) {
if (name.contentEquals(ts.token().text())) {
pos = ts.offset();
}
break;
}
}
break;
case METHOD:
if ((((JCMethodDecl) forTree.getLeaf()).getModifiers().flags & Flags.GENERATEDCONSTR) != 0) {
//no positions for generated constructors:
return new long[] {-1, -1, -1, -1};
}
name = ((MethodTree) forTree.getLeaf()).getName();
if (name.contentEquals("<init>")) {
name = ((ClassTree) forTree.getParentPath().getLeaf()).getSimpleName();
}
break;
case VARIABLE: name = ((VariableTree) forTree.getLeaf()).getName(); break;
}
if (name != null) {
return new long[] {
info.getTrees().getSourcePositions().getStartPosition(forTree.getCompilationUnit(), forTree.getLeaf()),
info.getTrees().getSourcePositions().getEndPosition(forTree.getCompilationUnit(), forTree.getLeaf()),
pos,
pos + name.length()
};
}
return new long[] {
info.getTrees().getSourcePositions().getStartPosition(forTree.getCompilationUnit(), forTree.getLeaf()),
info.getTrees().getSourcePositions().getEndPosition(forTree.getCompilationUnit(), forTree.getLeaf()),
info.getTrees().getSourcePositions().getStartPosition(forTree.getCompilationUnit(), forTree.getLeaf()),
info.getTrees().getSourcePositions().getEndPosition(forTree.getCompilationUnit(), forTree.getLeaf())
};
}
public static long[] declarationSpans(final CompilationInfo info, final String signature) {
final long[][] result = new long[1][];
new TreePathScanner<Void, Void>() {
@Override public Void visitClass(ClassTree node, Void p) {
handleDeclaration();
return super.visitClass(node, p);
}
@Override public Void visitMethod(MethodTree node, Void p) {
handleDeclaration();
return super.visitMethod(node, p);
}
@Override public Void visitVariable(VariableTree node, Void p) {
handleDeclaration();
return super.visitVariable(node, p);
}
private void handleDeclaration() {
Element el = info.getTrees().getElement(getCurrentPath());
if (el == null/*how?*/ || !JavaUtils.SUPPORTED_KINDS.contains(el.getKind())) return ;
String thisSignature = JavaUtils.serialize(ElementHandle.create(el));
if (thisSignature.equals(signature)) {
result[0] = nameSpan(info, getCurrentPath());
}
}
}.scan(info.getCompilationUnit(), null);
return result[0];
}
public static List<long[]> usages(final CompilationInfo info, final String signature) {
final List<long[]> result = new ArrayList<long[]>();
new TreePathScanner<Void, Void>() {
@Override public Void visitIdentifier(IdentifierTree node, Void p) {
handle();
return super.visitIdentifier(node, p);
}
@Override public Void visitMemberSelect(MemberSelectTree node, Void p) {
handle();
return super.visitMemberSelect(node, p);
}
private void handle() {
Element el = info.getTrees().getElement(getCurrentPath());
if (el == null || !JavaUtils.SUPPORTED_KINDS.contains(el.getKind())) return;
String thisSignature = JavaUtils.serialize(ElementHandle.create(el));
if (thisSignature.equals(signature)) {
result.add(ResolveService.nameSpan(info, getCurrentPath()));
}
}
}.scan(info.getCompilationUnit(), null);
return result;
}
}