blob: c364d68c30db7d1969c7e8a299ba2008e8d9dab6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017, 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.pivot.internal.resource;
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.CollectionLiteralPart;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.Constraint;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.EnumerationLiteral;
import org.eclipse.ocl.pivot.Feature;
import org.eclipse.ocl.pivot.Iteration;
import org.eclipse.ocl.pivot.LambdaType;
import org.eclipse.ocl.pivot.MapLiteralPart;
import org.eclipse.ocl.pivot.Model;
import org.eclipse.ocl.pivot.NamedElement;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.Parameter;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.ShadowPart;
import org.eclipse.ocl.pivot.TemplateBinding;
import org.eclipse.ocl.pivot.TemplateParameter;
import org.eclipse.ocl.pivot.TemplateParameterSubstitution;
import org.eclipse.ocl.pivot.TemplateSignature;
import org.eclipse.ocl.pivot.TemplateableElement;
import org.eclipse.ocl.pivot.TupleLiteralPart;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.WildcardType;
import org.eclipse.ocl.pivot.internal.manager.Orphanage;
import org.eclipse.ocl.pivot.resource.ASResource;
import org.eclipse.ocl.pivot.utilities.NameUtil;
import org.eclipse.ocl.pivot.utilities.PivotConstants;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.ocl.pivot.values.Unlimited;
import com.google.common.collect.Iterables;
/**
* The LUSSIDs class maintains the element to LUSSID and LUSSID to element mapping for the elements
* of an ASResource. It also privides the ability to return predictable xmi:id values.
*
* An xmi:id is provided for every explicitly referenced, and every potentially externally referenced element,
* so that the fall-back the @x/@y.1 style id referemces is never required.
*
* The xmi:id typically comprises a 5 Base64-like letter encoding of the bottom 30 bits of the LUSSID of the element.
* Additional Base64 letters are occasionally needed to avoid duplicates.
*
* The LUSSID (Locally Unique Semantically Sentsitive ID) is the hashcode of the hierarchical path of the element.
* The resource location, model name and external URI are ignored avoiding dependence on location and URI.
* Elements within ordered collections use the index, but elements within unordered collections use a further
* local LUSSID that captures the name / template bindings / parameter names / collection bounds so that LUSSID
* have substantial tolerance to insignaicant reordering of elements.
* @since 1.4
*/
public class PivotLUSSIDs extends LUSSIDs
{
private org.eclipse.ocl.pivot.Package typeOrphanage = null;
private org.eclipse.ocl.pivot.Class featureOrphanage = null;
public PivotLUSSIDs(@NonNull ASResource asResource, @NonNull Map<@NonNull Object, @Nullable Object> options) {
super(asResource, options);
for (EObject eRoot : asResource.getContents()) {
if (eRoot instanceof Model) {
for (org.eclipse.ocl.pivot.Package asPackage : ((Model)eRoot).getOwnedPackages()) {
if (Orphanage.isTypeOrphanage(asPackage)) {
typeOrphanage = asPackage;
featureOrphanage = NameUtil.getNameable(asPackage.getOwnedClasses(), PivotConstants.ORPHANAGE_NAME);
break;
}
}
break;
}
}
}
/**
* Return the hash of the aspects of element that distinguish it from its siblings.
* Return null if there are no distinguishing aspects.
*/
@Override
protected @Nullable Integer computeLocalLUSSID(@NonNull AS2ID as2id, @NonNull EObject element, boolean normalizeTemplateParameters) {
assert asResource == element.eResource();
int localId = 0;
if (!(element instanceof NamedElement)) {
return null;
}
String name = ((NamedElement)element).getName();
if (name == null) {
return null;
}
localId += name.hashCode();
if (element instanceof TemplateableElement) {
for (@NonNull TemplateBinding templateBinding : PivotUtil.getOwnedBindings((TemplateableElement)element)) {
for (@NonNull TemplateParameterSubstitution templateParameterSubstitution : PivotUtil.getOwnedSubstitutions(templateBinding)) {
Element actual = templateParameterSubstitution.getActual();
if (actual instanceof WildcardType) {
localId += TEMPLATE_BINDING_MULTIPLIER;
}
else if (actual instanceof Type) {
localId += TEMPLATE_BINDING_MULTIPLIER * computeReferenceLUSSID(as2id, (Type) actual, normalizeTemplateParameters);
}
else if (actual != null) {
localId += TEMPLATE_BINDING_MULTIPLIER * as2id.assignLUSSID(actual, false, normalizeTemplateParameters);
}
}
}
if (element instanceof CollectionType) {
CollectionType collectionType = (CollectionType)element;
if (!collectionType.isIsNullFree()) {
localId += COLLECTION_IS_NULL_FREE_MULTIPLIER;
}
int lowerValue = collectionType.getLower().intValue();
if (lowerValue != 0) {
localId += COLLECTION_LOWER_BOUND_MULTIPLIER * lowerValue;
}
Number upper = collectionType.getUpper();
if (!(upper instanceof Unlimited)) {
localId += COLLECTION_UPPER_BOUND_MULTIPLIER * upper.intValue();
}
}
else if (element instanceof LambdaType) {
LambdaType lambdaType = (LambdaType)element;
localId += LAMBDA_TYPE_CONTEXT_MULTIPLIER * computeReferenceLUSSID(as2id, PivotUtil.getContextType(lambdaType), normalizeTemplateParameters);
int lambdaTypeParameterTypeMultiplier = LAMBDA_TYPE_PARAMETER_TYPE_MULTIPLIER;
for (@NonNull Type parameterType : PivotUtil.getParameterType(lambdaType)) {
localId += lambdaTypeParameterTypeMultiplier * computeReferenceLUSSID(as2id, parameterType, normalizeTemplateParameters);
lambdaTypeParameterTypeMultiplier += SIBLING_INDEX_MULTIPLIER * LAMBDA_TYPE_PARAMETER_TYPE_MULTIPLIER;
}
localId += LAMBDA_TYPE_RETURN_TYPE_MULTIPLIER * computeReferenceLUSSID(as2id, PivotUtil.getResultType(lambdaType), normalizeTemplateParameters);
}
else if (element instanceof Iteration) {
Iterable<@NonNull Parameter> parameters = Iterables.concat(PivotUtil.getOwnedIterators((Iteration)element), PivotUtil.getOwnedAccumulators((Iteration)element));
localId += computeParametersLUSSID(as2id, parameters);
}
else if (element instanceof Operation) {
localId += computeParametersLUSSID(as2id, PivotUtil.getOwnedParameters((Operation)element));
}
}
else if (element instanceof Property) {
Property property = (Property)element;
if (property.isIsImplicit()) {
Property oppositeProperty = property.getOpposite();
if (oppositeProperty != null) {
String oppositeName = oppositeProperty.getName();
if (oppositeName != null) {
localId += OPPOSITE_PROPERTY_NAME_MULTIPLIER * oppositeName.hashCode();
}
}
else { // Never happens
System.out.println("No opposite for " + element);
}
}
}
return Integer.valueOf(localId);
}
protected int computeParametersLUSSID(@NonNull AS2ID as2id, @NonNull Iterable<@NonNull Parameter> parameters) {
int parametersLUSSID = 0;
int parameterIndex = 1;
for (@NonNull Parameter parameter : parameters) {
int index = -1;
Type parameterType = parameter.getType();
if (parameterType instanceof TemplateParameter) {
TemplateSignature templateSignature = ((TemplateParameter)parameterType).getOwningSignature();
if (templateSignature != null) {
List<TemplateParameter> templateParameters = templateSignature.getOwnedParameters();
index = templateParameters.indexOf(parameterType);
}
}
if (index >= 0) {
parametersLUSSID += parameterIndex * TEMPLATE_PARAMETER_INDEX_MULTIPLIER * (index + 1);
}
else if (parameterType != null) {
parametersLUSSID += parameterIndex * OPERATION_PARAMETER_TYPE_MULTIPLIER * computeReferenceLUSSID(as2id, parameterType, true);
}
parameterIndex++;
}
return parametersLUSSID;
}
protected int computeReferenceLUSSID(@NonNull AS2ID as2id, @NonNull Type type, boolean normalizeTemplateParameters) {
if (normalizeTemplateParameters && (type instanceof TemplateParameter)) {
boolean gotIt = false;
int index = 0;
for (EObject eContainer = type.eContainer(); eContainer != null; eContainer = eContainer.eContainer()) {
if (eContainer instanceof TemplateableElement) {
TemplateSignature templateSignature = ((TemplateableElement)eContainer).getOwnedSignature();
if (templateSignature != null) {
List<TemplateParameter> templateParameters = templateSignature.getOwnedParameters();
int localIndex = templateParameters.indexOf(type);
if (localIndex >= 0) {
index += localIndex;
gotIt = true;
}
else {
index += templateParameters.size();
}
}
}
}
if (gotIt) {
return TEMPLATE_PARAMETER_INDEX_MULTIPLIER * index;
}
}
return as2id.assignLUSSID(type, false, true);
}
@Override
protected boolean isExternallyReferenceable(@NonNull EObject eObject) {
if (eObject instanceof Type) { // Class, TemplateParameter
if ((typeOrphanage == null) || (eObject.eContainer() != typeOrphanage)) {
return true;
}
}
else if (eObject instanceof org.eclipse.ocl.pivot.Package) { // Profile
if (eObject != typeOrphanage) {
return true;
}
}
else if (eObject instanceof Feature) { // Iteration, Operation, Property
if ((featureOrphanage == null) || (eObject.eContainer() != featureOrphanage)) {
return true;
}
}
else if (eObject instanceof CollectionLiteralPart) {
return true;
}
else if (eObject instanceof Constraint) {
return true;
}
else if (eObject instanceof EnumerationLiteral) {
return true;
}
else if (eObject instanceof MapLiteralPart) {
return true;
}
else if (eObject instanceof Model) {
return true;
}
else if (eObject instanceof Parameter) {
return true;
}
else if (eObject instanceof ShadowPart) {
return true;
}
else if (eObject instanceof TupleLiteralPart) {
return true;
}
return false;
}
}