Java tutorial
/******************************************************************************* * This file is part of the Twig eclipse plugin. * * (c) Robert Gruendler <r.gruendler@gmail.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. ******************************************************************************/ package com.dubture.twig.core.index; import java.util.ArrayList; import java.util.List; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.declarations.MethodDeclaration; import org.eclipse.dltk.ast.declarations.ModuleDeclaration; import org.eclipse.dltk.ast.declarations.TypeDeclaration; import org.eclipse.dltk.ast.expressions.CallArgumentsList; import org.eclipse.dltk.ast.expressions.Expression; import org.eclipse.dltk.ast.references.SimpleReference; import org.eclipse.dltk.ast.references.VariableReference; import org.eclipse.dltk.ast.statements.Statement; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.index2.IIndexingRequestor.ReferenceInfo; import org.eclipse.php.core.index.PhpIndexingVisitorExtension; import org.eclipse.php.internal.core.compiler.ast.nodes.ArrayCreation; import org.eclipse.php.internal.core.compiler.ast.nodes.ArrayElement; import org.eclipse.php.internal.core.compiler.ast.nodes.ClassDeclaration; import org.eclipse.php.internal.core.compiler.ast.nodes.ClassInstanceCreation; import org.eclipse.php.internal.core.compiler.ast.nodes.ExpressionStatement; import org.eclipse.php.internal.core.compiler.ast.nodes.PHPCallExpression; import org.eclipse.php.internal.core.compiler.ast.nodes.PHPDocBlock; import org.eclipse.php.internal.core.compiler.ast.nodes.PHPMethodDeclaration; import org.eclipse.php.internal.core.compiler.ast.nodes.ReturnStatement; import org.eclipse.php.internal.core.compiler.ast.nodes.Scalar; import org.eclipse.php.internal.core.compiler.ast.visitor.PHPASTVisitor; import org.json.simple.JSONObject; import com.dubture.twig.core.TwigCoreConstants; import com.dubture.twig.core.log.Logger; import com.dubture.twig.core.model.Filter; import com.dubture.twig.core.model.Function; import com.dubture.twig.core.model.ITwigModelElement; import com.dubture.twig.core.model.Tag; import com.dubture.twig.core.model.Test; import com.dubture.twig.core.model.TwigType; import com.dubture.twig.core.util.TwigModelUtils; /** * * {@link TwigIndexingVisitorExtension} indexes: * * - Filters - Functions - TokenParsers (used to detect start/end tags like * if/endif, block/endblock etc * * * @author Robert Gruendler <r.gruendler@gmail.com> * */ @SuppressWarnings("restriction") public class TwigIndexingVisitorExtension extends PhpIndexingVisitorExtension { protected boolean inTwigExtension; protected boolean inTokenParser; protected boolean inTagParseMethod; protected ClassDeclaration currentClass; protected Tag tag; protected List<MethodDeclaration> methods = new ArrayList<MethodDeclaration>(); protected List<Function> functions = new ArrayList<Function>(); protected List<Filter> filters = new ArrayList<Filter>(); protected List<Test> tests = new ArrayList<Test>(); protected TwigIndexingVisitor visitor; public TwigIndexingVisitorExtension() { } @Override public void setSourceModule(ISourceModule module) { super.setSourceModule(module); visitor = new TwigIndexingVisitor(requestor, sourceModule); } @Override @SuppressWarnings("unchecked") public boolean visit(MethodDeclaration s) throws Exception { if (!methods.contains(s)) methods.add(s); if (s instanceof PHPMethodDeclaration) { PHPMethodDeclaration phpMethod = (PHPMethodDeclaration) s; if (inTwigExtension && phpMethod.getName().equals(TwigCoreConstants.GET_FILTERS)) { phpMethod.traverse(new PHPASTVisitor() { @Override public boolean visit(ArrayElement s) throws Exception { Expression key = s.getKey(); Expression value = s.getValue(); if (key == null | value == null) { return false; } if (key.getClass() == Scalar.class && value.getClass() == ClassInstanceCreation.class) { Scalar name = (Scalar) key; ClassInstanceCreation filterClass = (ClassInstanceCreation) value; CallArgumentsList ctorParams = filterClass.getCtorParams(); Object child = ctorParams.getChilds().get(0); if (child instanceof VariableReference && ((VariableReference) child).getName().equals("$this") && filterClass.getClassName().toString() .equals((TwigCoreConstants.TWIG_FILTER_METHOD))) { if (ctorParams.getChilds().size() > 2 && ctorParams.getChilds().get(1) instanceof Scalar) { Scalar internal = (Scalar) ctorParams.getChilds().get(1); String elemName = name.getValue().replaceAll("['\"]", ""); Filter filter = new Filter(elemName); filter.setInternalFunction(internal.getValue().replaceAll("['\"]", "")); filter.setPhpClass(currentClass.getName()); filters.add(filter); } } if (!(child instanceof Scalar)) { return true; } Scalar internal = (Scalar) child; if (filterClass.getClassName().toString() .equals(TwigCoreConstants.TWIG_FILTER_FUNCTION)) { String elemName = name.getValue().replaceAll("['\"]", ""); Filter filter = new Filter(elemName); filter.setInternalFunction(internal.getValue().replaceAll("['\"]", "")); filter.setPhpClass(currentClass.getName()); filters.add(filter); } } return true; } }); } else if (inTwigExtension && TwigCoreConstants.GET_TESTS.equals(s.getName())) { phpMethod.traverse(new PHPASTVisitor() { @Override public boolean visit(ArrayElement s) throws Exception { Expression key = s.getKey(); Expression value = s.getValue(); if (key == null || value == null) return false; if (key.getClass() == Scalar.class && value.getClass() == ClassInstanceCreation.class) { Scalar name = (Scalar) key; ClassInstanceCreation functionClass = (ClassInstanceCreation) value; CallArgumentsList args = functionClass.getCtorParams(); Scalar internalFunction = (Scalar) args.getChilds().get(0); if (internalFunction == null) return true; if (functionClass.getClassName().toString() .equals(TwigCoreConstants.TWIG_TEST_FUNCTION)) { String elemName = name.getValue().replaceAll("['\"]", ""); JSONObject metadata = new JSONObject(); metadata.put(TwigType.PHPCLASS, currentClass.getName()); Test test = new Test(elemName); test.setPhpClass(currentClass.getName()); test.setInternalFunction(internalFunction.getValue().replaceAll("['\"]", "")); tests.add(test); } } return true; } }); } else if (inTwigExtension && TwigCoreConstants.GET_FUNCTIONS.equals(s.getName())) { phpMethod.traverse(new PHPASTVisitor() { @Override public boolean visit(ArrayElement s) throws Exception { Expression key = s.getKey(); Expression value = s.getValue(); if (key == null || value == null) { return false; } if (key.getClass() == Scalar.class && value.getClass() == ClassInstanceCreation.class) { Scalar name = (Scalar) key; ClassInstanceCreation functionClass = (ClassInstanceCreation) value; CallArgumentsList args = functionClass.getCtorParams(); String functionClassName = functionClass.getClassName().toString(); int index = -1; if (functionClassName.equals(TwigCoreConstants.TWIG_FUNCTION_FUNCTION)) { index = 0; } else if (functionClassName.equals(TwigCoreConstants.TWIG_FUNCTION_METHOD)) { index = 1; } if (index > -1 && args.getChilds().get(index) instanceof Scalar) { Scalar internalFunction = (Scalar) args.getChilds().get(index); if (internalFunction == null) { return true; } String elemName = name.getValue().replaceAll("['\"]", ""); JSONObject metadata = new JSONObject(); metadata.put(TwigType.PHPCLASS, currentClass.getName()); Function function = new Function(elemName); function.setPhpClass(currentClass.getName()); function.setInternalFunction(internalFunction.getValue().replaceAll("['\"]", "")); functions.add(function); } } return true; } }); } else if (inTokenParser && TwigCoreConstants.PARSE_TOKEN_METHOD.equals(s.getName())) { inTagParseMethod = true; } else if (inTokenParser && TwigCoreConstants.PARSE_GET_TAG_METHOD.equals(s.getName())) { phpMethod.traverse(new PHPASTVisitor() { @Override public boolean visit(ReturnStatement s) throws Exception { if (s.getExpr().getClass() == Scalar.class) { Scalar scalar = (Scalar) s.getExpr(); tag.setStartTag(scalar.getValue().replaceAll("['\"]", "")); } return false; } }); } } return false; } @Override public boolean endvisit(MethodDeclaration s) throws Exception { inTagParseMethod = false; return true; } @Override public boolean visit(TypeDeclaration s) throws Exception { if (s instanceof ClassDeclaration) { inTwigExtension = false; currentClass = (ClassDeclaration) s; for (String superclass : currentClass.getSuperClassNames()) { if (superclass.equals(TwigCoreConstants.TWIG_EXTENSION)) { inTwigExtension = true; } else if (superclass.equals(TwigCoreConstants.TWIG_TOKEN_PARSER)) { tag = new Tag(); inTokenParser = true; } } return true; } return false; } @SuppressWarnings("unchecked") @Override public boolean endvisit(TypeDeclaration s) throws Exception { if (s instanceof ClassDeclaration) { if (tag != null) { if (tag.getStartTag() != null) { int length = currentClass.sourceEnd() - currentClass.sourceStart(); PHPDocBlock block = currentClass.getPHPDoc(); String desc = ""; if (block != null) { String shortDesc = block.getShortDescription() != null ? block.getShortDescription() : ""; String longDesc = block.getLongDescription() != null ? block.getLongDescription() : ""; desc = shortDesc + longDesc; } String endTag = tag.getEndTag(); JSONObject metadata = new JSONObject(); metadata.put(TwigType.PHPCLASS, currentClass.getName()); metadata.put(TwigType.DOC, desc); metadata.put(TwigType.IS_OPEN_CLOSE, endTag != null); Logger.debugMSG("indexing twig tag: " + tag.getStartTag() + " : " + tag.getEndTag() + " with metadata: " + metadata.toString()); ReferenceInfo info = new ReferenceInfo(ITwigModelElement.START_TAG, currentClass.sourceStart(), length, tag.getStartTag(), metadata.toString(), null); addReferenceInfo(info); if (endTag != null) { ReferenceInfo endIinfo = new ReferenceInfo(ITwigModelElement.END_TAG, currentClass.sourceStart(), length, tag.getEndTag(), metadata.toString(), null); addReferenceInfo(endIinfo); } } tag = null; } inTwigExtension = false; inTokenParser = false; currentClass = null; } return false; } @Override public boolean visit(Statement s) throws Exception { if (!inTagParseMethod) return false; s.traverse(new PHPASTVisitor() { @Override public boolean visit(PHPCallExpression callExpr) throws Exception { SimpleReference ref = callExpr.getCallName(); if (ref != null && TwigCoreConstants.PARSE_SUB.equals(ref.getName())) { callExpr.traverse(new PHPASTVisitor() { @Override public boolean visit(ArrayCreation array) throws Exception { for (ArrayElement elem : array.getElements()) { Expression value = elem.getValue(); if (value == null) continue; if (value.getClass() == Scalar.class) { Scalar scalar = (Scalar) value; String subParseMethod = scalar.getValue().replaceAll("['\"]", ""); for (MethodDeclaration method : currentClass.getMethods()) { if (subParseMethod.equals(method.getName())) { String[] endStatements = TwigModelUtils .getEndStatements((PHPMethodDeclaration) method); for (String stmt : endStatements) { if (stmt.startsWith("end")) { tag.setEndTag(stmt); return false; } } } } } } return true; } }); } return true; } }); return true; } @Override public boolean endvisit(Statement s) throws Exception { if (s instanceof ExpressionStatement) { ExpressionStatement stmt = (ExpressionStatement) s; if (stmt.getExpr() instanceof PHPCallExpression) { return true; } } return false; } @Override public boolean endvisit(ModuleDeclaration s) throws Exception { for (Test test : tests) { for (MethodDeclaration method : methods) { if (method.getName().equals(test.getInternalFunction())) { PHPMethodDeclaration phpMethod = (PHPMethodDeclaration) method; PHPDocBlock doc = phpMethod.getPHPDoc(); if (doc != null) { test.addDoc(doc); } Logger.debugMSG("indexing test tag: " + test.getElementName() + " with metadata: " + test.getMetadata()); ReferenceInfo info = new ReferenceInfo(ITwigModelElement.TEST, 0, 0, test.getElementName(), test.getMetadata(), null); addReferenceInfo(info); } } } for (Function function : functions) { for (MethodDeclaration method : methods) { if (method.getName().equals(function.getInternalFunction())) { PHPMethodDeclaration phpMethod = (PHPMethodDeclaration) method; PHPDocBlock doc = phpMethod.getPHPDoc(); if (doc != null) { function.addDoc(doc); } function.addArgs(method.getArguments()); Logger.debugMSG("indexing function: " + function.getElementName() + " with metadata: " + function.getMetadata()); ReferenceInfo info = new ReferenceInfo(ITwigModelElement.FUNCTION, 0, 0, function.getElementName(), function.getMetadata(), null); addReferenceInfo(info); } } } for (Filter filter : filters) { for (MethodDeclaration method : methods) { if (method.getName().equals(filter.getInternalFunction())) { PHPMethodDeclaration phpMethod = (PHPMethodDeclaration) method; PHPDocBlock doc = phpMethod.getPHPDoc(); if (doc != null) { filter.addDoc(doc); } filter.addArgs(method.getArguments()); Logger.debugMSG("indexing filter: " + filter.getElementName() + " with metadata: " + filter.getMetadata()); ReferenceInfo info = new ReferenceInfo(ITwigModelElement.FILTER, 0, 0, filter.getElementName(), filter.getMetadata(), null); addReferenceInfo(info); } } } return true; } protected void addReferenceInfo(ReferenceInfo info) { try { requestor.addReference(info); } catch (Exception e) { Logger.logException(e); } } @Override public boolean visitGeneral(ASTNode node) throws Exception { if (node instanceof org.eclipse.dltk.ast.statements.Block) { node.traverse(new TwigIndexingVisitor(requestor, sourceModule)); } return super.visitGeneral(node); } }