/** * <copyright> * </copyright> * * */ package org.emftext.term.propositional.expression.resource.expression.analysis; public class ExpressionDefaultResolverDelegate<ContainerType extends org.eclipse.emf.ecore.EObject, ReferenceType extends org.eclipse.emf.ecore.EObject> { private static class StringMatch { private String exactMatch; private String similarMatch; public StringMatch() { super(); } public StringMatch(String exactMatch) { super(); this.exactMatch = exactMatch; } public String getExactMatch() { return exactMatch; } public void setSimilarMatch(String similarMatch) { this.similarMatch = similarMatch; } public String getSimilarMatch() { return similarMatch; } } /** * The maximal distance between two identifiers according to the Levenshtein * distance to qualify for a quick fix. */ public int MAX_DISTANCE = 2; private boolean enableScoping = true; /** * This is a cache for the external objects that are referenced by the current * resource. We must cache this set because determining this set required to * resolve proxy objects, which causes reference resolving to slow down * exponentially. */ private java.util.Set<org.eclipse.emf.ecore.EObject> referencedExternalObjects; /** * We store the number of proxy objects that were present when * <code>referencedExternalObjects</code> was resolved, to recompute this set when * a proxy was resolved. This is required, because a resolved proxy may point to a * new external object. */ private int oldProxyCount = -1; private static org.emftext.term.propositional.expression.resource.expression.mopp.ExpressionMetaInformation metaInformation = new org.emftext.term.propositional.expression.resource.expression.mopp.ExpressionMetaInformation(); private org.emftext.term.propositional.expression.resource.expression.IExpressionNameProvider nameProvider = metaInformation.createNameProvider(); /** * This standard implementation searches for objects in the resource, which have * the correct type and a name/id attribute matching the identifier. If no * matching object is found, the identifier is used as URI. If the resource at * this URI has a root element of the correct type, this element is returned. */ protected void resolve(String identifier, ContainerType container, org.eclipse.emf.ecore.EReference reference, int position, boolean resolveFuzzy, org.emftext.term.propositional.expression.resource.expression.IExpressionReferenceResolveResult<ReferenceType> result) { try { org.eclipse.emf.ecore.EObject root = container; if (!enableScoping) { root = org.eclipse.emf.ecore.util.EcoreUtil.getRootContainer(container); } while (root != null) { boolean continueSearch = tryToResolveIdentifierInObjectTree(identifier, container, root, reference, position, resolveFuzzy, result, !enableScoping); if (!continueSearch) { return; } root = root.eContainer(); } boolean continueSearch = tryToResolveIdentifierAsURI(identifier, container, reference, position, resolveFuzzy, result); if (continueSearch) { java.util.Set<org.eclipse.emf.ecore.EObject> crossReferencedObjectsInOtherResource = findReferencedExternalObjects(container); for (org.eclipse.emf.ecore.EObject externalObject : crossReferencedObjectsInOtherResource) { continueSearch = tryToResolveIdentifierInObjectTree(identifier, container, externalObject, reference, position, resolveFuzzy, result, !enableScoping); if (!continueSearch) { return; } } } if (continueSearch) { continueSearch = tryToResolveIdentifierInGenModelRegistry(identifier, container, reference, position, resolveFuzzy, result); } } catch (java.lang.RuntimeException rte) { // catch exception here to prevent EMF proxy resolution from swallowing it rte.printStackTrace(); } } /** * Returns all EObjects that are referenced by EObjects in the resource that * contains <code>object</code>, but that are located in different resources. */ protected java.util.Set<org.eclipse.emf.ecore.EObject> findReferencedExternalObjects(org.eclipse.emf.ecore.EObject object) { org.eclipse.emf.ecore.EObject root = org.eclipse.emf.ecore.util.EcoreUtil.getRootContainer(object); java.util.Map<org.eclipse.emf.ecore.EObject, java.util.Collection<org.eclipse.emf.ecore.EStructuralFeature.Setting>> proxies = org.eclipse.emf.ecore.util.EcoreUtil.ProxyCrossReferencer.find(root); int proxyCount = 0; for (java.util.Collection<org.eclipse.emf.ecore.EStructuralFeature.Setting> settings : proxies.values()) { proxyCount += settings.size(); } // Use the cache if it is still valid if (referencedExternalObjects != null && oldProxyCount == proxyCount) { return referencedExternalObjects; } referencedExternalObjects = new java.util.LinkedHashSet<org.eclipse.emf.ecore.EObject>(); oldProxyCount = proxyCount; java.util.Set<org.eclipse.emf.ecore.EObject> referencedExternalObjects = new java.util.LinkedHashSet<org.eclipse.emf.ecore.EObject>(); referencedExternalObjects.addAll(getExternalObjects(root.eCrossReferences(), object)); java.util.Iterator<org.eclipse.emf.ecore.EObject> eAllContents = root.eAllContents(); while (eAllContents.hasNext()) { org.eclipse.emf.ecore.EObject next = eAllContents.next(); referencedExternalObjects.addAll(getExternalObjects(next.eCrossReferences(), object)); } return referencedExternalObjects; } /** * Returns all EObjects that are not contained in the same resource as the given * EObject. */ protected java.util.Set<org.eclipse.emf.ecore.EObject> getExternalObjects(java.util.Collection<org.eclipse.emf.ecore.EObject> objects, org.eclipse.emf.ecore.EObject object) { java.util.Set<org.eclipse.emf.ecore.EObject> externalObjects = new java.util.LinkedHashSet<org.eclipse.emf.ecore.EObject>(); for (org.eclipse.emf.ecore.EObject next : objects) { if (next.eResource() != object.eResource()) { externalObjects.add(next); } } return externalObjects; } /** * Searches for objects in the tree of EObjects that is rooted at * <code>root</code>, which have the correct type and a name/id attribute matching * the identifier. This method can be used to quickly implement custom reference * resolvers which require to search in a particular scope for referenced * elements, rather than in the whole resource as done by resolve(). */ protected boolean tryToResolveIdentifierInObjectTree(String identifier, org.eclipse.emf.ecore.EObject container, org.eclipse.emf.ecore.EObject root, org.eclipse.emf.ecore.EReference reference, int position, boolean resolveFuzzy, org.emftext.term.propositional.expression.resource.expression.IExpressionReferenceResolveResult<ReferenceType> result, boolean checkRootFirst) { org.eclipse.emf.ecore.EClass type = reference.getEReferenceType(); boolean continueSearch; if (checkRootFirst) { // check whether the root element matches continueSearch = checkElement(container, root, reference, position, type, identifier, resolveFuzzy, true, result); if (!continueSearch) { return false; } } // check the contents for (java.util.Iterator<org.eclipse.emf.ecore.EObject> iterator = root.eAllContents(); iterator.hasNext(); ) { org.eclipse.emf.ecore.EObject element = iterator.next(); continueSearch = checkElement(container, element, reference, position, type, identifier, resolveFuzzy, true, result); if (!continueSearch) { return false; } } // if the root element was already checked, we can return. if (checkRootFirst) { return true; } // check whether the root element matches continueSearch = checkElement(container, root, reference, position, type, identifier, resolveFuzzy, true, result); if (!continueSearch) { return false; } return true; } protected boolean tryToResolveIdentifierAsURI(String identifier, ContainerType container, org.eclipse.emf.ecore.EReference reference, int position, boolean resolveFuzzy, org.emftext.term.propositional.expression.resource.expression.IExpressionReferenceResolveResult<ReferenceType> result) { org.eclipse.emf.ecore.EClass type = reference.getEReferenceType(); org.eclipse.emf.ecore.resource.Resource resource = container.eResource(); if (resource != null) { org.eclipse.emf.common.util.URI uri = getURI(identifier, resource.getURI()); if (uri != null) { org.eclipse.emf.ecore.EObject element = loadResource(container.eResource().getResourceSet(), uri); if (element == null) { return true; } return checkElement(container, element, reference, position, type, identifier, resolveFuzzy, false, result); } } return true; } protected boolean tryToResolveIdentifierInGenModelRegistry(String identifier, ContainerType container, org.eclipse.emf.ecore.EReference reference, int position, boolean resolveFuzzy, org.emftext.term.propositional.expression.resource.expression.IExpressionReferenceResolveResult<ReferenceType> result) { org.eclipse.emf.ecore.EClass type = reference.getEReferenceType(); final java.util.Map<String, org.eclipse.emf.common.util.URI> packageNsURIToGenModelLocationMap = org.eclipse.emf.ecore.plugin.EcorePlugin.getEPackageNsURIToGenModelLocationMap(); for (String nextNS : packageNsURIToGenModelLocationMap.keySet()) { org.eclipse.emf.common.util.URI genModelURI = packageNsURIToGenModelLocationMap.get(nextNS); try { final org.eclipse.emf.ecore.resource.ResourceSet rs = container.eResource().getResourceSet(); org.eclipse.emf.ecore.resource.Resource genModelResource = rs.getResource(genModelURI, true); if (genModelResource == null) { continue; } final java.util.List<org.eclipse.emf.ecore.EObject> contents = genModelResource.getContents(); if (contents == null || contents.size() == 0) { continue; } org.eclipse.emf.ecore.EObject genModel = contents.get(0); boolean continueSearch = checkElement(container, genModel, reference, position, type, identifier, resolveFuzzy, false, result); if (!continueSearch) { return false; } } catch (Exception e) { // ignore exceptions that are raised by faulty genmodel registrations } } return true; } protected boolean checkElement(org.eclipse.emf.ecore.EObject container, org.eclipse.emf.ecore.EObject element, org.eclipse.emf.ecore.EReference reference, int position, org.eclipse.emf.ecore.EClass type, String identifier, boolean resolveFuzzy, boolean checkStringWise, org.emftext.term.propositional.expression.resource.expression.IExpressionReferenceResolveResult<ReferenceType> result) { if (element.eIsProxy()) { return true; } boolean hasCorrectType = hasCorrectEType(element, type); if (!hasCorrectType) { return true; } StringMatch match; // do not compare string-wise if identifier is a URI if (checkStringWise) { match = matches(element, identifier, resolveFuzzy); } else { match = new StringMatch(identifier); } String exactMatch = match.getExactMatch(); if (exactMatch == null) { String similarMatch = match.getSimilarMatch(); if (similarMatch != null) { org.eclipse.emf.ecore.EObject oldTarget; Object value = container.eGet(reference); if (value instanceof java.util.List) { java.util.List<?> list = (java.util.List<?>) container.eGet(reference); oldTarget = (org.eclipse.emf.ecore.EObject) list.get(position); } else { oldTarget = (org.eclipse.emf.ecore.EObject) container.eGet(reference, false); } result.addQuickFix(new org.emftext.term.propositional.expression.resource.expression.mopp.ExpressionChangeReferenceQuickFix("Replace with " + similarMatch, "IMG_TOOL_FORWARD", container, reference, oldTarget, element)); } return true; } // we can safely cast 'element' to 'ReferenceType' here, because we've checked the // type of 'element' against the type of the reference. unfortunately the compiler // does not know that this is sufficient, so we must call cast(), which is not // type safe by itself. result.addMapping(exactMatch, cast(element)); if (!resolveFuzzy) { return false; } return true; } /** * This method encapsulates an unchecked cast from EObject to ReferenceType. We * cannot do this cast strictly type safe, because type parameters are erased by * compilation. Thus, an instanceof check cannot be performed at runtime. */ @SuppressWarnings("unchecked") protected ReferenceType cast(org.eclipse.emf.ecore.EObject element) { return (ReferenceType) element; } protected String produceDeResolveErrorMessage(org.eclipse.emf.ecore.EObject refObject, org.eclipse.emf.ecore.EObject container, org.eclipse.emf.ecore.EReference reference, org.emftext.term.propositional.expression.resource.expression.IExpressionTextResource resource) { String msg = getClass().getSimpleName() + ": " + reference.getEType().getName() + " \"" + refObject.toString() + "\" not de-resolveable"; return msg; } protected String deResolve(ReferenceType element, ContainerType container, org.eclipse.emf.ecore.EReference reference) { org.eclipse.emf.ecore.resource.Resource elementResource = element.eResource(); // For elements in external resources we return the resource URI instead of the // name of the element. if (elementResource != null && !elementResource.equals(container.eResource())) { return elementResource.getURI().toString(); } return getName(element); } protected StringMatch matches(org.eclipse.emf.ecore.EObject element, String identifier, boolean matchFuzzy) { for (Object name : getNames(element)) { StringMatch match = matches(identifier, name, matchFuzzy); if (match.getExactMatch() != null) { return match; } } return new StringMatch(); } /** * This method is only kept for compatibility reasons. The current version * delegates all calls to a name provider, but previous custom implementation of * this class may have overridden this method. */ public java.util.List<String> getNames(org.eclipse.emf.ecore.EObject element) { return nameProvider.getNames(element); } protected StringMatch matches(String identifier, Object attributeValue, boolean matchFuzzy) { if (attributeValue != null && attributeValue instanceof String) { String name = (String) attributeValue; if (name.equals(identifier) || matchFuzzy) { return new StringMatch(name); } if (isSimilar(name, identifier)) { StringMatch match = new StringMatch(); match.setSimilarMatch(name); return match; } } return new StringMatch(); } protected String getName(ReferenceType element) { String deresolvedReference = null; if (element instanceof org.eclipse.emf.ecore.EObject) { org.eclipse.emf.ecore.EObject eObjectToDeResolve = (org.eclipse.emf.ecore.EObject) element; if (eObjectToDeResolve.eIsProxy()) { deresolvedReference = ((org.eclipse.emf.ecore.InternalEObject) eObjectToDeResolve).eProxyURI().fragment(); // If the proxy was created by EMFText, we can try to recover the identifier from // the proxy URI if (deresolvedReference != null && deresolvedReference.startsWith(org.emftext.term.propositional.expression.resource.expression.IExpressionContextDependentURIFragment.INTERNAL_URI_FRAGMENT_PREFIX)) { deresolvedReference = deresolvedReference.substring(org.emftext.term.propositional.expression.resource.expression.IExpressionContextDependentURIFragment.INTERNAL_URI_FRAGMENT_PREFIX.length()); deresolvedReference = deresolvedReference.substring(deresolvedReference.indexOf("_") + 1); } } } if (deresolvedReference != null) { return deresolvedReference; } // if the referenced element was not a proxy, we try the same magic that was used // while resolving elements to obtain names for elements java.util.List<String> names = getNames(element); for (String name : names) { if (name != null) { return name; } } return null; } protected boolean hasCorrectEType(org.eclipse.emf.ecore.EObject element, org.eclipse.emf.ecore.EClass expectedTypeEClass) { if (expectedTypeEClass.getInstanceClass() == null) { return expectedTypeEClass.isInstance(element); } return hasCorrectType(element, expectedTypeEClass.getInstanceClass()); } protected boolean hasCorrectType(org.eclipse.emf.ecore.EObject element, Class<?> expectedTypeClass) { return expectedTypeClass.isInstance(element); } protected org.eclipse.emf.ecore.EObject loadResource(org.eclipse.emf.ecore.resource.ResourceSet resourceSet, org.eclipse.emf.common.util.URI uri) { try { org.eclipse.emf.ecore.resource.Resource resource = resourceSet.getResource(uri, true); org.eclipse.emf.common.util.EList<org.eclipse.emf.ecore.EObject> contents = resource.getContents(); if (contents.size() > 0) { return contents.get(0); } } catch (java.lang.RuntimeException re) { // do nothing here. if no resource can be loaded the uriString is probably not a // valid resource URI } return null; } protected org.eclipse.emf.common.util.URI getURI(String identifier, org.eclipse.emf.common.util.URI baseURI) { if (identifier == null) { return null; } try { org.eclipse.emf.common.util.URI uri = org.eclipse.emf.common.util.URI.createURI(identifier); if (uri.isRelative()) { uri = uri.resolve(baseURI); } return uri; } catch (java.lang.IllegalArgumentException iae) { // the identifier string is not a valid URI return null; } } protected org.emftext.term.propositional.expression.resource.expression.IExpressionReferenceCache getCache(org.eclipse.emf.ecore.EObject object) { org.eclipse.emf.ecore.EObject root = org.eclipse.emf.ecore.util.EcoreUtil.getRootContainer(object); org.eclipse.emf.common.notify.Adapter adapter = org.emftext.term.propositional.expression.resource.expression.util.ExpressionEObjectUtil.getEAdapter(root, org.emftext.term.propositional.expression.resource.expression.analysis.ExpressionReferenceCache.class); org.emftext.term.propositional.expression.resource.expression.analysis.ExpressionReferenceCache cache = org.emftext.term.propositional.expression.resource.expression.util.ExpressionCastUtil.cast(adapter); if (cache != null) { return cache; } else { // cache does not exist. create a new one. cache = new org.emftext.term.propositional.expression.resource.expression.analysis.ExpressionReferenceCache(nameProvider); cache.initialize(root); root.eAdapters().add(cache); return cache; } } public void setEnableScoping(boolean enableScoping) { this.enableScoping = enableScoping; } public boolean getEnableScoping() { return enableScoping; } protected boolean isSimilar(String identifier, Object attributeValue) { if (attributeValue != null && attributeValue instanceof String) { String name = (String) attributeValue; if (org.emftext.term.propositional.expression.resource.expression.util.ExpressionStringUtil.computeLevenshteinDistance(identifier, name) <= MAX_DISTANCE) { return true; } } return false; } }