| /******************************************************************************* |
| * Copyright (c) 2004, 2008 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.core.tests.dom; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.wst.jsdt.core.IBuffer; |
| import org.eclipse.wst.jsdt.core.IClassFile; |
| import org.eclipse.wst.jsdt.core.IJavaScriptUnit; |
| import org.eclipse.wst.jsdt.core.IJavaScriptProject; |
| import org.eclipse.wst.jsdt.core.IProblemRequestor; |
| import org.eclipse.wst.jsdt.core.JavaScriptModelException; |
| import org.eclipse.wst.jsdt.core.WorkingCopyOwner; |
| import org.eclipse.wst.jsdt.core.compiler.CharOperation; |
| import org.eclipse.wst.jsdt.core.compiler.IProblem; |
| import org.eclipse.wst.jsdt.core.dom.AST; |
| import org.eclipse.wst.jsdt.core.dom.ASTNode; |
| import org.eclipse.wst.jsdt.core.dom.ASTParser; |
| import org.eclipse.wst.jsdt.core.dom.ASTRequestor; |
| import org.eclipse.wst.jsdt.core.dom.ASTVisitor; |
| import org.eclipse.wst.jsdt.core.dom.AnonymousClassDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.ArrayType; |
| import org.eclipse.wst.jsdt.core.dom.Assignment; |
| import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit; |
| import org.eclipse.wst.jsdt.core.dom.IBinding; |
| import org.eclipse.wst.jsdt.core.dom.IFunctionBinding; |
| import org.eclipse.wst.jsdt.core.dom.ITypeBinding; |
| import org.eclipse.wst.jsdt.core.dom.IVariableBinding; |
| import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.FunctionInvocation; |
| import org.eclipse.wst.jsdt.core.dom.PackageDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.QualifiedName; |
| import org.eclipse.wst.jsdt.core.dom.SimpleName; |
| import org.eclipse.wst.jsdt.core.dom.SimpleType; |
| import org.eclipse.wst.jsdt.core.dom.TypeDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.TypeDeclarationStatement; |
| import org.eclipse.wst.jsdt.core.tests.model.ModifyingResourceTests; |
| import org.eclipse.wst.jsdt.core.tests.util.Util; |
| |
| public class AbstractASTTests extends ModifyingResourceTests { |
| |
| public AbstractASTTests(String name) { |
| super(name); |
| } |
| |
| /* |
| * Removes the *start* and *end* markers from the given source and remembers |
| * the positions. |
| */ |
| public class MarkerInfo { |
| String path; |
| String source; |
| int[] astStarts, astEnds; |
| |
| public MarkerInfo(String source) { |
| this(null, source); |
| } |
| |
| public MarkerInfo(String path, String source) { |
| this.path = path; |
| this.source = source; |
| |
| int markerIndex = 1; |
| while (source.indexOf("/*start" + markerIndex + "*/") != -1) { |
| markerIndex++; |
| } |
| int astNumber = source.indexOf("/*start*/") != -1 ? markerIndex |
| : markerIndex - 1; |
| this.astStarts = new int[astNumber]; |
| this.astEnds = new int[astNumber]; |
| |
| for (int i = 1; i < markerIndex; i++) |
| setStartAndEnd(i); |
| if (astNumber == markerIndex) |
| setStartAndEnd(-1); |
| } |
| |
| public int indexOfASTStart(int astStart) { |
| for (int i = 0, length = this.astStarts.length; i < length; i++) |
| if (this.astStarts[i] == astStart) |
| return i; |
| return -1; |
| } |
| |
| private void removeMarkerFromSource(String marker, int sourceIndex, |
| int astNumber) { |
| char[] markerChars = marker.toCharArray(); |
| this.source = new String(CharOperation.replace(this.source |
| .toCharArray(), markerChars, CharOperation.NO_CHAR)); |
| // shift previously recorded positions |
| int markerLength = markerChars.length; |
| for (int i = 0; i < astNumber; i++) { |
| if (this.astStarts[i] > sourceIndex) |
| this.astStarts[i] -= markerLength; |
| if (this.astEnds[i] > sourceIndex) |
| this.astEnds[i] -= markerLength; |
| } |
| } |
| |
| private void setStartAndEnd(int markerIndex) { |
| String markerNumber; |
| if (markerIndex == -1) { |
| markerNumber = ""; |
| markerIndex = this.astStarts.length; // *start* is always last |
| } else |
| markerNumber = Integer.toString(markerIndex); |
| |
| String markerStart = "/*start" + markerNumber + "*/"; |
| String markerEnd = "/*end" + markerNumber + "*/"; |
| int astStart = source.indexOf(markerStart); // start of AST |
| // inclusive |
| this.astStarts[markerIndex - 1] = astStart; |
| removeMarkerFromSource(markerStart, astStart, markerIndex - 1); |
| int astEnd = this.source.indexOf(markerEnd); // end of AST exclusive |
| this.astEnds[markerIndex - 1] = astEnd; |
| removeMarkerFromSource(markerEnd, astEnd, markerIndex - 1); |
| } |
| |
| } |
| |
| public class BindingRequestor extends ASTRequestor { |
| HashMap bindings = new HashMap(); |
| |
| public void acceptBinding(String bindingKey, IBinding binding) { |
| this.bindings.put(bindingKey, binding); |
| } |
| |
| public IBinding[] getBindings(String[] bindingKeys) { |
| int length = this.bindings.size(); |
| IBinding[] result = new IBinding[length]; |
| for (int i = 0; i < length; i++) { |
| result[i] = (IBinding) this.bindings.get(bindingKeys[i]); |
| } |
| return result; |
| } |
| } |
| |
| protected void assertASTNodeEquals(String expected, ASTNode node) { |
| String actual = node.toString(); |
| if (!expected.equals(actual)) { |
| System.out.println(displayString(actual, 3) + ","); |
| } |
| assertEquals("Unexpected ast node", expected, actual); |
| } |
| |
| protected void assertASTNodesEqual(String expected, List nodes) { |
| StringBuffer buffer = new StringBuffer(); |
| Iterator iterator = nodes.iterator(); |
| while (iterator.hasNext()) { |
| ASTNode node = (ASTNode) iterator.next(); |
| buffer.append(node); |
| if (node instanceof JavaScriptUnit) { |
| IProblem[] problems = ((JavaScriptUnit) node).getProblems(); |
| if (problems != null) { |
| for (int i = 0, length = problems.length; i < length; i++) { |
| IProblem problem = problems[i]; |
| buffer.append('\n'); |
| buffer.append(problem); |
| } |
| } |
| } |
| buffer.append('\n'); |
| } |
| String actual = buffer.toString(); |
| if (!expected.equals(actual)) { |
| System.out.println(displayString(actual, 4) + ","); |
| } |
| assertEquals("Unexpected ast nodes", expected, actual); |
| } |
| |
| protected void assertBindingKeyEquals(String expected, String actual) { |
| assertBindingKeysEqual(expected, new String[] { actual }); |
| } |
| |
| protected void assertBindingKeysEqual(String expected, String[] actualKeys) { |
| StringBuffer buffer = new StringBuffer(); |
| for (int i = 0, length = actualKeys.length; i < length; i++) { |
| if (i > 0) |
| buffer.append('\n'); |
| buffer.append(actualKeys[i]); |
| } |
| String actual = buffer.toString(); |
| if (!expected.equals(actual)) { |
| System.out.print(displayString(actual, 4)); |
| System.out.println(','); |
| } |
| assertEquals("Unexpected binding keys", expected, actual); |
| } |
| |
| protected void assertBindingEquals(String expected, IBinding binding) { |
| assertBindingsEqual(expected, new IBinding[] { binding }); |
| } |
| |
| protected void assertBindingsEqual(String expected, |
| IBinding[] actualBindings) { |
| StringBuffer buffer = new StringBuffer(); |
| for (int i = 0, length = actualBindings.length; i < length; i++) { |
| if (i > 0) |
| buffer.append('\n'); |
| if (actualBindings[i] == null) |
| buffer.append("<null>"); |
| else |
| buffer.append(actualBindings[i].getKey()); |
| } |
| String actual = buffer.toString(); |
| if (!expected.equals(actual)) { |
| System.out.print(displayString(actual, 3)); |
| System.out.println(','); |
| } |
| assertEquals("Unexpected bindings", expected, actual); |
| } |
| |
| /* |
| * Builds an AST from the info source (which is assumed to be the source |
| * attached to the given class file), and returns the AST node that was |
| * delimited by the astStart and astEnd of the marker info. |
| */ |
| protected ASTNode buildAST(MarkerInfo markerInfo, IClassFile classFile, |
| boolean reportErrors) throws JavaScriptModelException { |
| ASTParser parser = ASTParser.newParser(AST.JLS3); |
| parser.setSource(classFile); |
| parser.setResolveBindings(true); |
| JavaScriptUnit unit = (JavaScriptUnit) parser.createAST(null); |
| |
| if (reportErrors) { |
| StringBuffer buffer = new StringBuffer(); |
| IProblem[] problems = unit.getProblems(); |
| for (int i = 0, length = problems.length; i < length; i++) |
| Util.appendProblem(buffer, problems[i], markerInfo.source |
| .toCharArray(), i + 1); |
| if (buffer.length() > 0) |
| System.err.println(buffer.toString()); |
| } |
| |
| return findNode(unit, markerInfo); |
| } |
| |
| protected ASTNode buildAST(IJavaScriptUnit cu) |
| throws JavaScriptModelException { |
| return buildAST(null/* use existing contents */, cu, true/* report errors */); |
| } |
| |
| protected ASTNode buildAST(String newContents, IJavaScriptUnit cu) |
| throws JavaScriptModelException { |
| return buildAST(newContents, cu, true/* report errors */); |
| } |
| |
| protected ASTNode buildAST(MarkerInfo markerInfo, IClassFile classFile) |
| throws JavaScriptModelException { |
| return buildAST(markerInfo, classFile, true/* report errors */); |
| } |
| |
| /* |
| * Removes the marker comments "*start*" and "*end*" from the given |
| * contents, builds an AST from the resulting source, and returns the AST |
| * node that was delimited by "*start*" and "*end*". |
| */ |
| protected ASTNode buildAST(String newContents, IJavaScriptUnit cu, |
| boolean reportErrors) throws JavaScriptModelException { |
| return buildAST(newContents, cu, reportErrors, false/* |
| * no statement |
| * recovery |
| */); |
| } |
| |
| protected ASTNode buildAST(String newContents, IJavaScriptUnit cu, |
| boolean reportErrors, boolean enableStatementRecovery) |
| throws JavaScriptModelException { |
| ASTNode[] nodes = buildASTs(newContents, cu, reportErrors, |
| enableStatementRecovery); |
| if (nodes.length == 0) |
| return null; |
| return nodes[0]; |
| } |
| |
| protected ASTNode[] buildASTs(String contents, IJavaScriptUnit cu) |
| throws JavaScriptModelException { |
| return buildASTs(contents, cu, true); |
| } |
| |
| /* |
| * Removes the marker comments "*start?*" and "*end?*" from the given new |
| * contents (where ? is either empty or a number), or use the current |
| * contents if the given new contents is null. Builds an AST from the |
| * resulting source. For each of the pairs, returns the AST node that was |
| * delimited by "*start?*" and "*end?*". |
| */ |
| protected ASTNode[] buildASTs(String newContents, IJavaScriptUnit cu, |
| boolean reportErrors) throws JavaScriptModelException { |
| return buildASTs(newContents, cu, reportErrors, false); |
| } |
| |
| protected ASTNode[] buildASTs(String newContents, IJavaScriptUnit cu, |
| boolean reportErrors, boolean enableStatementRecovery) |
| throws JavaScriptModelException { |
| MarkerInfo markerInfo; |
| if (newContents == null) { |
| markerInfo = new MarkerInfo(cu.getSource()); |
| } else { |
| markerInfo = new MarkerInfo(newContents); |
| } |
| newContents = markerInfo.source; |
| |
| JavaScriptUnit unit; |
| if (cu.isWorkingCopy()) { |
| cu.getBuffer().setContents(newContents); |
| unit = cu.reconcile(AST.JLS3, reportErrors, |
| enableStatementRecovery, null, null); |
| } else { |
| IBuffer buffer = cu.getBuffer(); |
| buffer.setContents(newContents); |
| buffer.save(null, false); |
| |
| ASTParser parser = ASTParser.newParser(AST.JLS3); |
| parser.setSource(cu); |
| parser.setResolveBindings(true); |
| parser.setStatementsRecovery(enableStatementRecovery); |
| unit = (JavaScriptUnit) parser.createAST(null); |
| } |
| |
| if (reportErrors) { |
| StringBuffer buffer = new StringBuffer(); |
| IProblem[] problems = unit.getProblems(); |
| for (int i = 0, length = problems.length; i < length; i++) |
| Util.appendProblem(buffer, problems[i], newContents |
| .toCharArray(), i + 1); |
| if (buffer.length() > 0) |
| System.err.println(buffer.toString()); |
| } |
| |
| ASTNode[] nodes = findNodes(unit, markerInfo); |
| if (nodes.length == 0) |
| return new ASTNode[] { unit }; |
| return nodes; |
| } |
| |
| protected MarkerInfo[] createMarkerInfos(String[] pathAndSources) { |
| MarkerInfo[] markerInfos = new MarkerInfo[pathAndSources.length / 2]; |
| int index = 0; |
| for (int i = 0, length = pathAndSources.length; i < length; i++) { |
| String path = pathAndSources[i]; |
| String source = pathAndSources[++i]; |
| markerInfos[index++] = new MarkerInfo(path, source); |
| } |
| return markerInfos; |
| } |
| |
| protected IVariableBinding[] createVariableBindings( |
| String[] pathAndSources, String[] bindingKeys) |
| throws JavaScriptModelException { |
| WorkingCopyOwner owner = new WorkingCopyOwner() { |
| }; |
| this.workingCopies = createWorkingCopies(pathAndSources, owner); |
| IBinding[] bindings = resolveBindings(bindingKeys, getJavaProject("P"), |
| owner); |
| int length = bindings.length; |
| IVariableBinding[] result = new IVariableBinding[length]; |
| System.arraycopy(bindings, 0, result, 0, length); |
| return result; |
| } |
| |
| protected IFunctionBinding[] createMethodBindings(String[] pathAndSources, |
| String[] bindingKeys) throws JavaScriptModelException { |
| return createMethodBindings(pathAndSources, bindingKeys, |
| getJavaProject("P")); |
| } |
| |
| protected IFunctionBinding[] createMethodBindings(String[] pathAndSources, |
| String[] bindingKeys, IJavaScriptProject project) |
| throws JavaScriptModelException { |
| WorkingCopyOwner owner = new WorkingCopyOwner() { |
| }; |
| this.workingCopies = createWorkingCopies(pathAndSources, owner); |
| IBinding[] bindings = resolveBindings(bindingKeys, project, owner); |
| int length = bindings.length; |
| IFunctionBinding[] result = new IFunctionBinding[length]; |
| System.arraycopy(bindings, 0, result, 0, length); |
| return result; |
| } |
| |
| protected ITypeBinding[] createTypeBindings(String[] pathAndSources, |
| String[] bindingKeys) throws JavaScriptModelException { |
| return createTypeBindings(pathAndSources, bindingKeys, |
| getJavaProject("P")); |
| } |
| |
| protected ITypeBinding[] createTypeBindings(String[] pathAndSources, |
| String[] bindingKeys, IJavaScriptProject project) |
| throws JavaScriptModelException { |
| WorkingCopyOwner owner = new WorkingCopyOwner() { |
| }; |
| this.workingCopies = createWorkingCopies(pathAndSources, owner); |
| IBinding[] bindings = resolveBindings(bindingKeys, project, owner); |
| int length = bindings.length; |
| ITypeBinding[] result = new ITypeBinding[length]; |
| System.arraycopy(bindings, 0, result, 0, length); |
| return result; |
| } |
| |
| protected IJavaScriptUnit[] createWorkingCopies(String[] pathAndSources, |
| WorkingCopyOwner owner) throws JavaScriptModelException { |
| MarkerInfo[] markerInfos = createMarkerInfos(pathAndSources); |
| return createWorkingCopies(markerInfos, owner); |
| } |
| |
| protected IJavaScriptUnit[] createWorkingCopies(MarkerInfo[] markerInfos, |
| WorkingCopyOwner owner) throws JavaScriptModelException { |
| return createWorkingCopies(markerInfos, owner, null); |
| } |
| |
| protected IJavaScriptUnit[] createWorkingCopies(MarkerInfo[] markerInfos, |
| WorkingCopyOwner owner, IProblemRequestor problemRequestor) |
| throws JavaScriptModelException { |
| int length = markerInfos.length; |
| IJavaScriptUnit[] copies = new IJavaScriptUnit[length]; |
| for (int i = 0; i < length; i++) { |
| MarkerInfo markerInfo = markerInfos[i]; |
| IJavaScriptUnit workingCopy = getCompilationUnit(markerInfo.path) |
| .getWorkingCopy(owner, null); |
| workingCopy.getBuffer().setContents(markerInfo.source); |
| workingCopy.makeConsistent(null); |
| copies[i] = workingCopy; |
| } |
| return copies; |
| } |
| |
| protected ASTNode findNode(JavaScriptUnit unit, final MarkerInfo markerInfo) { |
| ASTNode[] nodes = findNodes(unit, markerInfo); |
| if (nodes.length == 0) |
| return unit; |
| return nodes[0]; |
| } |
| |
| protected ASTNode[] findNodes(JavaScriptUnit unit, |
| final MarkerInfo markerInfo) { |
| class Visitor extends ASTVisitor { |
| ArrayList found = new ArrayList(); |
| |
| public void preVisit(ASTNode node) { |
| if (node instanceof JavaScriptUnit) |
| return; |
| int index = markerInfo.indexOfASTStart(node.getStartPosition()); |
| if (index != -1 |
| && node.getStartPosition() + node.getLength() == markerInfo.astEnds[index]) { |
| this.found.add(node); |
| markerInfo.astStarts[index] = -1; // so that 2 nodes with |
| // the same start and |
| // end will not be found |
| } |
| } |
| } |
| Visitor visitor = new Visitor(); |
| unit.accept(visitor); |
| int size = visitor.found.size(); |
| ASTNode[] result = new ASTNode[size]; |
| visitor.found.toArray(result); |
| return result; |
| } |
| |
| protected void resolveASTs(IJavaScriptUnit[] cus, String[] bindingKeys, |
| ASTRequestor requestor, IJavaScriptProject project, |
| WorkingCopyOwner owner) { |
| ASTParser parser = ASTParser.newParser(AST.JLS3); |
| parser.setResolveBindings(true); |
| parser.setProject(project); |
| parser.setWorkingCopyOwner(owner); |
| parser.createASTs(cus, bindingKeys, requestor, null); |
| } |
| |
| protected IBinding resolveBinding(ASTNode node) { |
| switch (node.getNodeType()) { |
| case ASTNode.PACKAGE_DECLARATION: |
| return ((PackageDeclaration) node).resolveBinding(); |
| case ASTNode.TYPE_DECLARATION: |
| return ((TypeDeclaration) node).resolveBinding(); |
| case ASTNode.ANONYMOUS_CLASS_DECLARATION: |
| return ((AnonymousClassDeclaration) node).resolveBinding(); |
| case ASTNode.TYPE_DECLARATION_STATEMENT: |
| return ((TypeDeclarationStatement) node).resolveBinding(); |
| case ASTNode.FUNCTION_DECLARATION: |
| return ((FunctionDeclaration) node).resolveBinding(); |
| case ASTNode.FUNCTION_INVOCATION: |
| return ((FunctionInvocation) node).resolveMethodBinding(); |
| case ASTNode.SIMPLE_NAME: |
| return ((SimpleName) node).resolveBinding(); |
| case ASTNode.ARRAY_TYPE: |
| return ((ArrayType) node).resolveBinding(); |
| case ASTNode.ASSIGNMENT: |
| return ((Assignment) node).getRightHandSide().resolveTypeBinding(); |
| case ASTNode.SIMPLE_TYPE: |
| return ((SimpleType) node).resolveBinding(); |
| case ASTNode.QUALIFIED_NAME: |
| return ((QualifiedName) node).resolveBinding(); |
| default: |
| throw new Error("Not yet implemented for this type of node: " |
| + node); |
| } |
| } |
| |
| protected IBinding[] resolveBindings(String[] bindingKeys, |
| IJavaScriptProject project, WorkingCopyOwner owner) { |
| BindingRequestor requestor = new BindingRequestor(); |
| resolveASTs(new IJavaScriptUnit[0], bindingKeys, requestor, project, |
| owner); |
| return requestor.getBindings(bindingKeys); |
| } |
| |
| /* |
| * Resolve the bindings of the nodes marked with *start?* and *end?*. |
| */ |
| protected IBinding[] resolveBindings(String contents, IJavaScriptUnit cu) |
| throws JavaScriptModelException { |
| return resolveBindings(contents, cu, true/* report errors */); |
| } |
| |
| /* |
| * Resolve the bindings of the nodes marked with *start?* and *end?*. |
| */ |
| protected IBinding[] resolveBindings(String contents, IJavaScriptUnit cu, |
| boolean reportErrors) throws JavaScriptModelException { |
| ASTNode[] nodes = buildASTs(contents, cu, reportErrors); |
| if (nodes == null) |
| return null; |
| int length = nodes.length; |
| IBinding[] result = new IBinding[length]; |
| for (int i = 0; i < length; i++) { |
| result[i] = resolveBinding(nodes[i]); |
| } |
| return result; |
| } |
| |
| // protected void tearDown() throws Exception { |
| // discardWorkingCopies(this.workingCopies); |
| // this.workingCopies = null; |
| // } |
| |
| } |