package processing.mode.experimental; import static processing.mode.experimental.ExperimentalMode.log; import static processing.mode.experimental.ExperimentalMode.logE; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.awt.ScrollPane; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Stack; import java.util.TreeMap; import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.UIManager; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.text.BadLocationException; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ArrayAccess; import org.eclipse.jdt.core.dom.ArrayType; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.Comment; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ExpressionStatement; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SimpleType; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import processing.app.Base; import processing.app.SketchCode; import processing.app.Toolkit; import processing.mode.java.preproc.PdePreprocessor; import com.google.classpath.ClassPath; import com.google.classpath.ClassPathFactory; import com.google.classpath.RegExpResourceFilter; public class ASTGenerator { protected ErrorCheckerService errorCheckerService; protected DebugEditor editor; public DefaultMutableTreeNode codeTree = new DefaultMutableTreeNode(); private DefaultMutableTreeNode currentParent = null; /** * AST Window */ private JFrame frame2; private JFrame frameAutoComp; /** * Swing component wrapper for AST, used for internal testing */ private JTree jtree; /** * JTree used for testing refactoring operations */ private JTree treeRename; private CompilationUnit compilationUnit; private JTable tableAuto; private JEditorPane javadocPane; private JScrollPane scrollPane; private JFrame frmRename; private JButton btnRename; private JButton btnListOccurrence; private JTextField txtRenameField; private JFrame frmOccurenceList; private JLabel lblRefactorOldName; public ASTGenerator(ErrorCheckerService ecs) { this.errorCheckerService = ecs; this.editor = ecs.getEditor(); setupGUI(); //addCompletionPopupListner(); addListeners(); } private void setupGUI(){ frame2 = new JFrame(); jtree = new JTree(); frame2.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); frame2.setBounds(new Rectangle(680, 100, 460, 620)); JScrollPane sp = new JScrollPane(); sp.setViewportView(jtree); frame2.add(sp); btnRename = new JButton("Rename"); btnListOccurrence = new JButton("Show Usage"); frmRename = new JFrame(); frmRename.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); frmRename.setBounds(new Rectangle(680, 50, 250, 130)); frmRename.setLayout(new BoxLayout(frmRename.getContentPane(), BoxLayout.Y_AXIS)); Toolkit.setIcon(frmRename); JPanel panelTop = new JPanel(), panelBottom = new JPanel(); panelTop.setLayout(new BoxLayout(panelTop, BoxLayout.Y_AXIS)); panelTop.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); panelBottom.setLayout(new BoxLayout(panelBottom, BoxLayout.X_AXIS)); panelBottom.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); panelBottom.add(Box.createHorizontalGlue()); panelBottom.add(btnListOccurrence); panelBottom.add(Box.createRigidArea(new Dimension(15, 0))); panelBottom.add(btnRename); frmRename.setTitle("Enter new name:"); txtRenameField = new JTextField(); txtRenameField.setPreferredSize(new Dimension(150, 60)); panelTop.add(txtRenameField); //renameWindow.setVisible(true); lblRefactorOldName = new JLabel(); lblRefactorOldName.setText("Old Name: "); panelTop.add(Box.createRigidArea(new Dimension(0, 10))); panelTop.add(lblRefactorOldName); frmRename.add(panelTop); frmRename.add(panelBottom); frmRename.setMinimumSize(frmRename.getSize()); frmOccurenceList = new JFrame(); frmOccurenceList.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); frmOccurenceList.setBounds(new Rectangle(1100, 50, 350, 500)); Toolkit.setIcon(frmOccurenceList); JScrollPane sp2 = new JScrollPane(); treeRename = new JTree(); sp2.setViewportView(treeRename); frmOccurenceList.add(sp2); //occurenceListFrame.setVisible(true); frameAutoComp = new JFrame(); frameAutoComp.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frameAutoComp.setBounds(new Rectangle(1280, 100, 460, 620)); Toolkit.setIcon(frameAutoComp); tableAuto = new JTable(); JScrollPane sp3 = new JScrollPane(); sp3.setViewportView(tableAuto); frameAutoComp.add(sp3); // jdocWindow = new JFrame(); // jdocWindow.setTitle("P5 InstaHelp"); // //jdocWindow.setUndecorated(true); // jdocWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); // javadocPane = new JEditorPane(); // javadocPane.setContentType("text/html"); // javadocPane.setEditable(false); // scrollPane = new JScrollPane(); // scrollPane.setViewportView(javadocPane); // jdocWindow.add(scrollPane); // jdocMap = new TreeMap(); //// loadJars(); } private DefaultMutableTreeNode buildAST(String source, CompilationUnit cu) { if (cu == null) { ASTParser parser = ASTParser.newParser(AST.JLS4); parser.setSource(source.toCharArray()); parser.setKind(ASTParser.K_COMPILATION_UNIT); Map options = JavaCore.getOptions(); JavaCore.setComplianceOptions(JavaCore.VERSION_1_6, options); options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_6); parser.setCompilerOptions(options); compilationUnit = (CompilationUnit) parser.createAST(null); } else { compilationUnit = cu; log("Other cu"); } // OutlineVisitor visitor = new OutlineVisitor(); // compilationUnit.accept(visitor); getCodeComments(); codeTree = new DefaultMutableTreeNode(new ASTNodeWrapper((ASTNode) compilationUnit .types().get(0))); visitRecur((ASTNode) compilationUnit.types().get(0), codeTree); SwingWorker worker = new SwingWorker() { @Override protected Object doInBackground() throws Exception { return null; } protected void done() { if (codeTree != null) { if (jtree.hasFocus() || frame2.hasFocus()) return; jtree.setModel(new DefaultTreeModel(codeTree)); ((DefaultTreeModel) jtree.getModel()).reload(); // if (!frame2.isVisible()) { // frame2.setVisible(true); // } // if (!frameAutoComp.isVisible()) { // // frameAutoComp.setVisible(true); // // } // if (!jdocWindow.isVisible()) { // long t = System.currentTimeMillis(); // loadJars(); // loadJavaDoc(); // log("Time taken: " // + (System.currentTimeMillis() - t)); // jdocWindow.setBounds(new Rectangle(errorCheckerService.getEditor() // .getX() + errorCheckerService.getEditor().getWidth(), // errorCheckerService.getEditor() // .getY(), 450, 600)); // jdocWindow.setVisible(true); // } jtree.validate(); } } }; worker.execute(); // logE("++>" + System.getProperty("java.class.path")); // log(System.getProperty("java.class.path")); // log("-------------------------------"); return codeTree; } private ClassPathFactory factory; /** * Used for searching for package declaration of a class */ private ClassPath classPath; private JFrame jdocWindow; /** * Loads up .jar files and classes defined in it for completion lookup */ protected void loadJars() { // SwingWorker worker = new SwingWorker() { // protected void done(){ // } // protected Object doInBackground() throws Exception { // return null; // } // }; // worker.execute(); Thread t = new Thread(new Runnable() { public void run() { try { factory = new ClassPathFactory(); StringBuffer tehPath = new StringBuffer(System .getProperty("java.class.path")); if(Base.isMacOS()){ // rt.jar equivalent on OS X is JAVA_HOME/bundle/Classes/classes.jar tehPath.append(File.pathSeparatorChar + System.getProperty("java.home") + File.separator + "bundle" + File.separator + "Classes" + File.separator + "classes.jar" + File.pathSeparatorChar); }else{ tehPath.append(File.pathSeparatorChar + System.getProperty("java.home") + File.separator + "lib" + File.separator + "rt.jar" + File.pathSeparatorChar); } if (errorCheckerService.classpathJars != null) { for (URL jarPath : errorCheckerService.classpathJars) { //log(jarPath.getPath()); tehPath.append(jarPath.getPath() + File.pathSeparatorChar); } } // String paths[] = tehPath.toString().split(File.separatorChar +""); // StringTokenizer st = new StringTokenizer(tehPath.toString(), // File.pathSeparatorChar + ""); // while (st.hasMoreElements()) { // String sstr = (String) st.nextElement(); // log(sstr); // } classPath = factory.createFromPath(tehPath.toString()); // for (String packageName : classPath.listPackages("")) { // log(packageName); // } // RegExpResourceFilter regExpResourceFilter = new RegExpResourceFilter( // ".*", // "ArrayList.class"); // String[] resources = classPath.findResources("", regExpResourceFilter); // for (String className : resources) { // log("-> " + className); // } log("Sketch classpath jars loaded."); if (Base.isMacOS()) { File f = new File(System.getProperty("java.home") + File.separator + "bundle" + File.separator + "Classes" + File.separator + "classes.jar"); log(f.getAbsolutePath() + " | classes.jar found?" + f.exists()); } else { File f = new File(System.getProperty("java.home") + File.separator + "lib" + File.separator + "rt.jar" + File.separator); log(f.getAbsolutePath() + " | rt.jar found?" + f.exists()); } } catch (Exception e) { e.printStackTrace(); } } }); t.start(); } private TreeMap jdocMap; private void loadJavaDoc() { // presently loading only p5 reference for PApplet Thread t = new Thread(new Runnable() { @Override public void run() { JavadocHelper.loadJavaDoc(jdocMap, editor.mode().getReferenceFolder()); } }); t.start(); } public DefaultMutableTreeNode buildAST(CompilationUnit cu) { return buildAST(errorCheckerService.sourceCode, cu); } public static CompletionCandidate[] checkForTypes(ASTNode node) { List vdfs = null; switch (node.getNodeType()) { case ASTNode.TYPE_DECLARATION: return new CompletionCandidate[] { new CompletionCandidate((TypeDeclaration) node) }; case ASTNode.METHOD_DECLARATION: MethodDeclaration md = (MethodDeclaration) node; log(getNodeAsString(md)); List params = (List) md .getStructuralProperty(MethodDeclaration.PARAMETERS_PROPERTY); CompletionCandidate[] cand = new CompletionCandidate[params.size() + 1]; cand[0] = new CompletionCandidate(md); for (int i = 0; i < params.size(); i++) { // cand[i + 1] = new CompletionCandidate(params.get(i).toString(), "", "", // CompletionCandidate.LOCAL_VAR); cand[i + 1] = new CompletionCandidate((SingleVariableDeclaration)params.get(i)); } return cand; case ASTNode.SINGLE_VARIABLE_DECLARATION: return new CompletionCandidate[] { new CompletionCandidate((SingleVariableDeclaration)node) }; case ASTNode.FIELD_DECLARATION: vdfs = ((FieldDeclaration) node).fragments(); break; case ASTNode.VARIABLE_DECLARATION_STATEMENT: vdfs = ((VariableDeclarationStatement) node).fragments(); break; case ASTNode.VARIABLE_DECLARATION_EXPRESSION: vdfs = ((VariableDeclarationExpression) node).fragments(); break; default: break; } if (vdfs != null) { CompletionCandidate ret[] = new CompletionCandidate[vdfs.size()]; int i = 0; for (VariableDeclarationFragment vdf : vdfs) { // ret[i++] = new CompletionCandidate(getNodeAsString2(vdf), "", "", // CompletionCandidate.LOCAL_VAR); ret[i++] = new CompletionCandidate(vdf); } return ret; } return null; } /** * Find the parent of the expression in a().b, this would give me the return * type of a(), so that we can find all children of a() begininng with b * * @param nearestNode * @param expression * @return */ public static ASTNode resolveExpression(ASTNode nearestNode, ASTNode expression, boolean noCompare) { log("Resolving " + getNodeAsString(expression) + " noComp " + noCompare); if (expression instanceof SimpleName) { return findDeclaration2(((SimpleName) expression), nearestNode); } else if (expression instanceof MethodInvocation) { log("3. Method Invo " + ((MethodInvocation) expression).getName()); return findDeclaration2(((MethodInvocation) expression).getName(), nearestNode); } else if (expression instanceof FieldAccess) { log("2. Field access " + getNodeAsString(((FieldAccess) expression).getExpression()) + "|||" + getNodeAsString(((FieldAccess) expression).getName())); if (noCompare) { /* * ASTNode ret = findDeclaration2(((FieldAccess) expression).getName(), * nearestNode); log("Found as ->"+getNodeAsString(ret)); * return ret; */ return findDeclaration2(((FieldAccess) expression).getName(), nearestNode); } else { /* * Note how for the next recursion, noCompare is reversed. Let's say * I've typed getABC().quark.nin where nin is incomplete(ninja being the * field), when execution first enters here, it calls resolveExpr again * for "getABC().quark" where we know that quark field must be complete, * so we toggle noCompare. And kaboom. */ return resolveExpression(nearestNode, ((FieldAccess) expression).getExpression(), !noCompare); } //return findDeclaration2(((FieldAccess) expression).getExpression(), nearestNode); } else if (expression instanceof QualifiedName) { log("1. Resolving " + ((QualifiedName) expression).getQualifier() + " ||| " + ((QualifiedName) expression).getName()); if (noCompare) { // no compare, as in "abc.hello." need to resolve hello here return findDeclaration2(((QualifiedName) expression).getName(), nearestNode); } else { //User typed "abc.hello.by" (bye being complete), so need to resolve "abc.hello." only return findDeclaration2(((QualifiedName) expression).getQualifier(), nearestNode); } } return null; } /** * Finds the type of the expression in foo.bar().a().b, this would give me the * type of b if it exists in return type of a(). If noCompare is true, * it'll return type of a() * @param nearestNode * @param astNode * @return */ public ClassMember resolveExpression3rdParty(ASTNode nearestNode, ASTNode astNode, boolean noCompare) { log("Resolve 3rdParty expr-- " + getNodeAsString(astNode) + " nearest node " + getNodeAsString(nearestNode)); ClassMember scopeParent = null; SimpleType stp = null; if(astNode instanceof SimpleName){ ASTNode decl = findDeclaration2(((SimpleName)astNode),nearestNode); if(decl != null){ // see if locally defined log(getNodeAsString(astNode)+" found decl -> " + getNodeAsString(decl)); return new ClassMember(extracTypeInfo(decl)); } else { // or in a predefined class? Class tehClass = findClassIfExists(((SimpleName) astNode).toString()); if (tehClass != null) { return new ClassMember(tehClass); } } astNode = astNode.getParent(); } switch (astNode.getNodeType()) { //TODO: Notice the redundancy in the 3 cases, you can simplify things even more. case ASTNode.FIELD_ACCESS: FieldAccess fa = (FieldAccess) astNode; if (fa.getExpression() == null) { // Local code or belongs to super class log("FA,Not implemented."); return null; } else { if (fa.getExpression() instanceof SimpleName) { stp = extracTypeInfo(findDeclaration2((SimpleName) fa.getExpression(), nearestNode)); if(stp == null){ /*The type wasn't found in local code, so it might be something like * log(), or maybe belonging to super class, etc. */ Class tehClass = findClassIfExists(((SimpleName)fa.getExpression()).toString()); if (tehClass != null) { // Method Expression is a simple name and wasn't located locally, but found in a class // so look for method in this class. return definedIn3rdPartyClass(new ClassMember(tehClass), fa .getName().toString()); } log("FA resolve 3rd par, Can't resolve " + fa.getExpression()); return null; } log("FA, SN Type " + getNodeAsString(stp)); scopeParent = definedIn3rdPartyClass(stp.getName().toString(), "THIS"); } else { scopeParent = resolveExpression3rdParty(nearestNode, fa.getExpression(), noCompare); } log("FA, ScopeParent " + scopeParent); return definedIn3rdPartyClass(scopeParent, fa.getName().toString()); } case ASTNode.METHOD_INVOCATION: MethodInvocation mi = (MethodInvocation) astNode; ASTNode temp = findDeclaration2(mi.getName(), nearestNode); if(temp instanceof MethodDeclaration){ // method is locally defined log(mi.getName() + " was found locally," + getNodeAsString(extracTypeInfo(temp))); return new ClassMember(extracTypeInfo(temp)); } if (mi.getExpression() == null) { //Local code or belongs to super class log("MI,Not implemented."); return null; } else { if (mi.getExpression() instanceof SimpleName) { stp = extracTypeInfo(findDeclaration2((SimpleName) mi.getExpression(), nearestNode)); if(stp == null){ /*The type wasn't found in local code, so it might be something like * System.console()., or maybe belonging to super class, etc. */ Class tehClass = findClassIfExists(((SimpleName)mi.getExpression()).toString()); if (tehClass != null) { // Method Expression is a simple name and wasn't located locally, but found in a class // so look for method in this class. return definedIn3rdPartyClass(new ClassMember(tehClass), mi .getName().toString()); } log("MI resolve 3rd par, Can't resolve " + mi.getExpression()); return null; } log("MI, SN Type " + getNodeAsString(stp)); ASTNode typeDec = findDeclaration2(stp.getName(),nearestNode); if(typeDec == null){ log(stp.getName() + " couldn't be found locally.."); Class tehClass = findClassIfExists(stp.getName().toString()); if (tehClass != null) { // Method Expression is a simple name and wasn't located locally, but found in a class // so look for method in this class. return definedIn3rdPartyClass(new ClassMember(tehClass), mi .getName().toString()); } //return new ClassMember(findClassIfExists(stp.getName().toString())); } //scopeParent = definedIn3rdPartyClass(stp.getName().toString(), "THIS"); return definedIn3rdPartyClass(new ClassMember(typeDec), mi .getName().toString()); } else { log("MI EXP.."+getNodeAsString(mi.getExpression())); // return null; scopeParent = resolveExpression3rdParty(nearestNode, mi.getExpression(), noCompare); log("MI, ScopeParent " + scopeParent); return definedIn3rdPartyClass(scopeParent, mi.getName().toString()); } } case ASTNode.QUALIFIED_NAME: QualifiedName qn = (QualifiedName) astNode; ASTNode temp2 = findDeclaration2(qn.getName(), nearestNode); if(temp2 instanceof FieldDeclaration){ // field is locally defined log(qn.getName() + " was found locally," + getNodeAsString(extracTypeInfo(temp2))); return new ClassMember(extracTypeInfo(temp2)); } if (qn.getQualifier() == null) { log("QN,Not implemented."); return null; } else { if (qn.getQualifier() instanceof SimpleName) { stp = extracTypeInfo(findDeclaration2(qn.getQualifier(), nearestNode)); if(stp == null){ /*The type wasn't found in local code, so it might be something like * log(), or maybe belonging to super class, etc. */ Class tehClass = findClassIfExists(qn.getQualifier().toString()); if (tehClass != null) { // note how similar thing is called on line 690. Check check. return definedIn3rdPartyClass(new ClassMember(tehClass), qn .getName().toString()); } log("QN resolve 3rd par, Can't resolve " + qn.getQualifier()); return null; } log("QN, SN Local Type " + getNodeAsString(stp)); //scopeParent = definedIn3rdPartyClass(stp.getName().toString(), "THIS"); ASTNode typeDec = findDeclaration2(stp.getName(),nearestNode); if(typeDec == null){ log(stp.getName() + " couldn't be found locally.."); Class tehClass = findClassIfExists(stp.getName().toString()); if (tehClass != null) { // note how similar thing is called on line 690. Check check. return definedIn3rdPartyClass(new ClassMember(tehClass), qn .getName().toString()); } log("QN resolve 3rd par, Can't resolve " + qn.getQualifier()); return null; } return definedIn3rdPartyClass(new ClassMember(typeDec), qn .getName().toString()); } else { scopeParent = resolveExpression3rdParty(nearestNode, qn.getQualifier(), noCompare); log("QN, ScopeParent " + scopeParent); return definedIn3rdPartyClass(scopeParent, qn.getName().toString()); } } case ASTNode.ARRAY_ACCESS: ArrayAccess arac = (ArrayAccess)astNode; return resolveExpression3rdParty(nearestNode, arac.getArray(), noCompare); default: log("Unaccounted type " + getNodeAsString(astNode)); break; } return null; } /** * For a().abc.a123 this would return a123 * * @param expression * @return */ public static ASTNode getChildExpression(ASTNode expression) { // ASTNode anode = null; if (expression instanceof SimpleName) { return expression; } else if (expression instanceof FieldAccess) { return ((FieldAccess) expression).getName(); } else if (expression instanceof QualifiedName) { return ((QualifiedName) expression).getName(); }else if (expression instanceof MethodInvocation) { return ((MethodInvocation) expression).getName(); }else if(expression instanceof ArrayAccess){ return ((ArrayAccess)expression).getArray(); } log(" getChildExpression returning NULL for " + getNodeAsString(expression)); return null; } public static ASTNode getParentExpression(ASTNode expression) { // ASTNode anode = null; if (expression instanceof SimpleName) { return expression; } else if (expression instanceof FieldAccess) { return ((FieldAccess) expression).getExpression(); } else if (expression instanceof QualifiedName) { return ((QualifiedName) expression).getQualifier(); } else if (expression instanceof MethodInvocation) { return ((MethodInvocation) expression).getExpression(); } else if (expression instanceof ArrayAccess) { return ((ArrayAccess) expression).getArray(); } log("getParentExpression returning NULL for " + getNodeAsString(expression)); return null; } private void trimCandidates(String newWord){ ArrayList newCandidate = new ArrayList(); newWord = newWord.toLowerCase(); for (CompletionCandidate comp : candidates) { if(comp.toString().toLowerCase().startsWith(newWord)){ log("Adding " + comp); newCandidate.add(comp); } } candidates = newCandidate; } /** * List of CompletionCandidates */ private ArrayList candidates; private String lastPredictedWord = " "; public void preparePredictions(final String word, final int line, final int lineStartNonWSOffset) { if(caretWithinLineComment()){ log("No predictions."); return; } SwingWorker worker = new SwingWorker() { @Override protected Object doInBackground() throws Exception { return null; } protected void done() { // If the parsed code contains pde enhancements, take 'em out. String word2 = ASTNodeWrapper.getJavaCode(word); //After typing 'arg.' all members of arg type are to be listed. This one is a flag for it boolean noCompare = false; if (word2.endsWith(".")) { // return all matches word2 = word2.substring(0, word2.length() - 1); noCompare = true; } if (word2.length() > 2 && !noCompare && word2.length() > lastPredictedWord.length()) { if (word2.startsWith(lastPredictedWord)) { log(word + " starts with " + lastPredictedWord); log("Don't recalc"); if (word2.contains(".")) { int x = word2.lastIndexOf('.'); trimCandidates(word2.substring(x + 1)); } else { trimCandidates(word2); } showPredictions(word); lastPredictedWord = word2; return; } } int lineNumber = line; // Adjust line number for tabbed sketches if (errorCheckerService != null) { editor = errorCheckerService.getEditor(); int codeIndex = editor.getSketch().getCodeIndex(editor .getCurrentTab()); if (codeIndex > 0) for (int i = 0; i < codeIndex; i++) { SketchCode sc = editor.getSketch().getCode(i); int len = Base.countLines(sc.getProgram()) + 1; lineNumber += len; } } // Ensure that we're not inside a comment. TODO: Binary search /*for (Comment comm : getCodeComments()) { int commLineNo = PdeToJavaLineNumber(compilationUnit .getLineNumber(comm.getStartPosition())); if(commLineNo == lineNumber){ log("Found a comment line " + comm); log("Comment LSO " + javaCodeOffsetToLineStartOffset(compilationUnit .getLineNumber(comm.getStartPosition()), comm.getStartPosition())); break; } }*/ // Now parse the expression into an ASTNode object ASTNode nearestNode = null; ASTParser parser = ASTParser.newParser(AST.JLS4); parser.setKind(ASTParser.K_EXPRESSION); parser.setSource(word2.toCharArray()); ASTNode testnode = parser.createAST(null); //logE("PREDICTION PARSER PROBLEMS: " + parser); // Find closest ASTNode of the document to this word logE("Typed: " + word2 + "|"); nearestNode = findClosestNode(lineNumber, (ASTNode) compilationUnit.types() .get(0)); if (nearestNode == null) //Make sure nearestNode is not NULL if couldn't find a closeset node nearestNode = (ASTNode) compilationUnit.types().get(0); logE(lineNumber + " Nearest ASTNode to PRED " + getNodeAsString(nearestNode)); candidates = new ArrayList(); lastPredictedWord = word2; // Determine the expression typed if (testnode instanceof SimpleName && !noCompare) { logE("One word expression " + getNodeAsString(testnode)); //==> Simple one word exprssion - so is just an identifier // Bottom up traversal of the AST to look for possible definitions at // higher levels. //nearestNode = nearestNode.getParent(); while (nearestNode != null) { // If the current class has a super class, look inside it for // definitions. if (nearestNode instanceof TypeDeclaration) { TypeDeclaration td = (TypeDeclaration) nearestNode; if (td .getStructuralProperty(TypeDeclaration.SUPERCLASS_TYPE_PROPERTY) != null) { SimpleType st = (SimpleType) td .getStructuralProperty(TypeDeclaration.SUPERCLASS_TYPE_PROPERTY); log("Superclass " + st.getName()); for (CompletionCandidate can : getMembersForType(st.getName() .toString(), word2, noCompare, false)) { candidates.add(can); } //findDeclaration(st.getName()) } } List sprops = nearestNode .structuralPropertiesForType(); for (StructuralPropertyDescriptor sprop : sprops) { ASTNode cnode = null; if (!sprop.isChildListProperty()) { if (nearestNode.getStructuralProperty(sprop) instanceof ASTNode) { cnode = (ASTNode) nearestNode.getStructuralProperty(sprop); CompletionCandidate[] types = checkForTypes(cnode); if (types != null) { for (int i = 0; i < types.length; i++) { if (types[i].getElementName().toLowerCase().startsWith(word2.toLowerCase())) candidates.add(types[i]); } } } } else { // Childlist prop List nodelist = (List) nearestNode .getStructuralProperty(sprop); for (ASTNode clnode : nodelist) { CompletionCandidate[] types = checkForTypes(clnode); if (types != null) { for (int i = 0; i < types.length; i++) { if (types[i].getElementName().toLowerCase().startsWith(word2.toLowerCase())) candidates.add(types[i]); } } } } } nearestNode = nearestNode.getParent(); } // We're seeing a simple name that's not defined locally or in // the parent class. So most probably a pre-defined type. log("Empty can. " + word2); RegExpResourceFilter regExpResourceFilter; regExpResourceFilter = new RegExpResourceFilter( Pattern.compile(".*"), Pattern .compile(word2 + "[a-zA-Z_0-9]*.class", Pattern.CASE_INSENSITIVE)); String[] resources = classPath .findResources("", regExpResourceFilter); for (String matchedClass2 : resources) { matchedClass2 = matchedClass2.replace('/', '.'); String matchedClass = matchedClass2.substring(0, matchedClass2 .length() - 6); if(ignorableImport(matchedClass2)) continue; int d = matchedClass.lastIndexOf('.'); matchedClass = matchedClass.substring(d + 1); candidates .add(new CompletionCandidate(matchedClass, matchedClass + " : " + matchedClass2.substring(0, d), matchedClass, CompletionCandidate.PREDEF_CLASS)); //log("-> " + className); } } else { // ==> Complex expression of type blah.blah2().doIt,etc // Have to resolve it by carefully traversing AST of testNode logE("Complex expression " + getNodeAsString(testnode)); log("candidates empty"); ASTNode childExpr = getChildExpression(testnode); log("Parent expression : " + getParentExpression(testnode)); log("Child expression : " + childExpr); if (childExpr instanceof ASTNode) { if (!noCompare) { log("Original testnode " + getNodeAsString(testnode)); testnode = getParentExpression(testnode); log("Corrected testnode " + getNodeAsString(testnode)); } ClassMember expr = resolveExpression3rdParty(nearestNode, testnode, noCompare); if (expr == null) { log("Expr is null"); } else { log("Expr is " + expr.toString()); candidates = getMembersForType(expr, childExpr.toString(), noCompare, false); } } else { log("ChildExpr is null"); } } showPredictions(word); } }; worker.execute(); } private void showPredictions(final String word) { if (sketchOutline != null) if (sketchOutline.isVisible()) return; Collections.sort(candidates); CompletionCandidate[][] candi = new CompletionCandidate[candidates.size()][1]; DefaultListModel defListModel = new DefaultListModel(); for (int i = 0; i < candi.length; i++) { candi[i][0] = candidates.get(i); defListModel.addElement(candidates.get(i)); } log("Total preds = " + candidates.size()); DefaultTableModel tm = new DefaultTableModel(candi, new String[] { "Suggestions" }); if (tableAuto.isVisible()) { tableAuto.setModel(tm); tableAuto.validate(); tableAuto.repaint(); } errorCheckerService.getEditor().textArea() .showSuggestion(defListModel, word); } /** * Loads classes from .jar files in sketch classpath * * @param typeName * @param child * @param noCompare * @return */ public ArrayList getMembersForType(String typeName, String child, boolean noCompare, boolean staticOnly) { ArrayList candidates = new ArrayList(); log("In GMFT(), Looking for match " + child.toString() + " in class " + typeName + " noCompare " + noCompare + " staticOnly " + staticOnly); Class probableClass = findClassIfExists(typeName); if(probableClass == null){ log("In GMFT(), class not found."); return candidates; } return getMembersForType(new ClassMember(probableClass), child, noCompare, staticOnly); } public ArrayList getMembersForType(ClassMember tehClass, String childToLookFor, boolean noCompare, boolean staticOnly) { String child = childToLookFor.toLowerCase(); ArrayList candidates = new ArrayList(); log("getMemFoType-> Looking for match " + child.toString() + " inside " + tehClass + " noCompare " + noCompare + " staticOnly " + staticOnly); if(tehClass == null){ return candidates; } // tehClass will either be a TypeDecl defined locally if(tehClass.getDeclaringNode() instanceof TypeDeclaration){ TypeDeclaration td = (TypeDeclaration) tehClass.getDeclaringNode(); for (int i = 0; i < td.getFields().length; i++) { List vdfs = td.getFields()[i] .fragments(); for (VariableDeclarationFragment vdf : vdfs) { if (noCompare) { candidates .add(new CompletionCandidate(vdf)); } else if (vdf.getName().toString().toLowerCase() .startsWith(child)) candidates .add(new CompletionCandidate(vdf)); } } for (int i = 0; i < td.getMethods().length; i++) { if (noCompare) { candidates.add(new CompletionCandidate(td.getMethods()[i])); } else if (td.getMethods()[i].getName().toString().toLowerCase() .startsWith(child)) candidates.add(new CompletionCandidate(td.getMethods()[i])); } ArrayList superClassCandidates = new ArrayList(); if(td.getSuperclassType() instanceof Type){ log(getNodeAsString(td.getSuperclassType()) + " <-Looking into superclass of " + tehClass); superClassCandidates = getMembersForType(new ClassMember(td .getSuperclassType()), childToLookFor, noCompare, staticOnly); } else { superClassCandidates = getMembersForType(new ClassMember(Object.class), childToLookFor, noCompare, staticOnly); } for (CompletionCandidate cc : superClassCandidates) { candidates.add(cc); } return candidates; } // Or tehClass will be a predefined class Class probableClass; if (tehClass.getClass_() != null) { probableClass = tehClass.getClass_(); } else { probableClass = findClassIfExists(tehClass.getTypeAsString()); if (probableClass == null) { log("Couldn't find class " + tehClass.getTypeAsString()); return candidates; } log("Loaded " + probableClass.toString()); } for (Method method : probableClass.getMethods()) { if (!Modifier.isStatic(method.getModifiers()) && staticOnly) { continue; } StringBuffer label = new StringBuffer(method.getName() + "("); for (int i = 0; i < method.getParameterTypes().length; i++) { label.append(method.getParameterTypes()[i].getSimpleName()); if (i < method.getParameterTypes().length - 1) label.append(","); } label.append(")"); if (noCompare) { candidates.add(new CompletionCandidate(method)); } else if (label.toString().toLowerCase().startsWith(child)) { candidates.add(new CompletionCandidate(method)); } } for (Field field : probableClass.getFields()) { if (!Modifier.isStatic(field.getModifiers()) && staticOnly) { continue; } if (noCompare) { candidates.add(new CompletionCandidate(field)); } else if (field.getName().toLowerCase().startsWith(child)) { candidates.add(new CompletionCandidate(field)); } } return candidates; } /** * Searches for the particular class in the default list of imports as well as * the Sketch classpath * @param className * @return */ private Class findClassIfExists(String className){ if(className == null){ return null; } Class tehClass = null; // First, see if the classname is a fully qualified name and loads straightaway tehClass = loadClass(className); if(tehClass instanceof Class){ log(tehClass.getName() + " located straightaway"); return tehClass; } log("Looking in the classloader for " + className); ArrayList imports = errorCheckerService .getProgramImports(); for (ImportStatement impS : imports) { String temp = impS.getPackageName(); if (temp.endsWith("*")) { temp = temp.substring(0, temp.length() - 1) + className; } else { int x = temp.lastIndexOf('.'); //log("fclife " + temp.substring(x + 1)); if (!temp.substring(x + 1).equals(className)) { continue; } } tehClass = loadClass(temp); if (tehClass instanceof Class) { log(tehClass.getName() + " located."); return tehClass; } log("Doesn't exist in package: " + impS.getImportName()); } PdePreprocessor p = new PdePreprocessor(null); for (String impS : p.getCoreImports()) { tehClass = loadClass(impS.substring(0,impS.length()-1) + className); if (tehClass instanceof Class) { log(tehClass.getName() + " located."); return tehClass; } log("Doesn't exist in package: " + impS); } for (String impS : p.getDefaultImports()) { if(className.equals(impS) || impS.endsWith(className)){ tehClass = loadClass(impS); if (tehClass instanceof Class) { log(tehClass.getName() + " located."); return tehClass; } log("Doesn't exist in package: " + impS); } } // And finally, the daddy String daddy = "java.lang." + className; tehClass = loadClass(daddy); if (tehClass instanceof Class) { log(tehClass.getName() + " located."); return tehClass; } log("Doesn't exist in java.lang"); return tehClass; } private Class loadClass(String className){ Class tehClass = null; if(className instanceof String){ try { tehClass = Class.forName(className, false, errorCheckerService.getSketchClassLoader()); } catch (ClassNotFoundException e) { //log("Doesn't exist in package: "); } } return tehClass; } public ClassMember definedIn3rdPartyClass(String className,String memberName){ Class probableClass = findClassIfExists(className); if (probableClass == null) { log("Couldn't load " + className); return null; } if (memberName.equals("THIS")) { return new ClassMember(probableClass); } else { return definedIn3rdPartyClass(new ClassMember(probableClass), memberName); } } public ClassMember definedIn3rdPartyClass(ClassMember tehClass,String memberName){ if(tehClass == null) return null; log("definedIn3rdPartyClass-> Looking for " + memberName + " in " + tehClass); String memberNameL = memberName.toLowerCase(); if(tehClass.getDeclaringNode() instanceof TypeDeclaration){ TypeDeclaration td = (TypeDeclaration) tehClass.getDeclaringNode(); for (int i = 0; i < td.getFields().length; i++) { List vdfs = td.getFields()[i] .fragments(); for (VariableDeclarationFragment vdf : vdfs) { if (vdf.getName().toString().toLowerCase() .startsWith(memberNameL)) return new ClassMember(vdf); } } for (int i = 0; i < td.getMethods().length; i++) { if (td.getMethods()[i].getName().toString().toLowerCase() .startsWith(memberNameL)) return new ClassMember(td.getMethods()[i]); } if(td.getSuperclassType() instanceof Type){ log(getNodeAsString(td.getSuperclassType()) + " <-Looking into superclass of " + tehClass); return definedIn3rdPartyClass(new ClassMember(td .getSuperclassType()),memberName); } else { return definedIn3rdPartyClass(new ClassMember(Object.class),memberName); } } Class probableClass = null; if(tehClass.getClass_() != null){ probableClass = tehClass.getClass_(); } else { probableClass = findClassIfExists(tehClass.getTypeAsString()); log("Loaded " + probableClass.toString()); } for (Method method : probableClass.getMethods()) { if (method.getName().equalsIgnoreCase(memberName)) { return new ClassMember(method); } } for (Field field : probableClass.getFields()) { if (field.getName().equalsIgnoreCase(memberName)) { return new ClassMember(field); } } return null; } public void updateJavaDoc(final CompletionCandidate candidate) { //TODO: Work on this later. return; /*String methodmatch = candidate.toString(); if (methodmatch.indexOf('(') != -1) { methodmatch = methodmatch.substring(0, methodmatch.indexOf('(')); } //log("jdoc match " + methodmatch); for (final String key : jdocMap.keySet()) { if (key.startsWith(methodmatch) && key.length() > 3) { log("Matched jdoc " + key); //visitRecur((ASTNode) compilationUnit.types().get(0), codeTree); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { log("Class: " + candidate.getDefiningClass()); if (candidate.getDefiningClass().equals("processing.core.PApplet")) { javadocPane.setText(jdocMap.get(key)); //jdocWindow.setVisible(true); //editor.textArea().requestFocus() } else javadocPane.setText(""); javadocPane.setCaretPosition(0); } }); break; } }*/ //jdocWindow.setVisible(false); } @SuppressWarnings("unchecked") private static ASTNode findClosestParentNode(int lineNumber, ASTNode node) { Iterator it = node .structuralPropertiesForType().iterator(); // logE("Props of " + node.getClass().getName()); while (it.hasNext()) { StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) it .next(); if (prop.isChildProperty() || prop.isSimpleProperty()) { if (node.getStructuralProperty(prop) != null) { // System.out // .println(node.getStructuralProperty(prop) + " -> " + (prop)); if (node.getStructuralProperty(prop) instanceof ASTNode) { ASTNode cnode = (ASTNode) node.getStructuralProperty(prop); // log("Looking at " + getNodeAsString(cnode)+ " for line num " + lineNumber); int cLineNum = ((CompilationUnit) cnode.getRoot()) .getLineNumber(cnode.getStartPosition() + cnode.getLength()); if (getLineNumber(cnode) <= lineNumber && lineNumber <= cLineNum) { return findClosestParentNode(lineNumber, cnode); } } } } else if (prop.isChildListProperty()) { List nodelist = (List) node .getStructuralProperty(prop); for (ASTNode cnode : nodelist) { int cLineNum = ((CompilationUnit) cnode.getRoot()) .getLineNumber(cnode.getStartPosition() + cnode.getLength()); // log("Looking at " + getNodeAsString(cnode)+ " for line num " + lineNumber); if (getLineNumber(cnode) <= lineNumber && lineNumber <= cLineNum) { return findClosestParentNode(lineNumber, cnode); } } } } return node; } @SuppressWarnings("unchecked") private static ASTNode findClosestNode(int lineNumber, ASTNode node) { ASTNode parent = findClosestParentNode(lineNumber, node); if (parent == null) return null; if (getLineNumber(parent) == lineNumber) return parent; List nodes = null; if (parent instanceof TypeDeclaration) { nodes = (List) ((TypeDeclaration) parent) .getStructuralProperty(TypeDeclaration.BODY_DECLARATIONS_PROPERTY); } else if (parent instanceof Block) { nodes = (List) ((Block) parent) .getStructuralProperty(Block.STATEMENTS_PROPERTY); } else { System.err.println("THIS CONDITION SHOULD NOT OCCUR - findClosestNode " + getNodeAsString(parent)); return null; } if (nodes.size() > 0) { ASTNode retNode = nodes.get(0); for (ASTNode cNode : nodes) { if (getLineNumber(cNode) <= lineNumber) retNode = cNode; else break; } return retNode; } return null; } public DefaultMutableTreeNode getAST() { return codeTree; } public String getLabelForASTNode(int lineNumber, String name, int offset) { return getASTNodeAt(lineNumber, name, offset, false).getLabel(); //return ""; } private String getLabelIfType(ASTNodeWrapper node, SimpleName sn){ ASTNode current = node.getNode().getParent(); String type = ""; StringBuffer fullName = new StringBuffer(); Stack parents = new Stack(); String simpleName = (sn == null) ? node.getNode().toString() : sn.toString(); switch (node.getNodeType()) { case ASTNode.TYPE_DECLARATION: case ASTNode.METHOD_DECLARATION: case ASTNode.FIELD_DECLARATION: while (current != null) { if (current instanceof TypeDeclaration) { parents.push(((TypeDeclaration) current).getName().toString()); } current = current.getParent(); } while (parents.size() > 0) { fullName.append(parents.pop() + "."); } fullName.append(simpleName); if (node.getNode() instanceof MethodDeclaration) { MethodDeclaration md = (MethodDeclaration) node.getNode(); if (!md.isConstructor()) type = md.getReturnType2().toString(); fullName.append('('); if (!md.parameters().isEmpty()) { List params = md.parameters(); for (ASTNode par : params) { if (par instanceof SingleVariableDeclaration) { SingleVariableDeclaration svd = (SingleVariableDeclaration) par; fullName.append(svd.getType() + " " + svd.getName() + ","); } } } if(fullName.charAt(fullName.length() - 1) == ',') fullName.deleteCharAt(fullName.length() - 1); fullName.append(')'); } else if(node.getNode() instanceof FieldDeclaration){ type = ((FieldDeclaration) node.getNode()).getType().toString(); } int x = fullName.indexOf("."); fullName.delete(0, x + 1); return type + " " + fullName; case ASTNode.SINGLE_VARIABLE_DECLARATION: SingleVariableDeclaration svd = (SingleVariableDeclaration)node.getNode(); return svd.getType() + " " + svd.getName(); case ASTNode.VARIABLE_DECLARATION_STATEMENT: return ((VariableDeclarationStatement) node.getNode()).getType() + " " + simpleName; case ASTNode.VARIABLE_DECLARATION_EXPRESSION: return ((VariableDeclarationExpression) node.getNode()).getType() + " " + simpleName; default: break; } return ""; } public void scrollToDeclaration(int lineNumber, String name, int offset) { getASTNodeAt(lineNumber, name, offset, true); } /** * * @param lineNumber * @param name * @param offset - line start nonwhitespace offset * @param scrollOnly * @return */ public ASTNodeWrapper getASTNodeAt(int lineNumber, String name, int offset, boolean scrollOnly) { log("----getASTNodeAt----"); if (errorCheckerService != null) { editor = errorCheckerService.getEditor(); int codeIndex = editor.getSketch().getCodeIndex(editor.getCurrentTab()); if (codeIndex > 0) { for (int i = 0; i < codeIndex; i++) { SketchCode sc = editor.getSketch().getCode(i); int len = Base.countLines(sc.getProgram()) + 1; lineNumber += len; } } } log("FLON: " + lineNumber); ASTNode lineNode = findLineOfNode(compilationUnit, lineNumber, offset, name); log("+> " + lineNode); ASTNode decl = null; String nodeLabel = null; String nameOfNode = null; // The node name which is to be scrolled to if (lineNode != null) { // Some delicate offset handling follows. ASTNodeWrapper lineNodeWrap = new ASTNodeWrapper(lineNode); int altOff = offset; int ret[][] = lineNodeWrap.getOffsetMapping(errorCheckerService); if(ret != null){ altOff = 0; int javaCodeMap[] = ret[0], pdeCodeMap[] = ret[1]; for (; altOff < javaCodeMap.length; altOff++) { if (javaCodeMap[altOff] == pdeCodeMap[offset]) { break; } } } log("FLON2: " + lineNumber + " LN spos " + lineNode.getStartPosition() + " off " + offset + " alt off" + altOff); /* * Now I need to see if multiple statements exist with this same line number * If that's the case, I need to ensure the offset is right. */ ASTNode parLineNode = lineNode.getParent(); Iterator it = parLineNode .structuralPropertiesForType().iterator(); boolean flag = true; int offAdjust = 0; while (it.hasNext() && flag) { StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) it .next(); if (prop.isChildListProperty()) { List nodelist = (List) parLineNode .getStructuralProperty(prop); for (ASTNode cnode : nodelist) { if (getLineNumber(cnode) == lineNumber) { if (cnode.getStartPosition() <= lineNode.getStartPosition() + altOff && cnode.getStartPosition() + cnode.getLength() > lineNode .getStartPosition() + altOff) { log(cnode); offAdjust = cnode.getStartPosition() - lineNode.getStartPosition(); lineNode = cnode; altOff -= offAdjust; flag = false; break; } } } } } log("FLON3 "+lineNode.getStartPosition() + " off " + offset + " alt off" + altOff); ASTNode simpName = pinpointOnLine(lineNode, altOff, lineNode.getStartPosition(), name); log("+++> " + simpName); if (simpName instanceof SimpleName) { nameOfNode = simpName.toString(); log(getNodeAsString(simpName)); decl = findDeclaration((SimpleName) simpName); if (decl != null) { logE("DECLA: " + decl.getClass().getName()); nodeLabel = getLabelIfType(new ASTNodeWrapper(decl), (SimpleName) simpName); //retLabelString = getNodeAsString(decl); } else logE("null"); log(getNodeAsString(decl)); // - findDecl3 testing ASTNode nearestNode = findClosestNode(lineNumber, (ASTNode) compilationUnit.types() .get(0)); ClassMember cmem = resolveExpression3rdParty(nearestNode, (SimpleName) simpName, false); if(cmem != null){ log("CMEM-> "+cmem); } else log("CMEM-> null"); } } if (decl != null && scrollOnly) { /* * For scrolling, we highlight just the name of the node, * i.e., a SimpleName instance. But the declared node always * points to the declared node itself, like TypeDecl, MethodDecl, etc. * This is important since it contains all the properties. */ ASTNode simpName2 = getNodeName(decl,nameOfNode); logE("FINAL String decl: " + getNodeAsString(decl)); logE("FINAL String label: " + getNodeAsString(simpName2)); errorCheckerService.highlightNode(simpName2); } return new ASTNodeWrapper(decl,nodeLabel); } /** * Given a declaration type astnode, returns the SimpleName peroperty * of that node. * @param node * @param name - The name we're looking for. * @return SimpleName */ private static ASTNode getNodeName(ASTNode node, String name){ List vdfs = null; switch (node.getNodeType()) { case ASTNode.TYPE_DECLARATION: return ((TypeDeclaration) node).getName(); case ASTNode.METHOD_DECLARATION: return ((MethodDeclaration) node).getName(); case ASTNode.SINGLE_VARIABLE_DECLARATION: return ((SingleVariableDeclaration) node).getName(); case ASTNode.FIELD_DECLARATION: vdfs = ((FieldDeclaration) node).fragments(); break; case ASTNode.VARIABLE_DECLARATION_STATEMENT: vdfs = ((VariableDeclarationStatement) node).fragments(); break; case ASTNode.VARIABLE_DECLARATION_EXPRESSION: vdfs = ((VariableDeclarationExpression) node).fragments(); break; default: break; } if (vdfs != null) { for (VariableDeclarationFragment vdf : vdfs) { if (vdf.getName().toString().equals(name)) { return vdf.getName(); } } } return null; } /** * Fetches line number of the node in its CompilationUnit. * @param node * @return */ public static int getLineNumber(ASTNode node) { return ((CompilationUnit) node.getRoot()).getLineNumber(node .getStartPosition()); } public static void main(String[] args) { //traversal2(); } public static void traversal2() { ASTParser parser = ASTParser.newParser(AST.JLS4); String source = readFile("/media/quarkninja/Work/TestStuff/low.java"); // String source = "package decl; \npublic class ABC{\n int ret(){\n}\n}"; parser.setSource(source.toCharArray()); parser.setKind(ASTParser.K_COMPILATION_UNIT); Map options = JavaCore.getOptions(); JavaCore.setComplianceOptions(JavaCore.VERSION_1_6, options); options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_6); parser.setCompilerOptions(options); CompilationUnit cu = (CompilationUnit) parser.createAST(null); log(CompilationUnit.propertyDescriptors(AST.JLS4).size()); DefaultMutableTreeNode astTree = new DefaultMutableTreeNode( "CompilationUnit"); logE("Errors: " + cu.getProblems().length); visitRecur(cu, astTree); log(astTree.getChildCount()); try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); JFrame frame2 = new JFrame(); JTree jtree = new JTree(astTree); frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame2.setBounds(new Rectangle(100, 100, 460, 620)); JScrollPane sp = new JScrollPane(); sp.setViewportView(jtree); frame2.add(sp); frame2.setVisible(true); } catch (Exception e) { e.printStackTrace(); } ASTNode found = NodeFinder.perform(cu, 468, 5); if (found != null) { log(found); } } private void addListeners(){ jtree.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { log(e); SwingWorker worker = new SwingWorker() { @Override protected Object doInBackground() throws Exception { return null; } protected void done() { if(jtree .getLastSelectedPathComponent() == null){ return; } DefaultMutableTreeNode tnode = (DefaultMutableTreeNode) jtree .getLastSelectedPathComponent(); if(tnode.getUserObject() == null){ return; } if (tnode.getUserObject() instanceof ASTNodeWrapper) { ASTNodeWrapper awrap = (ASTNodeWrapper) tnode.getUserObject(); errorCheckerService.highlightNode(awrap); } } }; worker.execute(); } }); btnRename.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(txtRenameField.getText().length() == 0) return; SwingWorker worker = new SwingWorker() { @Override protected Object doInBackground() throws Exception { return null; } protected void done() { refactorIt(); } }; worker.execute(); } }); btnListOccurrence.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { SwingWorker worker = new SwingWorker() { @Override protected Object doInBackground() throws Exception { return null; } protected void done() { handleShowUsage(); } }; worker.execute(); } }); treeRename.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { log(e); SwingWorker worker = new SwingWorker() { @Override protected Object doInBackground() throws Exception { return null; } protected void done() { if(treeRename .getLastSelectedPathComponent() == null){ return; } DefaultMutableTreeNode tnode = (DefaultMutableTreeNode) treeRename .getLastSelectedPathComponent(); if(tnode.getUserObject() == null){ return; } if (tnode.getUserObject() instanceof ASTNodeWrapper) { ASTNodeWrapper awrap = (ASTNodeWrapper) tnode.getUserObject(); errorCheckerService.highlightNode(awrap); } } }; worker.execute(); } }); } private void refactorIt(){ String newName = txtRenameField.getText().trim(); String selText = lastClickedWord == null ? editor.ta.getSelectedText() : lastClickedWord; DefaultMutableTreeNode defCU = findAllOccurrences(); if(defCU == null){ editor.statusError("Can't locate definition of " + selText); return; } if(!newName.matches("([a-zA-Z][a-zA-Z0-9_]*)|([_][a-zA-Z0-9_]+)")) { JOptionPane.showConfirmDialog(new JFrame(), newName + " isn't a valid name.", "Uh oh..", JOptionPane.PLAIN_MESSAGE); return; } //else log("New name looks K."); errorCheckerService.pauseThread(); treeRename.setModel(new DefaultTreeModel(defCU)); ((DefaultTreeModel) treeRename.getModel()).reload(); frmOccurenceList.setTitle("Usage of " + selText); frmOccurenceList.setVisible(true); int lineOffsetDisplacementConst = newName.length() - selText.length(); HashMap lineOffsetDisplacement = new HashMap(); // I need to store the pde and java offsets beforehand because once // the replace starts, all offsets returned are affected int offsetsMap[][][] = new int[defCU.getChildCount()][2][]; for (int i = 0; i < defCU.getChildCount(); i++) { ASTNodeWrapper awrap = (ASTNodeWrapper) ((DefaultMutableTreeNode) (defCU .getChildAt(i))).getUserObject(); offsetsMap[i][0] = awrap.getPDECodeOffsets(errorCheckerService); offsetsMap[i][1] = awrap.getJavaCodeOffsets(errorCheckerService); } for (int i = 0; i < defCU.getChildCount(); i++) { int pdeoffsets[] = offsetsMap[i][0]; int javaoffsets[] = offsetsMap[i][1]; // correction for pde enhancements related displacement on a line int off = 0; if (lineOffsetDisplacement.get(javaoffsets[0]) != null) { off = lineOffsetDisplacement.get(javaoffsets[0]); lineOffsetDisplacement.put(javaoffsets[0], lineOffsetDisplacementConst + off); } else { lineOffsetDisplacement.put(javaoffsets[0], lineOffsetDisplacementConst); } ErrorCheckerService.scrollToErrorLine(editor, pdeoffsets[0], pdeoffsets[1], javaoffsets[1] + off, javaoffsets[2]); //int k = JOptionPane.showConfirmDialog(new JFrame(), "Rename?","", JOptionPane.INFORMATION_MESSAGE)); editor.ta.setSelectedText(newName); } errorCheckerService.resumeThread(); errorCheckerService.runManualErrorCheck(); for (Integer lineNum : lineOffsetDisplacement.keySet()) { log(lineNum + "line, disp" + lineOffsetDisplacement.get(lineNum)); } editor.getSketch().setModified(true); errorCheckerService.runManualErrorCheck(); frmOccurenceList.setVisible(false); frmRename.setVisible(false); lastClickedWord = null; lastClickedWordNode = null; } public void handleShowUsage(){ log("Last clicked word:" + lastClickedWord); if(lastClickedWord == null && editor.ta.getSelectedText() == null){ editor.statusError("Highlight the class/function/variable name first"); return; } if(errorCheckerService.hasSyntaxErrors()){ editor.statusError("Can't perform action until syntax errors are fixed :("); return; } DefaultMutableTreeNode defCU = findAllOccurrences(); String selText = lastClickedWord == null ? editor.ta.getSelectedText() : lastClickedWord; if(defCU == null){ editor.statusError("Can't locate definition of " + selText); return; } if(defCU.getChildCount() == 0) return; treeRename.setModel(new DefaultTreeModel(defCU)); ((DefaultTreeModel) treeRename.getModel()).reload(); treeRename.setRootVisible(false); frmOccurenceList.setTitle("Usage of \"" + selText+ "\""); frmOccurenceList.setVisible(true); lastClickedWord = null; lastClickedWordNode = null; } protected String lastClickedWord = null; protected ASTNodeWrapper lastClickedWordNode = null; public String getLastClickedWord() { return lastClickedWord; } public void setLastClickedWord(int lineNumber, String lastClickedWord, int offset) { this.lastClickedWord = lastClickedWord; lastClickedWordNode = getASTNodeAt(lineNumber, lastClickedWord, offset, false); log("Last clicked node: " + lastClickedWordNode); } private DefaultMutableTreeNode findAllOccurrences(){ log("Last clicked word:" + lastClickedWord); String selText = lastClickedWord == null ? editor.ta.getSelectedText() : lastClickedWord; int line = editor.ta.getSelectionStartLine(); log(selText + "<- offsets " + (line) + ", " + (editor.ta.getSelectionStart() - editor.ta .getLineStartOffset(line)) + ", " + (editor.ta.getSelectionStop() - editor.ta .getLineStartOffset(line))); int offwhitespace = editor.ta .getLineStartNonWhiteSpaceOffset(line); ASTNodeWrapper wnode; if (lastClickedWord == null || lastClickedWordNode.getNode() == null) { wnode = getASTNodeAt(line + errorCheckerService.mainClassOffset, selText, editor.ta.getSelectionStart() - offwhitespace, false); } else{ wnode = lastClickedWordNode; } if(wnode.getNode() == null){ return null; } logE("Gonna find all occurrences of " + getNodeAsString(wnode.getNode())); //If wnode is a constructor, find the TD instead. if (wnode.getNodeType() == ASTNode.METHOD_DECLARATION) { MethodDeclaration md = (MethodDeclaration) wnode.getNode(); ASTNode node = md.getParent(); while (node != null) { if (node instanceof TypeDeclaration) { // log("Parent class " + getNodeAsString(node)); break; } node = node.getParent(); } if(node != null && node instanceof TypeDeclaration){ TypeDeclaration td = (TypeDeclaration) node; if(td.getName().toString().equals(md.getName().toString())){ logE("Renaming constructor of " + getNodeAsString(td)); wnode = new ASTNodeWrapper(td); } } } DefaultMutableTreeNode defCU = new DefaultMutableTreeNode( new ASTNodeWrapper( wnode .getNode(), selText)); dfsNameOnly(defCU, wnode.getNode(), selText); // Reverse the list obtained via dfs Stack tempS = new Stack(); for (int i = 0; i < defCU.getChildCount(); i++) { tempS.push(defCU.getChildAt(i)); } defCU.removeAllChildren(); while (!tempS.isEmpty()) { defCU.add((MutableTreeNode) tempS.pop()); } log(wnode); return defCU; } @SuppressWarnings({ "unchecked" }) /** * Generates AST Swing component * @param node * @param tnode */ public static void visitRecur(ASTNode node, DefaultMutableTreeNode tnode) { Iterator it = node .structuralPropertiesForType().iterator(); //logE("Props of " + node.getClass().getName()); DefaultMutableTreeNode ctnode = null; while (it.hasNext()) { StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) it .next(); if (prop.isChildProperty() || prop.isSimpleProperty()) { if (node.getStructuralProperty(prop) != null) { // System.out // .println(node.getStructuralProperty(prop) + " -> " + (prop)); if (node.getStructuralProperty(prop) instanceof ASTNode) { ASTNode cnode = (ASTNode) node.getStructuralProperty(prop); if (isAddableASTNode(cnode)) { ctnode = new DefaultMutableTreeNode( new ASTNodeWrapper((ASTNode) node .getStructuralProperty(prop))); tnode.add(ctnode); visitRecur(cnode, ctnode); } } else { tnode.add(new DefaultMutableTreeNode(node .getStructuralProperty(prop))); } } } else if (prop.isChildListProperty()) { List nodelist = (List) node .getStructuralProperty(prop); for (ASTNode cnode : nodelist) { if (isAddableASTNode(cnode)) { ctnode = new DefaultMutableTreeNode(new ASTNodeWrapper(cnode)); tnode.add(ctnode); visitRecur(cnode, ctnode); } else visitRecur(cnode, tnode); } } } } public void dfsNameOnly(DefaultMutableTreeNode tnode,ASTNode decl, String name) { Stack temp = new Stack(); temp.push(codeTree); while(!temp.isEmpty()){ DefaultMutableTreeNode cnode = (DefaultMutableTreeNode) temp.pop(); for (int i = 0; i < cnode.getChildCount(); i++) { temp.push(cnode.getChildAt(i)); } if(!(cnode.getUserObject() instanceof ASTNodeWrapper)) continue; ASTNodeWrapper awnode = (ASTNodeWrapper) cnode.getUserObject(); // log("Visiting: " + getNodeAsString(awnode.getNode())); if(isInstanceOfType(awnode.getNode(), decl, name)){ int val[] = errorCheckerService.JavaToPdeOffsets(awnode.getLineNumber(), 0); tnode.add(new DefaultMutableTreeNode(new ASTNodeWrapper(awnode .getNode(), "Line " + (val[1] + 1) + " | Tab: " + editor.getSketch().getCode(val[0]).getPrettyName()))); } } } protected SketchOutline sketchOutline; protected void showSketchOutline(){ sketchOutline = new SketchOutline(codeTree, errorCheckerService); sketchOutline.show(); } public int javaCodeOffsetToLineStartOffset(int line, int jOffset){ // Find the first node with this line number, return its offset - jOffset line = PdeToJavaLineNumber(line); log("Looking for line: " + line + ", jOff " + jOffset); Stack temp = new Stack(); temp.push(codeTree); while (!temp.isEmpty()) { DefaultMutableTreeNode cnode = (DefaultMutableTreeNode) temp.pop(); for (int i = 0; i < cnode.getChildCount(); i++) { temp.push(cnode.getChildAt(i)); } if (!(cnode.getUserObject() instanceof ASTNodeWrapper)) continue; ASTNodeWrapper awnode = (ASTNodeWrapper) cnode.getUserObject(); // log("Visiting: " + getNodeAsString(awnode.getNode())); if (awnode.getLineNumber() == line) { log("First element with this line no is: " + awnode + "LSO: " + (jOffset - awnode.getNode().getStartPosition())); return (jOffset - awnode.getNode().getStartPosition()); } } return -1; } private int PdeToJavaLineNumber(int lineNum){ int lineNumber = lineNum + errorCheckerService.getPdeImportsCount(); // Adjust line number for tabbed sketches int codeIndex = editor.getSketch().getCodeIndex(editor.getCurrentTab()); if (codeIndex > 0) for (int i = 0; i < codeIndex; i++) { SketchCode sc = editor.getSketch().getCode(i); int len = Base.countLines(sc.getProgram()) + 1; lineNumber += len; } return lineNumber; } private boolean isInstanceOfType(ASTNode node,ASTNode decl, String name){ if(node instanceof SimpleName){ SimpleName sn = (SimpleName) node; if (sn.toString().equals(name)) { ArrayList nodesToBeMatched = new ArrayList(); nodesToBeMatched.add(decl); if(decl instanceof TypeDeclaration){ log("decl is a TD"); TypeDeclaration td = (TypeDeclaration)decl; MethodDeclaration[] mlist = td.getMethods(); for (MethodDeclaration md : mlist) { if(md.getName().toString().equals(name)){ nodesToBeMatched.add(md); } } } log("Visiting: " + getNodeAsString(node)); ASTNode decl2 = findDeclaration(sn); logE("It's decl: " + getNodeAsString(decl2)); log("But we need: "+getNodeAsString(decl)); for (ASTNode astNode : nodesToBeMatched) { if(astNode.equals(decl2)){ return true; } } } } return false; } public void handleRefactor(){ log("Last clicked word:" + lastClickedWord); if(lastClickedWord == null && editor.ta.getSelectedText() == null){ editor.statusError("Highlight the class/function/variable name first"); return; } if(errorCheckerService.hasSyntaxErrors()){ editor.statusError("Can't perform action until syntax errors are fixed :("); return; } if (!frmRename.isVisible()){ frmRename.setVisible(true); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { String selText = lastClickedWord == null ? editor.ta.getSelectedText() : lastClickedWord; frmOccurenceList.setTitle("All occurrences of " + selText); lblRefactorOldName.setText("Current name: " + selText); txtRenameField.requestFocus(); } }); } frmRename.toFront(); } public static void printRecur(ASTNode node) { Iterator it = node .structuralPropertiesForType().iterator(); //logE("Props of " + node.getClass().getName()); while (it.hasNext()) { StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) it .next(); if (prop.isChildProperty() || prop.isSimpleProperty()) { if (node.getStructuralProperty(prop) != null) { // System.out // .println(node.getStructuralProperty(prop) + " -> " + (prop)); if (node.getStructuralProperty(prop) instanceof ASTNode) { ASTNode cnode = (ASTNode) node.getStructuralProperty(prop); log(getNodeAsString(cnode)); printRecur(cnode); } } } else if (prop.isChildListProperty()) { List nodelist = (List) node .getStructuralProperty(prop); for (ASTNode cnode : nodelist) { log(getNodeAsString(cnode)); printRecur(cnode); } } } } @SuppressWarnings("unchecked") private static ASTNode findLineOfNode(ASTNode node, int lineNumber, int offset, String name) { CompilationUnit root = (CompilationUnit) node.getRoot(); // log("Inside "+getNodeAsString(node) + " | " + root.getLineNumber(node.getStartPosition())); if (root.getLineNumber(node.getStartPosition()) == lineNumber) { logE(3 + getNodeAsString(node) + " len " + node.getLength()); return node; // if (offset < node.getLength()) // return node; // else { // logE(-11); // return null; // } } for (Object oprop : node.structuralPropertiesForType()) { StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) oprop; if (prop.isChildProperty() || prop.isSimpleProperty()) { if (node.getStructuralProperty(prop) != null) { if (node.getStructuralProperty(prop) instanceof ASTNode) { ASTNode retNode = findLineOfNode((ASTNode) node .getStructuralProperty(prop), lineNumber, offset, name); if (retNode != null) { // logE(11 + getNodeAsString(retNode)); return retNode; } } } } else if (prop.isChildListProperty()) { List nodelist = (List) node .getStructuralProperty(prop); for (ASTNode retNode : nodelist) { ASTNode rr = findLineOfNode(retNode, lineNumber, offset, name); if (rr != null) { // logE(12 + getNodeAsString(rr)); return rr; } } } } // logE("-1"); return null; } /** * * @param node * @param offset * - from textarea painter * @param lineStartOffset * - obtained from findLineOfNode * @param name * @param root * @return */ @SuppressWarnings("unchecked") public static ASTNode pinpointOnLine(ASTNode node, int offset, int lineStartOffset, String name) { if (node instanceof SimpleName) { SimpleName sn = (SimpleName) node; log(offset+ "off,pol " + getNodeAsString(sn)); if ((lineStartOffset + offset) >= sn.getStartPosition() && (lineStartOffset + offset) <= sn.getStartPosition() + sn.getLength()) { if (sn.toString().equals(name)) { return sn; } else { return null; } } else { return null; } } for (Object oprop : node.structuralPropertiesForType()) { StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) oprop; if (prop.isChildProperty() || prop.isSimpleProperty()) { if (node.getStructuralProperty(prop) != null) { if (node.getStructuralProperty(prop) instanceof ASTNode) { ASTNode retNode = pinpointOnLine((ASTNode) node .getStructuralProperty(prop), offset, lineStartOffset, name); if (retNode != null) { // logE(11 + getNodeAsString(retNode)); return retNode; } } } } else if (prop.isChildListProperty()) { List nodelist = (List) node .getStructuralProperty(prop); for (ASTNode retNode : nodelist) { ASTNode rr = pinpointOnLine(retNode, offset, lineStartOffset, name); if (rr != null) { // logE(12 + getNodeAsString(rr)); return rr; } } } } // logE("-1"); return null; } /** * Give this thing a {@link Name} instance - a {@link SimpleName} from the * ASTNode for ex, and it tries its level best to locate its declaration in * the AST. It really does. * * @param findMe * @return */ @SuppressWarnings("unchecked") private static ASTNode findDeclaration(Name findMe) { // WARNING: You're entering the Rube Goldberg territory of Experimental Mode. // To debug this code, thou must take the Recursive Leap of Faith. log("entering --findDeclaration1 -- " + findMe.toString()); ASTNode declaringClass = null; ASTNode parent = findMe.getParent(); ASTNode ret = null; ArrayList constrains = new ArrayList(); if (parent.getNodeType() == ASTNode.METHOD_INVOCATION) { Expression exp = (Expression) ((MethodInvocation) parent) .getStructuralProperty(MethodInvocation.EXPRESSION_PROPERTY); //TODO: Note the imbalance of constrains.add(ASTNode.METHOD_DECLARATION); // Possibly a bug here. Investigate later. if (((MethodInvocation) parent).getName().toString() .equals(findMe.toString())) { constrains.add(ASTNode.METHOD_DECLARATION); if (exp != null) { constrains.add(ASTNode.TYPE_DECLARATION); log("MI EXP: " + exp.toString() + " of type " + exp.getClass().getName() + " parent: " + exp.getParent()); if (exp instanceof MethodInvocation) { SimpleType stp = extracTypeInfo(findDeclaration(((MethodInvocation) exp) .getName())); if (stp == null) return null; declaringClass = findDeclaration(stp.getName()); return definedIn(declaringClass, ((MethodInvocation) parent) .getName().toString(), constrains, declaringClass); } else if (exp instanceof FieldAccess) { SimpleType stp = extracTypeInfo(findDeclaration(((FieldAccess) exp) .getName())); if (stp == null) return null; declaringClass = findDeclaration((stp.getName())); return definedIn(declaringClass, ((MethodInvocation) parent) .getName().toString(), constrains, declaringClass); } if (exp instanceof SimpleName) { SimpleType stp = extracTypeInfo(findDeclaration(((SimpleName) exp))); if (stp == null) return null; declaringClass = findDeclaration(stp.getName()); log("MI.SN " + getNodeAsString(declaringClass)); constrains.add(ASTNode.METHOD_DECLARATION); return definedIn(declaringClass, ((MethodInvocation) parent) .getName().toString(), constrains, declaringClass); } } } else { parent = parent.getParent(); // Move one up the ast. V V IMP!! } } else if (parent.getNodeType() == ASTNode.FIELD_ACCESS) { FieldAccess fa = (FieldAccess) parent; Expression exp = fa.getExpression(); if (fa.getName().toString().equals(findMe.toString())) { constrains.add(ASTNode.FIELD_DECLARATION); if (exp != null) { constrains.add(ASTNode.TYPE_DECLARATION); log("FA EXP: " + exp.toString() + " of type " + exp.getClass().getName() + " parent: " + exp.getParent()); if (exp instanceof MethodInvocation) { SimpleType stp = extracTypeInfo(findDeclaration(((MethodInvocation) exp) .getName())); if (stp == null) return null; declaringClass = findDeclaration(stp.getName()); return definedIn(declaringClass, fa.getName().toString(), constrains, declaringClass); } else if (exp instanceof FieldAccess) { SimpleType stp = extracTypeInfo(findDeclaration(((FieldAccess) exp) .getName())); if (stp == null) return null; declaringClass = findDeclaration((stp.getName())); constrains.add(ASTNode.TYPE_DECLARATION); return definedIn(declaringClass, fa.getName().toString(), constrains, declaringClass); } if (exp instanceof SimpleName) { SimpleType stp = extracTypeInfo(findDeclaration(((SimpleName) exp))); if (stp == null) return null; declaringClass = findDeclaration(stp.getName()); log("FA.SN " + getNodeAsString(declaringClass)); constrains.add(ASTNode.METHOD_DECLARATION); return definedIn(declaringClass, fa.getName().toString(), constrains, declaringClass); } } } else { parent = parent.getParent(); // Move one up the ast. V V IMP!! } } else if (parent.getNodeType() == ASTNode.QUALIFIED_NAME) { QualifiedName qn = (QualifiedName) parent; if (!findMe.toString().equals(qn.getQualifier().toString())) { SimpleType stp = extracTypeInfo(findDeclaration((qn.getQualifier()))); log(qn.getQualifier() + "->" + qn.getName()); declaringClass = findDeclaration(stp.getName()); log("QN decl class: " + getNodeAsString(declaringClass)); constrains.clear(); constrains.add(ASTNode.TYPE_DECLARATION); constrains.add(ASTNode.FIELD_DECLARATION); return definedIn(declaringClass, qn.getName().toString(), constrains, null); } else{ if(findMe instanceof QualifiedName){ QualifiedName qnn = (QualifiedName) findMe; log("findMe is a QN, " + (qnn.getQualifier().toString() + " other " + qnn.getName() .toString())); SimpleType stp = extracTypeInfo(findDeclaration((qnn.getQualifier()))); log(qnn.getQualifier() + "->" + qnn.getName()); declaringClass = findDeclaration(stp.getName()); log("QN decl class: " + getNodeAsString(declaringClass)); constrains.clear(); constrains.add(ASTNode.TYPE_DECLARATION); constrains.add(ASTNode.FIELD_DECLARATION); return definedIn(declaringClass, qnn.getName().toString(), constrains, null); } } } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE) { constrains.add(ASTNode.TYPE_DECLARATION); if (parent.getParent().getNodeType() == ASTNode.CLASS_INSTANCE_CREATION) constrains.add(ASTNode.CLASS_INSTANCE_CREATION); } else if(parent.getNodeType() == ASTNode.TYPE_DECLARATION){ // The condition where we look up the name of a class decl TypeDeclaration td = (TypeDeclaration) parent; if(findMe.equals(td.getName())) { return parent; } } else if (parent instanceof Expression) { // constrains.add(ASTNode.TYPE_DECLARATION); // constrains.add(ASTNode.METHOD_DECLARATION); // constrains.add(ASTNode.FIELD_DECLARATION); } // else if(findMe instanceof QualifiedName){ // QualifiedName qn = (QualifiedName) findMe; // System.out // .println("findMe is a QN, " // + (qn.getQualifier().toString() + " other " + qn.getName() // .toString())); // } while (parent != null) { log("findDeclaration1 -> " + getNodeAsString(parent)); for (Object oprop : parent.structuralPropertiesForType()) { StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) oprop; if (prop.isChildProperty() || prop.isSimpleProperty()) { if (parent.getStructuralProperty(prop) instanceof ASTNode) { // log(prop + " C/S Prop of -> " // + getNodeAsString(parent)); ret = definedIn((ASTNode) parent.getStructuralProperty(prop), findMe.toString(), constrains, declaringClass); if (ret != null) return ret; } } else if (prop.isChildListProperty()) { // log((prop) + " ChildList props of " // + getNodeAsString(parent)); List nodelist = (List) parent .getStructuralProperty(prop); for (ASTNode retNode : nodelist) { ret = definedIn(retNode, findMe.toString(), constrains, declaringClass); if (ret != null) return ret; } } } parent = parent.getParent(); } return null; } /** * A variation of findDeclaration() but accepts an alternate parent ASTNode * @param findMe * @param alternateParent * @return */ private static ASTNode findDeclaration2(Name findMe, ASTNode alternateParent) { ASTNode declaringClass = null; ASTNode parent = findMe.getParent(); ASTNode ret = null; ArrayList constrains = new ArrayList(); if (parent.getNodeType() == ASTNode.METHOD_INVOCATION) { Expression exp = (Expression) ((MethodInvocation) parent) .getStructuralProperty(MethodInvocation.EXPRESSION_PROPERTY); //TODO: Note the imbalance of constrains.add(ASTNode.METHOD_DECLARATION); // Possibly a bug here. Investigate later. if (((MethodInvocation) parent).getName().toString() .equals(findMe.toString())) { constrains.add(ASTNode.METHOD_DECLARATION); if (exp != null) { constrains.add(ASTNode.TYPE_DECLARATION); log("MI EXP: " + exp.toString() + " of type " + exp.getClass().getName() + " parent: " + exp.getParent()); if (exp instanceof MethodInvocation) { SimpleType stp = extracTypeInfo(findDeclaration2(((MethodInvocation) exp) .getName(), alternateParent)); if (stp == null) return null; declaringClass = findDeclaration2(stp.getName(), alternateParent); return definedIn(declaringClass, ((MethodInvocation) parent) .getName().toString(), constrains, declaringClass); } else if (exp instanceof FieldAccess) { SimpleType stp = extracTypeInfo(findDeclaration2(((FieldAccess) exp) .getName(), alternateParent)); if (stp == null) return null; declaringClass = findDeclaration2((stp.getName()), alternateParent); return definedIn(declaringClass, ((MethodInvocation) parent) .getName().toString(), constrains, declaringClass); } if (exp instanceof SimpleName) { SimpleType stp = extracTypeInfo(findDeclaration2(((SimpleName) exp), alternateParent)); if (stp == null) return null; declaringClass = findDeclaration2(stp.getName(), alternateParent); log("MI.SN " + getNodeAsString(declaringClass)); constrains.add(ASTNode.METHOD_DECLARATION); return definedIn(declaringClass, ((MethodInvocation) parent) .getName().toString(), constrains, declaringClass); } } } else { parent = parent.getParent(); // Move one up the ast. V V IMP!! alternateParent = alternateParent.getParent(); } } else if (parent.getNodeType() == ASTNode.FIELD_ACCESS) { FieldAccess fa = (FieldAccess) parent; Expression exp = fa.getExpression(); if (fa.getName().toString().equals(findMe.toString())) { constrains.add(ASTNode.FIELD_DECLARATION); if (exp != null) { constrains.add(ASTNode.TYPE_DECLARATION); log("FA EXP: " + exp.toString() + " of type " + exp.getClass().getName() + " parent: " + exp.getParent()); if (exp instanceof MethodInvocation) { SimpleType stp = extracTypeInfo(findDeclaration2(((MethodInvocation) exp) .getName(), alternateParent)); if (stp == null) return null; declaringClass = findDeclaration2(stp.getName(), alternateParent); return definedIn(declaringClass, fa.getName().toString(), constrains, declaringClass); } else if (exp instanceof FieldAccess) { SimpleType stp = extracTypeInfo(findDeclaration2(((FieldAccess) exp) .getName(), alternateParent)); if (stp == null) return null; declaringClass = findDeclaration2((stp.getName()), alternateParent); constrains.add(ASTNode.TYPE_DECLARATION); return definedIn(declaringClass, fa.getName().toString(), constrains, declaringClass); } if (exp instanceof SimpleName) { SimpleType stp = extracTypeInfo(findDeclaration2(((SimpleName) exp), alternateParent)); if (stp == null) return null; declaringClass = findDeclaration2(stp.getName(), alternateParent); log("FA.SN " + getNodeAsString(declaringClass)); constrains.add(ASTNode.METHOD_DECLARATION); return definedIn(declaringClass, fa.getName().toString(), constrains, declaringClass); } } } else { parent = parent.getParent(); // Move one up the ast. V V IMP!! alternateParent = alternateParent.getParent(); } } else if (parent.getNodeType() == ASTNode.QUALIFIED_NAME) { QualifiedName qn = (QualifiedName) parent; if (!findMe.toString().equals(qn.getQualifier().toString())) { SimpleType stp = extracTypeInfo(findDeclaration2((qn.getQualifier()), alternateParent)); if(stp == null) return null; declaringClass = findDeclaration2(stp.getName(), alternateParent); log(qn.getQualifier() + "->" + qn.getName()); log("QN decl class: " + getNodeAsString(declaringClass)); constrains.clear(); constrains.add(ASTNode.TYPE_DECLARATION); constrains.add(ASTNode.FIELD_DECLARATION); return definedIn(declaringClass, qn.getName().toString(), constrains, null); } else{ if(findMe instanceof QualifiedName){ QualifiedName qnn = (QualifiedName) findMe; log("findMe is a QN, " + (qnn.getQualifier().toString() + " other " + qnn.getName() .toString())); SimpleType stp = extracTypeInfo(findDeclaration2((qnn.getQualifier()), alternateParent)); log(qnn.getQualifier() + "->" + qnn.getName()); declaringClass = findDeclaration2(stp.getName(), alternateParent); log("QN decl class: " + getNodeAsString(declaringClass)); constrains.clear(); constrains.add(ASTNode.TYPE_DECLARATION); constrains.add(ASTNode.FIELD_DECLARATION); return definedIn(declaringClass, qnn.getName().toString(), constrains, null); } } } else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE) { constrains.add(ASTNode.TYPE_DECLARATION); if (parent.getParent().getNodeType() == ASTNode.CLASS_INSTANCE_CREATION) constrains.add(ASTNode.CLASS_INSTANCE_CREATION); } else if (parent instanceof Expression) { // constrains.add(ASTNode.TYPE_DECLARATION); // constrains.add(ASTNode.METHOD_DECLARATION); // constrains.add(ASTNode.FIELD_DECLARATION); } // TODO: in findDec, we also have a case where parent of type TD is handled. // Figure out if needed here as well. log("Alternate parent: " + getNodeAsString(alternateParent)); while (alternateParent != null) { // log("findDeclaration2 -> " // + getNodeAsString(alternateParent)); for (Object oprop : alternateParent.structuralPropertiesForType()) { StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) oprop; if (prop.isChildProperty() || prop.isSimpleProperty()) { if (alternateParent.getStructuralProperty(prop) instanceof ASTNode) { // log(prop + " C/S Prop of -> " // + getNodeAsString(alternateParent)); ret = definedIn((ASTNode) alternateParent .getStructuralProperty(prop), findMe.toString(), constrains, declaringClass); if (ret != null) return ret; } } else if (prop.isChildListProperty()) { // log((prop) + " ChildList props of " // + getNodeAsString(alternateParent)); List nodelist = (List) alternateParent .getStructuralProperty(prop); for (ASTNode retNode : nodelist) { ret = definedIn(retNode, findMe.toString(), constrains, declaringClass); if (ret != null) return ret; } } } alternateParent = alternateParent.getParent(); } return null; } private List getCodeComments(){ List commentList = compilationUnit.getCommentList(); // log("Total comments: " + commentList.size()); // int i = 0; // for (Comment comment : commentList) { // log(++i + ": "+comment + " Line:" // + compilationUnit.getLineNumber(comment.getStartPosition()) + ", " // + comment.getLength()); // } return commentList; } protected boolean caretWithinLineComment(){ String pdeLine = editor.getLineText(editor.textArea().getCaretLine()).trim(); int caretPos = editor.textArea().getCaretPosition() - editor.textArea() .getLineStartNonWhiteSpaceOffset(editor.textArea().getCaretLine()); int x = pdeLine.indexOf("//"); log(x + " , " + caretPos + ", Checking line for comment " + pdeLine); //lineStartOffset = editor.textArea(). if (x >= 0 && caretPos > x) { log("INSIDE a comment"); return true; } log("not within comment"); return false; } /** * A wrapper for java.lang.reflect types. * Will have to see if the usage turns out to be internal only here or not * and then accordingly decide where to place this class. TODO * @author quarkninja * */ public class ClassMember { private Field field; private Method method; private Constructor cons; private Class thisclass; private String stringVal; private String classType; private ASTNode astNode; private ASTNode declaringNode; public ClassMember(Class m) { thisclass = m; stringVal = "Predefined Class " + m.getName(); classType = m.getName(); } public ClassMember(Method m) { method = m; stringVal = "Method " + m.getReturnType().getName() + " | " + m.getName() + " defined in " + m.getDeclaringClass().getName(); classType = m.getReturnType().getName(); } public ClassMember(Field m) { field = m; stringVal = "Field " + m.getType().getName() + " | " + m.getName() + " defined in " + m.getDeclaringClass().getName(); classType = m.getType().getName(); } public ClassMember(Constructor m) { cons = m; stringVal = "Cons " + " " + m.getName() + " defined in " + m.getDeclaringClass().getName(); } public ClassMember(ASTNode node){ astNode = node; stringVal = getNodeAsString(node); if(node instanceof TypeDeclaration){ declaringNode = node; } if(node instanceof SimpleType){ classType = ((SimpleType)node).getName().toString(); } SimpleType stp = (node instanceof SimpleType) ? (SimpleType) node : extracTypeInfo(node); if(stp != null){ ASTNode decl =findDeclaration(stp.getName()); // Czech out teh mutation if(decl == null){ // a predefined type classType = stp.getName().toString(); Class probableClass = findClassIfExists(classType); thisclass = probableClass; } else{ // a local type declaringNode = decl; } } } public Class getClass_() { return thisclass; } public ASTNode getDeclaringNode(){ return declaringNode; } public Field getField() { return field; } public Method getMethod() { return method; } public Constructor getCons() { return cons; } public ASTNode getASTNode(){ return astNode; } public String toString() { return stringVal; } public String getTypeAsString(){ return classType; } } /** * Find the SimpleType from FD, SVD, VDS, etc * * @param node * @return */ public static SimpleType extracTypeInfo(ASTNode node) { if (node == null) { return null; } Type t = extracTypeInfo2(node); if (t instanceof PrimitiveType) { return null; } else if (t instanceof ArrayType) { ArrayType at = (ArrayType) t; log(at.getComponentType() + " <-comp type, ele type-> " + at.getElementType() + ", " + at.getElementType().getClass().getName()); if (at.getElementType() instanceof PrimitiveType) { return null; } else if (at.getElementType() instanceof SimpleType) { return (SimpleType) at.getElementType(); } else return null; } else if (t instanceof ParameterizedType) { ParameterizedType pmt = (ParameterizedType) t; log(pmt.getType() + ", " + pmt.getType().getClass()); if (pmt.getType() instanceof SimpleType) { return (SimpleType) pmt.getType(); } else return null; } return (SimpleType) t; } public static Type extracTypeInfo2(ASTNode node) { if (node == null) return null; switch (node.getNodeType()) { case ASTNode.METHOD_DECLARATION: return ((MethodDeclaration) node).getReturnType2(); case ASTNode.FIELD_DECLARATION: return ((FieldDeclaration) node).getType(); case ASTNode.VARIABLE_DECLARATION_EXPRESSION: return ((VariableDeclarationExpression) node).getType(); case ASTNode.VARIABLE_DECLARATION_STATEMENT: return ((VariableDeclarationStatement) node).getType(); case ASTNode.SINGLE_VARIABLE_DECLARATION: return ((SingleVariableDeclaration) node).getType(); case ASTNode.VARIABLE_DECLARATION_FRAGMENT: return extracTypeInfo2(node.getParent()); } log("Unknown type info request " + getNodeAsString(node)); return null; } @SuppressWarnings("unchecked") private static ASTNode definedIn(ASTNode node, String name, ArrayList constrains, ASTNode declaringClass) { if (node == null) return null; if (constrains != null) { // log("Looking at " + getNodeAsString(node) + " for " + name // + " in definedIn"); if (!constrains.contains(node.getNodeType()) && constrains.size() > 0) { // System.err.print("definedIn -1 " + " But constrain was "); // for (Integer integer : constrains) { // System.out.print(ASTNode.nodeClassForType(integer) + ","); // } // log(); return null; } } List vdfList = null; switch (node.getNodeType()) { case ASTNode.TYPE_DECLARATION: //logE(getNodeAsString(node)); TypeDeclaration td = (TypeDeclaration) node; if (td.getName().toString().equals(name)) { if (constrains.contains(ASTNode.CLASS_INSTANCE_CREATION)) { // look for constructor; MethodDeclaration[] methods = td.getMethods(); for (MethodDeclaration md : methods) { if (md.getName().toString().equalsIgnoreCase(name)) { log("Found a constructor."); return md; } } } else { // it's just the TD we're lookin for return node; } } else { if (constrains.contains(ASTNode.FIELD_DECLARATION)) { // look for fields FieldDeclaration[] fields = td.getFields(); for (FieldDeclaration fd : fields) { List fragments = fd.fragments(); for (VariableDeclarationFragment vdf : fragments) { if (vdf.getName().toString().equalsIgnoreCase(name)) return fd; } } } else if (constrains.contains(ASTNode.METHOD_DECLARATION)) { // look for methods MethodDeclaration[] methods = td.getMethods(); for (MethodDeclaration md : methods) { if (md.getName().toString().equalsIgnoreCase(name)) { return md; } } } } break; case ASTNode.METHOD_DECLARATION: //logE(getNodeAsString(node)); if (((MethodDeclaration) node).getName().toString().equalsIgnoreCase(name)) return node; break; case ASTNode.SINGLE_VARIABLE_DECLARATION: //logE(getNodeAsString(node)); if (((SingleVariableDeclaration) node).getName().toString().equalsIgnoreCase(name)) return node; break; case ASTNode.FIELD_DECLARATION: //logE("FD" + node); vdfList = ((FieldDeclaration) node).fragments(); break; case ASTNode.VARIABLE_DECLARATION_EXPRESSION: //logE("VDE" + node); vdfList = ((VariableDeclarationExpression) node).fragments(); break; case ASTNode.VARIABLE_DECLARATION_STATEMENT: //logE("VDS" + node); vdfList = ((VariableDeclarationStatement) node).fragments(); break; default: } if (vdfList != null) { for (VariableDeclarationFragment vdf : vdfList) { if (vdf.getName().toString().equalsIgnoreCase(name)) return node; } } return null; } JFrame frmImportSuggest; public void suggestImports(final String className){ if(frmImportSuggest != null) if(frmImportSuggest.isVisible()) return; log("Looking for class " + className); RegExpResourceFilter regf = new RegExpResourceFilter( Pattern.compile(".*"), Pattern .compile(className + ".class", Pattern.CASE_INSENSITIVE)); String[] resources = classPath .findResources("", regf); if(resources.length == 0){ log("Couldn't find import for class " + className); return; } for (int i = 0; i < resources.length; i++) { resources[i] = resources[i].replace('/', '.') .substring(0, resources[i].length() - 6); } if (resources.length >= 1) { final JList classList = new JList(resources); classList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); frmImportSuggest = new JFrame(); frmImportSuggest.setBounds(300, 300, 400, 300); frmImportSuggest.setLayout(new BoxLayout(frmImportSuggest .getContentPane(), BoxLayout.Y_AXIS)); ((JComponent) frmImportSuggest.getContentPane()).setBorder(BorderFactory .createEmptyBorder(5, 5, 5, 5)); JLabel lbl = new JLabel("The class \"" + className + "\" couldn't be determined, choose the import you need from the following list."); JScrollPane jsp = new JScrollPane(); jsp.setViewportView(classList); JButton btnInsertImport = new JButton("Insert import"); btnInsertImport.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { if (classList.getSelectedValue() != null) { try { String impString = "import " + classList.getSelectedValue() + ";\n"; int ct = editor.getSketch().getCurrentCodeIndex(); editor.getSketch().setCurrentCode(0); editor.textArea().getDocument().insertString(0, impString, null); editor.getSketch().setCurrentCode(ct); errorCheckerService.runManualErrorCheck(); frmImportSuggest.setVisible(false); frmImportSuggest = null; } catch (BadLocationException e) { log("Failed to insert import for " + className); e.printStackTrace(); } } } }); frmImportSuggest.add(lbl); frmImportSuggest.add(jsp); frmImportSuggest.add(btnInsertImport); frmImportSuggest.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); frmImportSuggest.setVisible(true); } } public static final String ignoredImports[] = { "com.oracle.", "sun.", "sunw.", "com.sun.", "javax.", "sunw.", "org.ietf.", "org.jcp.", "org.omg.", "org.w3c.", "org.xml.", "org.eclipse.", "com.ibm.", "org.netbeans.", "org.jsoup.", "org.junit.", "org.apache.", "antlr." }; private boolean ignorableImport(String impName) { //TODO: Trie man. for (ImportStatement impS : errorCheckerService.getProgramImports()) { if(impName.startsWith(impS.getPackageName())) return false; } for (String impS : ignoredImports) { if(impName.startsWith(impS)) return true; } return false; } public static boolean isAddableASTNode(ASTNode node) { switch (node.getNodeType()) { // case ASTNode.STRING_LITERAL: // case ASTNode.NUMBER_LITERAL: // case ASTNode.BOOLEAN_LITERAL: // case ASTNode.NULL_LITERAL: // return false; default: return true; } } /** * For any line or expression, finds the line start offset(java code). * @param node * @return */ public int getASTNodeLineStartOffset(ASTNode node){ int nodeLineNo = getLineNumber(node); while(node.getParent() != null){ if (getLineNumber(node.getParent()) == nodeLineNo) { node = node.getParent(); } else { break; } } return node.getStartPosition(); } /** * For any node, finds various offsets (java code). * * @param node * @return int[]{line number, line number start offset, node start offset, * node length} */ public int[] getASTNodeAllOffsets(ASTNode node){ int nodeLineNo = getLineNumber(node), nodeOffset = node.getStartPosition(), nodeLength = node .getLength(); while(node.getParent() != null){ if (getLineNumber(node.getParent()) == nodeLineNo) { node = node.getParent(); } else { break; } } return new int[]{nodeLineNo, node.getStartPosition(), nodeOffset,nodeLength}; } static private String getNodeAsString(ASTNode node) { if (node == null) return "NULL"; String className = node.getClass().getName(); int index = className.lastIndexOf("."); if (index > 0) className = className.substring(index + 1); // if(node instanceof BodyDeclaration) // return className; String value = className; if (node instanceof TypeDeclaration) value = ((TypeDeclaration) node).getName().toString() + " | " + className; else if (node instanceof MethodDeclaration) value = ((MethodDeclaration) node).getName().toString() + " | " + className; else if (node instanceof MethodInvocation) value = ((MethodInvocation) node).getName().toString() + " | " + className; else if (node instanceof FieldDeclaration) value = ((FieldDeclaration) node).toString() + " FldDecl| "; else if (node instanceof SingleVariableDeclaration) value = ((SingleVariableDeclaration) node).getName() + " - " + ((SingleVariableDeclaration) node).getType() + " | SVD "; else if (node instanceof ExpressionStatement) value = node.toString() + className; else if (node instanceof SimpleName) value = ((SimpleName) node).getFullyQualifiedName() + " | " + className; else if (node instanceof QualifiedName) value = node.toString() + " | " + className; else if(node instanceof FieldAccess) value = node.toString() + " | "; else if (className.startsWith("Variable")) value = node.toString() + " | " + className; else if (className.endsWith("Type")) value = node.toString() + " |" + className; value += " [" + node.getStartPosition() + "," + (node.getStartPosition() + node.getLength()) + "]"; value += " Line: " + ((CompilationUnit) node.getRoot()).getLineNumber(node .getStartPosition()); return value; } /** * CompletionPanel name * * @param node * @return */ static private String getNodeAsString2(ASTNode node) { if (node == null) return "NULL"; String className = node.getClass().getName(); int index = className.lastIndexOf("."); if (index > 0) className = className.substring(index + 1); // if(node instanceof BodyDeclaration) // return className; String value = className; if (node instanceof TypeDeclaration) value = ((TypeDeclaration) node).getName().toString(); else if (node instanceof MethodDeclaration) value = ((MethodDeclaration) node).getName().toString(); else if (node instanceof MethodInvocation) value = ((MethodInvocation) node).getName().toString() + " | " + className; else if (node instanceof FieldDeclaration) value = ((FieldDeclaration) node).toString(); else if (node instanceof SingleVariableDeclaration) value = ((SingleVariableDeclaration) node).getName().toString(); else if (node instanceof ExpressionStatement) value = node.toString() + className; else if (node instanceof SimpleName) value = ((SimpleName) node).getFullyQualifiedName() + " | " + className; else if (node instanceof QualifiedName) value = node.toString(); else if (node instanceof VariableDeclarationFragment) value = ((VariableDeclarationFragment) node).getName().toString(); else if (className.startsWith("Variable")) value = node.toString(); else if (node instanceof VariableDeclarationStatement) value = ((VariableDeclarationStatement) node).toString(); else if (className.endsWith("Type")) value = node.toString(); // value += " [" + node.getStartPosition() + "," // + (node.getStartPosition() + node.getLength()) + "]"; // value += " Line: " // + ((CompilationUnit) node.getRoot()).getLineNumber(node // .getStartPosition()); return value; } public void jdocWindowVisible(boolean visible) { jdocWindow.setVisible(visible); } public static String readFile(String path) { BufferedReader reader = null; try { reader = new BufferedReader( new InputStreamReader( new FileInputStream( new File( path)))); } catch (FileNotFoundException e) { e.printStackTrace(); } try { StringBuilder ret = new StringBuilder(); // ret.append("package " + className + ";\n"); String line; while ((line = reader.readLine()) != null) { ret.append(line); ret.append("\n"); } return ret.toString(); } catch (IOException e) { e.printStackTrace(); } finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } }