All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.jaxx.compiler.binding.JavaParserUtil Maven / Gradle / Ivy

There is a newer version: 3.1.5
Show newest version
/*
 * #%L
 * JAXX :: Compiler
 * %%
 * Copyright (C) 2008 - 2020 Code Lutin, Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
package org.nuiton.jaxx.compiler.binding;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.nuiton.jaxx.compiler.java.parser.JavaParserTreeConstants;
import org.nuiton.jaxx.compiler.java.parser.SimpleNode;

import java.beans.Introspector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created: 4 déc. 2009
 *
 * @author Tony Chemit - [email protected]
 * @version $Revision$
 *
 *          Mise a jour: $Date$ par :
 *          $Author$
 */
public class JavaParserUtil {

    /**
     * Logger
     */
    private static final Logger log = LogManager.getLogger(JavaParserUtil.class);

    private static final Comparator STRING_LENGTH_COMPARATOR = Comparator.comparingInt(String::length);

    /**
     * Obtain all expressions of a node and store them in {@code store} with their dependencies expressions.
     *
     * Also fill the {@code literals} list of literal expressions.
     *
     * @param node     the node to scan
     * @param store    the store of expressions detected with all the expression which compose the expression (can be empty)
     * @param literals the list of literal expressions detected
     * @param casts    the list of casted expression detected
     */
    public static void getExpressions(SimpleNode node, Map> store, List literals, Map> casts) {

        if (node.getId() == JavaParserTreeConstants.JJTMETHODDECLARATION ||
                node.getId() == JavaParserTreeConstants.JJTFIELDDECLARATION) {
            //TODO add all others non intressing type of node to reject directly
            return;
        }

        if (node.getId() == JavaParserTreeConstants.JJTPRIMARYEXPRESSION) {
            // get a primary expression, look for his dependencies
            scanForExpressions(node, null, store, literals, casts);
            return;
        }

        // recurse of childs of node
        for (int i = 0, count = node.jjtGetNumChildren(); i < count; i++) {
            getExpressions(node.getChild(i), store, literals, casts);
        }

    }

    /**
     * Remove from expressions store, all literal expressions and dependencies on it.
     *
     * @param store              the store of expressions with theirs dependencies
     * @param literalExpressions the unvierse of literal expressions
     */
    public static void removeLiteralExpressions(Map> store, List literalExpressions) {
        for (SimpleNode n : literalExpressions) {

            // on supprime toutes les dependences sur les expression literales
            // car on en a pas besoin pour decouvrir les expressions qui peuvent etre nulles...

            if (log.isDebugEnabled()) {
                log.debug("Reject literal expression " + n.getText());
            }
            for (List dependencies : store.values()) {
                dependencies.remove(n);
            }
            store.remove(n);
        }
    }

    /**
     * Remove from expressions sotre, all expressions with dependencies.
     *
     * @param store            the store of expressions with their dependencies
     * @param castsExpressions list of cast expression to keep
     */
    public static void removeNoneStandaloneExpressions(Map> store, Map> castsExpressions) {
        List rejectedExpressions = new ArrayList<>();

        for (Map.Entry> e : store.entrySet()) {

            List dependencies = e.getValue();
            SimpleNode node = e.getKey();
            if (castsExpressions.containsKey(node)) {
                // the expression is part of a cast, need to keep it
                continue;
            }
            if (!dependencies.isEmpty()) {

                // expression with dependencies, don't treate it, but treate all in dependencies :)
                rejectedExpressions.add(node);
                if (log.isDebugEnabled()) {
                    log.debug("Reject expression " + node.getText() + " with " + dependencies.size() + " dependencies");
                    for (SimpleNode n : dependencies) {
                        log.debug("  " + n.getText());
                    }
                }
            }
        }

        for (SimpleNode node : rejectedExpressions) {
            store.remove(node);
        }

        rejectedExpressions.clear();
    }


