/******************************************************************************* * Copyright (c) 2011, 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.ocl.pivot.internal.manager; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.ocl.pivot.Element; import org.eclipse.ocl.pivot.Property; import org.eclipse.ocl.pivot.TemplateParameter; import org.eclipse.ocl.pivot.TupleType; import org.eclipse.ocl.pivot.Type; import org.eclipse.ocl.pivot.TypedElement; import org.eclipse.ocl.pivot.ids.IdManager; import org.eclipse.ocl.pivot.ids.IdResolver; import org.eclipse.ocl.pivot.ids.TemplateParameterId; import org.eclipse.ocl.pivot.ids.TuplePartId; import org.eclipse.ocl.pivot.ids.TupleTypeId; import org.eclipse.ocl.pivot.ids.TypeId; import org.eclipse.ocl.pivot.internal.TuplePartImpl; import org.eclipse.ocl.pivot.internal.TupleTypeImpl; import org.eclipse.ocl.pivot.internal.TypedElementImpl; import org.eclipse.ocl.pivot.internal.complete.CompleteEnvironmentInternal; import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal; import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal; import org.eclipse.ocl.pivot.utilities.ClassUtil; import org.eclipse.ocl.pivot.utilities.NameUtil; import org.eclipse.ocl.pivot.values.TemplateParameterSubstitutions; /** * TupleTypeManager encapsulates the knowledge about known tuple types. */ public class TupleTypeManager { protected static class TupleIdResolver extends PivotIdResolver { private final TemplateParameterReferencesVisitor referencesVisitor; private TupleIdResolver(@NonNull EnvironmentFactoryInternal environmentFactory, TemplateParameterReferencesVisitor referencesVisitor) { super(environmentFactory); this.referencesVisitor = referencesVisitor; } @Override public @NonNull Element visitTemplateParameterId(@NonNull TemplateParameterId id) { int index = id.getIndex(); TemplateParameter templateParameter = referencesVisitor.templateParameters.get(index); if (templateParameter != null) { return templateParameter; } return super.visitTemplateParameterId(id); } } /** * TuplePart provides a convenient descriptor for a tuple part complying with the full EMF model protocols. */ public static class TuplePart extends TypedElementImpl { protected final @NonNull TuplePartId partId; public TuplePart(@NonNull TuplePartId partId) { this.partId = partId; setName(partId.getName()); } @Override public @NonNull TypeId getTypeId() { return partId.getTypeId(); } @Override public String toString() { return String.valueOf(name) + " : " + String.valueOf(type); } } /** * The TemplateParameterReferencesVisitor remembers the formal TemplateParameter for re-uyse during Tuple instantiation. */ protected static class TemplateParameterReferencesVisitor extends TemplateParameterSubstitutionVisitor { protected final @NonNull Map<Integer, TemplateParameter> templateParameters = new HashMap<Integer, TemplateParameter>(); public TemplateParameterReferencesVisitor(@NonNull EnvironmentFactoryInternal environmentFactory, Collection<? extends Type> partValues) { super(environmentFactory, null, null); for (Type partValue : partValues) { analyzeType(partValue, partValue); } } @Override public @NonNull Type put(@NonNull TemplateParameter formalTemplateParameter, @NonNull Type actualType) { templateParameters.put(formalTemplateParameter.getTemplateParameterId().getIndex(), formalTemplateParameter); return super.put(formalTemplateParameter, actualType); } } protected final @NonNull CompleteEnvironmentInternal completeEnvironment; protected final @NonNull PivotMetamodelManager metamodelManager; protected final org.eclipse.ocl.pivot.@NonNull Class oclTupleType; /** * Map from the tuple typeId to the tuple type. */ private @Nullable Map<TupleTypeId, TupleType> tupleid2tuple = null; public TupleTypeManager(@NonNull CompleteEnvironmentInternal allCompleteClasses) { this.completeEnvironment = allCompleteClasses; this.metamodelManager = allCompleteClasses.getEnvironmentFactory().getMetamodelManager(); this.oclTupleType = metamodelManager.getStandardLibrary().getOclTupleType(); } public void dispose() { tupleid2tuple = null; } public @Nullable Type getCommonType(@NonNull TupleType leftType, @NonNull TemplateParameterSubstitutions leftSubstitutions, @NonNull TupleType rightType, @NonNull TemplateParameterSubstitutions rightSubstitutions) { List<Property> leftProperties = leftType.getOwnedProperties(); List<Property> rightProperties = rightType.getOwnedProperties(); int iSize = leftProperties.size(); if (iSize != rightProperties.size()) { return null; } List<@NonNull TuplePartId> commonPartIds = new ArrayList<@NonNull TuplePartId>(iSize); for (int i = 0; i < iSize; i++) { Property leftProperty = leftProperties.get(i); if (leftProperty == null) { return null; // Never happens } String name = leftProperty.getName(); if (name == null) { return null; // Never happens } Property rightProperty = NameUtil.getNameable(rightProperties, name); if (rightProperty == null) { return null; // Happens for inconsistent tuples } Type leftPropertyType = leftProperty.getType(); if (leftPropertyType == null) { return null; // Never happens } Type rightPropertyType = rightProperty.getType(); if (rightPropertyType == null) { return null; // Never happens } Type commonType = metamodelManager.getCommonType(leftPropertyType, leftSubstitutions, rightPropertyType, rightSubstitutions); TuplePartId commonPartId = IdManager.getTuplePartId(i, name, commonType.getTypeId()); commonPartIds.add(commonPartId); } TupleTypeId commonTupleTypeId = IdManager.getTupleTypeId(TypeId.TUPLE_NAME, commonPartIds); return getTupleType(metamodelManager.getEnvironmentFactory().getIdResolver(), commonTupleTypeId); } public @NonNull TupleType getTupleType(@NonNull IdResolver idResolver, @NonNull TupleTypeId tupleTypeId) { Map<TupleTypeId, TupleType> tupleid2tuple2 = tupleid2tuple; if (tupleid2tuple2 == null) { synchronized (this) { tupleid2tuple2 = tupleid2tuple; if (tupleid2tuple2 == null) { tupleid2tuple2 = tupleid2tuple = new HashMap<TupleTypeId, TupleType>(); } } } TupleType tupleType = tupleid2tuple2.get(tupleTypeId); if (tupleType == null) { synchronized (tupleid2tuple2) { tupleType = tupleid2tuple2.get(tupleTypeId); if (tupleType == null) { tupleType = new TupleTypeImpl(tupleTypeId); TuplePartId[] partIds = tupleTypeId.getPartIds(); List<Property> ownedAttributes = tupleType.getOwnedProperties(); for (TuplePartId partId : partIds) { Type partType = idResolver.getType(partId.getTypeId(), tupleType); Type partType2 = metamodelManager.getPrimaryType(partType); ownedAttributes.add(new TuplePartImpl(partId, partType2)); } tupleType.getSuperClasses().add(oclTupleType); tupleid2tuple2.put(tupleTypeId, tupleType); completeEnvironment.addOrphanClass(tupleType); } } } return tupleType; } public @NonNull TupleType getTupleType(@NonNull String tupleName, @NonNull Collection<? extends TypedElement> parts, @Nullable TemplateParameterSubstitutions usageBindings) { Map<String, Type> partMap = new HashMap<String, Type>(); for (TypedElement part : parts) { Type type1 = part.getType(); if (type1 != null) { Type type2 = metamodelManager.getPrimaryType(type1); Type type3 = completeEnvironment.getSpecializedType(type2, usageBindings); partMap.put(part.getName(), type3); } } return getTupleType(tupleName, partMap); } /** * Return the named tuple typeId with the defined parts (which need not be alphabetically ordered). */ public @NonNull TupleType getTupleType(@NonNull String tupleName, @NonNull Map<String, ? extends Type> parts) { // // Find the outgoing template parameter references // FIXME this should be more readily and reliably computed in the caller @NonNull Collection<? extends Type> partValues = parts.values(); final TemplateParameterReferencesVisitor referencesVisitor = new TemplateParameterReferencesVisitor(metamodelManager.getEnvironmentFactory(), partValues); // FIXME this isn't realistically extensible // // Create the tuple part ids // int partsCount = parts.size(); @NonNull TuplePartId[] newPartIds = new @NonNull TuplePartId[partsCount]; List<String> sortedPartNames = new ArrayList<String>(parts.keySet()); Collections.sort(sortedPartNames); for (int i = 0; i < partsCount; i++) { @SuppressWarnings("null")@NonNull String partName = sortedPartNames.get(i); Type partType = parts.get(partName); if (partType != null) { TypeId partTypeId = partType.getTypeId(); TuplePartId tuplePartId = IdManager.getTuplePartId(i, partName, partTypeId); newPartIds[i] = tuplePartId; } } // // Create the tuple type id (and then specialize it) // TupleTypeId tupleTypeId = IdManager.getOrderedTupleTypeId(tupleName, newPartIds); PivotIdResolver pivotIdResolver = new TupleIdResolver(metamodelManager.getEnvironmentFactory(), referencesVisitor); // // Finally create the (specialize) tuple type // TupleType tupleType = getTupleType(pivotIdResolver /*metamodelManager.getIdResolver()*/, tupleTypeId); return tupleType; } public @NonNull TupleType getTupleType(@NonNull TupleType type, @Nullable TemplateParameterSubstitutions usageBindings) { // FIXME Remove duplication, unify type/multiplicity // return getTupleType(type.getName(), type.getOwnedAttribute(), usageBindings); TupleType specializedTupleType = type; Map<String, Type> resolutions = null; List<Property> parts = specializedTupleType.getOwnedProperties(); for (Property part : parts) { if (part != null) { Type propertyType = PivotUtilInternal.getType(part); if (propertyType != null) { Type resolvedPropertyType = completeEnvironment.getSpecializedType(propertyType, usageBindings); if (resolvedPropertyType != propertyType) { if (resolutions == null) { resolutions = new HashMap<String, Type>(); } resolutions.put(NameUtil.getSafeName(part), resolvedPropertyType); } } } } if (resolutions != null) { List<@NonNull TuplePartId> partIds = new ArrayList<@NonNull TuplePartId>(parts.size()); for (int i = 0; i < parts.size(); i++) { @SuppressWarnings("null") @NonNull Property part = parts.get(i); String partName = NameUtil.getSafeName(part); Type resolvedPropertyType = resolutions.get(partName); TypeId partTypeId = resolvedPropertyType != null ? resolvedPropertyType.getTypeId() : part.getTypeId(); TuplePartId tuplePartId = IdManager.getTuplePartId(i, partName, partTypeId); partIds.add(tuplePartId); } TupleTypeId tupleTypeId = IdManager.getTupleTypeId(ClassUtil.nonNullModel(type.getName()), partIds); specializedTupleType = getTupleType(metamodelManager.getEnvironmentFactory().getIdResolver(), tupleTypeId); return specializedTupleType; } else { return getTupleType(NameUtil.getSafeName(type), type.getOwnedProperties(), usageBindings); } } }