/*******************************************************************************
* 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.elements;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.AssertionFailedException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.groovy.search.VariableScope;
import org.grails.ide.eclipse.core.internal.plugins.GrailsCore;
import org.grails.ide.eclipse.editor.groovy.types.PerProjectServiceCache;
import org.grails.ide.eclipse.editor.groovy.types.PerProjectTypeCache;
/**
* @author Andrew Eisenberg
* @author Christian Dupuis
* @created Nov 23, 2009
*/
public abstract class AbstractGrailsElement implements IGrailsElement {
protected final ICompilationUnit unit;
protected ClassNode cachedGroovyClass = null;
private Set<String> serviceReferences = null;
private String primaryTypeName;
private Map<String, AnnotatedNode> cachedGeneratedMembers = new HashMap<String, AnnotatedNode>();
public AbstractGrailsElement(ICompilationUnit unit) {
Assert.isNotNull(unit, "Null ICompilationUnit");
this.unit = unit;
}
/**
* Gets the Groovy {@link ClassNode} associated with this Grails Element
* May return <code>null</code> if no {@link ClassNode} exists.
*/
@SuppressWarnings("cast")
public ClassNode getGroovyClass() {
if (cachedGroovyClass != null) {
return cachedGroovyClass;
}
if (unit instanceof GroovyCompilationUnit) {
if (unit.isWorkingCopy()) {
ModuleNode module = ((GroovyCompilationUnit) unit).getModuleNode();
if (module != null) {
String typeName = getPrimaryTypeName();
for (ClassNode clazz : (Iterable<ClassNode>) module.getClasses()) {
// ensure that the default is the first class unless a better match is found
if (clazz.getNameWithoutPackage().equals(typeName) || cachedGroovyClass == null) {
cachedGroovyClass = clazz;
}
}
}
} else {
// do not get the module node if this is not a working copy.
// this prevents temporarily turning the unit into a working copy
cachedGroovyClass =
GrailsCore.get().connect(unit.getJavaProject().getProject(), PerProjectTypeCache.class).createClassNodeFromSource(unit);
}
} else {
// not sure about this...
// should only get here for OtherGrailsKinds
// eventually could use the PerProjectTypeCache here
return null;
}
// might still be null at this point if there were no matching type names found
return cachedGroovyClass;
// throw new AssertionFailedException("Invalid name for compilation unit " + unit.getElementName()); //$NON-NLS-1$
}
protected String getPrimaryTypeName() {
if (primaryTypeName == null) {
String unitName = unit.getElementName();
int dotIndex = unitName.indexOf('.');
if (dotIndex > 0) {
primaryTypeName = unitName.substring(0, dotIndex);
} else {
throw new AssertionFailedException("Invalid name for compilation unit " + unitName); //$NON-NLS-1$
}
}
return primaryTypeName;
}
public ICompilationUnit getCompilationUnit() {
return unit;
}
public IResource getResource() {
return unit.getResource();
}
/**
* get a cached method. Not only saves processing time, but also
* ensures that FindOccurrences visitor works properly
* @param toCache
*/
protected void cacheGeneratedMember(AnnotatedNode toCache) {
if (toCache instanceof MethodNode) {
cachedGeneratedMembers.put(((MethodNode) toCache).getName(), toCache);
} else if (toCache instanceof PropertyNode) {
cachedGeneratedMembers.put(((PropertyNode) toCache).getName(), toCache);
} else if (toCache instanceof FieldNode) {
cachedGeneratedMembers.put(((FieldNode) toCache).getName(), toCache);
} else {
throw new IllegalArgumentException("Expecting a method, property, or field node. Instead got " + toCache);
}
}
protected AnnotatedNode getCachedMember(String name) {
return cachedGeneratedMembers.get(name);
}
/**
* Populates the scope with dependency injected services
* @param scope
*/
protected void populateInjectedServices(VariableScope scope) {
ClassNode groovyClass = getGroovyClass();
if (groovyClass != null) {
PerProjectServiceCache serviceCache = GrailsCore.get().connect(getResource().getProject(), PerProjectServiceCache.class);
List<FieldNode> fields = groovyClass.getFields();
serviceReferences = new HashSet<String>();
Map<String, ClassNode> services = serviceCache.findServicesFor(fields);
for (Entry<String, ClassNode> entry : services.entrySet()) {
scope.addVariable(entry.getKey(), entry.getValue(), groovyClass);
serviceReferences.add(entry.getKey());
}
}
}
@Override
public String toString() {
return getKind()+"("+getCompilationUnit()+")";
}
protected boolean isServiceReference(PropertyNode prop) {
if (serviceReferences == null) {
return false;
} else {
return serviceReferences.contains(prop.getName());
}
}
}