    public static Set getRequired(Set store, Map> casts) {
        if (store.isEmpty()) {
            return null;
        }

        Set castCodes = new LinkedHashSet<>();
        for (List cast : casts.values()) {
            for (SimpleNode node : cast) {
                castCodes.add(node);
                if (log.isDebugEnabled()) {
                    log.debug("cast = " + node.getText().trim());
                }
            }
        }

        List result = new ArrayList<>();
        for (SimpleNode node : store) {
            String expression = node.getText().trim();
            if (result.contains(expression)) {
                // already treated
                continue;
            }
            for (SimpleNode castCode : castCodes) {
                String str = castCode.getText().trim();
                int index = expression.indexOf(str);
                if (index > -1) {
                    // got a cast, replace the cast expression, by the simple expression
                    // we have (CAST)XXX --> XXX
                    if (log.isDebugEnabled()) {
                        log.debug("got a cast in expresion " + expression + " = " + castCode);
                    }
                    String tmp = "";
                    //FIXME : should check this is a complete cast : could be only a conversion...
                    if (index > 1) {
                        tmp = expression.substring(0, index - 1);
                    }
                    tmp += ((SimpleNode) castCode.jjtGetChild(1)).getText().trim() + expression.substring(index + str.length() + 1);
                    if (log.isDebugEnabled()) {
                        log.debug("REMOVED CAST : " + tmp);
                    }
                    expression = tmp;
                }
            }
            if (!expression.contains(".")) {
                // not an expression to keep
                // a simple field use like 'isEnabled()' or 'field'
                // or a not method invocation
                if (log.isDebugEnabled()) {
                    log.debug("Reject simple expression " + expression);
                }
                continue;
            }
            if (!expression.contains("(")) {
                // expression with no called method, probably is a constant
                // should test it, but for the moment just limits bindings to interfield expressions : a.b
                // is not possible, use a.getB() instead of
                if (log.isDebugEnabled()) {
                    log.debug("Reject constant or static expression " + expression);
                }
                continue;
            }

            if (log.isDebugEnabled()) {
                log.debug("Keep expression " + expression);
            }
            result.add(expression);
        }

        if (result.isEmpty()) {
            return null;
        }

        result.sort(STRING_LENGTH_COMPARATOR);
        if (log.isDebugEnabled()) {
            log.debug("======= start with values : " + result);
        }


        Set objectCodes = new LinkedHashSet<>();

        for (String expression : result) {

            // test if we have a cast in this expression

            Set tmp = new LinkedHashSet<>();

            String[] paths = expression.split("\\s*\\.\\s*");
            if (paths.length < 2) {
                // just a simple expression
                // TODO Should never come here...
                continue;
            }

            if (log.isDebugEnabled()) {
                log.debug("Expression to treate : " + expression + " :: " + Arrays.toString(paths));
            }

            StringBuilder buffer = new StringBuilder();
            String last = paths[0].trim();
            if (last.contains("(")) {
                // first path is a method invocation or a cast
                // at the moment allow cast only on the first member and do no perform any check

                // must check this is a complete method invocation
                String args = getMethodInvocationParameters(last);
                if (args == null) {
                    // this path is not a method invocation
                    // must break
                    continue;
                }
                if (!args.isEmpty()) {
                    // for the moment, we only accept method with no args
                    // must break
                    continue;
                }

            }
            buffer.append(last);
            tmp.add(buffer.toString());
            for (int i = 1, max = paths.length - 1; i < max; i++) {
                String s = paths[i].trim();
                String args = getMethodInvocationParameters(s);
                if (args == null) {
                    // this path is not a method invocation
                    // must break
                    // if previous
                    break;
                }
                if (!args.isEmpty()) {
                    // for the moment, we only accept method with no args
                    // must break
                    break;
                }
                buffer.append(".").append(s);
                last = buffer.toString();
                tmp.add(last);
            }
            objectCodes.addAll(tmp);
        }

        if (log.isDebugEnabled()) {
            log.debug("Detected requirements : " + objectCodes);
        }
        return objectCodes;
    }

    public static String getMethodInvocationParameters(String code) {
        int openIndex = code.indexOf("(");
        int closeIndex = code.lastIndexOf(")");
        if (openIndex > -1 && closeIndex > -1) {
            if (closeIndex == openIndex + 1) {
                return "";
            }
            // missing something
            return code.substring(openIndex + 1, closeIndex - 1).trim();
        }
        return null;
    }

    public static String getPropertyNameFromMethod(String code) {
        int openIndex = code.indexOf("(");
        if (openIndex != -1) {
            code = code.substring(0, openIndex);
        }
        int index = 3;
        if (code.startsWith("is")) {
            index = 2;
        }
        code = code.substring(index);
        code = Introspector.decapitalize(code);
        return code;
    }

    public static void scanForExpressions(SimpleNode node, SimpleNode lastExpressionNode, Map> store, List literals, Map> casts) {

        String nodeExpression = node.getText().trim();
        if (log.isTraceEnabled()) {
            log.trace("node " + node.getId() + " nbChilds : " + node.jjtGetNumChildren() + " : " + nodeExpression);
        }
        if (node.getId() == JavaParserTreeConstants.JJTLITERAL) {
            // expression literal qu'on ne veut pas garder ?
            if (log.isDebugEnabled()) {
                log.debug("detected literal " + nodeExpression + " for last expression " + lastExpressionNode.getText());
            }
            literals.add(lastExpressionNode);
            return;
        }
        if (node.getId() == JavaParserTreeConstants.JJTCASTEXPRESSION) {
            // expression literal qu'on ne veut pas garder ?
            if (log.isDebugEnabled()) {
                log.debug("detected cast " + nodeExpression + " for last expression " + lastExpressionNode.getText());
            }
            List simpleNodeList = casts.computeIfAbsent(lastExpressionNode, k -> new ArrayList<>());
            simpleNodeList.add(node);
        }

        if (node.getId() == JavaParserTreeConstants.JJTPRIMARYEXPRESSION) {

            store.computeIfAbsent(node, k -> new ArrayList<>());
            if (lastExpressionNode == null) {

                // premiere entree dans la methode (detection d'une nouvelle expression)
                // rien a faire


            } else {

                // on vient d'un appel recursif, on ajoute le noeud courant a la liste des expression de l'expression parent

                List simpleNodeList = store.get(lastExpressionNode);
                if (simpleNodeList == null) {
                    simpleNodeList = new ArrayList<>();
                    store.put(node, simpleNodeList);
                }
                simpleNodeList.add(node);
            }

            // on change la derniere expression rencontree
            lastExpressionNode = node;
        }

        // on parcours tous les fils du noeud courant
        for (int i = 0, count = node.jjtGetNumChildren(); i < count; i++) {
            scanForExpressions(node.getChild(i), lastExpressionNode, store, literals, casts);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy