Package org.jboss.forge.addon.gradle.parser

Source Code of org.jboss.forge.addon.gradle.parser.SimpleGroovyParser$PreInvocationWithClosure

/*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jboss.forge.addon.gradle.parser;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.GStringExpression;
import org.codehaus.groovy.ast.expr.MapEntryExpression;
import org.codehaus.groovy.ast.expr.MapExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.control.SourceUnit;
import org.gradle.jarjar.com.google.common.base.Optional;
import org.gradle.jarjar.com.google.common.base.Preconditions;
import org.gradle.jarjar.com.google.common.collect.Lists;
import org.gradle.jarjar.com.google.common.collect.Maps;

/**
* This is a minimal groovy parser necessary to obtain information about gradle project. It can create method invocation
* tree from given source, as gradle build configuration is invocation oriented.
*
* @author Adam WyƂuda
*/
public class SimpleGroovyParser
{
   private static class PreInvocationWithClosure
   {
      public int lineNumber = 1;
      public int columnNumber = 1;
      public int lastLineNumber = 1;
      public int lastColumnNumber = 1;

      public String methodName;
      public String stringParameter;
      public Map<String, String> mapParameter;

      public List<InvocationWithClosure> invocationWithClosureList = Lists.newArrayList();
      public List<InvocationWithMap> invocationWithMapList = Lists.newArrayList();
      public List<InvocationWithString> invocationWithStringList = Lists.newArrayList();
      public List<VariableAssignment> variableAssignmentList = Lists.newArrayList();

      public InvocationWithClosure create(String source)
      {
         String code = source.substring(SourceUtil.positionInSource(source, lineNumber, columnNumber),
                  SourceUtil.positionInSource(source, lastLineNumber, lastColumnNumber));
         return new InvocationWithClosure(code, methodName, stringParameter, mapParameter,
                  invocationWithClosureList, invocationWithStringList,
                  invocationWithMapList, variableAssignmentList,
                  lineNumber, columnNumber, lastLineNumber, lastColumnNumber);
      }
   }

   private final String source;
   private final InvocationWithClosure root;

   private SimpleGroovyParser(String source)
   {
      this.source = source;
      root = createInvocationWithClosureRoot(source);
   }

   public static SimpleGroovyParser fromSource(String source)
   {
      return new SimpleGroovyParser(source);
   }

   public List<InvocationWithClosure> getInvocationsWithClosure()
   {
      return root.getInvocationsWithClosure();
   }

   public List<InvocationWithMap> getInvocationsWithMap()
   {
      return root.getInvocationsWithMap();
   }

   public List<InvocationWithString> getInvocationsWithString()
   {
      return root.getInvocationsWithString();
   }

   public List<VariableAssignment> getVariableAssignments()
   {
      return root.getVariableAssignments();
   }

   public Optional<InvocationWithClosure> invocationWithClosureByName(String name)
   {
      return root.invocationWithClosureByName(name);
   }

   public Optional<InvocationWithString> invocationWithStringByName(String name)
   {
      return root.invocationWithStringByName(name);
   }

   public Optional<InvocationWithMap> invocationWithMapByName(String name)
   {
      return root.invocationWithMapByName(name);
   }

   public Optional<VariableAssignment> variableAssignmentByName(String name)
   {
      return root.variableAssignmentByName(name);
   }

   public List<InvocationWithClosure> allInvocationsAtPath(String... path)
   {
      Preconditions.checkArgument(path.length > 0, "Path must have at least one element");
      List<InvocationWithClosure> list = Lists.newArrayList();
      allInvocationsAtPath(list, root, path);
      return list;
   }

   private void allInvocationsAtPath(List<InvocationWithClosure> list, InvocationWithClosure invocation, String... path)
   {
      if (path.length == 0)
      {
         list.add(invocation);
         return;
      }
      String name = path[0];
      for (InvocationWithClosure subinvocation : invocation.getInvocationsWithClosure())
      {
         if (subinvocation.getMethodName().equals(name))
         {
            allInvocationsAtPath(list, subinvocation, Arrays.copyOfRange(path, 1, path.length));
         }
      }
   }

   private InvocationWithClosure createInvocationWithClosureRoot(String source)
   {
      BlockStatement sourceBlockStatement = parseSource(source);
      PreInvocationWithClosure root = new PreInvocationWithClosure();
      fillInvocationFromStatement(sourceBlockStatement, root);
      return root.create(source);
   }

