/*******************************************************************************
* Copyright (c) 2011 Codehaus.org, SpringSource, and others.
* 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:
* Andrew Eisenberg - Initial implemenation
*******************************************************************************/
package org.codehaus.groovy.eclipse.dsl.pointcuts;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.eclipse.dsl.lookup.ResolverCache;
import org.codehaus.jdt.groovy.internal.compiler.ast.JDTResolver;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.groovy.search.VariableScope;
/**
* The current context used to match against when evaluating pointcuts,
* Class is a bit messy
*
* @author andrew
* @created Nov 17, 2010
*/
public class GroovyDSLDContext {
public final String[] projectNatures;
/**
* This value does not change once it is set since inferencing happens on
* a per-file basis
*/
public final String fullPathName;
/**
* This value does not change once it is set since inferencing happens on
* a per-file basis
*/
public final String simpleFileName;
/**
* Patch from project to package root
*/
public final String packageRootPath;
/**
* Path from package root to file name (exclusive)
*/
public final String packageFolderPath;
/** will be null if this object created from deprecated API */
private ResolverCache resolverCache;
private BindingSet currentBinding;
private VariableScope currentScope;
/**
* the type of the expression currently being analyzed
* set by the type lookup, should not be set by the pointcuts
*/
private ClassNode targetType;
public GroovyDSLDContext(GroovyCompilationUnit unit, ModuleNode module, JDTResolver jdtResolver) throws CoreException {
this(getProjectNatures(unit),
getFullPathToFile(unit),
getPathToPackage(unit));
resolverCache = new ResolverCache(jdtResolver, module);
}
/**
* Not API!!!
* Should not use this constructor. It is only for testing
*/
@Deprecated
public GroovyDSLDContext(String[] projectNatures, String fullPathName, String packageRootPath) {
this.fullPathName = fullPathName;
this.packageRootPath = packageRootPath;
if (fullPathName != null) {
int lastDot = fullPathName.lastIndexOf('/');
this.simpleFileName = fullPathName.substring(lastDot+1);
} else {
this.simpleFileName = null;
}
// assumption is that packageRootPath is a prefix of fullPathName
String candidate;
if (packageRootPath != null && packageRootPath.length() < fullPathName.length()) {
candidate = fullPathName.substring(packageRootPath.length());
if (simpleFileName != null) {
int indexOf = candidate.lastIndexOf("/" + simpleFileName);
int start = candidate.startsWith("/") ? 1 : 0;
if (indexOf > 0 && candidate.length() > 0) {
candidate = candidate.substring(start, indexOf);
}
}
} else {
candidate = "";
}
packageFolderPath = candidate;
this.projectNatures = projectNatures;
}
private static String getPathToPackage(GroovyCompilationUnit unit) {
IResource resource = unit.getPackageFragmentRoot().getResource();
return resource == null ? null : resource.getFullPath().removeFirstSegments(1).toPortableString();
}
private static String getFullPathToFile(GroovyCompilationUnit unit) {
IResource resource = unit.getResource();
return resource == null ? null : resource.getFullPath().removeFirstSegments(1).toPortableString();
}
private static String[] getProjectNatures(GroovyCompilationUnit unit) throws CoreException {
return unit.getJavaProject().getProject().getDescription().getNatureIds();
}
/** cached type hierarchy for checking type matches (consider caching more) */
private Set<ClassNode> cachedHierarchy;
private boolean isStatic;
private boolean isPrimaryNode;
/**
* called by the type lookup, not by the pointcuts
* @param targetType
*/
public void setTargetType(ClassNode targetType) {
cachedHierarchy = null;
this.targetType = targetType;
}
/**
* Only the type lookup should use this method
* @param currentBinding
*/
public void setCurrentBinding(BindingSet currentBinding) {
this.currentBinding = currentBinding;
}
public void resetBinding() {
this.currentBinding = new BindingSet();
}
/**
* Only the type lookup and the proposl provider should use this method
* @param currentBinding
*/
public BindingSet getCurrentBinding() {
return currentBinding;
}
/**
* Adds the collection to the currnt binding. At this point, currentBinding should never be null
* Used by the pointcuts only
* @param bindingName
* @param toAdd
*/
public void addToBinding(String bindingName, Collection<?> toAdd) {
currentBinding.addToBinding(bindingName, toAdd);
}
public boolean matchesNature(String natureId) {
if (natureId == null) {
return false;
}
for (String nature : projectNatures) {
if (natureId.equals(nature)) {
return true;
}
}
return false;
}
/**
* @param typeName
* @return true iff typeName equals the targetType name or any tyoe in the hierarchy
*/
public boolean matchesType(String typeName) {
return matchesType(typeName, targetType);
}
public boolean matchesType(String typeName, ClassNode toCheck) {
// when left unspecified, always return true
if (typeName == null || toCheck == null) {
return true;
}
if (typeName.equals(toCheck.getName())) {
return true;
}
if (cachedHierarchy == null) {
// use linked hash set because order is important
cachedHierarchy = new LinkedHashSet<ClassNode>();
getAllSupers(toCheck, cachedHierarchy);
}
for (ClassNode node : cachedHierarchy) {
if (typeName.equals(node.getName())) {
return true;
}
}
return false;
}
public VariableScope getCurrentScope() {
return currentScope;
}
public void setCurrentScope(VariableScope currentScope) {
this.currentScope = currentScope;
isPrimaryNode = currentScope.isPrimaryNode();
}
public ClassNode getCurrentType() {
return targetType;
}
private void getAllSupers(ClassNode type, Set<ClassNode> set) {
if (type == null) {
return;
}
set.add(type);
getAllSupers(type.getSuperClass(), set);
for (ClassNode inter : type.getAllInterfaces()) {
if (! inter.getName().equals(type.getName())) {
getAllSupers(inter, set);
}
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("ContextPattern [fileName=");
builder.append(fullPathName);
builder.append(", targetType=");
builder.append(targetType);
builder.append(", currentScope=");
builder.append(currentScope);
builder.append("]");
return builder.toString();
}
public ResolverCache getResolverCache() {
return resolverCache;
}
public boolean isPrimaryNode() {
return isPrimaryNode;
}
public void setPrimaryNode(boolean isPrimaryNode) {
this.isPrimaryNode = isPrimaryNode;
}
public void setStatic(boolean s) {
isStatic = s;
}
public boolean isStatic() {
return isStatic;
}
}