blob: 2409b9425ed6bcdd4b3e29fe7fee9a80019d3fdc [file] [log] [blame]
/**
* Copyright (c) 2011-2012 Eclipse contributors 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.eclipse.emf.ecore.xcore.validation;
import static com.google.common.collect.Maps.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.codegen.ecore.genmodel.GenClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.xcore.XAnnotationDirective;
import org.eclipse.emf.ecore.xcore.XClassifier;
import org.eclipse.emf.ecore.xcore.XImportDirective;
import org.eclipse.emf.ecore.xcore.XPackage;
import org.eclipse.emf.ecore.xcore.XReference;
import org.eclipse.emf.ecore.xcore.XcorePackage;
import org.eclipse.emf.ecore.xcore.lib.XcoreIterableExtensions;
import org.eclipse.emf.ecore.xcore.util.XcoreUtil;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.common.types.util.TypeReferences;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.xbase.XBlockExpression;
import org.eclipse.xtext.xbase.XbasePackage;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xtype.XtypePackage;
import com.google.inject.Inject;
public class XcoreJavaValidator extends AbstractXcoreJavaValidator
{
@Inject
TypeReferences typeReferences;
@Override
protected List<EPackage> getEPackages()
{
List<EPackage> result = new ArrayList<EPackage>();
result.add(XcorePackage.eINSTANCE);
result.add(XbasePackage.eINSTANCE);
result.add(TypesPackage.eINSTANCE);
result.add(XtypePackage.eINSTANCE);
return result;
}
@Check
public void checkImports(XPackage xPackage)
{
final Map<EObject, XImportDirective> imports = newHashMap();
final Map<String, EObject> importedNames = newHashMap();
// Process all the import directives...
//
for (XImportDirective xImportDirective : xPackage.getImportDirectives())
{
// If there is a resolved imported object...
//
String importedNamespace = xImportDirective.getImportedNamespace();
if (importedNamespace.endsWith("*"))
{
warning("Discourage wildcard import of '" + importedNamespace, xImportDirective, XcorePackage.Literals.XIMPORT_DIRECTIVE__IMPORTED_NAMESPACE, XcoreIssueCodes.WILDCARD_IMPORT);
}
else
{
EObject importedObject = xImportDirective.getImportedObject();
if (importedObject != null && !importedObject.eIsProxy())
{
// Check that we've not imported this object already....
//
if (imports.containsKey(importedObject))
{
warning("Duplicate import of '" + importedNamespace, xImportDirective, XcorePackage.Literals.XIMPORT_DIRECTIVE__IMPORTED_NAMESPACE, XcoreIssueCodes.DUPLICATE_IMPORT);
}
else
{
// Keep track of the fact that this object was made visible by this import.
//
imports.put(importedObject, xImportDirective);
// Determine whether there is a simple name of the imported object.
//
String simpleName =
importedObject instanceof JvmType ?
((JvmType)importedObject).getSimpleName() :
importedObject instanceof GenClassifier ?
((GenClassifier)importedObject).getName() :
importedObject instanceof XAnnotationDirective ?
((XAnnotationDirective)importedObject).getName() :
null;
if (simpleName != null)
{
// Check whether this simple name hasn't already been used to import something else...
//
EObject previouslyImportedObject = importedNames.put(simpleName, importedObject);
if (previouslyImportedObject != null)
{
error("The import " + importedNamespace + " collides with another import", xImportDirective, XcorePackage.Literals.XIMPORT_DIRECTIVE__IMPORTED_NAMESPACE, XcoreIssueCodes.COLLIDING_IMPORT);
// Behave as if this import doesn't exist so we don't get any other warnings.
//
imports.remove(importedObject);
importedNames.put(simpleName, previouslyImportedObject);
}
// Track all the type qualified names that might be used to refer to the type being imported...
//
for (EObject eContainer = importedObject.eContainer(); eContainer instanceof JvmType; eContainer = eContainer.eContainer())
{
simpleName = ((JvmType)eContainer).getSimpleName() + "$" + simpleName;
importedNames.put(simpleName, importedObject);
}
}
}
}
}
}
// Check for collisions with locally declared classifiers.
//
for (XClassifier xClassifier : xPackage.getClassifiers())
{
// Ignore collisions with imports that resolve to JvmTypes...
//
String name = xClassifier.getName();
EObject importedObject = importedNames.get(name);
if (importedObject != null && !(importedObject instanceof JvmType))
{
importedNames.remove(name);
XImportDirective xImportDirective = imports.remove(importedObject);
error("The import " + xImportDirective.getImportedNamespace() + " collides with a local classifier", xImportDirective, XcorePackage.Literals.XIMPORT_DIRECTIVE__IMPORTED_NAMESPACE, XcoreIssueCodes.COLLIDING_IMPORT);
}
}
// Check for collisions with locally declared annotation directives.
//
for (XAnnotationDirective xAnnotationDirective : xPackage.getAnnotationDirectives())
{
// Ignore collisions with imports that resolve to JvmTypes...
//
String name = xAnnotationDirective.getName();
EObject importedObject = importedNames.get(name);
if (importedObject != null && !(importedObject instanceof JvmType))
{
importedNames.remove(name);
XImportDirective xImportDirective = imports.remove(importedObject);
error("The import " + xImportDirective.getImportedNamespace() + " collides with a local annotation directive", xImportDirective, null, XcoreIssueCodes.COLLIDING_IMPORT);
}
}
// Scan all the cross reference contents to consider all the names actually being used in this resource...
//
for (INode node : XcoreUtil.importableCrossReferences(xPackage))
{
String simpleName = NodeModelUtils.getTokenText(node);
if (simpleName.endsWith("::"))
{
simpleName = simpleName.substring(0, simpleName.length() - 2);
}
// If this simple name is imported, remove the corresponding object it imported as being used.
// Note that the same import may be used by Xcore to import several different types of things,
// i.e., a Jvm type, an GenModel classifier, and an Xcore annotation directive.
// As long as one of those things is used, the import is used.
//
if (importedNames.containsKey(simpleName))
{
EObject importedObject = importedNames.remove(simpleName);
imports.remove(importedObject);
}
else
{
// Consider all the qualified names as well...
//
while (simpleName.contains("$"))
{
simpleName = simpleName.substring(0, simpleName.lastIndexOf('$'));
if (importedNames.containsKey(simpleName))
{
imports.remove(importedNames.remove(simpleName));
break;
}
}
while (simpleName.contains("."))
{
simpleName = simpleName.substring(0, simpleName.lastIndexOf('.'));
if (importedNames.containsKey(simpleName))
{
imports.remove(importedNames.remove(simpleName));
break;
}
}
}
}
// Any imports left at this point must be unused ones.
//
for (XImportDirective xImportDirective : imports.values())
{
warning("The import '" + xImportDirective.getImportedNamespace() + "' is never used", xImportDirective, null, XcoreIssueCodes.UNUSED_IMPORT);
}
}
@Check
public void checkContainerOpposite(XReference xReference)
{
if (xReference.isContainer() && xReference.getOpposite() == null)
{
error("The container reference " + xReference.getName() + " must specify an opposite", xReference, XcorePackage.Literals.XREFERENCE__OPPOSITE, XcoreIssueCodes.CONTAINER_WITHOUT_OPPOSITE);
}
}
@Check
public void checkClasspath(XPackage xPackage)
{
for (Iterator<EObject> i = xPackage.eAllContents(); i.hasNext(); )
{
if (i.next() instanceof XBlockExpression)
{
if (typeReferences.findDeclaredType(Exceptions.class, xPackage) == null)
{
error
("The required library 'org.eclipse.xtext.xbase.lib' isn't on the classpath",
xPackage,
XcorePackage.Literals.XNAMED_ELEMENT__NAME,
XcoreIssueCodes.XBASE_LIB_NOT_ON_CLASSPATH);
}
if (typeReferences.findDeclaredType(XcoreIterableExtensions.class, xPackage) == null)
{
error
("The required library 'org.eclipse.emf.xcore.lib' isn't on the classpath",
xPackage,
XcorePackage.Literals.XNAMED_ELEMENT__NAME,
XcoreIssueCodes.XCORE_LIB_NOT_ON_CLASSPATH);
}
return;
}
}
}
}