blob: 68e12c2a9bb86fe6c3e73fbcfa56ec9bec6a5647 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2019 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.qvtd.compiler.internal.usage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CollectionItem;
import org.eclipse.ocl.pivot.CollectionLiteralExp;
import org.eclipse.ocl.pivot.CollectionLiteralPart;
import org.eclipse.ocl.pivot.CollectionRange;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.ExpressionInOCL;
import org.eclipse.ocl.pivot.IfExp;
import org.eclipse.ocl.pivot.IterateExp;
import org.eclipse.ocl.pivot.Iteration;
import org.eclipse.ocl.pivot.IteratorExp;
import org.eclipse.ocl.pivot.LanguageExpression;
import org.eclipse.ocl.pivot.LetExp;
import org.eclipse.ocl.pivot.LiteralExp;
import org.eclipse.ocl.pivot.MapLiteralExp;
import org.eclipse.ocl.pivot.MapLiteralPart;
import org.eclipse.ocl.pivot.NavigationCallExp;
import org.eclipse.ocl.pivot.NullLiteralExp;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.OperationCallExp;
import org.eclipse.ocl.pivot.OppositePropertyCallExp;
import org.eclipse.ocl.pivot.Parameter;
import org.eclipse.ocl.pivot.PrimitiveType;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.PropertyCallExp;
import org.eclipse.ocl.pivot.SelfType;
import org.eclipse.ocl.pivot.ShadowExp;
import org.eclipse.ocl.pivot.ShadowPart;
import org.eclipse.ocl.pivot.TemplateParameter;
import org.eclipse.ocl.pivot.TemplateSignature;
import org.eclipse.ocl.pivot.TemplateableElement;
import org.eclipse.ocl.pivot.TupleLiteralExp;
import org.eclipse.ocl.pivot.TupleLiteralPart;
import org.eclipse.ocl.pivot.TupleType;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.TypeExp;
import org.eclipse.ocl.pivot.TypedElement;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.VariableDeclaration;
import org.eclipse.ocl.pivot.VariableExp;
import org.eclipse.ocl.pivot.ids.OperationId;
import org.eclipse.ocl.pivot.internal.manager.TemplateParameterSubstitutionVisitor;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal.EnvironmentFactoryInternalExtension;
import org.eclipse.ocl.pivot.util.AbstractExtendingPivotVisitor;
import org.eclipse.ocl.pivot.util.Visitable;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.EnvironmentFactory;
import org.eclipse.ocl.pivot.utilities.ParserException;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.qvtd.compiler.ProblemHandler;
import org.eclipse.qvtd.compiler.internal.usage.RootDomainUsageAnalysis.DomainUsageConstant;
import org.eclipse.qvtd.compiler.internal.utilities.CompilerUtil;
import org.eclipse.qvtd.pivot.qvtbase.Rule;
import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.DomainUsage;
/**
* AbstractDomainUsageAnalysis provides shared functionality for the overall analysis and for nested operational analyses.
*/
public abstract class AbstractDomainUsageAnalysis extends AbstractExtendingPivotVisitor<@NonNull DomainUsage, @NonNull EnvironmentFactory> implements DomainUsageAnalysis.Internal
{
protected final @NonNull ProblemHandler problemHandler;
private DomainUsage selfUsage = null;
protected final @NonNull Map<@NonNull Element, DomainUsage> element2usage = new HashMap<>();
protected AbstractDomainUsageAnalysis(@NonNull EnvironmentFactory environmentFactory, @NonNull ProblemHandler problemHandler) {
super(environmentFactory);
this.problemHandler = problemHandler;
}
@Override
public @Nullable DomainUsage basicGetUsage(@Nullable Element element) {
return element2usage.get(element);
}
protected @NonNull DomainUsage doNavigationCallExp(@NonNull Property property, @NonNull NavigationCallExp object) {
DomainUsage actualSourceUsage = visit(object.getOwnedSource());
DomainUsage knownPropertyUsage = visit(property);
DomainUsage usage = knownPropertyUsage;
RootDomainUsageAnalysis rootAnalysis = getRootAnalysis();
Property oppositeProperty = property.getOpposite();
if (property.isIsComposite() // Composite properties have both ends in the same domain
|| ((oppositeProperty != null) && oppositeProperty.isIsComposite())
|| (property == rootAnalysis.getOclContainerProperty()) // FIXME ensure these are isComposite
|| (property == rootAnalysis.getOclContentsProperty())) {
usage = intersection(usage, actualSourceUsage);
}
/* else if (!property.isIsImplicit() && !property.isIsTransient() / *&& !rootAnalysis.isDirty(property)* / // Simple input domain nodes cannot reference middle or output domains
&& (usage.isMiddle() || usage.isOutput())
&& actualSourceUsage.isInput() && !actualSourceUsage.isMiddle() && !actualSourceUsage.isOutput()) {
// QVTruntimeUtil.errPrintln("Assuming !rootAnalysis.isDirty(property)");
usage = intersection(usage, actualSourceUsage);
} */
else if (!property.isIsImplicit() && !property.isIsTransient() /*&& !rootAnalysis.isDirty(property)*/) { // Simple input domain nodes cannot reference middle or output domains
if (/*usage.isMiddle() &&*/ !actualSourceUsage.isMiddle() && !actualSourceUsage.isThis()) { // Only testQVTcCompiler_Forward2Reverse needs this guard
/* if (actualSourceUsage.isInput() && !actualSourceUsage.isOutput()) {
// QVTruntimeUtil.errPrintln("Assuming !rootAnalysis.isDirty(property)");
usage = intersection(usage, actualSourceUsage);
}
else {
QVTruntimeUtil.errPrintln("Assuming actualSourceUsage.isInput() && !actualSourceUsage.isOutput()");
usage = intersection(usage, actualSourceUsage);
}
}
else if (usage.isOutput()) {
QVTruntimeUtil.errPrintln("Assuming actualSourceUsage.isInput() && !actualSourceUsage.isOutput()");
if (actualSourceUsage.isInput() && !actualSourceUsage.isMiddle() && !actualSourceUsage.isOutput()) {
// QVTruntimeUtil.errPrintln("Assuming !rootAnalysis.isDirty(property)");
usage = intersection(usage, actualSourceUsage);
}
}
else {
QVTruntimeUtil.errPrintln("Assuming !usage.isOutput()"); */
if (!usage.isPrimitive()) {
DomainUsage newUsage = intersection(usage, actualSourceUsage); // This may help narrow a multi-TypedModel endogeneous call to one side or the other.
if (!newUsage.isNone()) {
// if (!newUsage.equals(usage)) {
// QVTruntimeUtil.errPrintln(usage + " intersection " + actualSourceUsage + " is " + newUsage + " for " + property + " in " + object);
// }
usage = newUsage;
}
}
}
}
return usage;
}
protected @NonNull DomainUsage getAllInstancesUsage(@NonNull OperationCallExp object, @NonNull DomainUsage sourceUsage) {
return sourceUsage;
}
/* protected @NonNull DomainUsage getDomainUsage(@Nullable EObject object) {
Domain domain = QVTbaseUtil.basicGetContainingDomain(object);
if (domain != null) {
return visit(domain.getTypedModel());
}
else {
return getRootAnalysis().getMiddleUsage();
}
} */
public @NonNull Map<@NonNull Element, DomainUsage> getElements2Usage() {
return element2usage;
}
public @NonNull EnvironmentFactory getEnvironmentFactory() {
return context;
}
public @NonNull ProblemHandler getProblemHandler() {
return problemHandler;
}
protected abstract @NonNull RootDomainUsageAnalysis getRootAnalysis();
protected @NonNull DomainUsage getTypeUsage(@NonNull Type type) {
DomainUsage usage = getRootAnalysis().basicGetTypeUsage(type);
if (usage != null) {
return usage;
}
else {
return getRootAnalysis().getPrimitiveUsage();
}
}
@Override
public @NonNull DomainUsage getUsage(@NonNull Element element) {
DomainUsage usage = element2usage.get(element);
if (usage == null) {
usage = element.accept(this);
// if ((usage == null) || usage.isNone()) { // FIXME debugging
// usage = element.accept(this);
// }
assert usage != null : "null usage for " + element.eClass().getName() + " " + element;
setUsage(element, usage);
}
return usage;
}
/**
* Return the DomainUsage corresponding to the intersection of the first and second usages.
* The result is constant if either usage is constant, otherwise variable.
*
* An incoming variable usage is refined to the intersection.
*
* FIXME It is not clear how transitive intersection gives predictable/useful results.
*/
public @NonNull DomainUsage intersection(@NonNull DomainUsage firstUsage, @NonNull DomainUsage secondUsage) {
RootDomainUsageAnalysis rootAnalysis = getRootAnalysis();
int firstMask = firstUsage.getMask();
int secondMask = secondUsage.getMask();
int intersectionMask = firstMask & secondMask;
if (firstMask == secondMask) {
if (firstUsage != secondUsage) {
if (!firstUsage.isConstant()) {
replace((DomainUsage.Internal) firstUsage, secondUsage);
return secondUsage;
}
else if (!secondUsage.isConstant()) {
replace((DomainUsage.Internal) secondUsage, firstUsage);
return firstUsage;
}
}
return firstUsage;
}
else if ((firstMask & intersectionMask) == (secondMask & intersectionMask)) {
DomainUsage intersectionUsage = null;
if ((firstMask == intersectionMask) && firstUsage.isConstant()) {
intersectionUsage = firstUsage;
}
else if ((secondMask == intersectionMask) && secondUsage.isConstant()) {
intersectionUsage = secondUsage;
}
else if (firstUsage.isConstant() || secondUsage.isConstant()) {
intersectionUsage = rootAnalysis.getValidUsage(intersectionMask);
if (intersectionUsage == null) {
intersectionUsage = rootAnalysis.getConstantUsage(intersectionMask);
rootAnalysis.addValidUsage(intersectionMask, (DomainUsageConstant) intersectionUsage);
}
}
else if (firstMask == intersectionMask) {
intersectionUsage = firstUsage;
}
else if (secondMask == intersectionMask) {
intersectionUsage = secondUsage;
}
else {
intersectionUsage = rootAnalysis.createVariableUsage(intersectionMask);
}
if (!firstUsage.isConstant()) {
replace((DomainUsage.Internal) firstUsage, intersectionUsage);
return secondUsage;
}
else if (!secondUsage.isConstant()) {
replace((DomainUsage.Internal) secondUsage, intersectionUsage);
return firstUsage;
}
return intersectionUsage;
}
else {
DomainUsage usage = rootAnalysis.getValidUsage(intersectionMask);
if (usage != null) {
if ((usage != firstUsage) && !firstUsage.isConstant()) {
replace((DomainUsage.Internal) firstUsage, usage);
}
if ((usage != secondUsage) && !secondUsage.isConstant()) {
replace((DomainUsage.Internal) secondUsage, usage);
}
return usage;
}
else {
usage = rootAnalysis.createVariableUsage(intersectionMask);
if (!firstUsage.isConstant()) {
replace((DomainUsage.Internal) firstUsage, usage);
}
if (!secondUsage.isConstant()) {
replace((DomainUsage.Internal) secondUsage, usage);
}
return usage;
}
}
}
protected void popSelfUsage(@Nullable DomainUsage savedUsage) {
selfUsage = savedUsage;
}
protected DomainUsage pushSelfUsage(@NonNull DomainUsage usage) {
DomainUsage oldUsage = selfUsage;
int usageMask = usage.getMask();
DomainUsage constantUsage = getRootAnalysis().getValidUsage(usageMask);
if (constantUsage == null) {
usage = getRootAnalysis().createVariableUsage(usageMask);
}
selfUsage = usage;
return oldUsage;
}
protected void replace(DomainUsage.@NonNull Internal oldUsage, @NonNull DomainUsage newUsage) {
Iterable<Element> elements = oldUsage.getElements();
if (elements != null) {
for (@SuppressWarnings("null")@NonNull Element element : elements) {
setUsage(element, newUsage);
}
}
}
protected void setUsage(@NonNull Element element, @NonNull DomainUsage newUsage) {
Rule rule = QVTbaseUtil.basicGetContainingRule(element);
if ((rule != null) && (rule != element)) {
DomainUsage ruleUsage = getUsage(rule);
assert ruleUsage != null;
DomainUsage intersectionUsage = intersection(newUsage, ruleUsage);
if (intersectionUsage.isNone() && !newUsage.isNone()) {
CompilerUtil.addRuleError(problemHandler, rule, "No useable ''{0}'' domain in ''{1}'' for ''{2}''", newUsage, rule.getName(), element);
}
newUsage = intersectionUsage;
}
element2usage.put(element, newUsage);
((DomainUsage.Internal)newUsage).addUsedBy(element);
// System.out.println(" setUsage " + getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this))
// + " : " + element.eClass().getName() + "@" + Integer.toHexString(System.identityHashCode(element))
// + " <= " + newUsage
// );
}
@Override
public String toString() {
Map<@NonNull String, DomainUsage> map = new HashMap<>(element2usage.size());
List<@NonNull String> keys = new ArrayList<>(element2usage.size());
for (Map.Entry<@NonNull Element, DomainUsage> entry : element2usage.entrySet()) {
Element element = entry.getKey();
String key = element.eClass().getName() + " : " + element;
map.put(key, entry.getValue());
keys.add(key);
}
Collections.sort(keys);
StringBuilder s = new StringBuilder();
for (@NonNull String key : keys) {
DomainUsage usage = map.get(key);
if (s.length() > 0) {
s.append("\n");
}
s.append(key);
s.append(" => ");
s.append(usage);
}
return s.toString();
}
public @NonNull DomainUsage union(@NonNull DomainUsage firstUsage, @NonNull DomainUsage secondUsage) {
int firstMask = firstUsage.getMask();
int secondMask = secondUsage.getMask();
int unionMask = firstMask | secondMask;
if ((unionMask == firstMask) && firstUsage.isConstant()) {
return firstUsage;
}
else if ((unionMask == secondMask) && secondUsage.isConstant()) {
return secondUsage;
}
else if (firstUsage.isConstant() && secondUsage.isConstant()) {
return getRootAnalysis().getConstantUsage(unionMask);
}
else {
return getRootAnalysis().createVariableUsage(unionMask);
}
}
@Override
public @NonNull DomainUsage visit(@Nullable Element element) {
if (element == null) {
return getRootAnalysis().getAnyUsage();
}
DomainUsage usage = element2usage.get(element);
if (usage == null) {
usage = element.accept(this);
assert usage != null : "null usage for " + element.eClass().getName() + " " + element;
setUsage(element, usage);
}
return usage;
}
@Override
public @NonNull DomainUsage visiting(@NonNull Visitable visitable) {
throw new UnsupportedOperationException("Unsupported " + visitable.eClass().getName() + " for " + getClass().getSimpleName());
}
@Override
public @NonNull DomainUsage visitClass(org.eclipse.ocl.pivot.@NonNull Class object) {
return getTypeUsage(object);
}
@Override
public @NonNull DomainUsage visitCollectionItem(@NonNull CollectionItem object) {
return visit(object.getOwnedItem());
}
@Override
public @NonNull DomainUsage visitCollectionLiteralExp(@NonNull CollectionLiteralExp object) {
CollectionType collectionType = (CollectionType)PivotUtil.getType(object);
DomainUsage usage = visit(PivotUtil.getElementType(collectionType));
// DomainUsage usage = getRootAnalysis().getAnyUsage();
for (@SuppressWarnings("null")@NonNull CollectionLiteralPart part : object.getOwnedParts()) {
usage = intersection(usage, visit(part));
}
return usage;
}
@Override
public @NonNull DomainUsage visitCollectionRange(@NonNull CollectionRange object) {
DomainUsage firstUsage = visit(object.getOwnedFirst());
DomainUsage lastUsage = visit(object.getOwnedLast());
return intersection(firstUsage, lastUsage);
}
@Override
public @NonNull DomainUsage visitCollectionType(@NonNull CollectionType object) {
return visit(PivotUtil.getElementType(object));
}
@Override
public @NonNull DomainUsage visitExpressionInOCL(@NonNull ExpressionInOCL object) {
OCLExpression ownedBody = object.getOwnedBody();
if ((ownedBody == null) && (object.getBody() != null)) {
try {
ownedBody = ((EnvironmentFactoryInternalExtension)context).parseSpecification(object).getOwnedBody(); // FIXME why is this necessary
} catch (ParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Variable ownedContext = object.getOwnedContext();
if ((ownedContext != null) && (selfUsage != null)) {
setUsage(ownedContext, selfUsage);
}
else {
visit(ownedContext);
}
for (Variable parameter : object.getOwnedParameters()) {
visit(parameter);
}
visit(object.getOwnedResult());
return visit(ownedBody);
}
@Override
public @NonNull DomainUsage visitIfExp(@NonNull IfExp object) {
@SuppressWarnings("unused") DomainUsage conditionUsage = visit(object.getOwnedCondition());
DomainUsage thenUsage = visit(object.getOwnedThen());
DomainUsage elseUsage = visit(object.getOwnedElse());
return intersection(thenUsage, elseUsage);
}
@Override
public @NonNull DomainUsage visitIterateExp(@NonNull IterateExp object) {
DomainUsage sourceUsage = visit(object.getOwnedSource());
for (Variable iterator : object.getOwnedIterators()) {
if (iterator != null) {
setUsage(iterator, sourceUsage);
}
}
visit(object.getOwnedResult());
@SuppressWarnings("unused") DomainUsage bodyUsage = visit(object.getOwnedBody());
TemplateParameterSubstitutionVisitor visitor = new TemplateParameterSubstitutionVisitor((@NonNull EnvironmentFactoryInternal) context, object.getOwnedSource().getType(), null);
object.accept(visitor);
Iteration iteration = object.getReferredIteration();
for (EObject eObject = iteration; eObject != null; eObject = eObject.eContainer()) {
if (eObject instanceof TemplateableElement) {
TemplateSignature ownedSignature = ((TemplateableElement)eObject).getOwnedSignature();
if (ownedSignature != null) {
for (TemplateParameter templateParameter : ownedSignature.getOwnedParameters()) {
if (templateParameter != null) {
Type templateParameterType = visitor.get(templateParameter);
DomainUsage templateParameterUsage = visit(templateParameterType);
setUsage(templateParameter, templateParameterUsage);
}
}
}
}
}
DomainUsage returnUsage = visit(object.getType());
DomainUsage possibleUsage = union(sourceUsage, bodyUsage);
return intersection(returnUsage, possibleUsage);
}
@Override
public @NonNull DomainUsage visitIteratorExp(@NonNull IteratorExp object) {
DomainUsage sourceUsage = visit(object.getOwnedSource());
for (Variable iterator : object.getOwnedIterators()) {
if (iterator != null) {
setUsage(iterator, sourceUsage);
}
}
@SuppressWarnings("unused") DomainUsage bodyUsage = visit(object.getOwnedBody());
TemplateParameterSubstitutionVisitor visitor = new TemplateParameterSubstitutionVisitor((@NonNull EnvironmentFactoryInternal) context, object.getOwnedSource().getType(), null);
object.accept(visitor);
Iteration iteration = object.getReferredIteration();
for (EObject eObject = iteration; eObject != null; eObject = eObject.eContainer()) {
if (eObject instanceof TemplateableElement) {
TemplateSignature ownedSignature = ((TemplateableElement)eObject).getOwnedSignature();
if (ownedSignature != null) {
for (TemplateParameter templateParameter : ownedSignature.getOwnedParameters()) {
if (templateParameter != null) {
Type templateParameterType = visitor.get(templateParameter);
DomainUsage templateParameterUsage = visit(templateParameterType);
setUsage(templateParameter, templateParameterUsage);
}
}
}
}
}
DomainUsage returnUsage = visit(object.getType());
DomainUsage possibleUsage = union(sourceUsage, bodyUsage);
return intersection(returnUsage, possibleUsage);
}
@Override
public @NonNull DomainUsage visitLetExp(@NonNull LetExp object) {
visit(object.getOwnedVariable());
return visit(object.getOwnedIn());
}
@Override
public @NonNull DomainUsage visitLiteralExp(@NonNull LiteralExp object) {
return getRootAnalysis().getPrimitiveUsage();
}
@Override
public @NonNull DomainUsage visitMapLiteralExp(@NonNull MapLiteralExp object) {
DomainUsage usage = getRootAnalysis().getAnyUsage();
for (@SuppressWarnings("null")@NonNull MapLiteralPart part : object.getOwnedParts()) {
usage = intersection(usage, visit(part));
}
return usage;
}
@Override
public @NonNull DomainUsage visitMapLiteralPart(@NonNull MapLiteralPart object) {
DomainUsage keyUsage = visit(object.getOwnedKey());
DomainUsage valueUsage = visit(object.getOwnedValue());
return intersection(keyUsage, valueUsage);
}
@Override
public @NonNull DomainUsage visitNullLiteralExp(@NonNull NullLiteralExp object) {
return getRootAnalysis().createVariableUsage(getRootAnalysis().getAnyMask());
}
@Override
public @NonNull DomainUsage visitOperation(@NonNull Operation object) {
// System.out.println(" " + getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this)) + " : " + object);
DomainUsage savedUsage = pushSelfUsage(visit(object.getOwningClass()));
try {
for (@SuppressWarnings("null")@NonNull Parameter parameter : object.getOwnedParameters()) {
visit(parameter);
}
LanguageExpression bodyExpression = object.getBodyExpression();
if (bodyExpression == null) {
return visit(object.getType());
}
try {
ExpressionInOCL parseSpecification = ((EnvironmentFactoryInternalExtension)context).parseSpecification(bodyExpression);
return visit(parseSpecification);
} catch (ParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return visit(object.getType());
}
// ownedBody = parseSpecification.getOwnedBody(); // FIXME why is this necessary
// LanguageExpression bodyExpression = object.getBodyExpression();
}
finally {
popSelfUsage(savedUsage);
}
}
@Override
public @NonNull DomainUsage visitOperationCallExp(@NonNull OperationCallExp object) {
DomainUsage sourceUsage = visit(object.getOwnedSource());
DomainUsage savedUsage = pushSelfUsage(sourceUsage);
try {
Operation operation = ClassUtil.nonNullState(object.getReferredOperation());
RootDomainUsageAnalysis rootAnalysis = getRootAnalysis();
OperationId operationId = operation.getOperationId();
//
// Special case: usage of oclContainer()/oclContents() is unchanged from the usage of the source.
//
if ((operationId == rootAnalysis.getOclContainerId())
|| (operationId == rootAnalysis.getOclContentsId())) {
return sourceUsage;
}
String operationName = object.getReferredOperation().getName();
if ("allInstances".equals(operationName)) { // FIXME BUG 487257 Revise this
return getAllInstancesUsage(object, sourceUsage);
}
//
// Special case: left/right of "="/"<>" have same usage. Result is primitive.
//
// if ((operationId == getRootAnalysis().getOclAnyEqualsOperationId())
// || (operationId == getRootAnalysis().getOclAnyNotEqualsOperationId())) {
if ("=".equals(operationName) // FIXME BUG 487252 rationalize the derived operationIds
|| "<>".equals(operationName)) {
DomainUsage rightUsage = visit(object.getOwnedArguments().get(0));
intersection(sourceUsage, rightUsage);
return getRootAnalysis().getPrimitiveUsage();
}
/* if ("oclAsType".equals(operationName) && !sourceUsage.isPrimitive()) { // FIX ME fudge for Adolfo's suspect EObjects
TemplateParameter templateParameter = operation.getType().isTemplateParameter();
if (templateParameter != null) {
List<Parameter> ownedParameters = operation.getOwnedParameters();
int iMax = Math.min(ownedParameters.size(), object.getOwnedArguments().size());
for (int i = 0; i < iMax; i++) {
Parameter parameter = ownedParameters.get(i);
if (parameter.isIsTypeof() && (parameter.getType() == templateParameter)) {
OCLExpression argument = object.getOwnedArguments().get(i);
DomainUsage argumentUsage = visit(argument);
intersection(sourceUsage, argumentUsage);
}
}
}
return sourceUsage;
} */
TemplateParameter templateParameter = operation.getType().isTemplateParameter();
if (templateParameter != null) { // Handle e.g oclAsType()
List<Parameter> ownedParameters = operation.getOwnedParameters();
int iMax = Math.min(ownedParameters.size(), object.getOwnedArguments().size());
for (int i = 0; i < iMax; i++) {
Parameter parameter = ownedParameters.get(i);
if (parameter.isIsTypeof() && (parameter.getType() == templateParameter)) {
OCLExpression argument = object.getOwnedArguments().get(i);
DomainUsage argumentUsage = visit(argument);
return argumentUsage;
}
}
}
DomainUsageAnalysis analysis = rootAnalysis.getAnalysis(operation);
Map<DomainUsage, DomainUsage> referred2specialized = new HashMap<>();
// DomainUsage possibleUsage = union(sourceUsage, union(rootAnalysis.getThisUsage(), rootAnalysis.getPrimitiveUsage()));
List<@NonNull Parameter> ownedParameters = ClassUtil.nullFree(operation.getOwnedParameters());
int iMax = Math.min(ownedParameters.size(), object.getOwnedArguments().size());
for (int i = 0; i < iMax; i++) {
Parameter parameter = ownedParameters.get(i);
OCLExpression argument = object.getOwnedArguments().get(i);
DomainUsage referredParameterUsage = analysis.getUsage(parameter);
DomainUsage specializedParameterUsage;
if (referredParameterUsage.isConstant()) {
specializedParameterUsage = referredParameterUsage;
}
else {
specializedParameterUsage = referred2specialized.get(referredParameterUsage);
if (specializedParameterUsage == null) {
// specializedParameterUsage = new DomainUsageVariable(getRootAnalysis(), referredParameterUsage.getMask());
specializedParameterUsage = ((DomainUsage.Internal)referredParameterUsage).cloneVariable();
referred2specialized.put(referredParameterUsage, specializedParameterUsage);
}
}
/* DomainUsage argumentUsage = */ visit(argument);
// argumentUsage = intersection(argumentUsage, specializedParameterUsage);
// possibleUsage = union(possibleUsage, argumentUsage); -- the operationUsage is 'correct'; consideration of source/arguments cannot refine.
}
DomainUsage operationUsage = analysis.basicGetUsage(operation);
if ((operationUsage != null) && !operationUsage.isConstant()) {
operationUsage = ((DomainUsage.Internal)operationUsage).cloneVariable();
}
DomainUsage operationCallUsage = visit(object.getType());
DomainUsage returnUsage = operationUsage != null ? intersection(operationUsage, operationCallUsage) : operationCallUsage;
return returnUsage;
}
finally {
popSelfUsage(savedUsage);
}
}
@Override
public @NonNull DomainUsage visitOppositePropertyCallExp(@NonNull OppositePropertyCallExp object) {
Property property = ClassUtil.nonNullState(object.getReferredProperty());
Property oppositeProperty = ClassUtil.nonNullState(property.getOpposite());
return doNavigationCallExp(oppositeProperty, object);
}
@Override
public @NonNull DomainUsage visitParameter(@NonNull Parameter object) {
// Operation operation = object.getOwningOperation();
// Map<Element, DomainUsage> map = analyzeOperation(operation);
// return map.get(object);
DomainUsage usage = visit(object.getType());
return getRootAnalysis().getValidOrVariableUsage(usage);
}
@Override
public @NonNull DomainUsage visitPrimitiveType(@NonNull PrimitiveType object) {
return getRootAnalysis().getPrimitiveUsage();
}
@Override
public @NonNull DomainUsage visitProperty(@NonNull Property property) {
DomainUsage annotatedUsage = getRootAnalysis().getAnnotatedUsage(property);
DomainUsage superUsage = super.visitProperty(property);
if (annotatedUsage != null) {
return intersection(annotatedUsage, superUsage);
}
else {
return superUsage;
}
}
@Override
public @NonNull DomainUsage visitPropertyCallExp(@NonNull PropertyCallExp object) {
Property property = ClassUtil.nonNullState(object.getReferredProperty());
return doNavigationCallExp(property, object);
}
@Override
public @NonNull DomainUsage visitSelfType(@NonNull SelfType object) {
return ClassUtil.nonNullState(selfUsage);
}
@Override
public @NonNull DomainUsage visitShadowExp(@NonNull ShadowExp object) {
DomainUsage usage = visit(object.getType());
for (@SuppressWarnings("null")@NonNull ShadowPart part : object.getOwnedParts()) {
/*usage = intersection(usage,*/ visit(part); //);
}
return usage;
}
@Override
public @NonNull DomainUsage visitShadowPart(@NonNull ShadowPart object) {
visit(object.getOwnedInit());
return visit(object.getType());
}
@Override
public @NonNull DomainUsage visitTemplateParameter(@NonNull TemplateParameter object) {
RootDomainUsageAnalysis rootAnalysis = getRootAnalysis();
int anyMask = rootAnalysis.getAnyMask();
DomainUsage usage = rootAnalysis.getValidUsage(anyMask);
if (usage == null) {
usage = rootAnalysis.createVariableUsage(anyMask);
}
return usage;
}
@Override
public @NonNull DomainUsage visitTupleLiteralExp(@NonNull TupleLiteralExp object) {
DomainUsage usage = getRootAnalysis().getAnyUsage();
for (@SuppressWarnings("null")@NonNull TupleLiteralPart part : object.getOwnedParts()) {
usage = intersection(usage, visit(part)); // FIXME need HYBRID
}
return usage;
}
@Override
public @NonNull DomainUsage visitTupleLiteralPart(@NonNull TupleLiteralPart object) {
DomainUsage typeUsage = visit(object.getType());
OCLExpression ownedInit = object.getOwnedInit();
if (ownedInit == null) {
return typeUsage;
}
DomainUsage initUsage = visit(ownedInit);
return intersection(typeUsage, initUsage);
}
@Override
public @NonNull DomainUsage visitTupleType(@NonNull TupleType object) {
DomainUsage usage = getRootAnalysis().getAnyUsage();
for (@SuppressWarnings("null")@NonNull Property part : object.getOwnedProperties()) {
usage = intersection(usage, visit(part.getType()));
}
return usage;
}
@Override
public @NonNull DomainUsage visitType(@NonNull Type object) {
throw new UnsupportedOperationException();
// return getRootAnalysis().getPrimitiveUsage();
}
@Override
public @NonNull DomainUsage visitTypeExp(@NonNull TypeExp object) {
Type referredType = object.getReferredType();
DomainUsage usage;
if (referredType instanceof TemplateParameter) { // XXX Never happens
usage = getTypeUsage(referredType);
}
else {
usage = visit(referredType);
}
return getRootAnalysis().getValidOrVariableUsage(usage);
}
@Override
public @NonNull DomainUsage visitTypedElement(@NonNull TypedElement object) {
return visit(object.getType());
}
@Override
public @NonNull DomainUsage visitVariable(@NonNull Variable object) {
DomainUsage typeUsage = visit(object.getType());
OCLExpression ownedInit = object.getOwnedInit();
if (ownedInit == null) {
return typeUsage;
}
DomainUsage initUsage = visit(ownedInit);
return intersection(typeUsage, initUsage);
}
@Override
public @NonNull DomainUsage visitVariableDeclaration(@NonNull VariableDeclaration object) {
return visit(object.getType());
}
@Override
public @NonNull DomainUsage visitVariableExp(@NonNull VariableExp object) {
VariableDeclaration referredVariable = object.getReferredVariable();
DomainUsage usage = visit(referredVariable);
assert usage != null : referredVariable + " usage not defined";
return usage;
}
}