/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package ca.weblite.netbeans.mirah.lexer; import ca.weblite.netbeans.mirah.ClassIndex; import ca.weblite.netbeans.mirah.ClassIndex.ClassPathQuery; import ca.weblite.netbeans.mirah.ClassIndex.Future; import ca.weblite.netbeans.mirah.lexer.MirahParser.DocumentDebugger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Set; import javax.swing.text.Document; import mirah.lang.ast.ClassDefinition; import mirah.lang.ast.ClosureDefinition; import mirah.lang.ast.FieldAccess; import mirah.lang.ast.FieldDeclaration; import mirah.lang.ast.LocalAssignment; import mirah.lang.ast.LocalDeclaration; import mirah.lang.ast.MethodDefinition; import mirah.lang.ast.Node; import mirah.lang.ast.Package; import mirah.lang.ast.NodeScanner; import mirah.lang.ast.StaticMethodDefinition; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.lexer.TokenSequence; import org.netbeans.modules.editor.NbEditorUtilities; import org.openide.util.Exceptions; /** * * @author shannah */ public class SourceQuery implements List<Node>{ Document doc; DocumentDebugger dbg; List<Node> results = null; public SourceQuery(Document doc){ this.doc = doc; this.dbg = MirahParser.getDocumentDebugger(doc); } public SourceQuery(Document doc, List<Node> results){ this.doc = doc; this.dbg = MirahParser.getDocumentDebugger(doc); this.results = new ArrayList<Node>(); this.results.addAll(results); } public SourceQuery(Document doc, Node root){ this.doc = doc; this.dbg = MirahParser.getDocumentDebugger(doc); this.results = new ArrayList<Node>(); this.results.add(root); } public SourceQuery findClasses(int offset){ ClassScanner scanner = new ClassScanner(); scanner.offset = offset; for( Object node : dbg.compiler.compiler().getParsedNodes() ){ ((Node)node).accept(scanner, null); } return new SourceQuery(doc, scanner.found); } public SourceQuery findMethods(int offset){ MethodOffsetScanner scanner = new MethodOffsetScanner(); scanner.offset = offset; for( Object node : dbg.compiler.compiler().getParsedNodes() ){ ((Node)node).accept(scanner, null); } return new SourceQuery(doc, scanner.found); } public SourceQuery findMethod(int offset){ MethodDefinition cdef = null; int currRange = -1; for ( Node n : findMethods(offset)){ if ( cdef == null || n.position().endChar()- n.position().startChar() < currRange ){ cdef = (MethodDefinition)n; currRange = n.position().endChar()-n.position().startChar(); } } List<Node> found = new ArrayList<Node>(); if ( cdef != null ){ found.add(cdef); } return new SourceQuery(doc, found); } public SourceQuery findClass(int offset){ ClassDefinition cdef = null; int currRange = -1; for ( Node n : findClasses(offset)){ if ( cdef == null || n.position().endChar()- n.position().startChar() < currRange ){ cdef = (ClassDefinition)n; currRange = n.position().endChar()-n.position().startChar(); } } List<Node> found = new ArrayList<Node>(); if ( cdef != null ){ found.add(cdef); } return new SourceQuery(doc, found); } public SourceQuery findParent(Object filter){ List<Node> found = new ArrayList<Node>(); for ( Node n : this.results ){ Node cls = null; Node curr = n; while ( curr != null && (filter == null || !filter.equals(curr))){ curr = curr.parent(); } if ( curr != n && (filter == null || filter.equals(curr)) ){ found.add(curr); } } return new SourceQuery(doc, found); } public SourceQuery findParentClass(){ return findParent(new Object(){ @Override public boolean equals(Object obj) { return obj instanceof ClassDefinition; } }); } public SourceQuery findParentClosure(){ return findParent(new Object(){ @Override public boolean equals(Object obj) { return obj instanceof ClosureDefinition; } }); } public String getType(){ if ( results != null && !results.isEmpty() ){ return dbg.getType(results.get(0)).name(); } return null; } public SourceQuery findParentClosureOrClass(){ return findParent(new Object(){ @Override public boolean equals(Object obj) { return obj instanceof ClosureDefinition || obj instanceof ClassDefinition; } }); } public SourceQuery findParentPackage(){ return findParent(new Object(){ @Override public boolean equals(Object obj) { return obj instanceof mirah.lang.ast.Package; } }); } public SourceQuery findClass(final String name){ final List<Node> found = new ArrayList<Node>(); if ( results == null ){ if ( dbg == null ){ return new SourceQuery(doc, found); } for( Object node : dbg.compiler.compiler().getParsedNodes() ){ if ( node instanceof Node ){ ((Node)node).accept(new NodeScanner(){ @Override public boolean enterClassDefinition(ClassDefinition node, Object arg) { if ( name == null || name.equals(node.name().identifier())){ found.add(node); } return super.enterClassDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } }, null); } } } else { for ( Node res : results ){ res.accept(new NodeScanner(){ @Override public boolean enterClassDefinition(ClassDefinition node, Object arg) { if ( name == null || name.equals(node.name().identifier())){ found.add(node); } return super.enterClassDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } }, null); } } return new SourceQuery(doc, found); } private static class ClosureScanner extends BaseScanner { private String className; @Override public boolean enterClosureDefinition(ClosureDefinition node, Object arg) { if ( className == null ){ found.add(node); } else if ( node.superclass() != null ){ if ( "*".equals(className) ){ found.add(node); } else if ( className.equals(node.superclass().typeref().name())){ found.add(node); } } else if ( node.interfaces() != null && node.interfaces_size()>0 ){ if ( "*".equals(className) ){ found.add(node); } else { for ( int i=0; i<node.interfaces_size(); i++){ if ( className.equals(node.interfaces(i).typeref().name())){ found.add(node); break; } } } } return super.enterClosureDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } }; private static class MethodScanner extends BaseScanner { private String methodName; @Override public boolean enterMethodDefinition(MethodDefinition node, Object arg) { if ( methodName == null ){ found.add(node); } else if ( node.name() != null ){ if ( "*".equals(methodName) ){ found.add(node); } else if ( methodName.equals(node.name().identifier())){ found.add(node); } } return super.enterMethodDefinition(node, arg); } }; private static class FieldDefScanner extends BaseScanner { private String fieldName; @Override public boolean enterFieldDeclaration(FieldDeclaration node, Object arg) { if ( fieldName == null ){ found.add(node); } else if ( node.name() != null ){ if ( "*".equals(fieldName) ){ found.add(node); } else if ( fieldName.equals(node.name().identifier()) || fieldName.equals("@"+node.name().identifier())){ found.add(node); } } return super.enterFieldDeclaration(node, arg); //To change body of generated methods, choose Tools | Templates. } }; private static class LocalVarScanner extends BaseScanner { private String varName; @Override public boolean enterLocalAssignment(LocalAssignment node, Object arg) { if ( varName == null ){ found.add(node); } else if ( node.name() != null ){ if ( "*".equals(varName) ){ found.add(node); } else if ( varName.equals(node.name().identifier())){ found.add(node); } } return super.enterLocalAssignment(node, arg); //To change body of generated methods, choose Tools | Templates. } }; private static class BaseScanner extends NodeScanner { int offset = -1; ArrayList<Node> found = new ArrayList<Node>(); } private static class ClassScanner extends BaseScanner { @Override public boolean enterClassDefinition(ClassDefinition node, Object arg) { if ( node.position() != null && node.position().startChar() <= offset && node.position().endChar() > offset ){ found.add(node); } return super.enterClassDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } } private static class MethodOffsetScanner extends BaseScanner { @Override public boolean enterMethodDefinition(MethodDefinition node, Object arg) { if ( node.position() != null && node.position().startChar() <= offset && node.position().endChar() > offset ){ found.add(node); } return super.enterMethodDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public boolean enterStaticMethodDefinition(StaticMethodDefinition node, Object arg) { if ( node.position() != null && node.position().startChar() <= offset && node.position().endChar() > offset ){ found.add(node); } return super.enterStaticMethodDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } } public SourceQuery findClosures(final String className){ ClosureScanner scanner = new ClosureScanner(); scanner.className = className; return find(scanner); } public SourceQuery findMethods(final String methodName){ MethodScanner scanner = new MethodScanner(); scanner.methodName = methodName; return find(scanner); } public SourceQuery findLocalVars(final String varname){ LocalVarScanner scanner = new LocalVarScanner(); scanner.varName = varname; return find(scanner); } private SourceQuery find(BaseScanner scanner){ if ( results == null ){ if ( dbg == null ){ return new SourceQuery(doc, scanner.found); } for( Object node : dbg.compiler.compiler().getParsedNodes() ){ if ( node instanceof Node ){ ((Node)node).accept(scanner, null); } } } else { for ( Node res : results ){ res.accept(scanner, null); } } return new SourceQuery(doc, scanner.found); } public SourceQuery findFieldDefinitions(final String fieldName){ FieldDefScanner scanner = new FieldDefScanner(); scanner.fieldName = fieldName; if ( results == null ){ if ( dbg == null ){ return new SourceQuery(doc, scanner.found); } for( Object node : dbg.compiler.compiler().getParsedNodes() ){ if ( node instanceof Node ){ ((Node)node).accept(scanner, null); } } } else { for ( Node res : results ){ res.accept(scanner, null); } } return new SourceQuery(doc, scanner.found); } public String getFQN(String className, int offset){ try { DocumentQuery dq = new DocumentQuery(doc); List<String> imports = dq.getImports(); for ( String imprt : imports ){ if ( imprt.endsWith("."+className) ){ return imprt; } } SourceQuery pkg = findClass(offset).findParentPackage(); String packageName = null; if ( !pkg.isEmpty() ){ Package p = (Package)pkg.get(0); packageName = p.name().identifier(); } ClassIndex index = new ClassIndex(); ClassPath[] classpaths = ClassQuery.getClassPaths(NbEditorUtilities.getFileObject(doc)); Set<String> foundClasses = new HashSet<String>(); for ( ClassPath cp : classpaths ){ final boolean[] complete = new boolean[]{false}; Future results = new Future(){ @Override protected void resultsAdded() { complete[0] = true; } }; index.findClass(new ClassPathQuery(100, className, "", cp), results); foundClasses.addAll(results.getMatches()); } // First let's look for classes in the same package if ( packageName != null ){ for ( String cls : foundClasses ){ if ( cls.indexOf(".") != -1 ){ String pname = cls.substring(0, cls.lastIndexOf(".")); if ( pname.equals(packageName)){ return cls; } } } } // Next let's look for classes in packages that have been imported for ( String cls : foundClasses ){ if ( cls.indexOf(".") != -1 ){ String pname = cls.substring(0, cls.lastIndexOf(".")); if ( imports.contains(pname+".*")){ return cls; } } } // Next let's look for classes in subpackages of this package if ( packageName != null ){ for ( String cls : foundClasses ){ if ( cls.indexOf(".") != -1 ){ String pname = cls.substring(0, cls.lastIndexOf(".")); if ( pname.startsWith(packageName)){ return cls; } } } } // Next look for classes that are closest to the current package if ( packageName != null ){ final String fPackageName = packageName; List<String> sortedPkgs = new ArrayList<String>(); sortedPkgs.addAll(foundClasses); Collections.sort(sortedPkgs, new Comparator<String>(){ @Override public int compare(String o1, String o2) { int d1 = distance(o1, fPackageName); int d2 = distance(o2, fPackageName); if ( d1 < d2 ){ return -1; } else if ( d1 > d2 ){ return 1; } else { return 0; } } }); if ( !sortedPkgs.isEmpty()){ return sortedPkgs.get(0)+"."+className; } } if ( !foundClasses.isEmpty()){ for ( String cls : foundClasses ){ return cls; } } } catch (Exception ex) { Exceptions.printStackTrace(ex); } return null; } int distance(String package1, String package2){ Set<String> parts1 = new HashSet<String>(); Set<String> parts2 = new HashSet<String>(); parts1.addAll(Arrays.asList(package1.split("."))); parts2.addAll(Arrays.asList(package2.split("."))); int parts1Size = parts1.size(); parts1.removeAll(parts2); return Math.round((float)(parts1Size-parts1.size())/(float)parts1Size *100f); } public String getMethodId(MethodDefinition m){ StringBuilder sb = new StringBuilder(); sb.append(m.name().identifier()); for ( int i=0; i<m.arguments().required_size(); i++){ if ( m.arguments().required(i) == null){ continue; } if ( m.arguments().required(i).type() == null ){ continue; } if ( m.arguments().required(i).type().typeref() == null ){ continue; } String argType = getFQN(m.arguments().required(i).type().typeref().name(), m.position().startChar()); sb.append("_").append(argType); } return sb.toString(); } @Override public int size() { return results.size(); } @Override public boolean isEmpty() { return results.isEmpty(); } @Override public boolean contains(Object o) { return results.contains(o); } @Override public Iterator<Node> iterator() { return results.iterator(); } @Override public Object[] toArray() { return results.toArray(); } @Override public <T> T[] toArray(T[] a) { return results.toArray(a); } @Override public boolean add(Node e) { return results.add(e); } @Override public boolean remove(Object o) { return results.remove(o); } @Override public boolean containsAll(Collection<?> c) { return results.containsAll(c); } @Override public boolean addAll(Collection<? extends Node> c) { return results.addAll(c); } @Override public boolean addAll(int index, Collection<? extends Node> c) { return results.addAll(index, c); } @Override public boolean removeAll(Collection<?> c) { return results.removeAll(c); } @Override public boolean retainAll(Collection<?> c) { return results.retainAll(c); } @Override public void clear() { results.clear(); } @Override public Node get(int index) { return results.get(index); } public MethodDefinition getMethod(int index){ return (MethodDefinition)get(index); } public ClosureDefinition getClosure(int index){ return (ClosureDefinition)get(index); } @Override public Node set(int index, Node element) { return results.set(index, element); } @Override public void add(int index, Node element) { results.add(index, element); } @Override public Node remove(int index) { return results.remove(index); } @Override public int indexOf(Object o) { return results.indexOf(o); } @Override public int lastIndexOf(Object o) { return results.lastIndexOf(o); } @Override public ListIterator<Node> listIterator() { return results.listIterator(); } @Override public ListIterator<Node> listIterator(int index) { return results.listIterator(index); } @Override public List<Node> subList(int fromIndex, int toIndex) { return results.subList(fromIndex, toIndex); } }