com.dubture.twig.core.index.TwigIndexingVisitorExtension.java Source code

Java tutorial

Introduction

Here is the source code for com.dubture.twig.core.index.TwigIndexingVisitorExtension.java

Source

/*******************************************************************************
 * 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);
    }
}