Parse candidate files locally on demand and present exact usages - may or may not work correctly for methods whose real parameters are not indexed
authorJan Lahoda <jlahoda@netbeans.org>
Tue, 28 Jun 2011 13:42:12 +0200
changeset 61270b9ea6d69f6
parent 611 0809c602e451
child 613 42e4e6a66230
Parse candidate files locally on demand and present exact usages - may or may not work correctly for methods whose real parameters are not indexed
remoting/ide/usages/nbproject/genfiles.properties
remoting/ide/usages/nbproject/project.xml
remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/Nodes.java
remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsages.java
     1.1 --- a/remoting/ide/usages/nbproject/genfiles.properties	Tue Jun 28 13:41:05 2011 +0200
     1.2 +++ b/remoting/ide/usages/nbproject/genfiles.properties	Tue Jun 28 13:42:12 2011 +0200
     1.3 @@ -1,8 +1,8 @@
     1.4 -build.xml.data.CRC32=80c6633a
     1.5 +build.xml.data.CRC32=dc8b4dc8
     1.6  build.xml.script.CRC32=58a6b47a
     1.7  build.xml.stylesheet.CRC32=a56c6a5b@1.47
     1.8  # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
     1.9  # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
    1.10 -nbproject/build-impl.xml.data.CRC32=80c6633a
    1.11 +nbproject/build-impl.xml.data.CRC32=dc8b4dc8
    1.12  nbproject/build-impl.xml.script.CRC32=583fd407
    1.13  nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.47
     2.1 --- a/remoting/ide/usages/nbproject/project.xml	Tue Jun 28 13:41:05 2011 +0200
     2.2 +++ b/remoting/ide/usages/nbproject/project.xml	Tue Jun 28 13:42:12 2011 +0200
     2.3 @@ -76,6 +76,14 @@
     2.4                      </run-dependency>
     2.5                  </dependency>
     2.6                  <dependency>
     2.7 +                    <code-name-base>org.openide.actions</code-name-base>
     2.8 +                    <build-prerequisite/>
     2.9 +                    <compile-dependency/>
    2.10 +                    <run-dependency>
    2.11 +                        <specification-version>6.23</specification-version>
    2.12 +                    </run-dependency>
    2.13 +                </dependency>
    2.14 +                <dependency>
    2.15                      <code-name-base>org.openide.awt</code-name-base>
    2.16                      <build-prerequisite/>
    2.17                      <compile-dependency/>
    2.18 @@ -116,6 +124,14 @@
    2.19                      </run-dependency>
    2.20                  </dependency>
    2.21                  <dependency>
    2.22 +                    <code-name-base>org.openide.text</code-name-base>
    2.23 +                    <build-prerequisite/>
    2.24 +                    <compile-dependency/>
    2.25 +                    <run-dependency>
    2.26 +                        <specification-version>6.39</specification-version>
    2.27 +                    </run-dependency>
    2.28 +                </dependency>
    2.29 +                <dependency>
    2.30                      <code-name-base>org.openide.util</code-name-base>
    2.31                      <build-prerequisite/>
    2.32                      <compile-dependency/>
     3.1 --- a/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/Nodes.java	Tue Jun 28 13:41:05 2011 +0200
     3.2 +++ b/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/Nodes.java	Tue Jun 28 13:42:12 2011 +0200
     3.3 @@ -42,7 +42,13 @@
     3.4  
     3.5  package org.netbeans.modules.jackpot30.ide.usages;
     3.6  
     3.7 +import com.sun.source.tree.IdentifierTree;
     3.8 +import com.sun.source.tree.MemberSelectTree;
     3.9 +import com.sun.source.tree.Tree;
    3.10 +import com.sun.source.util.SourcePositions;
    3.11 +import com.sun.source.util.TreePathScanner;
    3.12  import java.awt.Image;
    3.13 +import java.io.IOException;
    3.14  import java.util.ArrayList;
    3.15  import java.util.Collection;
    3.16  import java.util.HashMap;
    3.17 @@ -51,23 +57,42 @@
    3.18  import java.util.Map;
    3.19  import java.util.logging.Level;
    3.20  import java.util.logging.Logger;
    3.21 +import javax.lang.model.element.Element;
    3.22 +import javax.lang.model.element.ElementKind;
    3.23 +import javax.lang.model.element.ExecutableElement;
    3.24 +import javax.lang.model.element.TypeElement;
    3.25  import javax.swing.Action;
    3.26 +import org.netbeans.api.java.source.CompilationController;
    3.27 +import org.netbeans.api.java.source.CompilationInfo;
    3.28 +import org.netbeans.api.java.source.ElementHandle;
    3.29 +import org.netbeans.api.java.source.JavaSource;
    3.30 +import org.netbeans.api.java.source.JavaSource.Phase;
    3.31 +import org.netbeans.api.java.source.Task;
    3.32 +import org.netbeans.api.java.source.UiUtils;
    3.33  import org.netbeans.api.project.FileOwnerQuery;
    3.34  import org.netbeans.api.project.Project;
    3.35  import org.netbeans.api.project.ProjectUtils;
    3.36  import org.netbeans.spi.project.ui.LogicalViewProvider;
    3.37 +import org.openide.actions.OpenAction;
    3.38 +import org.openide.cookies.EditorCookie;
    3.39 +import org.openide.cookies.LineCookie;
    3.40 +import org.openide.cookies.OpenCookie;
    3.41  import org.openide.filesystems.FileObject;
    3.42  import org.openide.filesystems.FileUtil;
    3.43  import org.openide.loaders.DataObject;
    3.44  import org.openide.loaders.DataObjectNotFoundException;
    3.45  import org.openide.nodes.AbstractNode;
    3.46 +import org.openide.nodes.ChildFactory;
    3.47  import org.openide.nodes.Children;
    3.48  import org.openide.nodes.FilterNode;
    3.49  import org.openide.nodes.Node;
    3.50  import org.openide.nodes.NodeOp;
    3.51 +import org.openide.text.Line;
    3.52  import org.openide.util.Exceptions;
    3.53  import org.openide.util.ImageUtilities;
    3.54  import org.openide.util.NbBundle;
    3.55 +import org.openide.util.lookup.AbstractLookup;
    3.56 +import org.openide.util.lookup.InstanceContent;
    3.57  
    3.58  /**
    3.59   *
    3.60 @@ -75,7 +100,7 @@
    3.61   */
    3.62  public class Nodes {
    3.63  
    3.64 -    public static Node constructSemiLogicalView(Iterable<? extends FileObject> filesWithOccurrences) {
    3.65 +    public static Node constructSemiLogicalView(Iterable<? extends FileObject> filesWithOccurrences, ElementHandle<?> eh) {
    3.66          Map<Project, Collection<FileObject>> projects = new HashMap<Project, Collection<FileObject>>();
    3.67  
    3.68          for (FileObject file : filesWithOccurrences) {
    3.69 @@ -99,13 +124,13 @@
    3.70          List<Node> nodes = new LinkedList<Node>();
    3.71  
    3.72          for (Project p : projects.keySet()) {
    3.73 -            nodes.add(constructSemiLogicalView(p, projects.get(p)));
    3.74 +            nodes.add(constructSemiLogicalView(p, projects.get(p), eh));
    3.75          }
    3.76  
    3.77          return new AbstractNode(new DirectChildren(nodes));
    3.78      }
    3.79  
    3.80 -    private static Node constructSemiLogicalView(final Project p, Iterable<? extends FileObject> files) {
    3.81 +    private static Node constructSemiLogicalView(final Project p, Iterable<? extends FileObject> files, ElementHandle<?> eh) {
    3.82          LogicalViewProvider lvp = p.getLookup().lookup(LogicalViewProvider.class);
    3.83          final Node view;
    3.84  
    3.85 @@ -151,7 +176,7 @@
    3.86              fileNodes.add(foundChild);
    3.87          }
    3.88  
    3.89 -        return new Wrapper(view, fileNodes);
    3.90 +        return new Wrapper(view, fileNodes, eh);
    3.91      }
    3.92  
    3.93      private static Node locateChild(Node parent, LogicalViewProvider lvp, FileObject file) {
    3.94 @@ -164,8 +189,8 @@
    3.95  
    3.96      private static class Wrapper extends FilterNode {
    3.97  
    3.98 -        public Wrapper(Node orig, Collection<? extends Node> fileNodes) {
    3.99 -            super(orig, new WrapperChildren(orig, fileNodes));
   3.100 +        public Wrapper(Node orig, Collection<? extends Node> fileNodes, ElementHandle<?> eh) {
   3.101 +            super(orig, new WrapperChildren(orig, fileNodes, eh));
   3.102          }
   3.103  
   3.104          @Override
   3.105 @@ -193,10 +218,12 @@
   3.106  
   3.107          private final Node orig;
   3.108          private final Collection<? extends Node> fileNodes;
   3.109 +        private final ElementHandle<?> eh;
   3.110  
   3.111 -        public WrapperChildren(Node orig, Collection<? extends Node> fileNodes) {
   3.112 +        public WrapperChildren(Node orig, Collection<? extends Node> fileNodes, ElementHandle<?> eh) {
   3.113              this.orig = orig;
   3.114              this.fileNodes = fileNodes;
   3.115 +            this.eh = eh;
   3.116  
   3.117          }
   3.118  
   3.119 @@ -225,9 +252,12 @@
   3.120          @Override
   3.121          protected Node[] createNodes(Node key) {
   3.122              if (fileNodes.contains(key)) {
   3.123 -                return new Node[] {new FilterNode(key)}; //XXX
   3.124 +                FileObject file = key.getLookup().lookup(FileObject.class);
   3.125 +                Children c = file != null ? Children.create(new UsagesChildren(file, eh), true) : Children.LEAF;
   3.126 +                
   3.127 +                return new Node[] {new FilterNode(key, c)}; //XXX
   3.128              }
   3.129 -            return new Node[] {new Wrapper(key, fileNodes)};
   3.130 +            return new Node[] {new Wrapper(key, fileNodes, eh)};
   3.131          }
   3.132  
   3.133      }
   3.134 @@ -244,4 +274,162 @@
   3.135          }
   3.136      }
   3.137  
   3.138 +    private static Node noOccurrencesNode() {
   3.139 +        AbstractNode noOccurrences = new AbstractNode(Children.LEAF);
   3.140 +
   3.141 +        noOccurrences.setDisplayName("No Occurrences Found");
   3.142 +
   3.143 +        return noOccurrences;
   3.144 +    }
   3.145 +    
   3.146 +    private static final class UsagesChildren extends ChildFactory<Node> {
   3.147 +
   3.148 +        private final FileObject file;
   3.149 +        private final ElementHandle<?> eh;
   3.150 +
   3.151 +        public UsagesChildren(FileObject file, ElementHandle<?> eh) {
   3.152 +            this.file = file;
   3.153 +            this.eh = eh;
   3.154 +        }
   3.155 +
   3.156 +        @Override
   3.157 +        protected boolean createKeys(final List<Node> toPopulate) {
   3.158 +            try {
   3.159 +                JavaSource.forFileObject(file).runUserActionTask(new Task<CompilationController>() {
   3.160 +                    @Override public void run(final CompilationController parameter) throws Exception {
   3.161 +                        parameter.toPhase(Phase.RESOLVED);
   3.162 +
   3.163 +                        final Element toFind = eh.resolve(parameter);
   3.164 +
   3.165 +                        if (toFind == null) {
   3.166 +                            return;
   3.167 +                        }
   3.168 +
   3.169 +                        new TreePathScanner<Void, Void>() {
   3.170 +                            @Override public Void visitIdentifier(IdentifierTree node, Void p) {
   3.171 +                                handleNode();
   3.172 +                                return super.visitIdentifier(node, p);
   3.173 +                            }
   3.174 +                            @Override public Void visitMemberSelect(MemberSelectTree node, Void p) {
   3.175 +                                handleNode();
   3.176 +                                return super.visitMemberSelect(node, p);
   3.177 +                            }
   3.178 +                            private void handleNode() {
   3.179 +                                Element el = parameter.getTrees().getElement(getCurrentPath());
   3.180 +
   3.181 +                                if (Nodes.equals(parameter, toFind, el)) {
   3.182 +                                    toPopulate.add(new OccurrenceNode(parameter, getCurrentPath().getLeaf()));
   3.183 +                                }
   3.184 +                            }
   3.185 +                        }.scan(parameter.getCompilationUnit(), null);
   3.186 +                    }
   3.187 +                }, true);
   3.188 +            } catch (IOException ex) {
   3.189 +                Exceptions.printStackTrace(ex);
   3.190 +            }
   3.191 +
   3.192 +            if (toPopulate.isEmpty()) toPopulate.add(noOccurrencesNode());
   3.193 +
   3.194 +            return true;
   3.195 +        }
   3.196 +
   3.197 +        @Override
   3.198 +        protected Node createNodeForKey(Node key) {
   3.199 +            return key;
   3.200 +        }
   3.201 +
   3.202 +    }
   3.203 +
   3.204 +    private static boolean equals(CompilationInfo info, Element toFind, Element what) {
   3.205 +        if (toFind == what) return true;
   3.206 +        if (what == null) return false;
   3.207 +        if (toFind.getKind() != what.getKind()) return false;
   3.208 +        if (toFind.getKind() != ElementKind.METHOD) return false;
   3.209 +
   3.210 +        return info.getElements().overrides((ExecutableElement) what, (ExecutableElement) toFind, (TypeElement) what.getEnclosingElement());
   3.211 +    }
   3.212 +
   3.213 +    private static final class OccurrenceNode extends AbstractNode {
   3.214 +        private final FileObject file;
   3.215 +        private final int pos;
   3.216 +        private final String htmlDisplayName;
   3.217 +
   3.218 +        public OccurrenceNode(CompilationInfo info, Tree occurrence) {
   3.219 +            this(info, occurrence, new InstanceContent());
   3.220 +        }
   3.221 +
   3.222 +        private OccurrenceNode(CompilationInfo info, Tree occurrence, InstanceContent content) {
   3.223 +            super(Children.LEAF, new AbstractLookup(content));
   3.224 +
   3.225 +            int[] span;
   3.226 +
   3.227 +            switch (occurrence.getKind()) {
   3.228 +                case MEMBER_SELECT: span = info.getTreeUtilities().findNameSpan((MemberSelectTree) occurrence); break;
   3.229 +                default:
   3.230 +                    SourcePositions sp = info.getTrees().getSourcePositions();
   3.231 +
   3.232 +                    span = new int[] {(int) sp.getStartPosition(info.getCompilationUnit(), occurrence),
   3.233 +                                      (int) sp.getEndPosition(info.getCompilationUnit(), occurrence)};
   3.234 +                    break;
   3.235 +            }
   3.236 +
   3.237 +            long startLine = info.getCompilationUnit().getLineMap().getLineNumber(span[0]);
   3.238 +            long startLineStart = info.getCompilationUnit().getLineMap().getStartPosition(startLine);
   3.239 +
   3.240 +            String dn;
   3.241 +
   3.242 +            try {
   3.243 +                DataObject od = DataObject.find(info.getFileObject());
   3.244 +                LineCookie lc = od.getLookup().lookup(LineCookie.class);
   3.245 +                Line l = lc.getLineSet().getCurrent((int) startLine - 1);
   3.246 +                od.getLookup().lookup(EditorCookie.class).openDocument();
   3.247 +                String line = l.getText();
   3.248 +                int endOnLine = (int) Math.min(line.length(), span[1] - startLineStart);
   3.249 +
   3.250 +                dn = translate(line.substring(0, (int) (span[0] - startLineStart))) + "<b>" + translate(line.substring((int) (span[0] - startLineStart), endOnLine)) + "</b>" + translate(line.substring(endOnLine));
   3.251 +            } catch (IOException ex) {
   3.252 +                Exceptions.printStackTrace(ex);
   3.253 +                dn = "Occurrence";
   3.254 +            }
   3.255 +
   3.256 +            this.htmlDisplayName = dn;
   3.257 +            this.file = info.getFileObject();
   3.258 +            this.pos = span[0];
   3.259 +            
   3.260 +            content.add(new OpenCookie() {
   3.261 +                @Override public void open() {
   3.262 +                    UiUtils.open(file, pos);
   3.263 +                }
   3.264 +            });
   3.265 +        }
   3.266 +
   3.267 +        @Override
   3.268 +        public String getHtmlDisplayName() {
   3.269 +            return htmlDisplayName;
   3.270 +        }
   3.271 +
   3.272 +        @Override
   3.273 +        public Action[] getActions(boolean context) {
   3.274 +            return new Action[] {
   3.275 +                OpenAction.get(OpenAction.class)
   3.276 +            };
   3.277 +        }
   3.278 +
   3.279 +        @Override
   3.280 +        public Action getPreferredAction() {
   3.281 +            return OpenAction.get(OpenAction.class);
   3.282 +        }
   3.283 +
   3.284 +    }
   3.285 +
   3.286 +    private static String[] c = new String[] {"&", "<", ">", "\n", "\""}; // NOI18N
   3.287 +    private static String[] tags = new String[] {"&amp;", "&lt;", "&gt;", "<br>", "&quot;"}; // NOI18N
   3.288 +
   3.289 +    private static String translate(String input) {
   3.290 +        for (int cntr = 0; cntr < c.length; cntr++) {
   3.291 +            input = input.replaceAll(c[cntr], tags[cntr]);
   3.292 +        }
   3.293 +
   3.294 +        return input;
   3.295 +    }
   3.296  }
     4.1 --- a/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsages.java	Tue Jun 28 13:41:05 2011 +0200
     4.2 +++ b/remoting/ide/usages/src/org/netbeans/modules/jackpot30/ide/usages/RemoteUsages.java	Tue Jun 28 13:42:12 2011 +0200
     4.3 @@ -88,6 +88,7 @@
     4.4          final int pos = comp.getCaretPosition();
     4.5  
     4.6          try {
     4.7 +            final ElementHandle<?>[] handle = new ElementHandle<?>[1];
     4.8              final String[] serialized = new String[1];
     4.9              
    4.10              JavaSource.forFileObject(file).runUserActionTask(new Task<CompilationController>() {
    4.11 @@ -98,7 +99,7 @@
    4.12                      Element el = parameter.getTrees().getElement(tp);
    4.13  
    4.14                      if (el != null && Common.SUPPORTED_KINDS.contains(el.getKind())) {
    4.15 -                        serialized[0] = serialize(ElementHandle.create(el));
    4.16 +                        serialized[0] = serialize(handle[0] = ElementHandle.create(el));
    4.17                      }
    4.18                  }
    4.19              }, true);
    4.20 @@ -117,7 +118,7 @@
    4.21                  }
    4.22              }
    4.23  
    4.24 -            RemoteUsagesWindowTopComponent.openFor(Nodes.constructSemiLogicalView(result));
    4.25 +            RemoteUsagesWindowTopComponent.openFor(Nodes.constructSemiLogicalView(result, handle[0]));
    4.26          } catch (URISyntaxException ex) {
    4.27              Exceptions.printStackTrace(ex);
    4.28          } catch (IOException ex) {