blob: 45798e2b62e45d5280fd3d602ac778f3c8d959f7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.api.java.source;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.DocTreePath;
import com.sun.source.util.DocTreePathScanner;
import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.tree.DCTree;
import com.sun.tools.javac.util.JCDiagnostic;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import javax.lang.model.element.Element;
import javax.swing.text.Position;
import javax.swing.text.Position.Bias;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.modules.java.source.PositionRefProvider;
import org.openide.filesystems.FileObject;
import org.openide.util.Exceptions;
import org.openide.util.Parameters;
/**
* Represents a handle for {@link TreePath} which can be kept and later resolved
* by another javac. The Javac {@link Element}s are valid only in the single
* {@link javax.tools.CompilationTask} or single run of the
* {@link org.netbeans.api.java.source.CancellableTask}. If the client needs to
* keep a reference to the {@link TreePath} and use it in the other
* CancellableTask he has to serialize it into the {@link TreePathHandle}.
* <div class="nonnormative">
* <p>
* Typical usage of TreePathHandle enclElIsCorrespondingEl:
* </p>
* <pre>
* final TreePathHandle[] tpHandle = new TreePathHandle[1];
* javaSource.runCompileControlTask(new CancellableTask<CompilationController>() {
* public void run(CompilationController compilationController) {
* parameter.toPhase(Phase.RESOLVED);
* CompilationUnitTree cu = compilationController.getTree ();
* TreePath treePath = getInterestingTreePath (cu);
* treePathHandle[0] = TreePathHandle.create (element, compilationController);
* }
* },priority);
*
* otherJavaSource.runCompileControlTask(new CancellableTask<CompilationController>() {
* public void run(CompilationController compilationController) {
* parameter.toPhase(Phase.RESOLVED);
* TreePath treePath = treePathHanlde[0].resolve (compilationController);
* ....
* }
* },priority);
* </pre>
* </div>
*
*
* @author Jan Becicka
* @author Ralph Benjamin Ruijs
* @since 0.124
*/
public final class DocTreePathHandle {
private static Logger log = Logger.getLogger(DocTreePathHandle.class.getName());
private final Delegate delegate;
private DocTreePathHandle(Delegate d) {
if (d == null) {
throw new IllegalArgumentException();
}
this.delegate = d;
}
/**
* Resolves an {@link DocTreePath} from the {@link DocTreePathHandle}.
*
* @param compilationInfo representing the
* {@link javax.tools.CompilationTask}
* @return resolved subclass of {@link DocTreePath} or null if the doctree does
* not exist on the classpath/sourcepath of
* {@link javax.tools.CompilationTask}.
* @throws IllegalArgumentException when this {@link DocTreePathHandle} is not
* created for a source represented by the compilationInfo.
*/
public DocTreePath resolve(final CompilationInfo compilationInfo) throws IllegalArgumentException {
final DocTreePath result = this.delegate.resolve(compilationInfo);
if (result == null) {
Logger.getLogger(DocTreePathHandle.class.getName()).info("Cannot resolve: " + toString());
}
return result;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DocTreePathHandle)) {
return false;
}
if (delegate.getClass() != ((DocTreePathHandle) obj).delegate.getClass()) {
return false;
}
return delegate.equalsHandle(((DocTreePathHandle) obj).delegate);
}
@Override
public int hashCode() {
return delegate.hashCode();
}
/**
* returns {@link TreePathHandle} corresponding to this {@link DocTreePathHandle}
*
* @return {@link TreePathHandle}
*/
public TreePathHandle getTreePathHandle() {
return this.delegate.getTreePathHandle();
}
/**
* Returns the {@link DocTree.Kind} of this DocTreePathHandle, it returns the kind
* of the {@link DocTree} from which the handle was created.
*
* @return {@link Tree.Kind}
*/
public DocTree.Kind getKind() {
return this.delegate.getKind();
}
/**
* Factory method for creating {@link DocTreePathHandle}.
*
* @param docTreePath for which the {@link DocTreePathHandle} should be created.
* @param javac
* @return a new {@link DocTreePathHandle}
* @throws java.lang.IllegalArgumentException if arguments are not supported
*/
public static DocTreePathHandle create(final DocTreePath docTreePath, CompilationInfo javac) throws IllegalArgumentException {
Parameters.notNull("docTreePath", docTreePath);
Parameters.notNull("javac", javac);
TreePathHandle treePathHandle = TreePathHandle.create(docTreePath.getTreePath(), javac);
if(treePathHandle.getFileObject() == null) {
return null;
}
JCDiagnostic.DiagnosticPosition position = ((DCTree) docTreePath.getLeaf()).pos((DCTree.DCDocComment)docTreePath.getDocComment());
if (position == null) {
DocTree docTree = docTreePath.getLeaf();
if(docTree == docTreePath.getDocComment()) {
return new DocTreePathHandle(new DocCommentDelegate(treePathHandle));
}
int index = listChildren(docTreePath.getParentPath().getLeaf()).indexOf(docTree);
assert index != (-1);
return new DocTreePathHandle(new CountingDelegate(treePathHandle, index, docTreePath.getLeaf().getKind()));
}
int preferredPosition = position.getPreferredPosition();
Position pos = preferredPosition >= 0 ? createPositionRef(treePathHandle.getFileObject(), preferredPosition, Bias.Forward) : null;
return new DocTreePathHandle(new DocTreeDelegate(pos, new DocTreeDelegate.KindPath(docTreePath), treePathHandle));
}
private static boolean isSupported(Element el) {
switch (el.getKind()) {
case PACKAGE:
case CLASS:
case INTERFACE:
case ENUM:
case METHOD:
case CONSTRUCTOR:
case INSTANCE_INIT:
case STATIC_INIT:
case FIELD:
case ANNOTATION_TYPE:
case ENUM_CONSTANT:
return true;
default:
return false;
}
}
private static List<DocTree> listChildren(@NonNull DocTree t) {
final List<DocTree> result = new LinkedList<DocTree>();
t.accept(new DocTreeScanner<Void, Void>() {
@Override
public Void scan(DocTree node, Void p) {
result.add(node);
return null;
}
}, null);
return result;
}
private static Position createPositionRef(FileObject file, int position, Position.Bias bias) {
try {
PositionRefProvider prp = PositionRefProvider.get(file);
Position positionRef = prp != null ? prp.createPosition(position, bias) : null;
if (positionRef != null) {
return positionRef;
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
throw new IllegalStateException("Cannot create PositionRef for file " + file.getPath() + ". CloneableEditorSupport not found");
}
@Override
public String toString() {
return "DocTreePathHandle[delegate:" + delegate + "]";
}
static interface Delegate {
public DocTreePath resolve(final CompilationInfo compilationInfo) throws IllegalArgumentException;
public boolean equalsHandle(Delegate obj);
public int hashCode();
public DocTree.Kind getKind();
public TreePathHandle getTreePathHandle();
}
private static final class DocTreeDelegate implements Delegate {
private final Position position;
private final KindPath kindPath;
private final TreePathHandle treePathHandle;
private final DocTree.Kind kind;
private DocTreeDelegate(Position position, KindPath kindPath, TreePathHandle treePathHandle) {
this.kindPath = kindPath;
this.position = position;
this.treePathHandle = treePathHandle;
if (kindPath != null) {
this.kind = kindPath.kindPath.get(0);
} else {
this.kind = null;
}
}
/**
* Resolves an {@link TreePath} from the {@link TreePathHandle}.
*
* @param javac representing the
* {@link javax.tools.CompilationTask}
* @return resolved subclass of {@link Element} or null if the element
* does not exist on the classpath/sourcepath of
* {@link javax.tools.CompilationTask}.
* @throws IllegalArgumentException when this {@link TreePathHandle} is
* not created for a source represented by the compilationInfo.
*/
public DocTreePath resolve(final CompilationInfo javac) throws IllegalArgumentException {
assert javac != null;
TreePath treePath = treePathHandle.resolve(javac);
if(treePath == null) {
throw new IllegalArgumentException("treePathHandle.resolve(compilationInfo) returned null for treePathHandle " + treePathHandle); //NOI18N
}
DocTreePath tp = null;
DocCommentTree doc = javac.getDocTrees().getDocCommentTree(treePath);
if (doc == null) {
// no doc comment for the TreePath
return null;
}
int pos = position != null ? position.getOffset() : -1;
tp = pos < 0 ? new DocTreePath(treePath, doc) : resolvePathForPos(javac, treePath, doc, pos + 1);
if (tp != null) {
return tp;
}
tp = resolvePathForPos(javac, treePath, doc, pos);
return tp;
}
private DocTreePath resolvePathForPos(CompilationInfo javac, TreePath treePath, DocCommentTree doc, int pos) {
DocTreePath tp = javac.getTreeUtilities().pathFor(treePath, doc, pos);
while (tp != null) {
KindPath kindPath1 = new KindPath(tp);
kindPath.getList().remove(Tree.Kind.ERRONEOUS);
if (kindPath1.equals(kindPath)) {
return tp;
}
tp = tp.getParentPath();
}
return null;
}
public boolean equalsHandle(Delegate obj) {
DocTreeDelegate other = (DocTreeDelegate) obj;
int otherOffset = other.position != null ? other.position.getOffset() : -1;
int thisOffset = this.position != null ? this.position.getOffset() : -1;
if (thisOffset != otherOffset) {
return false;
}
return other.getTreePathHandle().equals(treePathHandle);
}
@Override
public int hashCode() {
if (this.position == null) {
return 553 + treePathHandle.hashCode();
}
int hash = 7;
hash = 79 * hash + this.position.getOffset();
hash = 79 * hash + this.treePathHandle.hashCode();
return hash;
}
/**
* Returns the {@link Tree.Kind} of this TreePathHandle, it returns the
* kind of the {@link Tree} from which the handle was created.
*
* @return {@link Tree.Kind}
*/
public DocTree.Kind getKind() {
return kind;
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "[kind:" + kind + ", treepathHandle:" + treePathHandle + "]";
}
@Override
public TreePathHandle getTreePathHandle() {
return treePathHandle;
}
static class KindPath {
private ArrayList<DocTree.Kind> kindPath = new ArrayList();
KindPath(DocTreePath treePath) {
while (treePath != null) {
kindPath.add(treePath.getLeaf().getKind());
treePath = treePath.getParentPath();
}
}
public int hashCode() {
return kindPath.hashCode();
}
public boolean equals(Object object) {
if (object instanceof KindPath) {
return kindPath.equals(((KindPath) object).kindPath);
}
return false;
}
public ArrayList<DocTree.Kind> getList() {
return kindPath;
}
}
}
private static final class DocCommentDelegate implements Delegate {
private final TreePathHandle parent;
public DocCommentDelegate(TreePathHandle parent) {
this.parent = parent;
}
public DocTreePath resolve(CompilationInfo javac) throws IllegalArgumentException {
TreePath p = parent.resolve(javac);
if (p == null) {
return null;
}
DocCommentTree docCommentTree = javac.getDocTrees().getDocCommentTree(p);
return new DocTreePath(p, docCommentTree);
}
public boolean equalsHandle(Delegate obj) {
return this == obj;//XXX
}
@Override
public TreePathHandle getTreePathHandle() {
return parent;
}
@Override
public DocTree.Kind getKind() {
return DocTree.Kind.DOC_COMMENT;
}
}
private static final class CountingDelegate implements Delegate {
private final TreePathHandle parent;
private final int index;
private final DocTree.Kind kind;
public CountingDelegate(TreePathHandle parent, int index, DocTree.Kind kind) {
this.parent = parent;
this.index = index;
this.kind = kind;
}
public DocTreePath resolve(CompilationInfo javac) throws IllegalArgumentException {
TreePath p = parent.resolve(javac);
if (p == null) {
return null;
}
DocCommentTree docCommentTree = javac.getDocTrees().getDocCommentTree(p);
return getChild(docCommentTree, index);
}
public boolean equalsHandle(Delegate obj) {
return this == obj;//XXX
}
public DocTree.Kind getKind() {
return kind;
}
@Override
public TreePathHandle getTreePathHandle() {
return parent;
}
}
private static DocTreePath getChild(@NonNull DocCommentTree t, final int index) {
final DocTreePath[] result = new DocTreePath[1];
t.accept(new DocTreePathScanner<DocTreePath, Void>() {
int count = 0;
@Override
public DocTreePath scan(DocTree node, Void p) {
if(index == count) {
result[0] = getCurrentPath();
}
return null;
}
}, null);
return result[0];
}
}