| /* |
| * 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]; |
| } |
| } |