   private BlockStatement parseSource(String source)
   {
      SourceUnit sourceUnit = SourceUnit.create("script", source);
      sourceUnit.parse();
      sourceUnit.nextPhase();
      sourceUnit.convert();
      ModuleNode moduleNode = sourceUnit.getAST();
      return moduleNode.getStatementBlock();
   }

   /**
    * Goes through blockStatement recursively to create InvocationWithClosure tree.
    */
   private void fillInvocationFromStatement(BlockStatement blockStatement, PreInvocationWithClosure node)
   {
      for (Statement statement : blockStatement.getStatements())
      {
         processStatement(statement, node);
      }
   }

   private void processStatement(Statement statement, PreInvocationWithClosure node)
   {
      // If statement is an expression like function call
      if (statement instanceof ExpressionStatement)
      {
         Expression expression = ((ExpressionStatement) statement).getExpression();

         // If expression is method call
         if (expression instanceof MethodCallExpression)
         {
            processMethodCallExpression(expression, node);
         }

         // If expression is binary expression, which might be variable assignment
         if (expression instanceof BinaryExpression)
         {
            processBinaryExpression((BinaryExpression) expression, node);
         }
      }
   }

   private void processMethodCallExpression(Expression expression, PreInvocationWithClosure node)
   {
      String methodName = ((MethodCallExpression) expression).getMethodAsString();

      Expression argumentsExpression = ((MethodCallExpression) expression).getArguments();

      // In case argument expression is a (G)String constant or closure
      if (argumentsExpression instanceof ArgumentListExpression)
      {
         processArgumentListExpression(expression, (ArgumentListExpression) argumentsExpression, node,
                  methodName);
      }

      // If argument expression is a TupleExpression then it may be a map
      else if (argumentsExpression instanceof TupleExpression &&
               ((TupleExpression) argumentsExpression).getExpressions().size() == 1)
      {
         processTupleExpression(expression, (TupleExpression) argumentsExpression, node, methodName);
      }
   }

   private void processArgumentListExpression(Expression expression,
            ArgumentListExpression argumentsExpression,
            PreInvocationWithClosure node, String methodName)
   {
      // If it's single argument call
      if (argumentsExpression.getExpressions().size() == 1)
      {
         Expression argumentExpression = argumentsExpression.getExpressions().get(0);

         // If argument is a string constant
         if (isStringOrGString(argumentExpression))
         {
            String string = valueFromStringOrGString(argumentExpression);

            String code = source
                     .substring(
                              SourceUtil.positionInSource(source, expression.getLineNumber(), expression.getColumnNumber()),
                              SourceUtil.positionInSource(source, expression.getLastLineNumber(),
                                       expression.getLastColumnNumber()));
            InvocationWithString invocation = new InvocationWithString(code, methodName, string,
                     expression.getLineNumber(), expression.getColumnNumber(),
                     expression.getLastLineNumber(), expression.getLastColumnNumber());
            node.invocationWithStringList.add(invocation);
         }

         // If argument is a closure
         else if (argumentExpression instanceof ClosureExpression)
         {
            processClosureExpression(expression, (ClosureExpression) argumentExpression, node, methodName,
                     "", Maps.<String, String> newHashMap());
         }
      }
      // If it's two argument call
      else if (argumentsExpression.getExpressions().size() == 2)
      {
         Expression firstArgumentExpression = argumentsExpression.getExpressions().get(0);
         Expression secondArgumentExpression = argumentsExpression.getExpressions().get(1);
        
         String stringParameter = "";
         Map<String, String> mapParameter = Maps.newHashMap();
        
         if (isStringOrGString(firstArgumentExpression))
         {
            stringParameter = valueFromStringOrGString(firstArgumentExpression);
         }
         else if (firstArgumentExpression instanceof MapExpression)
         {
            mapParameter = mapFromMapEntryExpressions(((MapExpression) firstArgumentExpression).getMapEntryExpressions());
         }
        
         if (secondArgumentExpression instanceof ClosureExpression)
         {
            processClosureExpression(expression, (ClosureExpression) secondArgumentExpression, node, methodName,
                     stringParameter, mapParameter);
         }
      }
   }

