diff --git a/java.editor/nbproject/project.xml b/java.editor/nbproject/project.xml --- a/java.editor/nbproject/project.xml +++ b/java.editor/nbproject/project.xml @@ -203,6 +203,15 @@ + org.netbeans.modules.java.platform + + + + 1 + 1.15 + + + org.netbeans.modules.java.preprocessorbridge @@ -264,6 +273,15 @@ + org.netbeans.modules.project.libraries + + + + 1 + 1.23 + + + org.netbeans.modules.projectapi diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/AnnotationsHolder.java b/java.editor/src/org/netbeans/modules/java/editor/overridden/AnnotationsHolder.java --- a/java.editor/src/org/netbeans/modules/java/editor/overridden/AnnotationsHolder.java +++ b/java.editor/src/org/netbeans/modules/java/editor/overridden/AnnotationsHolder.java @@ -60,7 +60,8 @@ * @author Jan Lahoda */ public class AnnotationsHolder implements PropertyChangeListener { - + + private static final Logger LOGGER = Logger.getLogger(AnnotationsHolder.class.getName()); private static final Map file2Annotations = new HashMap(); public static synchronized AnnotationsHolder get(FileObject file) { @@ -82,7 +83,7 @@ return a; } catch (IOException ex) { - IsOverriddenAnnotationHandler.LOG.log(Level.INFO, null, ex); + LOGGER.log(Level.INFO, null, ex); return null; } diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/Bundle.properties b/java.editor/src/org/netbeans/modules/java/editor/overridden/Bundle.properties --- a/java.editor/src/org/netbeans/modules/java/editor/overridden/Bundle.properties +++ b/java.editor/src/org/netbeans/modules/java/editor/overridden/Bundle.properties @@ -23,7 +23,7 @@ # Contributor(s): # # The Original Software is NetBeans. The Initial Developer of the Original -# Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun +# Software is Sun Microsystems, Inc. Portions Copyright 1997-2009 Sun # Microsystems, Inc. All Rights Reserved. # # If you wish your version of this file to be governed by only the CDDL @@ -51,3 +51,24 @@ NAME_AnonynmousInner=anonymous inner CTL_IsOverriddenAnnotationAction=Overriden Annotation +CTL_GoToImplementation=Go to Implementation +goto-implementation=Go to Implementation + +LAB_IsOverridenAnnotation-override-is-overridden-combined=Overrides and is overridden +TP_COMBINED-override-is-overridden-combined=Overrides and is overridden +LAB_IsOverridenAnnotation-implements-is-overridden-combined=Implements and is overridden +TP_COMBINED-implements-is-overridden-combined=Implements and is overridden +#XXX: inside interfaces, but this should ideally be overrides/has implementations: +LAB_IsOverridenAnnotation-implements-has-implementations-combined=Implements and has implementations +TP_COMBINED-implements-has-implementations-combined=Implements and has implementations +LAB_IsOverriddenAnnotation=Is Overridden +LAB_OverridesAnnotation=Overrides +LAB_ImplementsAnnotation=Implements +LAB_HasImplementationsAnnotation=Has Implementations + +LBL_NoMethod=No Method or Type +LBL_NoOverridingMethod=No overridding method found +LBL_NoOverridingType=No overridding type found +ERR_CycleInDependencies=Cycle in dependencies +LBL_ImplementorsOverriders=Implementors/Overridders +ERR_NoDependencies=Cannot detect project dependencies diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/ComputeAnnotations.java b/java.editor/src/org/netbeans/modules/java/editor/overridden/ComputeAnnotations.java new file mode 100644 --- /dev/null +++ b/java.editor/src/org/netbeans/modules/java/editor/overridden/ComputeAnnotations.java @@ -0,0 +1,196 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 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]" + * + * 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 2009 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.editor.overridden; + +import com.sun.source.tree.Tree; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.lang.model.element.Element; +import javax.swing.text.BadLocationException; +import javax.swing.text.Position; +import javax.swing.text.StyledDocument; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.ElementHandle; +import org.netbeans.api.java.source.JavaParserResultTask; +import org.netbeans.api.java.source.JavaSource.Phase; +import org.netbeans.modules.parsing.api.Snapshot; +import org.netbeans.modules.parsing.spi.Parser.Result; +import org.netbeans.modules.parsing.spi.Scheduler; +import org.netbeans.modules.parsing.spi.SchedulerEvent; +import org.netbeans.modules.parsing.spi.SchedulerTask; +import org.netbeans.modules.parsing.spi.TaskFactory; +import org.openide.text.NbDocument; + +/** + * + * @author lahvac + */ +public class ComputeAnnotations extends JavaParserResultTask { + + private final AtomicBoolean cancel = new AtomicBoolean(); + + public ComputeAnnotations() { + super(Phase.RESOLVED); + } + + @Override + public void run(Result result, SchedulerEvent event) { + cancel.set(false); + + CompilationInfo info = CompilationInfo.get(result); + + if (info.getChangedTree() != null) { + //XXX: currently only method bodies are rebuilt. + return ; + } + + long start = System.currentTimeMillis(); + StyledDocument doc = (StyledDocument) result.getSnapshot().getSource().getDocument(false); + + if (doc == null) { + return ; + } + + List annotations = new LinkedList(); + + createAnnotations(info, doc, new ComputeOverriding(cancel).process(info), false, annotations); + createAnnotations(info, doc, new ComputeOverriders(cancel).process(info, null, null, false), true, annotations); + + if (cancel.get()) return ; + + AnnotationsHolder.get(info.getFileObject()).setNewAnnotations(annotations); + + long end = System.currentTimeMillis(); + + Logger.getLogger("TIMER").log(Level.FINE, "Is Overridden Annotations", new Object[] {info.getFileObject(), end - start}); + } + + private void createAnnotations(CompilationInfo info, StyledDocument doc, Map, List> descriptions, boolean overridden, List annotations) { + if (descriptions != null) { + for (Entry, List> e : descriptions.entrySet()) { + Element ee = e.getKey().resolve(info); + Tree t = info.getTrees().getTree(ee); + + if (t == null) { + //XXX: log + continue; + } + + AnnotationType type; + String dn; + + if (overridden) { + if (ee.getEnclosingElement().getKind().isInterface() || ee.getKind().isInterface()) { + type = AnnotationType.HAS_IMPLEMENTATION; + dn = "Is Implemented"; + } else { + type = AnnotationType.IS_OVERRIDDEN; + dn = "Is Overridden"; + } + } else { + if (ee.getEnclosingElement().getKind().isInterface() || ee.getKind().isInterface()) { + type = AnnotationType.IMPLEMENTS; + dn = "Implements"; + } else { + type = AnnotationType.OVERRIDES; + dn = "Overrides"; + } + } + + Position pos = getPosition(doc, (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), t)); + + annotations.add(new IsOverriddenAnnotation(doc, pos, type, dn, e.getValue())); + } + } + } + + @Override + public int getPriority() { + return Integer.MAX_VALUE; + } + + @Override + public Class getSchedulerClass() { + return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER; + } + + @Override + public void cancel() { + cancel.set(true); + } + + private static Position getPosition(final StyledDocument doc, final int offset) { + class Impl implements Runnable { + private Position pos; + public void run() { + if (offset < 0 || offset >= doc.getLength()) + return ; + + try { + pos = doc.createPosition(offset - NbDocument.findLineColumn(doc, offset)); + } catch (BadLocationException ex) { + //should not happen? + Logger.getLogger(ComputeAnnotations.class.getName()).log(Level.FINE, null, ex); + } + } + } + + Impl i = new Impl(); + + doc.render(i); + + return i.pos; + } + + public static final class FactoryImpl extends TaskFactory { + + @Override + public Collection create(Snapshot snapshot) { + return Collections.singleton(new ComputeAnnotations()); + } + + } +} diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/ComputeOverriders.java b/java.editor/src/org/netbeans/modules/java/editor/overridden/ComputeOverriders.java new file mode 100644 --- /dev/null +++ b/java.editor/src/org/netbeans/modules/java/editor/overridden/ComputeOverriders.java @@ -0,0 +1,575 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 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]" + * + * 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 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.java.editor.overridden; + +import java.awt.Point; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.ElementFilter; +import javax.lang.model.util.Types; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.queries.SourceForBinaryQuery; +import org.netbeans.api.java.source.ClassIndex; +import org.netbeans.api.java.source.ClassIndex.SearchKind; +import org.netbeans.api.java.source.ClassIndex.SearchScope; +import org.netbeans.api.java.source.ClasspathInfo; +import org.netbeans.api.java.source.CompilationController; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.ElementHandle; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.SourceUtils; +import org.netbeans.api.java.source.Task; +import org.netbeans.spi.java.classpath.support.ClassPathSupport; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileStateInvalidException; +import org.openide.filesystems.URLMapper; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.TopologicalSortException; +import org.openide.util.Utilities; + +/** + * + * @author Jan Lahoda + */ +public class ComputeOverriders { + + private static final Logger LOG = Logger.getLogger(ComputeOverriders.class.getName()); + + private final AtomicBoolean cancel; + + public ComputeOverriders(AtomicBoolean cancel) { + this.cancel = cancel; + } + + private static Set findReverseSourceRoots(final URL thisSourceRoot, Map> sourceDeps, final FileObject thisFile) { + long startTime = System.currentTimeMillis(); + + try { + //TODO: from SourceUtils (which filters out source roots that do not belong to open projects): + //Create inverse dependencies + final Map> inverseDeps = new HashMap> (); + for (Map.Entry> entry : sourceDeps.entrySet()) { + final URL u1 = entry.getKey(); + final List l1 = entry.getValue(); + for (URL u2 : l1) { + List l2 = inverseDeps.get(u2); + if (l2 == null) { + l2 = new ArrayList(); + inverseDeps.put (u2,l2); + } + l2.add (u1); + } + } + //Collect dependencies + final Set result = new HashSet(); + final LinkedList todo = new LinkedList (); + todo.add (thisSourceRoot); + while (!todo.isEmpty()) { + final URL u = todo.removeFirst(); + if (!result.contains(u)) { + result.add (u); + final List ideps = inverseDeps.get(u); + if (ideps != null) { + todo.addAll (ideps); + } + } + } + return result; + } finally { + long endTime = System.currentTimeMillis(); + + Logger.getLogger("TIMER").log(Level.FINE, "Find Reverse Source Roots", //NOI18N + new Object[]{thisFile, endTime - startTime}); + } + } + + private static FileObject findSourceRoot(FileObject file) { + final ClassPath cp = ClassPath.getClassPath(file, ClassPath.SOURCE); + if (cp != null) { + return cp.findOwnerRoot(file); + } + //Null is a valid value for files which have no source path (default filesystem). + return null; + } + + private Set findBinaryRootsForSourceRoot(FileObject sourceRoot, Map> binaryDeps) { +// BinaryForSourceQuery.findBinaryRoots(thisSourceRoot).getRoots(); + Set result = new HashSet(); + + for (URL bin : binaryDeps.keySet()) { + if (cancel.get()) return Collections.emptySet(); + for (FileObject s : SourceForBinaryQuery.findSourceRoots(bin).getRoots()) { + if (s == sourceRoot) { + result.add(bin); + } + } + } + + return result; + } + + Map, List> process(CompilationInfo info, TypeElement te, ExecutableElement ee, boolean interactive) { + long startTime = System.currentTimeMillis(); + + try { + return processImpl(info, te, ee, interactive); + } finally { + Logger.getLogger("TIMER").log(Level.FINE, "Overridden - Total", //NOI18N + new Object[] {info.getFileObject(), System.currentTimeMillis() - startTime}); + } + } + + private Map, List> processImpl(CompilationInfo info, TypeElement te, ExecutableElement ee, boolean interactive) { + FileObject file = info.getFileObject(); + FileObject thisSourceRoot = findSourceRoot(file); + + if (thisSourceRoot == null) { + return null; + } + + + //XXX: special case "this" source root (no need to create a new JS and load the classes again for it): +// reverseSourceRoots.add(thisSourceRoot); + +// LOG.log(Level.FINE, "reverseSourceRoots: {0}", reverseSourceRoots); //NOI18N + +// if (LOG.isLoggable(Level.FINE)) { +// LOG.log(Level.FINE, "method: {0}", ee.toString()); //NOI18N +// } + + + final Map, List>> methods = new HashMap, List>>(); + + if (ee == null) { + if (te == null) { + fillInMethods(info.getTopLevelElements(), methods); + } else { + methods.put(ElementHandle.create(te), Collections.>emptyList()); + } + } else { + TypeElement owner = (TypeElement) ee.getEnclosingElement(); + + methods.put(ElementHandle.create(owner), Collections.singletonList(ElementHandle.create(ee))); + } + + final Map, List> overriding = new HashMap, List>(); + + long startTime = System.currentTimeMillis(); + long[] classIndexTime = new long[1]; + final Map, Set>>> users = computeUsers(info, thisSourceRoot, methods.keySet(), classIndexTime, interactive); + long endTime = System.currentTimeMillis(); + + if (users == null) { + return null; + } + + Logger.getLogger("TIMER").log(Level.FINE, "Overridden Candidates - Class Index", //NOI18N + new Object[] {file, classIndexTime[0]}); + Logger.getLogger("TIMER").log(Level.FINE, "Overridden Candidates - Total", //NOI18N + new Object[] {file, endTime - startTime}); + + for (Map.Entry, Set>>> data : users.entrySet()) { + for (Map.Entry, Set>> deps : data.getValue().entrySet()) { + if (cancel.get()) return null; + findOverriddenAnnotations(data.getKey(), deps.getValue(), deps.getKey(), methods.get(deps.getKey()), overriding); + } + } + + return overriding; + } + private static final ClassPath EMPTY = ClassPathSupport.createClassPath(new URL[0]); + + private static void fillInMethods(Iterable types, Map, List>> methods) { + for (TypeElement te : types) { + List> l = new LinkedList>(); + + for (ExecutableElement ee : ElementFilter.methodsIn(te.getEnclosedElements())) { + l.add(ElementHandle.create(ee)); + } + + methods.put(ElementHandle.create(te), l); + + fillInMethods(ElementFilter.typesIn(te.getEnclosedElements()), methods); + } + } + private Set> computeUsers(URL source, Set> base, long[] classIndexCumulative) { + ClasspathInfo cpinfo = ClasspathInfo.create(EMPTY, EMPTY, ClassPathSupport.createClassPath(source)); + + return computeUsers(cpinfo, ClassIndex.SearchScope.SOURCE, base, classIndexCumulative); + } + + private Set> computeUsers(ClasspathInfo cpinfo, SearchScope scope, Set> base, long[] classIndexCumulative) { + long startTime = System.currentTimeMillis(); + + try { + List> l = new LinkedList>(base); + Set> result = new HashSet>(); + + while (!l.isEmpty()) { + if (cancel.get()) return null; + + ElementHandle eh = l.remove(0); + + result.add(eh); + Set> typeElements = cpinfo.getClassIndex().getElements(eh, Collections.singleton(SearchKind.IMPLEMENTORS), EnumSet.of(scope)); + //XXX: Canceling + if (typeElements != null) { + l.addAll(typeElements); + } + } + return result; + } finally { + classIndexCumulative[0] += (System.currentTimeMillis() - startTime); + } + } + + static List reverseSourceRootsInOrderOverride; + + private List reverseSourceRootsInOrder(CompilationInfo info, URL thisSourceRoot, FileObject thisSourceRootFO, Map> sourceDeps, Map> binaryDeps, boolean interactive) { + if (reverseSourceRootsInOrderOverride != null) { + return reverseSourceRootsInOrderOverride; + } + + Set sourceRootsSet; + + if (sourceDeps.containsKey(thisSourceRoot)) { + sourceRootsSet = findReverseSourceRoots(thisSourceRoot, sourceDeps, info.getFileObject()); + } else { + sourceRootsSet = new HashSet(); + + for (URL binary : findBinaryRootsForSourceRoot(thisSourceRootFO, binaryDeps)) { + List deps = binaryDeps.get(binary); + + if (deps != null) { + sourceRootsSet.addAll(deps); + } + } + } + + List sourceRoots; + try { + sourceRoots = new LinkedList(Utilities.topologicalSort(sourceDeps.keySet(), sourceDeps)); + } catch (TopologicalSortException ex) { + if (interactive) { + Exceptions.attachLocalizedMessage(ex,NbBundle.getMessage(GoToImplementation.class, "ERR_CycleInDependencies")); + Exceptions.printStackTrace(ex); + } else { + LOG.log(Level.FINE, null, ex); + } + return null; + } + + sourceRoots.retainAll(sourceRootsSet); + + Collections.reverse(sourceRoots); + + return sourceRoots; + } + + private Map, Set>>> computeUsers(CompilationInfo info, FileObject thisSourceRoot, Set> baseHandles, long[] classIndexCumulative, boolean interactive) { + Map> sourceDeps = getDependencies(false); + Map> binaryDeps = getDependencies(true); + + if (sourceDeps == null || binaryDeps == null) { + if (interactive) { + NotifyDescriptor nd = new NotifyDescriptor.Message(NbBundle.getMessage(GoToImplementation.class, "ERR_NoDependencies"), NotifyDescriptor.ERROR_MESSAGE); + + DialogDisplayer.getDefault().notifyLater(nd); + } else { + LOG.log(Level.FINE, NbBundle.getMessage(GoToImplementation.class, "ERR_NoDependencies")); + } + + return null; + } + + URL thisSourceRootURL; + + try { + thisSourceRootURL = thisSourceRoot.getURL(); + } catch (FileStateInvalidException ex) { + Exceptions.printStackTrace(ex); + return null; + } + + List sourceRoots = reverseSourceRootsInOrder(info, thisSourceRootURL, thisSourceRoot, sourceDeps, binaryDeps, interactive); + + if (sourceRoots == null) { + return null; + } + + Map, Set>> auxHandles = new HashMap, Set>>(); + + if (!sourceDeps.containsKey(thisSourceRootURL)) { + Set binaryRoots = new HashSet(); + + for (URL sr : sourceRoots) { + List deps = sourceDeps.get(sr); + + if (deps != null) { + binaryRoots.addAll(deps); + } + } + + binaryRoots.retainAll(binaryDeps.keySet()); + + for (ElementHandle handle : baseHandles) { + Set> types = computeUsers(ClasspathInfo.create(EMPTY, ClassPathSupport.createClassPath(binaryRoots.toArray(new URL[0])), EMPTY), SearchScope.DEPENDENCIES, Collections.singleton(handle), classIndexCumulative); + + if (types == null/*canceled*/ || cancel.get()) { + return null; + } + + auxHandles.put(handle, types); + } + } + + Map, Set>>> result = new LinkedHashMap, Set>>>(); + + for (URL file : sourceRoots) { + for (ElementHandle base : baseHandles) { + if (cancel.get()) return null; + + Set> baseTypes = new HashSet>(); + + baseTypes.add(base); + + Set> aux = auxHandles.get(base); + + if (aux != null) { + baseTypes.addAll(aux); + } + + for (URL dep : sourceDeps.get(file)) { + Map, Set>> depTypesMulti = result.get(dep); + Set> depTypes = depTypesMulti != null ? depTypesMulti.get(base) : null; + + if (depTypes != null) { + baseTypes.addAll(depTypes); + } + } + + Set> types = computeUsers(file, baseTypes, classIndexCumulative); + + if (types == null/*canceled*/ || cancel.get()) { + return null; + } + + types.removeAll(baseTypes); + + Map, Set>> currentUsers = result.get(file); + + if (currentUsers == null) { + result.put(file, currentUsers = new LinkedHashMap, Set>>()); + } + + currentUsers.put(base, types); + } + } + + return result; + } + + private void findOverriddenAnnotations( + URL sourceRoot, + final Set> users, + final ElementHandle originalType, + final List> methods, + final Map, List> overriding) { + if (!users.isEmpty()) { + FileObject sourceRootFile = URLMapper.findFileObject(sourceRoot); + ClasspathInfo cpinfo = ClasspathInfo.create(sourceRootFile); + + JavaSource js = JavaSource.create(cpinfo); + + try { + js.runUserActionTask(new Task() { + public void run(CompilationController controller) throws Exception { + Set seenElements = new HashSet(); + Element resolvedOriginalType = originalType.resolve(controller); + + if (resolvedOriginalType == null) { + return ; + } + + for (ElementHandle typeHandle : users) { + if (cancel.get()) return ; + TypeElement type = typeHandle.resolve(controller); + + if (type == null || !seenElements.add(type)) { + continue; + } + + Types types = controller.getTypes(); + + if (types.isSubtype(types.erasure(type.asType()), types.erasure(resolvedOriginalType.asType()))) { + List classOverriders = overriding.get(originalType); + + if (classOverriders == null) { + overriding.put(originalType, classOverriders = new LinkedList()); + } + + classOverriders.add(new ElementDescription(controller, type, true)); + + for (ElementHandle originalMethodHandle : methods) { + ExecutableElement originalMethod = originalMethodHandle.resolve(controller); + + if (originalMethod != null) { + ExecutableElement overrider = getImplementationOf(controller, originalMethod, type); + + if (overrider == null) { + continue; + } + + List overriddingMethods = overriding.get(originalMethodHandle); + + if (overriddingMethods == null) { + overriding.put(originalMethodHandle, overriddingMethods = new ArrayList()); + } + + overriddingMethods.add(new ElementDescription(controller, overrider, true)); + } else { + Logger.getLogger("global").log(Level.SEVERE, "IsOverriddenAnnotationHandler: originalMethod == null!"); //NOI18N + } + } + } + } + } + }, true); + } catch (Exception e) { + Exceptions.printStackTrace(e); + } + } + } + + private static ExecutableElement getImplementationOf(CompilationInfo info, ExecutableElement overridee, TypeElement implementor) { + for (ExecutableElement overrider : ElementFilter.methodsIn(implementor.getEnclosedElements())) { + if (info.getElements().overrides(overrider, overridee, implementor)) { + return overrider; + } + } + + return null; + } + + static void performGoToAction(List declarations, Point position) { + String caption = NbBundle.getMessage(GoToImplementation.class, "LBL_ImplementorsOverriders"); + + PopupUtil.showPopup(new IsOverriddenPopup(caption, declarations), caption, position.x, position.y, true, 0); + } + + static Map> dependenciesOverride; + + private static Map> getDependencies(boolean binary) { + if (dependenciesOverride != null) { + return dependenciesOverride; + } + + ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class); + + if (l == null) { + return null; + } + + Class clazz = null; + String method = null; + + try { + clazz = l.loadClass("org.netbeans.modules.parsing.impl.indexing.friendapi.IndexingController"); + method = binary ? "getBinaryRootDependencies" : "getRootDependencies"; + } catch (ClassNotFoundException ex) { + Logger.getLogger(GoToImplementation.class.getName()).log(Level.FINE, null, ex); + try { + clazz = l.loadClass("org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater"); + method = binary ? "getDependencies" : "doesnotexist"; + } catch (ClassNotFoundException inner) { + Logger.getLogger(GoToImplementation.class.getName()).log(Level.FINE, null, inner); + return null; + } + } + + try { + Method getDefault = clazz.getDeclaredMethod("getDefault"); + Object instance = getDefault.invoke(null); + Method dependenciesMethod = clazz.getDeclaredMethod(method); + + return (Map>) dependenciesMethod.invoke(instance); + } catch (IllegalAccessException ex) { + Logger.getLogger(GoToImplementation.class.getName()).log(Level.FINE, null, ex); + return null; + } catch (IllegalArgumentException ex) { + Logger.getLogger(GoToImplementation.class.getName()).log(Level.FINE, null, ex); + return null; + } catch (InvocationTargetException ex) { + Logger.getLogger(GoToImplementation.class.getName()).log(Level.FINE, null, ex); + return null; + } catch (NoSuchMethodException ex) { + Logger.getLogger(GoToImplementation.class.getName()).log(Level.FINE, null, ex); + return null; + } catch (SecurityException ex) { + Logger.getLogger(GoToImplementation.class.getName()).log(Level.FINE, null, ex); + return null; + } catch (ClassCastException ex) { + Logger.getLogger(GoToImplementation.class.getName()).log(Level.FINE, null, ex); + return null; + } + } + +} diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationHandler.java b/java.editor/src/org/netbeans/modules/java/editor/overridden/ComputeOverriding.java rename from java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationHandler.java rename to java.editor/src/org/netbeans/modules/java/editor/overridden/ComputeOverriding.java --- a/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationHandler.java +++ b/java.editor/src/org/netbeans/modules/java/editor/overridden/ComputeOverriding.java @@ -42,19 +42,14 @@ package org.netbeans.modules.java.editor.overridden; import com.sun.source.tree.CompilationUnitTree; -import com.sun.source.tree.Tree; -import java.io.IOException; -import java.net.URL; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.EnumSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import javax.lang.model.element.Element; @@ -66,123 +61,23 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; -import javax.swing.text.BadLocationException; -import javax.swing.text.Position; -import javax.swing.text.StyledDocument; -import org.netbeans.api.java.classpath.ClassPath; -import org.netbeans.api.java.classpath.ClassPath.Entry; -import org.netbeans.api.java.queries.SourceForBinaryQuery; -import org.netbeans.api.java.source.CancellableTask; -import org.netbeans.api.java.source.Task; -import org.netbeans.api.java.source.ClassIndex.SearchKind; -import org.netbeans.api.java.source.ClasspathInfo; -import org.netbeans.api.java.source.CompilationController; import org.netbeans.api.java.source.CompilationInfo; import org.netbeans.api.java.source.ElementHandle; -import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.java.source.SourceUtils; -import org.netbeans.api.java.source.ClassIndex; -import org.netbeans.spi.java.classpath.support.ClassPathSupport; -import org.openide.filesystems.FileObject; -import org.openide.filesystems.FileUtil; -import org.openide.text.NbDocument; -import org.openide.util.Exceptions; -import org.openide.util.NbBundle; -import org.openide.util.RequestProcessor; -import org.openide.util.TopologicalSortException; -import org.openide.util.Utilities; /** * * @author Jan Lahoda */ -public class IsOverriddenAnnotationHandler implements CancellableTask { +public class ComputeOverriding { - private static final boolean enableReverseLookups = Boolean.getBoolean("org.netbeans.java.editor.enableReverseLookups"); - static final Logger LOG = Logger.getLogger(IsOverriddenAnnotationHandler.class.getName()); + static final Logger LOG = Logger.getLogger(ComputeOverriding.class.getName()); - private FileObject file; + private final AtomicBoolean cancel; - public IsOverriddenAnnotationHandler(FileObject file) { - this.file = file; - Logger.getLogger("TIMER").log(Level.FINE, "IsOverriddenAnnotationHandler", new Object[] {file, this}); //NOI18N - } - - private IsOverriddenVisitor visitor; - - public void run(CompilationInfo info) throws IOException { - resume(); - - StyledDocument doc = (StyledDocument) info.getDocument(); - - if (doc == null) { - LOG.log(Level.FINE, "Cannot get document!"); //NOI18N - return ; - } - - long startTime = System.currentTimeMillis(); - - try { - List annotations = process(info, doc); - - if (annotations == null) { - //cancelled: - return ; - } - - newAnnotations(annotations); - } finally { - synchronized (this) { - visitor = null; - } - - Logger.getLogger("TIMER").log(Level.FINE, "Overridden in", //NOI18N - new Object[] {file, System.currentTimeMillis() - startTime}); - } - } - - private FileObject findSourceRoot() { - final ClassPath cp = ClassPath.getClassPath(file, ClassPath.SOURCE); - if (cp != null) { - for (FileObject root : cp.getRoots()) { - if (FileUtil.isParentOf(root, file)) - return root; - } - } - //Null is a valid value for files which have no source path (default filesystem). - return null; - } - - //temporary hack: - private synchronized Set findReverseSourceRoots(final FileObject thisSourceRoot, final FileObject thisFile) { - final Object o = new Object(); - final Set reverseSourceRoots = new HashSet(); - - RequestProcessor.getDefault().post(new Runnable() { - public void run() { - long startTime = System.currentTimeMillis(); - Set reverseSourceRootsInt = new HashSet(ReverseSourceRootsLookup.reverseSourceRootsLookup(thisSourceRoot)); - long endTime = System.currentTimeMillis(); - - Logger.getLogger("TIMER").log(Level.FINE, "Find Reverse Source Roots", //NOI18N - new Object[] {thisFile, endTime - startTime}); - - synchronized (o) { - reverseSourceRoots.addAll(reverseSourceRootsInt); - } - - wakeUp(); - } - }); - - try { - wait(); - } catch (InterruptedException ex) { - Exceptions.printStackTrace(ex); - } - - return reverseSourceRoots; + public ComputeOverriding(AtomicBoolean cancel) { + this.cancel = cancel; } public static AnnotationType detectOverrides(CompilationInfo info, TypeElement type, ExecutableElement ee, List result) { @@ -201,7 +96,7 @@ for (ExecutableElement overridee : lee) { if (info.getElements().overrides(ee, overridee, SourceUtils.getEnclosingTypeElement(ee))) { if (seenMethods.add(overridee)) { - result.add(new ElementDescription(info, overridee)); + result.add(new ElementDescription(info, overridee, false)); } } } @@ -219,16 +114,8 @@ return null; } - public List process(CompilationInfo info, final StyledDocument doc) { - IsOverriddenVisitor v; - - synchronized (this) { - if (isCanceled()) - return null; - - v = visitor = new IsOverriddenVisitor(doc, info); - } - + public Map, List> process(CompilationInfo info) { + IsOverriddenVisitor v = new IsOverriddenVisitor(info, cancel); CompilationUnitTree unit = info.getCompilationUnit(); long startTime1 = System.currentTimeMillis(); @@ -238,27 +125,9 @@ long endTime1 = System.currentTimeMillis(); Logger.getLogger("TIMER").log(Level.FINE, "Overridden Scanner", //NOI18N - new Object[] {file, endTime1 - startTime1}); + new Object[] {info.getFileObject(), endTime1 - startTime1}); - Set reverseSourceRoots; - - if (enableReverseLookups) { - FileObject thisSourceRoot = findSourceRoot(); - if (thisSourceRoot == null) { - return null; - } - - reverseSourceRoots = findReverseSourceRoots(thisSourceRoot, info.getFileObject()); - - //XXX: special case "this" source root (no need to create a new JS and load the classes again for it): - reverseSourceRoots.add(thisSourceRoot); - } else { - reverseSourceRoots = null; - } - - LOG.log(Level.FINE, "reverseSourceRoots: {0}", reverseSourceRoots); //NOI18N - - List annotations = new ArrayList(); + Map, List> result = new HashMap, List>(); for (ElementHandle td : v.type2Declaration.keySet()) { if (isCanceled()) @@ -295,330 +164,30 @@ } Set seenMethods = new HashSet(); - List overrides = new ArrayList(); - + List descriptions = new LinkedList(); + for (ExecutableElement overridee : lee) { if (info.getElements().overrides(ee, overridee, SourceUtils.getEnclosingTypeElement(ee))) { if (seenMethods.add(overridee)) { - overrides.add(new ElementDescription(info, overridee)); + descriptions.add(new ElementDescription(info, overridee, false)); } } } - - if (!overrides.isEmpty()) { - int position = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), v.declaration2Tree.get(methodHandle)); - Position pos = getPosition(doc, position); - - if (pos == null) { - //cannot compute the position, skip - continue; - } - - StringBuffer tooltip = new StringBuffer(); - boolean wasOverrides = false; - - boolean newline = false; - - for (ElementDescription ed : overrides) { - if (newline) { - tooltip.append("\n"); //NOI18N - } - - newline = true; - - if (ed.getModifiers().contains(Modifier.ABSTRACT)) { - tooltip.append(NbBundle.getMessage(IsOverriddenAnnotationHandler.class, "TP_Implements", ed.getDisplayName())); - } else { - tooltip.append(NbBundle.getMessage(IsOverriddenAnnotationHandler.class, "TP_Overrides", ed.getDisplayName())); - wasOverrides = true; - } - } - - annotations.add(new IsOverriddenAnnotation(doc, pos, wasOverrides ? AnnotationType.OVERRIDES : AnnotationType.IMPLEMENTS, tooltip.toString(), overrides)); - } - } - - if (enableReverseLookups) { - String typeOverridden = null; - AnnotationType typeType = null; - TypeElement resolved = td.resolve(info); - - - if (resolved == null) { - Logger.getLogger("global").log(Level.SEVERE, "IsOverriddenAnnotationHandler: resolved == null!"); //NOI18N - continue; - } - - if (resolved.getKind().isInterface()) { - typeOverridden = NbBundle.getMessage(IsOverriddenAnnotationHandler.class, "CAP_HasImplementations"); - typeType = AnnotationType.HAS_IMPLEMENTATION; - } - - if (resolved.getKind().isClass()) { - typeOverridden = NbBundle.getMessage(IsOverriddenAnnotationHandler.class, "CAP_IsOverridden"); - typeType = AnnotationType.IS_OVERRIDDEN; - } - - final Map, List> overriding = new HashMap, List>(); - final List overridingClasses = new ArrayList(); - - long startTime = System.currentTimeMillis(); - long[] classIndexTime = new long[1]; - final Map>> users = computeUsers(reverseSourceRoots, ElementHandle.create(resolved), classIndexTime); - long endTime = System.currentTimeMillis(); - - if (users == null) { - return null; - } - - Logger.getLogger("TIMER").log(Level.FINE, "Overridden Users Class Index", //NOI18N - new Object[] {file, classIndexTime[0]}); - Logger.getLogger("TIMER").log(Level.FINE, "Overridden Users", //NOI18N - new Object[] {file, endTime - startTime}); - - for (Map.Entry>> data : users.entrySet()) { - if (isCanceled()) - return null; - - findOverriddenAnnotations(data.getKey(), data.getValue(), td, v.type2Declaration.get(td), overriding, overridingClasses); - } - - if (!overridingClasses.isEmpty()) { - Tree t = v.declaration2Class.get(td); - - if (t != null) { - Position pos = getPosition(doc, (int) info.getTrees().getSourcePositions().getStartPosition(unit, t)); - - if (pos == null) { - //cannot compute the position, skip - continue; - } - - annotations.add(new IsOverriddenAnnotation(doc, pos, typeType, typeOverridden.toString(), overridingClasses)); - } - } - - for (ElementHandle original : overriding.keySet()) { - if (isCanceled()) - return null; - - Position pos = getPosition(doc, (int) info.getTrees().getSourcePositions().getStartPosition(unit, v.declaration2Tree.get(original))); - - if (pos == null) { - //cannot compute the position, skip - continue; - } - - Set mods = original.resolve(info).getModifiers(); - String tooltip = null; - - if (mods.contains(Modifier.ABSTRACT)) { - tooltip = NbBundle.getMessage(IsOverriddenAnnotationHandler.class, "TP_HasImplementations"); - } else { - tooltip = NbBundle.getMessage(IsOverriddenAnnotationHandler.class, "TP_IsOverridden"); - } - - IsOverriddenAnnotation ann = new IsOverriddenAnnotation(doc, pos, mods.contains(Modifier.ABSTRACT) ? AnnotationType.HAS_IMPLEMENTATION : AnnotationType.IS_OVERRIDDEN, tooltip, overriding.get(original)); - - annotations.add(ann); - } + + result.put(methodHandle, descriptions); } } if (isCanceled()) return null; else - return annotations; - } - - private static final ClassPath EMPTY = ClassPathSupport.createClassPath(new URL[0]); - - private Set> computeUsers(FileObject source, Set> base, long[] classIndexCumulative) { - ClasspathInfo cpinfo = ClasspathInfo.create(/*source);/*/EMPTY, EMPTY, ClassPathSupport.createClassPath(new FileObject[] {source})); - - long startTime = System.currentTimeMillis(); - - try { - List> l = new LinkedList>(base); - Set> result = new HashSet>(); - - while (!l.isEmpty()) { - ElementHandle eh = l.remove(0); - - result.add(eh); - Set> typeElements = cpinfo.getClassIndex().getElements(eh, Collections.singleton(SearchKind.IMPLEMENTORS), EnumSet.of(ClassIndex.SearchScope.SOURCE)); - //XXX: Canceling - if (typeElements != null) { - l.addAll(typeElements); - } - } return result; - } finally { - classIndexCumulative[0] += (System.currentTimeMillis() - startTime); - } - } - - private Map>> computeUsers(Set sources, ElementHandle base, long[] classIndexCumulative) { - Map> edges = new HashMap>(); - Map> dependsOn = new HashMap>(); - - for (FileObject source : sources) { - edges.put(source, new ArrayList()); - } - - for (FileObject source : sources) { - List deps = new ArrayList(); - - dependsOn.put(source, deps); - - for (Entry entry : ClassPath.getClassPath(source, ClassPath.COMPILE).entries()) { //TODO: should also check BOOT? - for (FileObject s : SourceForBinaryQuery.findSourceRoots(entry.getURL()).getRoots()) { - Collection targets = edges.get(s); - - if (targets != null) { - targets.add(source); - } - - deps.add(s); - } - } - } - - List sourceRoots = new ArrayList(sources); - - try { - Utilities.topologicalSort(sourceRoots, edges); - } catch (TopologicalSortException ex) { - LOG.log(Level.WARNING, "internal error", ex); //NOI18N - return null; - } - - Map>> result = new HashMap>>(); - - for (FileObject file : sourceRoots) { - Set> baseTypes = new HashSet>(); - - baseTypes.add(base); - - for (FileObject dep : dependsOn.get(file)) { - Set> depTypes = result.get(dep); - - if (depTypes != null) { - baseTypes.addAll(depTypes); - } - } - - Set> types = computeUsers(file, baseTypes, classIndexCumulative); - - types.removeAll(baseTypes); - - result.put(file, types); - } - - return result; - } - private void findOverriddenAnnotations( - FileObject sourceRoot, - final Set> users, - final ElementHandle originalType, - final List> methods, - final Map, List> overriding, - final List overridingClasses) { - ClasspathInfo cpinfo = ClasspathInfo.create(sourceRoot); - - if (!users.isEmpty()) { - JavaSource js = JavaSource.create(cpinfo); - - try { - js.runUserActionTask(new Task() { - - public void run(CompilationController controller) throws Exception { - Set seenElements = new HashSet(); - - for (ElementHandle typeHandle : users) { - if (isCanceled()) - return; - TypeElement type = typeHandle.resolve(controller); - Element resolvedOriginalType = originalType.resolve(controller); - - if (!seenElements.add(resolvedOriginalType)) - continue; - - if (controller.getTypes().isSubtype(type.asType(), resolvedOriginalType.asType())) { - overridingClasses.add(new ElementDescription(controller, type)); - - for (ElementHandle originalMethodHandle : methods) { - ExecutableElement originalMethod = originalMethodHandle.resolve(controller); - - if (originalMethod != null) { - ExecutableElement overrider = getImplementationOf(controller, originalMethod, type); - - if (overrider == null) - continue; - - List overriddingMethods = overriding.get(originalMethodHandle); - - if (overriddingMethods == null) { - overriding.put(originalMethodHandle, overriddingMethods = new ArrayList()); - } - - overriddingMethods.add(new ElementDescription(controller, overrider)); - } else { - Logger.getLogger("global").log(Level.SEVERE, "IsOverriddenAnnotationHandler: originalMethod == null!"); //NOI18N - } - } - } - } - } - },true); - } catch (Exception e) { - Exceptions.printStackTrace(e); - } - } - } - - private ExecutableElement getImplementationOf(CompilationInfo info, ExecutableElement overridee, TypeElement implementor) { - for (ExecutableElement overrider : ElementFilter.methodsIn(implementor.getEnclosedElements())) { - if (info.getElements().overrides(overrider, overridee, implementor)) { - return overrider; - } - } - - return null; - } - - private boolean canceled; - - public synchronized void cancel() { - canceled = true; - - if (visitor != null) { - visitor.cancel(); - } - - wakeUp(); - } - - private synchronized void resume() { - canceled = false; - } - - private synchronized void wakeUp() { - notifyAll(); } private synchronized boolean isCanceled() { - return canceled; + return cancel.get(); } - private void newAnnotations(List as) { - AnnotationsHolder a = AnnotationsHolder.get(file); - - if (a != null) { - a.setNewAnnotations(as); - } - } - private static void sortOutMethods(CompilationInfo info, Map> where, Element td, boolean current) { if (current) { Map> newlyAdded = new HashMap>(); @@ -662,27 +231,4 @@ } } - private static Position getPosition(final StyledDocument doc, final int offset) { - class Impl implements Runnable { - private Position pos; - public void run() { - if (offset < 0 || offset >= doc.getLength()) - return ; - - try { - pos = doc.createPosition(offset - NbDocument.findLineColumn(doc, offset)); - } catch (BadLocationException ex) { - //should not happen? - LOG.log(Level.FINE, null, ex); - } - } - } - - Impl i = new Impl(); - - doc.render(i); - - return i.pos; - } - } diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/ElementDescription.java b/java.editor/src/org/netbeans/modules/java/editor/overridden/ElementDescription.java --- a/java.editor/src/org/netbeans/modules/java/editor/overridden/ElementDescription.java +++ b/java.editor/src/org/netbeans/modules/java/editor/overridden/ElementDescription.java @@ -40,6 +40,7 @@ */ package org.netbeans.modules.java.editor.overridden; +import java.awt.Image; import java.util.Collection; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; @@ -57,6 +58,7 @@ import org.netbeans.api.java.source.ui.ElementIcons; import org.netbeans.modules.editor.java.Utilities; import org.openide.filesystems.FileObject; +import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; /** @@ -71,13 +73,15 @@ private ElementHandle outtermostElement; private Collection modifiers; private String displayName; - - public ElementDescription(CompilationInfo info, Element element) { + private final boolean overriddenFlag; + + public ElementDescription(CompilationInfo info, Element element, boolean overriddenFlag) { this.originalCPInfo = info.getClasspathInfo(); this.handle = ElementHandle.create(element); this.outtermostElement = ElementHandle.create(SourceUtils.getOutermostEnclosingTypeElement(element)); this.modifiers = element.getModifiers(); this.displayName = element.accept(new ElementNameVisitor(), true); + this.overriddenFlag = overriddenFlag; } public FileObject getSourceFile() { @@ -93,9 +97,19 @@ } public Icon getIcon() { - return ElementIcons.getElementIcon(handle.getKind(), modifiers); + Image badge; + + if (overriddenFlag) { + badge = ImageUtilities.loadImage("org/netbeans/modules/java/editor/resources/is-overridden-badge.png"); + } else { + badge = ImageUtilities.loadImage("org/netbeans/modules/java/editor/resources/overrides-badge.png"); + } + + Image icon = ImageUtilities.icon2Image(ElementIcons.getElementIcon(handle.getKind(), modifiers)); + + return ImageUtilities.image2Icon(ImageUtilities.mergeImages(icon, badge, 16, 0)); } - + public String getDisplayName() { return displayName; } diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/GoToImplementation.java b/java.editor/src/org/netbeans/modules/java/editor/overridden/GoToImplementation.java new file mode 100644 --- /dev/null +++ b/java.editor/src/org/netbeans/modules/java/editor/overridden/GoToImplementation.java @@ -0,0 +1,212 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008-2009 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]" + * + * 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 2008-2009 Sun Microsystems, Inc. + */ +package org.netbeans.modules.java.editor.overridden; + +import com.sun.source.tree.BlockTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.util.SourcePositions; +import com.sun.source.util.TreePath; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.swing.SwingUtilities; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.EditorActionRegistration; +import org.netbeans.api.java.lexer.JavaTokenId; +import org.netbeans.api.java.source.CompilationController; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.ElementHandle; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.JavaSource.Phase; +import org.netbeans.api.java.source.Task; +import org.netbeans.api.lexer.TokenSequence; +import org.netbeans.editor.BaseAction; +import org.netbeans.editor.ext.ExtKit; +import org.netbeans.modules.editor.java.JavaKit; +import org.netbeans.modules.java.editor.semantic.Utilities; +import org.openide.awt.StatusDisplayer; +import org.openide.util.Exceptions; +import org.openide.util.NbBundle; + +@EditorActionRegistration( + name = "goto-implementation", + mimeType = JavaKit.JAVA_MIME_TYPE, + popupText = "#CTL_GoToImplementation" +) +public final class GoToImplementation extends BaseAction { + + public GoToImplementation() { + super(SAVE_POSITION | ABBREV_RESET); +// putValue(SHORT_DESCRIPTION, NbBundle.getMessage(GoToImplementation.class, "CTL_GoToImplementation")); +// String name = NbBundle.getMessage(GoToImplementation.class, "CTL_GoToImplementation_trimmed"); +// putValue(ExtKit.TRIMMED_TEXT,name); +// putValue(POPUP_MENU_TEXT, name); + } + + public void actionPerformed(ActionEvent e, final JTextComponent c) { + try { + JavaSource.forDocument(c.getDocument()).runUserActionTask(new Task() { + public void run(CompilationController parameter) throws Exception { + parameter.toPhase(Phase.RESOLVED); + + Element el = resolveElement(parameter, c.getCaretPosition()); + + if (el == null) { + StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(GoToImplementation.class, "LBL_NoMethod")); + return ; + } + + TypeElement type = el.getKind() == ElementKind.METHOD ? (TypeElement) el.getEnclosingElement() : (TypeElement) el; + ExecutableElement method = el.getKind() == ElementKind.METHOD ? (ExecutableElement) el : null; + + Map, List> overriding = new ComputeOverriders(new AtomicBoolean()).process(parameter, type, method, true); + + List overridingMethods = overriding.get(ElementHandle.create(el)); + + if (overridingMethods == null || overridingMethods.isEmpty()) { + String key = el.getKind() == ElementKind.METHOD ? "LBL_NoOverridingMethod" : "LBL_NoOverridingType"; + + StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(GoToImplementation.class, key)); + return; + } + + Point p = new Point(c.modelToView(c.getCaretPosition()).getLocation()); + + SwingUtilities.convertPointToScreen(p, c); + + performGoToAction(overridingMethods, p); + } + }, true); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + + static void performGoToAction(List declarations, Point position) { + String caption = NbBundle.getMessage(GoToImplementation.class, "LBL_ImplementorsOverriders"); + + PopupUtil.showPopup(new IsOverriddenPopup(caption, declarations), caption, position.x, position.y, true, 0); + } + + private static Set SUPPORTED_ELEMENTS = EnumSet.of(ElementKind.METHOD, ElementKind.ANNOTATION_TYPE, ElementKind.CLASS, ElementKind.ENUM, ElementKind.INTERFACE); + + private static Element resolveElement(CompilationInfo info, int caret) { + TreePath tp = info.getTreeUtilities().pathFor(caret); + + TokenSequence ts = info.getTokenHierarchy().tokenSequence(JavaTokenId.language()); + + ts.move(caret); + + boolean isIdentifier = ts.moveNext() && ts.token().id() == JavaTokenId.IDENTIFIER; + + if (!isIdentifier) { + ts.move(caret); + isIdentifier = ts.movePrevious() && ts.token().id() == JavaTokenId.IDENTIFIER; + } + + if (isIdentifier) { + Element elementUnderCaret = info.getTrees().getElement(tp); + + if (elementUnderCaret != null && SUPPORTED_ELEMENTS.contains(elementUnderCaret.getKind())) { + return (Element) elementUnderCaret; + } + } + + Element el = resolveHeader(tp, info, caret); + + if (el == null) { + tp = info.getTreeUtilities().pathFor(caret + 1); + el = resolveHeader(tp, info, caret); + } + + return el; + } + + private static Element resolveHeader(TreePath tp, CompilationInfo info, int caret) { + while (tp.getLeaf().getKind() != Kind.METHOD && tp.getLeaf().getKind() != Kind.CLASS && tp.getLeaf().getKind() != Kind.COMPILATION_UNIT) { + tp = tp.getParentPath(); + } + + if (tp.getLeaf().getKind() == Kind.COMPILATION_UNIT) { + return null; + } + + long bodyStart; + + if (tp.getLeaf().getKind() == Kind.METHOD) { + MethodTree mt = (MethodTree) tp.getLeaf(); + SourcePositions sp = info.getTrees().getSourcePositions(); + BlockTree body = mt.getBody(); + bodyStart = body != null ? sp.getStartPosition(info.getCompilationUnit(), body) : Integer.MAX_VALUE; + } else { + assert tp.getLeaf().getKind() == Kind.CLASS; + Document doc = info.getSnapshot().getSource().getDocument(false); + + if (doc == null) { + return null; + } + + bodyStart = Utilities.findBodyStart(tp.getLeaf(), info.getCompilationUnit(), info.getTrees().getSourcePositions(), doc); + } + + if (caret >= bodyStart) { + return null; + } + + Element el = info.getTrees().getElement(tp); + + if (el == null || !SUPPORTED_ELEMENTS.contains(el.getKind())) { + return null; + } + + return (Element) el; + } + +} diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/GoToSuperTypeAction.java b/java.editor/src/org/netbeans/modules/java/editor/overridden/GoToSuperTypeAction.java --- a/java.editor/src/org/netbeans/modules/java/editor/overridden/GoToSuperTypeAction.java +++ b/java.editor/src/org/netbeans/modules/java/editor/overridden/GoToSuperTypeAction.java @@ -117,7 +117,7 @@ ExecutableElement ee = (ExecutableElement) resolved; - type[0] = IsOverriddenAnnotationHandler.detectOverrides(parameter, (TypeElement) ee.getEnclosingElement(), ee, result); + type[0] = ComputeOverriding.detectOverrides(parameter, (TypeElement) ee.getEnclosingElement(), ee, result); } }, true); diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationAction.java b/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationAction.java --- a/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationAction.java +++ b/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationAction.java @@ -24,7 +24,7 @@ * Contributor(s): * * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2009 Sun * Microsystems, Inc. All Rights Reserved. * * If you wish your version of this file to be governed by only the CDDL @@ -41,13 +41,28 @@ package org.netbeans.modules.java.editor.overridden; import java.awt.Point; +import java.awt.Toolkit; import java.awt.event.ActionEvent; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.MissingResourceException; +import java.util.Set; import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.Action; +import javax.swing.SwingUtilities; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; +import org.netbeans.api.java.source.ui.ElementOpen; import org.netbeans.editor.AnnotationDesc; import org.netbeans.editor.Annotations; import org.netbeans.editor.BaseDocument; @@ -57,7 +72,6 @@ import org.openide.ErrorManager; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; /** @@ -65,7 +79,7 @@ * @author Jan Lahoda */ public final class IsOverriddenAnnotationAction extends AbstractAction { - + public IsOverriddenAnnotationAction() { putValue(NAME, NbBundle.getMessage(IsOverriddenAnnotationAction.class, "CTL_IsOverriddenAnnotationAction")); //NOI18N @@ -119,7 +133,7 @@ AnnotationsHolder ah = AnnotationsHolder.get(file); if (ah == null) { - IsOverriddenAnnotationHandler.LOG.log(Level.INFO, "component=" + component + " does not have attached a IsOverriddenAnnotationHandler"); //NOI18N + Logger.getLogger(IsOverriddenAnnotationAction.class.getName()).log(Level.INFO, "component=" + component + " does not have attached a IsOverriddenAnnotationHandler"); //NOI18N return null; } @@ -134,13 +148,42 @@ return null; } + private List findAnnotations(JTextComponent component, int offset) { + FileObject file = getFile(component); + + if (file == null) { + if (ErrorManager.getDefault().isLoggable(ErrorManager.WARNING)) { + ErrorManager.getDefault().log(ErrorManager.WARNING, "component=" + component + " does not have a file specified in the document."); //NOI18N + } + return null; + } + + AnnotationsHolder ah = AnnotationsHolder.get(file); + + if (ah == null) { + Logger.getLogger(IsOverriddenAnnotationAction.class.getName()).log(Level.INFO, "component=" + component + " does not have attached a IsOverriddenAnnotationHandler"); //NOI18N + + return null; + } + + List annotations = new LinkedList(); + + for(IsOverriddenAnnotation a : ah.getAnnotations()) { + if (a.getPosition().getOffset() == offset) { + annotations.add(a); + } + } + + return annotations; + } + boolean invokeDefaultAction(final JTextComponent comp) { final Document doc = comp.getDocument(); if (doc instanceof BaseDocument) { final int currentPosition = comp.getCaretPosition(); final Annotations annotations = ((BaseDocument) doc).getAnnotations(); - final IsOverriddenAnnotation[] annotation = new IsOverriddenAnnotation[1]; + final Map> caption2Descriptions = new LinkedHashMap>(); final Point[] p = new Point[1]; doc.render(new Runnable() { @@ -148,26 +191,101 @@ try { int line = Utilities.getLineOffset((BaseDocument) doc, currentPosition); int startOffset = Utilities.getRowStartFromLineOffset((BaseDocument) doc, line); + p[0] = comp.modelToView(startOffset).getLocation(); AnnotationDesc desc = annotations.getActiveAnnotation(line); - p[0] = comp.modelToView(startOffset).getLocation(); - annotation[0] = findAnnotation(comp, desc, startOffset); + + if (desc == null) { + return ; + } + + Collection annots; + + if (COMBINED_TYPES.contains(desc.getAnnotationType())) { + annots = findAnnotations(comp, startOffset); + } else { + annots = Collections.singletonList(findAnnotation(comp, desc, startOffset)); + } + + for (IsOverriddenAnnotation a : annots) { + if (a != null) { + caption2Descriptions.put(computeCaption(a.getType(), a.getShortDescription()), a.getDeclarations()); + } + } } catch (BadLocationException ex) { - Exceptions.printStackTrace(ex); + ErrorManager.getDefault().notify(ex); } } }); - if (annotation[0] == null) + if (caption2Descriptions.isEmpty()) return false; JumpList.checkAddEntry(comp, currentPosition); - - annotation[0].mouseClicked(comp, p[0]); + + mouseClicked(caption2Descriptions, comp, p[0]); return true; } return false; } - + + private static void mouseClicked(Map> caption2Descriptions, JTextComponent c, Point p) { + if (caption2Descriptions.size() == 1 && caption2Descriptions.values().iterator().next().size() == 1) { + ElementDescription desc = caption2Descriptions.values().iterator().next().get(0); + FileObject file = desc.getSourceFile(); + + if (file != null) { + ElementOpen.open(file, desc.getHandle()); + } else { + Toolkit.getDefaultToolkit().beep(); + } + + return ; + } + + Point position = new Point(p); + + SwingUtilities.convertPointToScreen(position, c); + + StringBuilder caption = new StringBuilder(); + List descriptions = new LinkedList(); + boolean first = true; + + for (Entry> e : caption2Descriptions.entrySet()) { + if (!first) { + caption.append("/"); + } + first = false; + caption.append(e.getKey()); + descriptions.addAll(e.getValue()); + } + + PopupUtil.showPopup(new IsOverriddenPopup(caption.toString(), descriptions), caption.toString(), position.x, position.y, true, 0); + } + + private static String computeCaption(AnnotationType type, String shortDescription) throws MissingResourceException, IllegalStateException { + String caption; + switch (type) { + case IMPLEMENTS: + caption = NbBundle.getMessage(IsOverriddenAnnotation.class, "CAP_Implements"); + break; + case OVERRIDES: + caption = NbBundle.getMessage(IsOverriddenAnnotation.class, "CAP_Overrides"); + break; + case HAS_IMPLEMENTATION: + case IS_OVERRIDDEN: + caption = shortDescription; + break; + default: + throw new IllegalStateException("Currently not implemented: " + type); //NOI18N + } + return caption; + } + + private static final Set COMBINED_TYPES = new HashSet(Arrays.asList( + "org-netbeans-modules-editor-java-implements-has-implementations-combined", + "org-netbeans-modules-editor-java-implements-is-overridden-combined", + "org-netbeans-modules-editor-java-override-is-overridden-combined" + )); } diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationHandlerFactory.java b/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationHandlerFactory.java deleted file mode 100644 --- a/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationHandlerFactory.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2009 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 Microsystems, Inc. Portions Copyright 1997-2007 Sun - * Microsystems, 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.java.editor.overridden; - -import org.netbeans.api.java.source.CancellableTask; -import org.netbeans.api.java.source.CompilationInfo; -import org.netbeans.api.java.source.JavaSource.Phase; -import org.netbeans.api.java.source.JavaSource.Priority; -import org.netbeans.api.java.source.support.EditorAwareJavaSourceTaskFactory; -import org.openide.filesystems.FileObject; - -/** - * - * @author Jan Lahoda - */ -@org.openide.util.lookup.ServiceProvider(service=org.netbeans.api.java.source.JavaSourceTaskFactory.class) -public class IsOverriddenAnnotationHandlerFactory extends EditorAwareJavaSourceTaskFactory { - - /** - * Creates a new instance of IsOverriddenAnnotationHandlerFactory - */ - public IsOverriddenAnnotationHandlerFactory() { - super(Phase.RESOLVED, Priority.MIN); - } - - public CancellableTask createTask(FileObject file) { - return new IsOverriddenAnnotationHandler(file); - } - -} diff --git a/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenVisitor.java b/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenVisitor.java --- a/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenVisitor.java +++ b/java.editor/src/org/netbeans/modules/java/editor/overridden/IsOverriddenVisitor.java @@ -47,16 +47,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; -import javax.swing.text.Document; import org.netbeans.api.java.source.CompilationInfo; import org.netbeans.api.java.source.ElementHandle; import org.netbeans.api.java.source.support.CancellableTreePathScanner; -import org.netbeans.api.java.source.ClassIndex; /** * @@ -65,7 +64,6 @@ class IsOverriddenVisitor extends CancellableTreePathScanner { private CompilationInfo info; - private Document doc; Map, List>> type2Declaration; Map, MethodTree> declaration2Tree; @@ -73,8 +71,8 @@ private Map> type2Handle; - IsOverriddenVisitor(Document doc, CompilationInfo info) { - this.doc = doc; + IsOverriddenVisitor(CompilationInfo info, AtomicBoolean cancel) { + super(cancel); this.info = info; type2Declaration = new HashMap, List>>(); diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/Bundle.properties b/java.editor/src/org/netbeans/modules/java/editor/resources/Bundle.properties --- a/java.editor/src/org/netbeans/modules/java/editor/resources/Bundle.properties +++ b/java.editor/src/org/netbeans/modules/java/editor/resources/Bundle.properties @@ -23,7 +23,7 @@ # Contributor(s): # # The Original Software is NetBeans. The Initial Developer of the Original -# Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun +# Software is Sun Microsystems, Inc. Portions Copyright 1997-2009 Sun # Microsystems, Inc. All Rights Reserved. # # If you wish your version of this file to be governed by only the CDDL diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/DefaultKeyBindings.xml b/java.editor/src/org/netbeans/modules/java/editor/resources/DefaultKeyBindings.xml --- a/java.editor/src/org/netbeans/modules/java/editor/resources/DefaultKeyBindings.xml +++ b/java.editor/src/org/netbeans/modules/java/editor/resources/DefaultKeyBindings.xml @@ -51,6 +51,7 @@ + diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/has-implementations-annotation.xml b/java.editor/src/org/netbeans/modules/java/editor/resources/has-implementations-annotation.xml --- a/java.editor/src/org/netbeans/modules/java/editor/resources/has-implementations-annotation.xml +++ b/java.editor/src/org/netbeans/modules/java/editor/resources/has-implementations-annotation.xml @@ -44,7 +44,7 @@ "-//NetBeans//DTD annotation type 1.0//EN" "http://www.netbeans.org/dtds/annotation-type-1_0.dtd"> diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/implements-annotation.xml b/java.editor/src/org/netbeans/modules/java/editor/resources/implements-annotation.xml --- a/java.editor/src/org/netbeans/modules/java/editor/resources/implements-annotation.xml +++ b/java.editor/src/org/netbeans/modules/java/editor/resources/implements-annotation.xml @@ -44,8 +44,8 @@ "-//NetBeans//DTD annotation type 1.1//EN" "http://www.netbeans.org/dtds/annotation-type-1_1.dtd"> diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/implements-has-implementations-combined.xml b/java.editor/src/org/netbeans/modules/java/editor/resources/implements-has-implementations-combined.xml new file mode 100644 --- /dev/null +++ b/java.editor/src/org/netbeans/modules/java/editor/resources/implements-has-implementations-combined.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/implements-is-overridden-combined.xml b/java.editor/src/org/netbeans/modules/java/editor/resources/implements-is-overridden-combined.xml new file mode 100644 --- /dev/null +++ b/java.editor/src/org/netbeans/modules/java/editor/resources/implements-is-overridden-combined.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/is-overridden-annotation.xml b/java.editor/src/org/netbeans/modules/java/editor/resources/is-overridden-annotation.xml --- a/java.editor/src/org/netbeans/modules/java/editor/resources/is-overridden-annotation.xml +++ b/java.editor/src/org/netbeans/modules/java/editor/resources/is-overridden-annotation.xml @@ -45,7 +45,7 @@ "http://www.netbeans.org/dtds/annotation-type-1_1.dtd"> diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/is-overridden-badge.png b/java.editor/src/org/netbeans/modules/java/editor/resources/is-overridden-badge.png new file mode 100644 index 0000000000000000000000000000000000000000..72adb7d54ddfd0be5ebe80b8acdd69b35014c944 GIT binary patch literal 210 zc%17D@N?(olHy`uVBq!ia0vp^>_9BQ!3HF6HKu+5QjEnx?oJHr&dIz4a@dl*-CY>| zgW!U_%O?XxI14-?iy0WWg+Z8+Vb&Z8kRc_WzOL*ySw%(EdFIWtssIW}mbgZgIOpf) zrskC}I2WZRmZYXAlxLP?D7bt2281{Ai31hsdb&7j+ghW9_Nvb!FF3V_^84%(m6G;+PmvBZH@_pUXO@geCxbOgcCK diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/layer.xml b/java.editor/src/org/netbeans/modules/java/editor/resources/layer.xml --- a/java.editor/src/org/netbeans/modules/java/editor/resources/layer.xml +++ b/java.editor/src/org/netbeans/modules/java/editor/resources/layer.xml @@ -118,6 +118,7 @@ + @@ -300,6 +301,9 @@ + + + @@ -431,6 +435,9 @@ + + + diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/override-is-overridden-combined.png b/java.editor/src/org/netbeans/modules/java/editor/resources/override-is-overridden-combined.png new file mode 100644 index 0000000000000000000000000000000000000000..339a3d24214ced8433fba3a897e74bfcdeb386bd GIT binary patch literal 547 zc$@(v0^I$HP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXM! z2owMV4M8*j00E;(L_t(I%cYaSOB+!T#($Ho(Ga5*Z7mf=5f-uysF&a&9$ErI=&9&Y zEd8enT0!vU)vE^&QZKgDs6irBv@P1wU>nS4&AR(~c`KW30jaVBnEx=uo`@=AUfN~OWfCS(I4|LuI8h{cJrN?d>L0qp9 zFMTaKA5L~CpC6%=&-kE8c09+k;R)$5;)uvCtDs5HAhmb8TYZ1Lh56W`r#D5-t#SOl z#Kn(uUQW(4HI_91Uo$WOpAbcF_wx=TPeySXbyn8i;`ts{+9KJPV7t7@)L1qF)O5YC zHTV^X=) diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/override-is-overridden-combined.xml b/java.editor/src/org/netbeans/modules/java/editor/resources/override-is-overridden-combined.xml new file mode 100644 --- /dev/null +++ b/java.editor/src/org/netbeans/modules/java/editor/resources/override-is-overridden-combined.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/overrides-annotation.xml b/java.editor/src/org/netbeans/modules/java/editor/resources/overrides-annotation.xml --- a/java.editor/src/org/netbeans/modules/java/editor/resources/overrides-annotation.xml +++ b/java.editor/src/org/netbeans/modules/java/editor/resources/overrides-annotation.xml @@ -44,7 +44,7 @@ "-//NetBeans//DTD annotation type 1.0//EN" "http://www.netbeans.org/dtds/annotation-type-1_0.dtd"> diff --git a/java.editor/src/org/netbeans/modules/java/editor/resources/overrides-badge.png b/java.editor/src/org/netbeans/modules/java/editor/resources/overrides-badge.png new file mode 100644 index 0000000000000000000000000000000000000000..a966cafb5a01d4a94d0fa46e4c2c7468e35aabf1 GIT binary patch literal 240 zc%17D@N?(olHy`uVBq!ia0vp^>_E)P!3HG%MVKuHQjEnx?oJHr&dIz4a@dl*-CY>| zgW!U_%O``>7I;J!Gca%qgD@k*tT_@OLrOe-UD^ zR&9_oS;+d@LE()~=R+SWhC>^t2G}LN>(); + ComputeOverriders.dependenciesOverride.put(r1, Collections.emptyList()); + ComputeOverriders.dependenciesOverride.put(r2, Collections.singletonList(r1)); + ComputeOverriders.dependenciesOverride.put(r3, Collections.singletonList(r1)); + ComputeOverriders.dependenciesOverride.put(r4, Arrays.asList(r1, r2)); + + Map, List> output = new ComputeOverriders(new AtomicBoolean()).process(info, null, null, false); + Map> outputStrings = new LinkedHashMap>(); + + for (Entry, List> e : output.entrySet()) { + List descs = new LinkedList(); + + for (ElementDescription ed : e.getValue()) { + descs.add(ed.getHandle().toString()); + } + + outputStrings.put(e.getKey().toString(), descs); + } + + Map> golden = new LinkedHashMap>(); + + golden.put("ElementHandle[kind=CLASS; sigs=test.Test ]", Arrays.asList( + "ElementHandle[kind=CLASS; sigs=dep.Dep ]", + "ElementHandle[kind=CLASS; sigs=dep3.Dep3 ]", + "ElementHandle[kind=CLASS; sigs=dep4.Dep4 ]" + )); + golden.put("ElementHandle[kind=METHOD; sigs=test.Test test ()V ]", Arrays.asList( + "ElementHandle[kind=METHOD; sigs=dep.Dep test ()V ]", + "ElementHandle[kind=METHOD; sigs=dep3.Dep3 test ()V ]", + "ElementHandle[kind=METHOD; sigs=dep4.Dep4 test ()V ]" + )); + + assertEquals(golden, outputStrings); + } + + + private Map root2ClassPath = new HashMap(); + private Map root2BuildRoot = new HashMap(); + private Map buildRoot2Source = new HashMap(); + + private FileObject sourceDirectories; + private FileObject buildDirectories; + + private void prepareSourceRoot(String name, String... dependsOn) throws Exception { + FileObject src = FileUtil.createFolder(sourceDirectories, name); + FileObject build = FileUtil.createFolder(buildDirectories, name); + + List dependencies = new LinkedList(); + + for(String dep : dependsOn) { + FileObject depFO = buildDirectories.getFileObject(dep); + + assertNotNull(depFO); + dependencies.add(depFO); + } + + root2ClassPath.put(src, ClassPathSupport.createClassPath(dependencies.toArray(new FileObject[0]))); + root2BuildRoot.put(src, build); + buildRoot2Source.put(build, src); + } + + private void prepareSource(String sourceRoot, String fileName, String code) throws Exception { + FileObject root = sourceDirectories.getFileObject(sourceRoot); + + assertNotNull(root); + + FileObject source = FileUtil.createData(root, fileName); + + TestUtilities.copyStringToFile(source, code); + + SourceUtilsTestUtil.compileRecursively(root); + } + + public void setUp() throws Exception { + SourceUtilsTestUtil.setLookup(new Object[0], ComputeOverridersTest.class.getClassLoader()); + Main.initializeURLFactory(); + + clearWorkDir(); + File wd = getWorkDir(); + assert wd.isDirectory() && wd.list().length == 0; + FileObject dir = FileUtil.toFileObject(wd); + + assertNotNull(dir); + + sourceDirectories = FileUtil.createFolder(dir, "src"); + buildDirectories = FileUtil.createFolder(dir, "build"); + + FileObject cache = FileUtil.createFolder(dir, "cache"); + + List lookupContent = new LinkedList(); + + lookupContent.add(new TestProxyClassPathProvider()); + lookupContent.add(new TestSourceForBinaryQuery()); + lookupContent.add(new TestSourceLevelQueryImplementation()); + lookupContent.add(JavaDataLoader.findObject(JavaDataLoader.class, true)); + + SourceUtilsTestUtil.setLookup(lookupContent.toArray(), SourceUtilsTestUtil.class.getClassLoader()); + + IndexUtil.setCacheFolder(FileUtil.toFile(cache)); + + RepositoryUpdater.getDefault().start(true); + } + + private FileObject findRoot(FileObject file) { + FileObject root = null; + for (Entry e : root2ClassPath.entrySet()) { + if (e.getKey() == file || FileUtil.isParentOf(e.getKey(), file)) { + root = e.getKey(); + break; + } + } + return root; + } + + private class TestProxyClassPathProvider implements ClassPathProvider { + + public ClassPath findClassPath(FileObject file, String type) { + try { + FileObject root = findRoot(file); + + if (root == null) { + return null; + } + + if (ClassPath.BOOT == type) { + return ClassPathSupport.createClassPath(SourceUtilsTestUtil.getBootClassPath().toArray(new URL[0])); + } + + if (ClassPath.SOURCE == type) { + return ClassPathSupport.createClassPath(new FileObject[] { + root + }); + } + + if (ClassPath.COMPILE == type) { + return root2ClassPath.get(root); + } + + if (ClassPath.EXECUTE == type) { + return ClassPathSupport.createProxyClassPath( + root2ClassPath.get(root), + ClassPathSupport.createClassPath(new FileObject[] { + root2BuildRoot.get(root) + }) + ); + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + } + + private class TestSourceForBinaryQuery implements SourceForBinaryQueryImplementation { + + public SourceForBinaryQuery.Result findSourceRoots(URL binaryRoot) { + if (SourceUtilsTestUtil.getBootClassPath().contains(binaryRoot)) + return null; + + FileObject file = URLMapper.findFileObject(binaryRoot); + + if (file == null) { + return null; + } + + final FileObject source = buildRoot2Source.get(file); + + if (source == null) { + return null; + } + + return new SourceForBinaryQuery.Result() { + public FileObject[] getRoots() { + return new FileObject[] { + source, + }; + } + + public void addChangeListener(ChangeListener l) {} + public void removeChangeListener(ChangeListener l) {} + }; + } + + } + +} \ No newline at end of file diff --git a/java.editor/test/unit/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationCreatorTest.java b/java.editor/test/unit/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationCreatorTest.java --- a/java.editor/test/unit/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationCreatorTest.java +++ b/java.editor/test/unit/src/org/netbeans/modules/java/editor/overridden/IsOverriddenAnnotationCreatorTest.java @@ -187,20 +187,20 @@ StyledDocument doc = ec.openDocument(); - List annotations = new IsOverriddenAnnotationHandler(testSource).process(info, doc); - List result = new ArrayList(); - - for (IsOverriddenAnnotation annotation : annotations) { - result.add(annotation.debugDump()); - } - - Collections.sort(result); - - for (String r : result) { - ref(r); - } - - compareReferenceFiles(); +// List annotations = new ComputeOverriding(testSource).process(info, doc); +// List result = new ArrayList(); +// +// for (IsOverriddenAnnotation annotation : annotations) { +// result.add(annotation.debugDump()); +// } +// +// Collections.sort(result); +// +// for (String r : result) { +// ref(r); +// } +// +// compareReferenceFiles(); } /**Copied from org.netbeans.api.project. diff --git a/java.hints/src/org/netbeans/modules/java/hints/AddOverrideAnnotation.java b/java.hints/src/org/netbeans/modules/java/hints/AddOverrideAnnotation.java --- a/java.hints/src/org/netbeans/modules/java/hints/AddOverrideAnnotation.java +++ b/java.hints/src/org/netbeans/modules/java/hints/AddOverrideAnnotation.java @@ -71,8 +71,8 @@ import org.netbeans.api.java.source.WorkingCopy; import org.netbeans.modules.java.editor.codegen.GeneratorUtils; import org.netbeans.modules.java.editor.overridden.AnnotationType; +import org.netbeans.modules.java.editor.overridden.ComputeOverriding; import org.netbeans.modules.java.editor.overridden.ElementDescription; -import org.netbeans.modules.java.editor.overridden.IsOverriddenAnnotationHandler; import org.netbeans.modules.java.hints.spi.AbstractHint; import org.netbeans.spi.editor.hints.ChangeInfo; import org.netbeans.spi.editor.hints.ErrorDescription; @@ -109,7 +109,7 @@ ExecutableElement ee = (ExecutableElement) e; List result = new ArrayList(); - AnnotationType type = IsOverriddenAnnotationHandler.detectOverrides(compilationInfo, (TypeElement) ee.getEnclosingElement(), ee, result); + AnnotationType type = ComputeOverriding.detectOverrides(compilationInfo, (TypeElement) ee.getEnclosingElement(), ee, result); boolean hasOverriddenAnnotation = false; diff --git a/java.hints/src/org/netbeans/modules/java/hints/errors/UncaughtException.java b/java.hints/src/org/netbeans/modules/java/hints/errors/UncaughtException.java --- a/java.hints/src/org/netbeans/modules/java/hints/errors/UncaughtException.java +++ b/java.hints/src/org/netbeans/modules/java/hints/errors/UncaughtException.java @@ -83,8 +83,8 @@ import org.netbeans.api.java.source.WorkingCopy; import org.netbeans.modules.editor.java.Utilities; import org.netbeans.modules.java.editor.overridden.AnnotationType; +import org.netbeans.modules.java.editor.overridden.ComputeOverriding; import org.netbeans.modules.java.editor.overridden.ElementDescription; -import org.netbeans.modules.java.editor.overridden.IsOverriddenAnnotationHandler; import org.netbeans.modules.java.hints.spi.ErrorRule; import org.netbeans.spi.editor.hints.ChangeInfo; import org.netbeans.spi.editor.hints.Fix; @@ -222,7 +222,7 @@ if (!org.netbeans.modules.java.hints.errors.Utilities.isMethodHeaderInsideGuardedBlock(info, (MethodTree) pathRec.getLeaf())) { List eds = new LinkedList(); TypeElement enclosingType = (TypeElement) method.getEnclosingElement(); - AnnotationType at = IsOverriddenAnnotationHandler.detectOverrides(info, enclosingType, method,eds); + AnnotationType at = ComputeOverriding.detectOverrides(info, enclosingType, method,eds); List declaredThrows = null; if (at != null) { diff --git a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SELibrarySourceForBinaryQuery.java b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SELibrarySourceForBinaryQuery.java --- a/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SELibrarySourceForBinaryQuery.java +++ b/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SELibrarySourceForBinaryQuery.java @@ -148,7 +148,7 @@ * @param URL url * @return true if the URL is normal */ - private static boolean isNormalizedURL (URL url) throws MalformedURLException { + static boolean isNormalizedURL (URL url) throws MalformedURLException { if ("jar".equals(url.getProtocol())) { //NOI18N String path = url.getPath(); int index = path.indexOf("!/"); //NOI18N