/******************************************************************************* * Copyright (c) 2012 Pivotal Software, 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: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.editor.groovy.types; import java.util.HashMap; import java.util.Map; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.eclipse.jdt.groovy.search.AbstractSimplifiedTypeLookup; import org.eclipse.jdt.groovy.search.ITypeLookup; import org.eclipse.jdt.groovy.search.VariableScope; import org.grails.ide.eclipse.core.internal.plugins.GrailsElementKind; import org.grails.ide.eclipse.editor.groovy.elements.ControllerClass; import org.grails.ide.eclipse.editor.groovy.elements.DomainClass; import org.grails.ide.eclipse.editor.groovy.elements.GrailsProject; import org.grails.ide.eclipse.editor.groovy.elements.GrailsWorkspaceCore; import org.grails.ide.eclipse.editor.groovy.elements.IGrailsElement; /** * A type lookup for Grails elements * @author Andrew Eisenberg * @author Christian Dupuis * @author Nieraj Singh */ public class GrailsTypeLookup extends AbstractSimplifiedTypeLookup implements ITypeLookup { private GrailsProject gp; private Map<String, IGrailsElement> elementCache; /** * FIXADE this should be pulled out into an {@link IGrailsElement} * We need to create a TestClass grails element. * * this is the type of the model field that is used in * unit tests. Every time a controller action is invoked * the model field's type is set to the type of whatever * controller action has been invoked. Otherwise, model * is grabbed from the DSLD */ private TypeAndDeclaration modelTypeAndDeclaration = null; private GrailsElementKind kind; @Override protected TypeAndDeclaration lookupTypeAndDeclaration( ClassNode declaringType, String name, VariableScope scope) { if (this.kind.isConfigElement() && declaringType.getName().equals(VariableScope.OBJECT_CLASS_NODE.getName())) { // STS-2330 avoid underlines when inside of nested closures in generic config elements. return new TypeAndDeclaration(VariableScope.OBJECT_CLASS_NODE, VariableScope.OBJECT_CLASS_NODE); } // avoid inferencing controller names in strings, // but do inference names in strings for config elements if (isQuotedString() && !this.kind.isConfigElement()) { return null; } if (declaringType instanceof DomainClass.NamedQueryClassNode) { return ((DomainClass.NamedQueryClassNode) declaringType).lookupTypeAndDeclaration(declaringType, name, scope); } else if (name.equals("model")) { // special case for the model field in test cases. // no need to separate this out yet, but consider doing so if there are more special cases return modelTypeAndDeclaration; } String declaringTypeName = declaringType.getName(); IGrailsElement declaringElt = elementCache.get(declaringTypeName); if (declaringElt == null) { declaringElt = gp.getGrailsElement(declaringType); elementCache.put(declaringTypeName, declaringElt); } TypeAndDeclaration tAndD = declaringElt.lookupTypeAndDeclaration(declaringType, name, scope); if (declaringElt.getKind() == GrailsElementKind.CONTROLLER_CLASS && ((ControllerClass) declaringElt).isControllerAction(name) && isInTestCase()) { // keep track of this so that the next time "model" is referenced, we know to use this, instead of any other kind of inferencing. modelTypeAndDeclaration = tAndD; } return tAndD; } public void initialize(GroovyCompilationUnit unit, VariableScope topLevelScope) { elementCache = new HashMap<String, IGrailsElement>(); gp = GrailsWorkspaceCore.get().getGrailsProjectFor(unit); if (gp != null) { IGrailsElement rootElement = gp.getGrailsElement(unit); kind = gp.getElementKind(unit); ClassNode groovyClass = rootElement.getGroovyClass(); if (groovyClass != null) { elementCache.put(groovyClass.getName(), rootElement); rootElement.initializeTypeLookup(topLevelScope); } } else { kind = GrailsElementKind.INVALID; } } private boolean isInTestCase() { return kind == GrailsElementKind.UNIT_TEST || kind == GrailsElementKind.INTEGRATION_TEST; } }