blob: 7a7665869ad641580064e9d31709980c9397f6a0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* E.D.Willink - initial API and implementation
*******************************************************************************/
package org.eclipse.qvtd.pivot.qvtcorebase.analysis;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.Annotation;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.Detail;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.internal.complete.CompleteModelInternal;
import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.qvtd.pivot.qvtbase.Domain;
import org.eclipse.qvtd.pivot.qvtbase.QVTbaseFactory;
import org.eclipse.qvtd.pivot.qvtbase.Rule;
import org.eclipse.qvtd.pivot.qvtbase.Transformation;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil;
public class RootDomainUsageAnalysis extends AbstractDomainUsageAnalysis
{
protected abstract class AbstractDomainUsage implements DomainUsage.Internal
{
protected final int bitMask;
protected AbstractDomainUsage(int bitMask) {
this.bitMask = bitMask;
}
@Override
public int compareTo(DomainUsage.Internal o) {
return getMask() - o.getMask();
}
@Override
public int getMask() {
return bitMask;
}
@Override
public @Nullable TypedModel getTypedModel() throws IllegalStateException {
int residue = bitMask;
for (int i = 0; residue != 0; i++) {
int bit = 1 << i;
if ((residue & bit) != 0) {
residue &= ~bit;
if (residue == 0) {
return RootDomainUsageAnalysis.this.getTypedModel(i);
}
throw new IllegalStateException("Amiguous TypedModel: " + this);
}
}
return null;
}
@Override
public @NonNull Iterable<TypedModel> getTypedModels() {
List<TypedModel> typedModels = new ArrayList<TypedModel>();
int residue = bitMask;
for (int i = 0; residue != 0; i++) {
int bit = 1 << i;
if ((residue & bit) != 0) {
residue &= ~bit;
typedModels.add(RootDomainUsageAnalysis.this.getTypedModel(i));
}
}
return typedModels;
}
@Override
public boolean isCheckable() {
return (bitMask & checkableUsage.bitMask) != 0;
}
@Override
public boolean isEnforceable() {
return (bitMask & enforceableUsage.bitMask) != 0;
}
@Override
public boolean isMiddle() {
return (bitMask & middleUsage.bitMask) != 0;
}
protected String toString(@NonNull String prefix) {
StringBuilder s = new StringBuilder();
s.append(prefix);
boolean first = true;
for (int i = 0; i < bit2typedModel.size(); i++) {
int iMask = 1 << i;
if ((bitMask & iMask) != 0) {
if (!first) {
s.append("|");
}
s.append(bit2typedModel.get(i).getName());
first = false;
}
}
if (first) {
s.append("$none$");
}
return s.toString();
}
}
/**
* A DomainUsageConstant identifies a specific domain result from the DomainUsageAnalysis of an OCL AST node.
*/
protected class DomainUsageConstant extends AbstractDomainUsage
{
protected DomainUsageConstant(int bitMask) {
super(bitMask);
}
@Override
public void addUsedBy(@NonNull Element element) {}
@Override
public @NonNull DomainUsage cloneVariable() {
return this;
}
@Override
public @Nullable Iterable<Element> getElements() {
return null;
}
@Override
public boolean isConstant() {
return true;
}
@Override
public String toString() {
return toString("Constant ");
}
public @NonNull DomainUsageConstant union(@NonNull DomainUsageConstant usage) {
return RootDomainUsageAnalysis.this.getConstantUsage(bitMask | usage.bitMask);
}
}
/**
* A DomainUsageVariable identifies a constrained domain result from the DomainUsageAnalysis of an OCL AST node.
*/
protected class DomainUsageVariable extends AbstractDomainUsage
{
protected final @NonNull List<Element> usedBy = new ArrayList<Element>();
protected DomainUsageVariable(int bitMask) {
super(bitMask);
assert bitMask != 0;
}
@Override
public void addUsedBy(@NonNull Element element) {
usedBy.add(element);
}
@Override
public @NonNull DomainUsage cloneVariable() {
return new DomainUsageVariable(bitMask);
}
@Override
public @Nullable Iterable<Element> getElements() {
return usedBy;
}
@Override
public boolean isConstant() {
return false;
}
@Override
public String toString() {
return toString("Variable ");
}
}
/**
* A Nested DomainUsageAnalysis is used for a nested context such as an operation which is analyzed without
* regard to its invocation parameters. The invoking context is responsible for 'specializing' the generic
* domain analysis to suit the invocations.
*/
protected class Nested extends AbstractDomainUsageAnalysis
{
protected Nested() {
super(RootDomainUsageAnalysis.this.getEnvironmentFactory());
}
@Override
protected @NonNull RootDomainUsageAnalysis getRootAnalysis() {
return RootDomainUsageAnalysis.this;
}
}
/**
* No TypedModels is used by control infrastructure such as MappingLoops.
*/
private static final @NonNull Integer NONE_USAGE_BIT_MASK = 0;
/**
* The first bit is reserved for the primitive TypedModel that is used by DataTypes.
*/
private static final @NonNull Integer PRIMITIVE_USAGE_BIT_MASK = 1;
/**
* The model name to typed model bit mapping.
*/
protected final @NonNull Map<String, Integer> name2bit = new HashMap<String, Integer>();
/**
* The bit number to typed model 'mapping'.
*/
protected final @NonNull List<TypedModel> bit2typedModel = new ArrayList<TypedModel>();
/**
* Map from Integer to all in-use Constant Usages
*/
private final @NonNull Map<Integer, DomainUsageConstant> constantUsages = new HashMap<Integer, DomainUsageConstant>();
/**
* Map from Integer to all single TypedModel Constant Usages
*/
private final @NonNull Map<Integer, DomainUsageConstant> validUsages = new HashMap<Integer, DomainUsageConstant>();
/**
* The TypedModels that are not primitive and not checkable and not enforceable.
*/
private DomainUsageConstant middleUsage = null;
/**
* The TypedModels that are checkable.
*/
private DomainUsageConstant checkableUsage = null;
/**
* The TypedModels that are enforceable.
*/
private DomainUsageConstant enforceableUsage = null;
/**
* The domains in which each class may be used.
*/
protected final @NonNull Map<org.eclipse.ocl.pivot.Class, DomainUsageConstant> class2usage = new HashMap<org.eclipse.ocl.pivot.Class, DomainUsageConstant>();
/**
* The domains in which the containing class of a property may be used.
*/
protected final @NonNull Map<Property, DomainUsage> property2containingClassUsage = new HashMap<Property, DomainUsage>();
/**
* The domains in which the referred type of a property may be used.
*/
protected final @NonNull Map<Property, DomainUsage> property2referredTypeUsage = new HashMap<Property, DomainUsage>();
/**
* The nested analyses for declared operations.
*/
protected final @NonNull Map<Operation, DomainUsageAnalysis.Internal> operation2analysis = new HashMap<Operation, DomainUsageAnalysis.Internal>();
protected RootDomainUsageAnalysis(@NonNull EnvironmentFactoryInternal environmentFactory) {
super(environmentFactory);
TypedModel primitiveTypeModel = QVTbaseFactory.eINSTANCE.createTypedModel();
primitiveTypeModel.setName("$primitive$");
add(primitiveTypeModel);
validUsages.put(NONE_USAGE_BIT_MASK, getConstantUsage(NONE_USAGE_BIT_MASK));
validUsages.put(PRIMITIVE_USAGE_BIT_MASK, getConstantUsage(PRIMITIVE_USAGE_BIT_MASK));
}
protected int add(@NonNull TypedModel typedModel) {
int nextBit = bit2typedModel.size();
bit2typedModel.add(typedModel);
name2bit.put(typedModel.getName(), nextBit);
return nextBit;
}
public @NonNull DomainUsageAnalysis analyzeOperation(@NonNull Operation object) {
DomainUsageAnalysis.Internal analysis = operation2analysis.get(object);
if (analysis == null) {
analysis = createNestedAnalysis();
operation2analysis.put(object, analysis);
DomainUsage usage = analysis.visit(object);
setUsage(object, usage);
}
return analysis;
}
public @NonNull Map<Element, DomainUsage> analyzeTransformation(@NonNull Transformation transformation) {
int checkableMask = 0;
int enforceableMask = 0;
CompleteModelInternal completeModel = context.getCompleteModel();
for (@SuppressWarnings("null")@NonNull TypedModel typedModel : transformation.getModelParameter()) {
int nextBit = add(typedModel);
int bitMask = 1 << nextBit;
DomainUsageConstant typedModelUsage = getConstantUsage(bitMask);
validUsages.put(bitMask, typedModelUsage);
boolean isCheckable = false;
boolean isEnforceable = false;
for (Rule rule : transformation.getRule()) {
for (Domain domain : rule.getDomain()) {
if (domain.getTypedModel() == typedModel) {
if (domain.isIsCheckable()) {
isCheckable = true;
}
if (domain.isIsEnforceable()) {
isEnforceable = true;
}
}
}
}
if (isCheckable) {
checkableMask |= bitMask;
}
if (isEnforceable) {
enforceableMask |= bitMask;
}
setUsage(typedModel, typedModelUsage);
Variable ownedContext = typedModel.getOwnedContext();
if (ownedContext != null) {
setUsage(ownedContext, typedModelUsage);
}
Set<CompleteClass> completeClasses = new HashSet<CompleteClass>();
for (org.eclipse.ocl.pivot.Package asPackage : QVTbaseUtil.getAllUsedPackages(typedModel)) {
for (org.eclipse.ocl.pivot.Class asClass : asPackage.getOwnedClasses()) {
if (asClass != null) {
for (CompleteClass completeClass : completeModel.getCompleteClass(asClass).getSuperCompleteClasses()) {
completeClasses.add(completeClass);
}
}
}
}
for (CompleteClass completeClass : completeClasses) {
for (org.eclipse.ocl.pivot.Class asClass : completeClass.getPartialClasses()) {
DomainUsageConstant oldUsage = class2usage.get(asClass);
DomainUsageConstant newUsage = oldUsage != null ? typedModelUsage.union(oldUsage) : typedModelUsage;
class2usage.put(asClass, newUsage);
}
}
}
for (org.eclipse.ocl.pivot.Class asClass : class2usage.keySet()) {
DomainUsage newUsage = class2usage.get(asClass);
for (Property property : asClass.getOwnedProperties()) {
property2containingClassUsage.put(property, newUsage);
DomainUsage referredTypeUsage = null;
for (Element annotation : property.getOwnedAnnotations()) {
if (annotation instanceof Annotation) {
Annotation annotation2 = (Annotation)annotation;
if (DomainUsage.QVT_DOMAINS_ANNOTATION_SOURCE.equals(annotation2.getName())) {
for (Detail detail : annotation2.getOwnedDetails()) {
if (DomainUsage.QVT_DOMAINS_ANNOTATION_REFERRED_DOMAIN.equals(detail.getName())) {
int mask = 0;
for (String value : detail.getValues()) {
Integer bit = name2bit.get(value.trim());
if (bit != null) {
mask |= 1 << bit;
}
}
referredTypeUsage = getValidUsage(mask);
}
}
}
}
}
if (referredTypeUsage == null) {
referredTypeUsage = visit(property.getType());
}
property2referredTypeUsage.put(property, referredTypeUsage);
}
}
class2usage.put(context.getStandardLibrary().getOclTypeType(), getAnyUsage()); // Needed by oclIsKindOf() etc
checkableUsage = getConstantUsage(getAnyMask() & checkableMask);
enforceableUsage = getConstantUsage(getAnyMask() & enforceableMask);
middleUsage = getConstantUsage(getAnyMask() & ~checkableMask & ~enforceableMask);
Variable ownedContext = transformation.getOwnedContext();
if (ownedContext != null) {
setUsage(ownedContext, getAnyUsage());
}
visit(transformation);
return element2usage;
}
protected @NonNull Nested createNestedAnalysis() {
return new Nested();
}
public @NonNull DomainUsage createVariableUsage(int intersectionMask) {
return new DomainUsageVariable(intersectionMask);
}
public @NonNull DomainUsageAnalysis getAnalysis(@NonNull Operation operation) {
DomainUsageAnalysis analysis = operation2analysis.get(operation);
if (analysis == null) {
analysis = analyzeOperation(operation);
}
return analysis;
}
protected int getAnyMask() {
return (1 << bit2typedModel.size()) - 1;
}
public @NonNull DomainUsageConstant getAnyUsage() {
return getConstantUsage(getAnyMask());
}
public @NonNull DomainUsage getCheckableUsage() {
return ClassUtil.nonNullState(checkableUsage);
}
public @NonNull DomainUsageConstant getConstantUsage(int bitMask) {
DomainUsageConstant usage = constantUsages.get(bitMask);
if (usage == null) {
usage = new DomainUsageConstant(bitMask);
constantUsages.put(bitMask, usage);
}
return usage;
}
public @NonNull DomainUsage getEnforceableUsage() {
return ClassUtil.nonNullState(enforceableUsage);
}
public @NonNull DomainUsage getMiddleUsage() {
return ClassUtil.nonNullState(middleUsage);
}
@SuppressWarnings("null")
public @NonNull DomainUsage getNoneUsage() {
return constantUsages.get(NONE_USAGE_BIT_MASK);
}
@SuppressWarnings("null")
public @NonNull DomainUsage getPrimitiveUsage() {
return constantUsages.get(PRIMITIVE_USAGE_BIT_MASK);
}
@Override
protected @NonNull RootDomainUsageAnalysis getRootAnalysis() {
return this;
}
@SuppressWarnings("null")
public @NonNull TypedModel getTypedModel(int i) {
return bit2typedModel.get(i);
}
public @Nullable DomainUsageConstant getValidUsage(int bitMask) {
return validUsages.get(bitMask);
}
}