| /******************************************************************************* |
| * Copyright (c) 2010, 2018 Willink Transformations and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * E.D.Willink - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ocl.xtext.base.scoping; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.log4j.Logger; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.ocl.pivot.Element; |
| import org.eclipse.ocl.pivot.NamedElement; |
| import org.eclipse.ocl.pivot.Namespace; |
| import org.eclipse.ocl.pivot.PrimitiveType; |
| import org.eclipse.ocl.pivot.Type; |
| import org.eclipse.ocl.pivot.internal.scoping.Attribution; |
| import org.eclipse.ocl.pivot.internal.scoping.EnvironmentView; |
| import org.eclipse.ocl.pivot.internal.scoping.NullAttribution; |
| import org.eclipse.ocl.pivot.internal.scoping.ScopeView; |
| import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal; |
| import org.eclipse.ocl.pivot.internal.utilities.IllegalLibraryException; |
| import org.eclipse.ocl.pivot.internal.utilities.PivotObjectImpl; |
| import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal; |
| import org.eclipse.ocl.pivot.utilities.ParserContext; |
| import org.eclipse.ocl.pivot.utilities.Pivotable; |
| import org.eclipse.ocl.xtext.base.as2cs.AliasAnalysis; |
| import org.eclipse.ocl.xtext.base.utilities.ElementUtil; |
| import org.eclipse.ocl.xtext.basecs.BaseCSPackage; |
| import org.eclipse.ocl.xtext.basecs.ContextLessElementCS; |
| import org.eclipse.ocl.xtext.basecs.ElementCS; |
| import org.eclipse.ocl.xtext.basecs.ModelElementCS; |
| import org.eclipse.ocl.xtext.basecs.TemplateBindingCS; |
| import org.eclipse.ocl.xtext.basecs.TemplateParameterSubstitutionCS; |
| import org.eclipse.ocl.xtext.basecs.TypeRefCS; |
| import org.eclipse.xtext.naming.QualifiedName; |
| import org.eclipse.xtext.resource.EObjectDescription; |
| import org.eclipse.xtext.resource.IEObjectDescription; |
| import org.eclipse.xtext.scoping.IScope; |
| import org.eclipse.xtext.scoping.impl.AbstractScope; |
| |
| /** |
| * ScopeViews support access to some or all of the elements in a scope. |
| * Accesses are filtered on the fly since a cache of results does not remain valid |
| * for long enough to merit it, with incremental reparsing regularly trashing |
| * the CST. |
| */ |
| public class BaseScopeView extends AbstractScope implements IScopeView |
| { |
| private static final Logger logger = Logger.getLogger(BaseScopeView.class); |
| |
| /** |
| * The <code>NULLSCOPEVIEW</code> to be returned by the most outer scope |
| */ |
| public static final @NonNull IScopeView NULLSCOPEVIEW = new IScopeView() |
| { |
| @Override |
| public Iterable<IEObjectDescription> getAllElements() { |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public @NonNull Attribution getAttribution() { |
| return NullAttribution.INSTANCE; |
| } |
| |
| @Override |
| public ElementCS getChild() { |
| return null; |
| } |
| |
| @Override |
| public EStructuralFeature getContainmentFeature() { |
| return null; |
| } |
| |
| @Override |
| public Iterable<IEObjectDescription> getElements(EObject object) { |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public Iterable<IEObjectDescription> getElements(QualifiedName name) { |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public @NonNull IScopeView getParent() { |
| return NULLSCOPEVIEW; |
| } |
| |
| @Override |
| public @NonNull IScopeView getRoot() { |
| return NULLSCOPEVIEW; |
| } |
| |
| @Override |
| public IEObjectDescription getSingleElement(QualifiedName name) { |
| return null; |
| } |
| |
| @Override |
| public IEObjectDescription getSingleElement(EObject object) { |
| return null; |
| } |
| |
| @Override |
| public ElementCS getTarget() { |
| return null; |
| } |
| |
| @Override |
| public boolean isQualified() { |
| return false; |
| } |
| }; |
| |
| public static @NonNull BaseScopeView getScopeView(@NonNull EnvironmentFactoryInternal environmentFactory, @NonNull ElementCS target, @NonNull EReference targetReference) { |
| return new BaseScopeView(environmentFactory, target, null, targetReference, false); |
| } |
| |
| private static @NonNull IScopeView getParent(@NonNull EnvironmentFactoryInternal environmentFactory, @NonNull ElementCS target, @NonNull EReference targetReference, boolean isQualified) { |
| ElementCS csParent = target.getParent(); |
| if (csParent == null) { |
| return NULLSCOPEVIEW; |
| } |
| return new BaseScopeView(environmentFactory, csParent, target, targetReference, isQualified); |
| } |
| |
| protected final @NonNull EnvironmentFactoryInternal environmentFactory; |
| protected final @NonNull ElementCS target; // CS node in which a lookup is to be performed |
| protected final @Nullable ElementCS child; // CS node from which a lookup is to be performed |
| protected final @NonNull EReference targetReference; // The AST reference to the location at which the lookup is to be stored |
| protected final boolean isQualified; |
| private Attribution attribution = null; // Lazily computed Attributes helper for the target CS node |
| |
| private final @Nullable ParserContext parserContext; // FIXME only non-null for API compatibility |
| |
| protected BaseScopeView(@NonNull EnvironmentFactoryInternal environmentFactory, @NonNull ElementCS target, @Nullable ElementCS child, @NonNull EReference targetReference, boolean isQualified) { |
| super(getParent(environmentFactory, target, targetReference, isQualified), false); |
| this.environmentFactory = environmentFactory; |
| this.target = target; |
| this.child = child; |
| this.targetReference = targetReference; |
| this.isQualified = isQualified; |
| this.parserContext = ElementUtil.basicGetParserContext(target); |
| if (parserContext != null) { |
| assert parserContext.getMetamodelManager().getEnvironmentFactory() == environmentFactory; |
| } |
| } |
| |
| @SuppressWarnings("deprecation") |
| protected @NonNull EnvironmentView createEnvironmentView(@Nullable String name) { |
| return parserContext != null ? new EnvironmentView(parserContext, targetReference, name) : new EnvironmentView(environmentFactory, targetReference, name); |
| } |
| |
| @SuppressWarnings("deprecation") |
| @Override |
| public @NonNull Attribution getAttribution() { |
| Attribution attribution2 = attribution; |
| if (attribution2 == null) { |
| attribution2 = parserContext != null ? parserContext.getAttribution(target) : PivotUtilInternal.getAttribution(target); |
| attribution = attribution2; |
| } |
| return attribution2; |
| } |
| |
| @Override |
| public Iterable<IEObjectDescription> getAllElements() { |
| EnvironmentView environmentView = createEnvironmentView(null); |
| try { |
| // computeLookupWithParents(environmentView); |
| Attribution attribution = getAttribution(); |
| ScopeView aScope = attribution.computeLookup(target, environmentView, this); |
| if (aScope != null) { |
| environmentView.computeLookups(aScope); |
| } |
| } catch (IllegalLibraryException e) { |
| } |
| return getDescriptions(environmentView); |
| } |
| |
| @Override |
| protected final Iterable<IEObjectDescription> getAllLocalElements() { |
| EnvironmentView environmentView = createEnvironmentView(null); |
| Attribution attribution = getAttribution(); |
| attribution.computeLookup(target, environmentView, this); |
| return getDescriptions(environmentView); |
| } |
| |
| @Override |
| public @Nullable ElementCS getChild() { |
| return child; |
| } |
| |
| @Override |
| public EStructuralFeature getContainmentFeature() { |
| // assert ((child == null) && (containmentFeature == null)) || ((child != null) && (child.eContainmentFeature() == containmentFeature)); |
| return child != null ? child.eContainmentFeature() : targetReference; |
| } |
| |
| private @NonNull Element getContextRoot(@NonNull Element context) { |
| while (!(context instanceof Namespace) && !(context instanceof Type)) { |
| EObject container = context.eContainer(); |
| if (container instanceof Element) { |
| context = (Element) container; |
| } |
| else { |
| break; |
| } |
| } |
| return context; |
| } |
| |
| private @Nullable IEObjectDescription getDescription(EnvironmentView environmentView) { |
| int contentsSize = environmentView.getSize(); |
| if (contentsSize == 0) { |
| return null; |
| } |
| if (contentsSize != 1) { |
| logger.warn("Unhandled ambiguous content for '" + environmentView.getName() + "'"); |
| } |
| for (Map.Entry<String, Object> entry : environmentView.getEntries()) { |
| Object value = entry.getValue(); |
| if (value instanceof List<?>) { |
| List<?> values = (List<?>) value; |
| value = values.get(values.size() - 1); |
| } |
| if (value instanceof EObject) { |
| return EObjectDescription.create(entry.getKey(), (EObject) value); |
| } |
| } |
| return null; |
| } |
| |
| private @NonNull List<IEObjectDescription> getDescriptions(EnvironmentView environmentView) { |
| List<IEObjectDescription> contents = new ArrayList<IEObjectDescription>(); |
| for (Map.Entry<String, Object> entry : environmentView.getEntries()) { |
| Object values = entry.getValue(); |
| if (values instanceof EObject) { |
| contents.add(EObjectDescription.create(entry.getKey(), |
| (EObject) values)); |
| } else if (values instanceof List<?>) { |
| for (Object value : (List<?>) values) { |
| contents.add(EObjectDescription.create(entry.getKey(), |
| (EObject) value)); |
| } |
| } |
| } |
| return contents; |
| } |
| |
| @Override |
| public /*@NonNull*/ Iterable<IEObjectDescription> getElements(QualifiedName name) { |
| if (name == null) |
| throw new NullPointerException("name"); //$NON-NLS-1$ |
| EnvironmentView environmentView = createEnvironmentView(name.toString()); |
| int size = environmentView.computeLookups(this); |
| if (size <= 0) { |
| return Collections.emptyList(); |
| } |
| else if (size == 1) { |
| return Collections.singletonList(getDescription(environmentView)); |
| } |
| else { |
| List<IEObjectDescription> contents = getDescriptions(environmentView); |
| return contents; |
| } |
| } |
| |
| @Override |
| public /*@NonNull*/ Iterable<IEObjectDescription> getElements(EObject object) { |
| String descriptiveName = null; |
| if (targetReference == BaseCSPackage.Literals.IMPORT_CS__REFERRED_NAMESPACE) { |
| descriptiveName = getNonASURI(object); |
| } |
| else if (targetReference == BaseCSPackage.Literals.MODEL_ELEMENT_REF_CS__REFERRED_ELEMENT) { |
| descriptiveName = getNonASURI(object); |
| } |
| else if (targetReference == BaseCSPackage.Literals.REFERENCE_CS__REFERRED_OPPOSITE) { |
| descriptiveName = ((NamedElement)object).getName(); |
| } |
| else if (targetReference == BaseCSPackage.Literals.REFERENCE_CS__REFERRED_KEYS) { |
| descriptiveName = ((NamedElement)object).getName(); |
| } |
| else if ((targetReference == BaseCSPackage.Literals.TYPED_TYPE_REF_CS__REFERRED_TYPE) && (object instanceof Type)) { |
| if (object instanceof PrimitiveType) { // FIXME Redundant if namespaces correct |
| descriptiveName = ((PrimitiveType)object).getName(); |
| } |
| else { |
| EObject csRef = getTarget(); |
| while ((csRef.eContainer() instanceof TypeRefCS) |
| || (csRef.eContainer() instanceof TemplateParameterSubstitutionCS) |
| || (csRef.eContainer() instanceof TemplateBindingCS)) { |
| csRef = csRef.eContainer(); |
| } |
| ModelElementCS csContext = (ModelElementCS) csRef.eContainer(); |
| Resource eResource = EcoreUtil.getRootContainer(csContext).eResource(); |
| if (eResource == null) { |
| return Collections.emptyList(); |
| } |
| AliasAnalysis aliasAnalysis = AliasAnalysis.getAdapter(eResource, environmentFactory); |
| Element context = csContext.getPivot(); |
| if (context == null) { |
| return Collections.emptyList(); |
| } |
| context = getContextRoot(context); |
| QualifiedPath contextPath = new QualifiedPath(aliasAnalysis.getPath(context)); |
| QualifiedPath objectPath = new QualifiedPath(aliasAnalysis.getPath((Element) object)); |
| QualifiedPath qualifiedRelativeName = objectPath.deresolve(contextPath); |
| IEObjectDescription objectDescription = EObjectDescription.create(qualifiedRelativeName, object); |
| return Collections.singletonList(objectDescription); |
| } |
| } |
| else if (object instanceof NamedElement) { |
| EObject csRef = getTarget(); |
| while ((csRef.eContainer() instanceof ContextLessElementCS) |
| || (csRef.eContainer() instanceof TypeRefCS) |
| || (csRef.eContainer() instanceof TemplateParameterSubstitutionCS) |
| || (csRef.eContainer() instanceof TemplateBindingCS)) { |
| csRef = csRef.eContainer(); |
| } |
| Pivotable csContext = (Pivotable) csRef.eContainer(); |
| Resource eResource = csContext.eResource(); |
| if (eResource == null) { |
| return Collections.emptyList(); |
| } |
| AliasAnalysis aliasAnalysis = AliasAnalysis.getAdapter(eResource, environmentFactory); |
| Element context = csContext.getPivot(); |
| if (context == null) { |
| return Collections.emptyList(); |
| } |
| context = getContextRoot(context); |
| QualifiedPath contextPath = new QualifiedPath(aliasAnalysis.getPath(context)); |
| QualifiedPath objectPath = new QualifiedPath(aliasAnalysis.getPath((Element) object)); |
| QualifiedPath qualifiedRelativeName = objectPath.deresolve(contextPath); |
| IEObjectDescription objectDescription = EObjectDescription.create(qualifiedRelativeName, object); |
| return Collections.singletonList(objectDescription); |
| } |
| if (descriptiveName != null) { |
| IEObjectDescription objectDescription = EObjectDescription.create(descriptiveName, object); |
| return Collections.singletonList(objectDescription); |
| } |
| return super.getElements(object); // FIXME Implement |
| } |
| |
| // public MetamodelManager getMetamodelManager() { |
| // return metamodelManager; |
| // } |
| |
| private @Nullable String getNonASURI(@Nullable EObject object) { |
| URI uri = null; |
| if (object == null) { |
| return null; |
| } |
| if (object instanceof PivotObjectImpl) { |
| EObject target = ((PivotObjectImpl)object).getESObject(); |
| if (target != null) { |
| uri = EcoreUtil.getURI(target); |
| } |
| } |
| if (uri == null) { |
| uri = EcoreUtil.getURI(object); |
| } |
| if (PivotUtilInternal.isASURI(uri)) { |
| uri = PivotUtilInternal.getNonASURI(uri); |
| } |
| return uri.toString(); |
| } |
| |
| @Override |
| public @NonNull IScopeView getParent() { |
| IScope parent = super.getParent(); |
| assert parent instanceof IScopeView; |
| return (IScopeView) parent; |
| } |
| |
| @Override |
| public @NonNull IScopeView getRoot() { |
| IScopeView parent = getParent(); |
| if (parent == NULLSCOPEVIEW) { |
| return this; |
| } |
| else { |
| return parent.getRoot(); |
| } |
| } |
| |
| @Override |
| public IEObjectDescription getSingleElement(EObject object) { |
| if (object instanceof NamedElement) { |
| return EObjectDescription.create(((NamedElement)object).getName(), object); |
| } |
| else { |
| return super.getSingleElement(object); // FIXME Implement |
| } |
| } |
| |
| @Override |
| public @Nullable IEObjectDescription getSingleElement(QualifiedName name) { |
| if (name == null) |
| throw new NullPointerException("name"); //$NON-NLS-1$ |
| EnvironmentView environmentView = createEnvironmentView(name.toString()); |
| int size = environmentView.computeLookups(this); |
| if (size <= 0) { |
| return null; |
| } |
| else if (size == 1) { |
| return getDescription(environmentView); |
| } |
| else { |
| return null; // FIXME Return an 'ambiguous' description |
| // return environmentView.getDescriptions().get(0); |
| } |
| } |
| |
| @Override |
| public final @NonNull ElementCS getTarget() { |
| return target; |
| } |
| |
| @Override |
| public final boolean isQualified() { |
| return isQualified; |
| } |
| |
| @Override |
| public String toString() { |
| EObject target = getTarget(); |
| StringBuilder s = new StringBuilder(); |
| s.append("["); //$NON-NLS-1$ |
| s.append(target.eClass().getName()); |
| EStructuralFeature containmentFeature2 = getContainmentFeature(); |
| if (containmentFeature2 != null) { |
| s.append("::"); //$NON-NLS-1$ |
| s.append(containmentFeature2.getName()); |
| } |
| s.append("] "); //$NON-NLS-1$ |
| s.append(String.valueOf(target)); |
| return s.toString(); |
| } |
| } |