/*******************************************************************************
* Copyright (c) 2012 BMW Car IT 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
*******************************************************************************/
package org.jnario.spec.validation;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newLinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtend.core.validation.IssueCodes;
import org.eclipse.xtend.core.xtend.XtendClass;
import org.eclipse.xtend.core.xtend.XtendField;
import org.eclipse.xtend.core.xtend.XtendFile;
import org.eclipse.xtend.core.xtend.XtendFunction;
import org.eclipse.xtend.core.xtend.XtendPackage;
import org.eclipse.xtend.core.xtend.XtendTypeDeclaration;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.ComposedChecks;
import org.eclipse.xtext.xbase.XExpression;
import org.jnario.ExampleColumn;
import org.jnario.spec.naming.ExampleNameProvider;
import org.jnario.spec.spec.After;
import org.jnario.spec.spec.Before;
import org.jnario.spec.spec.Example;
import org.jnario.spec.spec.ExampleGroup;
import org.jnario.spec.spec.TestFunction;
import org.jnario.validation.JnarioJavaValidator;
import com.google.inject.Inject;
/**
* @author Sebastian Benz - Initial contribution and API
*/
@SuppressWarnings("restriction")
@ComposedChecks(validators={JnarioJavaValidator.class})
public class SpecJavaValidator extends AbstractSpecJavaValidator {
@Inject
private ExampleNameProvider exampleNameProvider;
private SpecModifierValidator setupModifierValidator = new SpecModifierValidator(newArrayList("all"), this);
@Override
@Check
protected void checkModifiers(XtendClass xtendClass) {
EObject eContainer = xtendClass.eContainer();
if (!(eContainer instanceof ExampleGroup)) {
super.checkModifiers(xtendClass);
}
}
@Override
@Check
protected void checkModifiers(XtendFunction xtendFunction) {
if ((xtendFunction instanceof Before) || (xtendFunction instanceof After)) {
setupModifierValidator.checkModifiers(xtendFunction, exampleNameProvider.toMethodName(xtendFunction));
}else{
super.checkModifiers(xtendFunction);
}
}
@Check
public void checkClasses(XtendFile file) {
//TODO this check should not be file local, but instead check for any other sources which might declare a
// java type with the same name. Also this then belongs to Xbase and should be defined on JvmDeclaredType
Set<String> names = newLinkedHashSet();
for (XtendTypeDeclaration clazz : file.getXtendTypes()) {
String name = exampleNameProvider.toJavaClassName(clazz);
if (!names.add(name))
error("The type "+clazz.getName()+" is already defined.", clazz, XtendPackage.Literals.XTEND_TYPE_DECLARATION__NAME, -1, IssueCodes.DUPLICATE_TYPE_NAME);
}
}
@Check
public void checkClasses(ExampleGroup exampleGroup) {
checkClasses(filter(exampleGroup.getMembers(), XtendClass.class));
if(exampleGroup.getTargetOperation() == null && exampleGroup.getName() == null && exampleGroup.getTargetType() == null){
error("Example groups must have a name", XtendPackage.Literals.XTEND_TYPE_DECLARATION__NAME);
}
}
protected void checkClasses(Iterable<XtendClass> xtendClasses) {
Set<String> names = newLinkedHashSet();
for (XtendClass clazz : xtendClasses) {
String name = exampleNameProvider.toJavaClassName(clazz);
if (!names.add(name))
error("The spec '"+exampleNameProvider.describe((ExampleGroup) clazz)+"' is already defined.", clazz, XtendPackage.Literals.XTEND_TYPE_DECLARATION__NAME, -1, IssueCodes.DUPLICATE_TYPE_NAME);
}
}
protected void error(String message, EObject source, EStructuralFeature feature, String code, String... issueData) {
if(issueData.length == 0){
super.error(message, source, feature, code, issueData);
}else if(!IssueCodes.DUPLICATE_METHOD.equals(issueData[0])){
super.error(message, source, feature, code, issueData);
}else if(!(source instanceof Example)){
super.error(message, source, feature, code, issueData);
}
}
@Override
public void checkLocalUsageOfDeclaredFields(XtendField field) {
if(!(field instanceof ExampleColumn)){
super.checkLocalUsageOfDeclaredFields(field);
}
}
@Check
public void checkExamplesHaveNames(TestFunction example){
String methodName = exampleNameProvider.toMethodName(example);
if(isNullOrEmpty(methodName)){
error("Name must not be empty", XtendPackage.Literals.XTEND_FUNCTION__NAME);
}
}
protected void mustBeJavaStatementExpression(XExpression expr) {
// disabled as this is the case for example definitions
}
@Override
protected void checkNoJavaKeyword(EObject obj, EAttribute attribute) {
if (obj instanceof ExampleGroup) {
return;
}
super.checkNoJavaKeyword(obj, attribute);
}
// @Check
// public void checkImports(XtendFile file) {
// final Map<JvmType, XImportDeclaration> imports = newHashMap();
// final Map<JvmType, XImportDeclaration> staticImports = newHashMap();
// final Map<String, JvmType> importedNames = newHashMap();
//
// for (XImportDeclaration imp : file.getImports()) {
// if (imp.getImportedNamespace() != null) {
// warning("The use of wildcard imports is deprecated.", imp, null, IssueCodes.IMPORT_WILDCARD_DEPRECATED);
// } else {
// JvmType importedType = imp.getImportedType();
// if (importedType != null && !importedType.eIsProxy()) {
// Map<JvmType, XImportDeclaration> map = imp.isStatic() ? staticImports : imports;
// if (map.containsKey(importedType)) {
// warning("Duplicate import of '" + importedType.getSimpleName() + "'.", imp, null,
// IssueCodes.IMPORT_DUPLICATE);
// } else {
// map.put(importedType, imp);
// if (!imp.isStatic()) {
// JvmType currentType = importedType;
// String currentSuffix = currentType.getSimpleName();
// JvmType collidingImport = importedNames.put(currentSuffix, importedType);
// if(collidingImport != null)
// error("The import '" + importedType.getIdentifier() + "' collides with the import '"
// + collidingImport.getIdentifier() + "'.", imp, null, IssueCodes.IMPORT_COLLISION);
// while (currentType.eContainer() instanceof JvmType) {
// currentType = (JvmType) currentType.eContainer();
// currentSuffix = currentType.getSimpleName()+"$"+currentSuffix;
// JvmType collidingImport2 = importedNames.put(currentSuffix, importedType);
// if(collidingImport2 != null)
// error("The import '" + importedType.getIdentifier() + "' collides with the import '"
// + collidingImport2.getIdentifier() + "'.", imp, null, IssueCodes.IMPORT_COLLISION);
// }
// }
// }
// }
// }
// }
//
// for (final XtendClass xtendClass : file.getXtendTypes()) {
// String clazzName = exampleNameProvider.toJavaClassName(xtendClass);
// if(importedNames.containsKey(clazzName)){
// JvmType importedType = importedNames.get(clazzName);
// if(importedType != null){
// XImportDeclaration xtendImport = imports.get(importedType);
// if(xtendImport != null)
// error("The import '" + importedType.getIdentifier() + "' conflicts with a type defined in the same file", xtendImport, null, IssueCodes.IMPORT_CONFLICT );
// }
// }
// ICompositeNode node = NodeModelUtils.findActualNodeFor(xtendClass);
// if (node != null) {
// for (INode n : node.getAsTreeIterable()) {
// if (n.getGrammarElement() instanceof CrossReference) {
// EClassifier classifier = ((CrossReference) n.getGrammarElement()).getType().getClassifier();
// if (classifier instanceof EClass
// && (TypesPackage.Literals.JVM_TYPE.isSuperTypeOf((EClass) classifier) || TypesPackage.Literals.JVM_CONSTRUCTOR
// .isSuperTypeOf((EClass) classifier))) {
// String simpleName = n.getText().trim();
// // handle StaticQualifier Workaround (see Xbase grammar)
// if (simpleName.endsWith("::"))
// simpleName = simpleName.substring(0, simpleName.length() - 2);
// if (importedNames.containsKey(simpleName)) {
// JvmType type = importedNames.remove(simpleName);
// imports.remove(type);
// } else {
// while (simpleName.contains("$")) {
// simpleName = simpleName.substring(0, simpleName.lastIndexOf('$'));
// if (importedNames.containsKey(simpleName)) {
// imports.remove(importedNames.remove(simpleName));
// break;
// }
// }
// }
// }
// }
// }
// }
// }
// for (XImportDeclaration imp : imports.values()) {
// warning("The import '" + imp.getImportedTypeName() + "' is never used.", imp, null,
// IssueCodes.IMPORT_UNUSED);
// }
// }
@Check
public void checkDuplicateFacts(ExampleGroup exampleGroup){
Map<String, Example> names = newHashMap();
for (Example example : filter(exampleGroup.getMembers(), Example.class)) {
String exampleName = exampleNameProvider.toMethodName(example);
Example duplicate = names.get(exampleName);
if(duplicate != null){
markAsDuplicate(duplicate);
markAsDuplicate(example);
}else{
names.put(exampleName, example);
}
}
}
public void markAsDuplicate(Example duplicate) {
error("Duplicate fact", duplicate, XtendPackage.Literals.XTEND_FUNCTION__NAME, -1);
}
@Check
public void checkAbstract(XtendFunction function) {
if (function instanceof TestFunction) {
return;
}
super.checkAbstract(function);
}
}