/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.dsl.inferencing.suggestions;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.groovy.eclipse.dsl.GroovyDSLCoreActivator;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.groovy.core.util.JavaConstants;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
/**
* Handles primitive types as well.
*
* @author Nieraj Singh
* @created 2011-09-13
*/
public class JavaValidParameterizedTypeRule extends AbstractJavaTypeVerifiedRule {
public JavaValidParameterizedTypeRule(IJavaProject project) {
super(project);
}
public ValueStatus checkValidity(Object value) {
if (!(value instanceof String)) {
return ValueStatus.getErrorStatus(value);
}
String typeToCheck = (String) value;
// This only checks if the value is syntactically correct Java. It does
// not check if
// the type or the parameters of the parameterised type exists. Further
// checks need to be performed below.
StringBuffer source = new StringBuffer();
Type astType = getASTType(typeToCheck, source);
// Check the parameters to see if they are valid Java types. The
// ASTNode only checks for syntactic
// correctness, not whether the type actually exists or not,
// therefore an IType is needed.
List<String> allNonExistantTypes = new ArrayList<String>();
if (astType != null) {
try {
boolean isValid = allTypesExist(astType, source, allNonExistantTypes);
if (!isValid) {
String message = composeErrorMessage(allNonExistantTypes);
return ValueStatus.getErrorStatus(value, message);
} else {
return ValueStatus.getValidStatus(value);
}
} catch (JavaModelException e) {
GroovyDSLCoreActivator.logException(e);
return ValueStatus.getErrorStatus(value, e.getLocalizedMessage());
}
}
return ValueStatus.getErrorStatus(value, INVALID_JAVA);
}
protected String getTypeName(Type type, StringBuffer source) {
String name = null;
name = source.substring(type.getStartPosition(), ASTNodes.getExclusiveEnd(type));
if (type instanceof ParameterizedType) {
// Strip everything after the first <
int index = name.indexOf('<');
if (index >= 0) {
name = name.substring(0, index);
}
}
return name;
}
protected boolean allTypesExist(Type type, StringBuffer source, List<String> nonExistantTypes) throws JavaModelException {
// Type exists, but must still check parameters if it is a
// parameterized type
String typeName = getTypeName(type, source);
IType actualType = getActualType(typeName);
if (actualType != null) {
if (type instanceof ParameterizedType) {
List<?> parameterisedNodes = ((ParameterizedType) type).typeArguments();
// Must check the actual arguments for the parameterised
// type to see if they exists
if (parameterisedNodes != null) {
boolean allParamsValid = true;
for (Object node : parameterisedNodes) {
if (!(node instanceof Type) || !allTypesExist((Type) node, source, nonExistantTypes)) {
allParamsValid = false;
break;
}
}
return allParamsValid;
}
}
return true;
} else if (type instanceof PrimitiveType || type instanceof ArrayType) {
return true;
} else {
if (nonExistantTypes != null && !nonExistantTypes.contains(typeName)) {
nonExistantTypes.add(typeName);
}
}
return false;
}
protected Type getASTType(String typeToCheck, StringBuffer sourceBuffer) {
sourceBuffer.append("class __C__ {"); //$NON-NLS-1$
int valueOffset = sourceBuffer.length();
sourceBuffer.append(typeToCheck).append(" v;");
sourceBuffer.append("}");
ASTParser parser = ASTParser.newParser(JavaConstants.AST_LEVEL);
parser.setSource(sourceBuffer.toString().toCharArray());
Map<String, String> options = new HashMap<String, String>();
JavaCore.setComplianceOptions(JavaCore.VERSION_1_5, options);
parser.setCompilerOptions(options);
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
ASTNode selected = NodeFinder.perform(cu, valueOffset, typeToCheck.length());
if (selected instanceof Name) {
selected = selected.getParent();
}
if (selected instanceof Type) {
return (Type) selected;
}
return null;
}
}