[xtext][validation] Improved validation of imported ePackage + according quickfixes

This commit is contained in:
Sebastian Zarnekow 2011-11-07 22:28:22 +01:00
parent dc7a65287f
commit 3f87738752

View file

@ -19,6 +19,7 @@ import java.util.TreeSet;
import org.eclipse.emf.codegen.util.CodeGenUtil;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
@ -28,9 +29,12 @@ import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.EValidator;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.EcoreValidator;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.AbstractMetamodelDeclaration;
@ -56,8 +60,14 @@ import org.eclipse.xtext.UnorderedGroup;
import org.eclipse.xtext.XtextPackage;
import org.eclipse.xtext.conversion.IValueConverterService;
import org.eclipse.xtext.conversion.ValueConverterException;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.ClasspathUriUtil;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.util.Triple;
import org.eclipse.xtext.util.Tuples;
@ -89,10 +99,16 @@ public class XtextValidator extends AbstractDeclarativeValidator {
public static final String EMPTY_ENUM_LITERAL= "org.eclipse.xtext.grammar.EmptyEnumLiteral";
public static final String INVALID_HIDDEN_TOKEN = "org.eclipse.xtext.grammar.InvalidHiddenToken";
public static final String INVALID_HIDDEN_TOKEN_FRAGMENT = "org.eclipse.xtext.grammar.InvalidHiddenTokenFragment";
public static final String INVALID_PACKAGE_REFERENCE_INHERITED = "org.eclipse.xtext.grammar.InvalidPackageReference.inherited";
public static final String INVALID_PACKAGE_REFERENCE_EXTERNAL = "org.eclipse.xtext.grammar.InvalidPackageReference.external";
public static final String INVALID_PACKAGE_REFERENCE_NOT_ON_CLASSPATH = "org.eclipse.xtext.grammar.InvalidPackageReference.notOnClasspath";
@Inject
private IValueConverterService valueConverter;
@Inject
private ResourceDescriptionsProvider resourceDescriptionsProvider;
private KeywordInspector keywordHidesTerminalInspector;
@Override
@ -392,30 +408,131 @@ public class XtextValidator extends AbstractDeclarativeValidator {
if (metamodel.getEPackage() == null)
return;
String nsURI = metamodel.getEPackage().getNsURI();
List<GeneratedMetamodel> allGeneratedMetamodels = new ArrayList<GeneratedMetamodel>();
Grammar grammar = GrammarUtil.getGrammar(metamodel);
Set<Grammar> visited = Sets.newHashSet();
for (Grammar usedGrammar : grammar.getUsedGrammars())
Iterables.addAll(allGeneratedMetamodels, getAllGeneratedMetamodels(usedGrammar, visited));
if (allGeneratedMetamodels.isEmpty())
List<GeneratedMetamodel> allGeneratedMetamodels = getInheritedGeneratedMetamodels(metamodel);
String text = getUsedUri(metamodel);
for (GeneratedMetamodel generatedMetamodel : allGeneratedMetamodels) {
EPackage generatedPackage = generatedMetamodel.getEPackage();
if (generatedPackage != null && nsURI.equals((generatedPackage.getNsURI()))) {
if (!text.equals(nsURI)) {
error(
"Metamodels that have been generated by a super grammar must be referenced by nsURI: " + nsURI,
XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE,
INVALID_PACKAGE_REFERENCE_INHERITED,
nsURI);
return;
}
}
}
checkExternalPackage(metamodel, text);
}
protected void checkExternalPackage(ReferencedMetamodel metamodelReference, String importURI) {
EPackage referencedPackage = metamodelReference.getEPackage();
if (referencedPackage.eIsProxy() || isRuntime(metamodelReference))
return;
if (isRegisteredPackage(referencedPackage)) {
warning(
"The imported package is not on the classpath of this project which may lead to follow up errors.",
XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE,
INVALID_PACKAGE_REFERENCE_NOT_ON_CLASSPATH,
importURI);
return;
}
if (!importURI.equals(referencedPackage.getNsURI())) {
IResourceDescriptions descriptions = resourceDescriptionsProvider.getResourceDescriptions(metamodelReference.eResource());
Iterable<IEObjectDescription> packagesInIndex = descriptions.getExportedObjects(
EcorePackage.Literals.EPACKAGE,
QualifiedName.create(referencedPackage.getNsURI()),
false);
if (!Iterables.isEmpty(packagesInIndex)) {
warning(
"Packages should be imported by their namespace URI.",
XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE,
INVALID_PACKAGE_REFERENCE_EXTERNAL,
referencedPackage.getNsURI());
} else if (!ClasspathUriUtil.isClasspathUri(URI.createURI(importURI))) {
warning(
"The imported package is not on the classpath of this project which may lead to follow up errors.",
XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE,
INVALID_PACKAGE_REFERENCE_NOT_ON_CLASSPATH,
importURI);
return;
} else {
return;
}
}
Map<EObject, Collection<Setting>> allReferences = EcoreUtil.CrossReferencer.find(Collections.singletonList(referencedPackage));
Set<Object> allReferencedInstances = Sets.newHashSet();
for(Setting setting: Iterables.concat(allReferences.values())) {
Object referenced = setting.get(true);
if (allReferencedInstances.add(referenced) && referenced instanceof EObject) {
EPackage transitive = EcoreUtil2.getContainerOfType((EObject)referenced, EPackage.class);
if (isRegisteredPackage(transitive)) {
if (referenced instanceof EDataType)
continue;
if (referenced == EcorePackage.Literals.EOBJECT)
continue;
IResourceDescriptions descriptions = resourceDescriptionsProvider.getResourceDescriptions(metamodelReference.eResource());
Iterable<IEObjectDescription> packagesInIndex = descriptions.getExportedObjects(EcorePackage.Literals.EPACKAGE, QualifiedName.create(importURI), false);
if (!Iterables.isEmpty(packagesInIndex)) {
if (setting.getEObject().eResource().getURI().isPlatformResource())
warning(
"The imported package refers to elements the package registry instead of using the instances from the workspace",
XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE,
INVALID_PACKAGE_REFERENCE_EXTERNAL,
referencedPackage.getNsURI());
else
warning(
"The imported package refers to elements the package registry instead of using the instances from the workspace",
XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE,
INVALID_PACKAGE_REFERENCE_EXTERNAL);
return;
} else {
warning(
"The imported package refers to elements that are not on the classpath of this project. The package '" + transitive.getNsURI() + "' was loaded from the registry.",
XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE,
INVALID_PACKAGE_REFERENCE_NOT_ON_CLASSPATH,
referencedPackage.getNsURI());
return;
}
}
}
}
}
private boolean isRuntime(ReferencedMetamodel metamodelReference) {
ResourceSet resourceSet = metamodelReference.eResource().getResourceSet();
if (resourceSet instanceof XtextResourceSet) {
Object context = ((XtextResourceSet) resourceSet).getClasspathURIContext();
boolean result = context == null || context instanceof Class || context instanceof ClassLoader;
return result;
}
return false;
}
protected boolean isRegisteredPackage(EPackage ePackage) {
return ePackage != null && (ePackage.eResource() == null || ePackage.getNsURI().equals(ePackage.eResource().getURI().toString()));
}
protected String getUsedUri(ReferencedMetamodel metamodel) {
List<INode> nodes = NodeModelUtils.findNodesForFeature(metamodel,
XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE);
if (nodes.size() != 1)
throw new IllegalArgumentException();
String text = nodes.get(0).getText();
text = (String) valueConverter.toValue(text, "STRING", nodes.get(0));
for (GeneratedMetamodel generatedMetamodel : allGeneratedMetamodels) {
EPackage generatedPackage = generatedMetamodel.getEPackage();
if (generatedPackage != null && nsURI.equals((generatedPackage.getNsURI()))) {
// TODO provide a quickfix
if (!text.equals(nsURI)) {
error(
"Metamodels that have been generated by a super grammar must be referenced by nsURI: " + nsURI,
XtextPackage.Literals.ABSTRACT_METAMODEL_DECLARATION__EPACKAGE);
}
}
}
return text;
}
protected List<GeneratedMetamodel> getInheritedGeneratedMetamodels(ReferencedMetamodel metamodel) {
List<GeneratedMetamodel> allGeneratedMetamodels = new ArrayList<GeneratedMetamodel>();
Grammar grammar = GrammarUtil.getGrammar(metamodel);
Set<Grammar> visited = Sets.newHashSet();
for (Grammar usedGrammar : grammar.getUsedGrammars())
Iterables.addAll(allGeneratedMetamodels, getAllGeneratedMetamodels(usedGrammar, visited));
if (allGeneratedMetamodels.isEmpty())
return Collections.emptyList();
return allGeneratedMetamodels;
}
private Iterable<GeneratedMetamodel> getAllGeneratedMetamodels(Grammar grammar, Set<Grammar> visited) {