/******************************************************************************* * Copyright (c) 2012 VMWare, Inc. * 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: * VMWare, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.search; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.ModuleNode; import org.codehaus.groovy.ast.expr.BinaryExpression; import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.Expression; 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.TupleExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.ast.stmt.ExpressionStatement; import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.groovy.syntax.Token; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.JavaCore; public abstract class URLMappingsSearcher extends FileSearcher { /** * Abstracts away from the detailed syntax of a URL mapping but provides a way to access its controller * and action attributes, regardless of how they are represented (at least two forms exist: * "/foo(controller: 'name') * "/foo { * controller = 'name' * } * It is also possible to mix these notations in one call * "/foo(controller: 'name') * "/foo(controller: 'bell') { * action = 'ring' * } */ public class URLMappingCall { private Collection<Attribute> attributes = new ArrayList<URLMappingsSearcher.Attribute>(); public URLMappingCall(MethodCallExpression call) { Expression args = call.getArguments(); if (args instanceof TupleExpression) { TupleExpression argList = (TupleExpression) args; for (Expression arg : argList.getExpressions()) { if (arg instanceof MapExpression) { MapExpression map = (MapExpression) arg; for (MapEntryExpression namedArg : map.getMapEntryExpressions()) { String name = SearchUtil.getStringValue(namedArg.getKeyExpression()); if (name!=null) { Expression value = namedArg.getValueExpression(); add(new Attribute(name, value)); } } } else if (arg instanceof ClosureExpression) { ClosureExpression closure = (ClosureExpression) arg; List<Statement> stms = SearchUtil.getStatements(closure); for (Statement s : stms) { if (s instanceof ExpressionStatement) { ExpressionStatement es = (ExpressionStatement) s; Expression e = es.getExpression(); if (e instanceof BinaryExpression) { BinaryExpression be = (BinaryExpression) e; Token op = be.getOperation(); if ("=".equals(op.getText())) { Expression _varExp = be.getLeftExpression(); if (_varExp instanceof VariableExpression) { VariableExpression varExp = (VariableExpression) _varExp; Expression valExp = be.getRightExpression(); String name = varExp.getName(); if (name!=null) { add(new Attribute(name, valExp)); } } } } } } } } } } public Collection<Attribute> getAttributes() { return attributes; } private void add(Attribute a) { attributes.add(a); } } public class Attribute { public final String name; public final Expression value; public Attribute(String name, Expression value) { this.name = name; this.value = value; } } public class URLMappingsVisitor extends SearchingCodeVisitor { public URLMappingsVisitor(ICompilationUnit cu) { super(cu); } @Override public void visitMethodCallExpression(MethodCallExpression call) { String methodName = SearchUtil.getStringValue(call.getMethod()); if ("name".equals(methodName)) { visitNamedURLMapping(this, call); } else { visitURLMappingCall(this, new URLMappingCall(call)); } } private void visitNamedURLMapping(URLMappingsVisitor visitor, MethodCallExpression call) { Expression _args = call.getArguments(); if (_args instanceof TupleExpression) { TupleExpression args = (TupleExpression) _args; if (args.getExpressions().size()==1) { Expression arg = args.getExpression(0); if (arg instanceof MapExpression) { MapExpression map = (MapExpression) arg; if (map.getMapEntryExpressions().size()==1) { MapEntryExpression entry = map.getMapEntryExpressions().get(0); //String name = SearchUtil.getStringValue(entry.getKeyExpression()); Expression actualMapping = entry.getValueExpression(); actualMapping.visit(this); } } } } } } public URLMappingsSearcher(IFile file) { super(file); } @Override public void perform() throws IOException, CoreException { IJavaElement jel = JavaCore.create(file); searchIn((GroovyCompilationUnit)jel); } private void searchIn(GroovyCompilationUnit cu) { if (cu!=null) { ModuleNode module = cu.getModuleNode(); if (module!=null) { List<ClassNode> classes = module.getClasses(); for (ClassNode classNode : classes) { if (classNode.getName().equals("UrlMappings")) { searchIn(cu, classNode); } } } } } protected abstract void visitURLMappingCall(SearchingCodeVisitor visitor, URLMappingCall call); private void searchIn(ICompilationUnit cu, ClassNode classNode) { FieldNode mappings = classNode.getField("mappings"); if (mappings!=null && mappings.isStatic()) { searchIn(cu, SearchUtil.getClosureValue(mappings)); } } private void searchIn(ICompilationUnit cu, ClosureExpression closureValue) { SearchingCodeVisitor visitor = new URLMappingsVisitor(cu); visitor.visitClosureExpression(closureValue); } }