   private void processClosureExpression(Expression expression,
            ClosureExpression closureExpression,
            PreInvocationWithClosure node,
            String methodName, String stringParameter, Map<String, String> mapParameter)
   {
      BlockStatement blockStatement = (BlockStatement) (closureExpression).getCode();

      PreInvocationWithClosure invocation = new PreInvocationWithClosure();
      invocation.methodName = methodName;
      invocation.stringParameter = stringParameter;
      invocation.mapParameter = mapParameter;
     
      invocation.lineNumber = expression.getLineNumber();
      invocation.columnNumber = expression.getColumnNumber();
      invocation.lastLineNumber = expression.getLastLineNumber();
      invocation.lastColumnNumber = expression.getLastColumnNumber();

      fillInvocationFromStatement(blockStatement, invocation);
      node.invocationWithClosureList.add(invocation.create(source));
   }

   private void processTupleExpression(Expression expression,
            TupleExpression argumentsExpression, PreInvocationWithClosure node,
            String methodName)
   {
      Expression argumentExpression = ((TupleExpression) argumentsExpression).getExpressions().get(0);

      // In case argument expression is a map
      if (argumentExpression instanceof NamedArgumentListExpression)
      {
         processNamedArgumentListExpression(expression, (NamedArgumentListExpression) argumentExpression, node,
                  methodName);
      }
   }

   private void processNamedArgumentListExpression(Expression expression,
            NamedArgumentListExpression argumentListExpression,
            PreInvocationWithClosure node, String methodName)
   {
      Map<String, String> parameters = mapFromMapEntryExpressions(argumentListExpression.getMapEntryExpressions());
      String code = source.substring(
               SourceUtil.positionInSource(source, expression.getLineNumber(), expression.getColumnNumber()),
               SourceUtil.positionInSource(source, expression.getLastLineNumber(), expression.getLastColumnNumber()));
      InvocationWithMap invocation = new InvocationWithMap(code, methodName, parameters,
               expression.getLineNumber(), expression.getColumnNumber(),
               expression.getLineNumber(), expression.getLastColumnNumber());
      node.invocationWithMapList.add(invocation);
   }

   private void processBinaryExpression(BinaryExpression expression, PreInvocationWithClosure node)
   {
      // This condition must be true to be string variable assignment
      // but not new variable declaration
      if (!(expression instanceof DeclarationExpression) &&
               (expression.getLeftExpression() instanceof VariableExpression ||
               expression.getLeftExpression() instanceof PropertyExpression) &&
               expression.getOperation().getText().toString().equals("=") &&
               isStringOrGString(expression.getRightExpression()))
      {
         String variable = expression.getLeftExpression().getText();
         String value = valueFromStringOrGString(expression.getRightExpression());

         int lineNumber = expression.getLineNumber();
         int columnNumber = expression.getColumnNumber();
         int lastLineNumber = expression.getLastLineNumber();
         int lastColumnNumber = expression.getLastColumnNumber();

         String code = source.substring(SourceUtil.positionInSource(source, lineNumber, columnNumber),
                  SourceUtil.positionInSource(source, lastLineNumber, lastColumnNumber));
         VariableAssignment variableAssignment =
                  new VariableAssignment(code, variable, value,
                           lineNumber, columnNumber, lastLineNumber, lastColumnNumber);
         node.variableAssignmentList.add(variableAssignment);
      }
   }

   private Map<String, String> mapFromMapEntryExpressions(List<MapEntryExpression> mapEntries)
   {
      Map<String, String> map = new HashMap<String, String>();
      for (MapEntryExpression mapEntryExpression : mapEntries)
      {
         Expression keyExpression = mapEntryExpression.getKeyExpression();
         Expression valueExpression = mapEntryExpression.getValueExpression();
         if (keyExpression instanceof ConstantExpression &&
                  isStringOrGString(valueExpression))
         {
            String key = ((ConstantExpression) keyExpression).getValue().toString();
            String value = valueFromStringOrGString(valueExpression);
            map.put(key, value);
         }
      }
      return map;
   }

   private String valueFromStringOrGString(Expression expression)
   {
      if (isString(expression))
      {
         return (String) ((ConstantExpression) expression).getValue();
      }
      else if (isGString(expression))
      {
         return (String) ((GStringExpression) expression).getText();
      }
      throw new IllegalArgumentException("Given expression is neither String nor GString expression");
   }

   private boolean isStringOrGString(Expression expression)
   {
      return isString(expression) || isGString(expression);
   }

   private boolean isString(Expression expression)
   {
      return expression instanceof ConstantExpression &&
               ((ConstantExpression) expression).getValue() instanceof String;
   }

   private boolean isGString(Expression expression)
   {
      return expression instanceof GStringExpression;
   }
}
TOP

Related Classes of org.jboss.forge.addon.gradle.parser.SimpleGroovyParser$PreInvocationWithClosure

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.