mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 08:48:55 +00:00
[xtext][validation] Improved validation of imported ePackage + according quickfixes
This commit is contained in:
parent
dc7a65287f
commit
3f87738752
1 changed files with 134 additions and 17 deletions
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue