blob: fca5141e2176c2b55949932fdbad417e095ba700 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2016 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.utilities;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
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.Iteration;
import org.eclipse.ocl.pivot.LambdaType;
import org.eclipse.ocl.pivot.MapType;
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.PivotPackage;
import org.eclipse.ocl.pivot.Precedence;
import org.eclipse.ocl.pivot.PrimitiveType;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.SelfType;
import org.eclipse.ocl.pivot.TemplateParameter;
import org.eclipse.ocl.pivot.TemplateSignature;
import org.eclipse.ocl.pivot.TemplateableElement;
import org.eclipse.ocl.pivot.TupleType;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.VariableDeclaration;
import org.eclipse.ocl.pivot.internal.manager.Orphanage;
import org.eclipse.ocl.pivot.internal.utilities.AS2XMIid;
import org.eclipse.ocl.pivot.internal.utilities.PivotConstantsInternal;
import org.eclipse.ocl.pivot.util.AbstractExtendingVisitor;
import org.eclipse.ocl.pivot.util.Visitable;
/**
* The AS2XMIidVisitor generates an xmi:id for an AS element. Using one of three policies.
* <p>
* null - no xmi:id generated - saves space
* <p>
* false - xmi:id generated/reuses UUID - UUID only used internally so no need for predicatability
* <p>
* true - xmi:id generated/reuses friendly name - ID may be independently generated - must be predictable
* <p>
* Simple elements such as Package/Type/Property get a dot-separated hierarchical name.
* <p>
* Operations get a dot-separated hierarchical name suffixed by dot-dot-separated argument types.
* <p>
* Internally referenceable elements such as TemplateSignature get a UUID, reusing any xmi:id provided
* in the context Moniker to XMIId Map.
*
*/
public class AS2XMIidVisitor extends AbstractExtendingVisitor<Boolean, AS2XMIid>
{
public static final int OVERFLOW_LIMIT = 1024;
public static final @NonNull String OVERFLOW_MARKER = "##"; //$NON-NLS-1$
public static final @NonNull String NULL_MARKER = "-null-element-"; //$NON-NLS-1$
public static final @NonNull String FRAGMENT_SEPARATOR = "#"; //$NON-NLS-1$
public static final @NonNull String ACCUMULATOR_PREFIX = "a"; //$NON-NLS-1$
public static final @NonNull String BODYCONDITION_PREFIX = "c="; //$NON-NLS-1$
/**
* @since 1.1
*/
public static final @NonNull String ENUMERATION_LITERAL_PREFIX = "e."; //$NON-NLS-1$
public static final @NonNull String INVARIANT_PREFIX = "ci"; //$NON-NLS-1$
public static final @NonNull String ITERATION_PREFIX = "i."; //$NON-NLS-1$
public static final @NonNull String ITERATOR_PREFIX = "i"; //$NON-NLS-1$
public static final @NonNull String OPERATION_PREFIX = "o."; //$NON-NLS-1$
public static final @NonNull String PARAMETER_PREFIX = "p"; //$NON-NLS-1$
public static final @NonNull String PACKAGE_PREFIX = "P."; //$NON-NLS-1$
public static final @NonNull String POSTCONDITION_PREFIX = "c+"; //$NON-NLS-1$
public static final @NonNull String PRECONDITION_PREFIX = "c-"; //$NON-NLS-1$
public static final @NonNull String PRECEDENCE_PREFIX = "Z."; //$NON-NLS-1$
public static final @NonNull String PROPERTY_PREFIX = "p."; //$NON-NLS-1$
public static final @NonNull String TEMPLATE_PARAMETER_PREFIX = "t."; //$NON-NLS-1$
public static final @NonNull String TEMPLATE_SIGNATURE_PREFIX = "s."; //$NON-NLS-1$
public static final @NonNull String TYPE_PREFIX = "T."; //$NON-NLS-1$
public static final @NonNull String OPERATION_PARAMETER_SEPARATOR = ".."; //$NON-NLS-1$
public static final @NonNull String SCOPE_SEPARATOR = "."; //$NON-NLS-1$
public static final @NonNull String TEMPLATE_PARAMETER_SEPARATOR = ".."; //$NON-NLS-1$
protected final @NonNull StringBuilder s = new StringBuilder();
public AS2XMIidVisitor(@NonNull AS2XMIid context) {
super(context);
}
protected void appendName(@Nullable String name) {
if (name == null) {
s.append(NULL_MARKER);
}
else {
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (('0' <= c) && (c <= '9')) {
s.append(c);
}
else if (('A' <= c) && (c <= 'Z')) {
s.append(c);
}
else if (('a' <= c) && (c <= 'z')) {
s.append(c);
}
else if (c == '_') {
s.append(c);
}
else if (c == '$') {
s.append(c);
}
else if (c == '%') {
s.append("%%");
}
else {
s.append("%");
s.append((int)c);
s.append("%");
}
}
}
}
protected void appendNameOf(@NonNull Object element) {
if (element instanceof Nameable) {
s.append(((Nameable)element).getName());
}
else {
s.append(System.identityHashCode(element));
}
}
protected void appendOperation(Operation object) {
appendParent(object);
appendName(object.getName());
List<Parameter> parameters = object instanceof Iteration ? ((Iteration)object).getOwnedIterators() : object.getOwnedParameters();
for (Parameter parameter : parameters) {
s.append(OPERATION_PARAMETER_SEPARATOR);
appendType(parameter.getType());
}
}
protected void appendParent(@Nullable EObject element) {
if (toString().length() >= OVERFLOW_LIMIT) {
s.append(OVERFLOW_MARKER);
}
else if (element == null) {
s.append(NULL_MARKER);
s.append(SCOPE_SEPARATOR);
}
else {
EObject eContainer = element.eContainer();
if (eContainer instanceof Model) {
}
else if (eContainer != null) {
// if (!(eContainer instanceof TemplateParameter)) {
appendParent(eContainer);
// }
appendNameOf(eContainer);
s.append(SCOPE_SEPARATOR);
}
}
}
protected void appendType(@Nullable Type type) {
if (type == null) {
s.append(NULL_MARKER);
}
else {
if (type.isClass() != null) {
appendParent(type);
}
appendName(type.getName());
}
}
public @Nullable String getID(@NonNull Element element, boolean internalUUIDs) {
Boolean status = element.accept(this);
if (status == null) {
return null;
}
else if (status) {
return s.toString();
}
else {
return context.getID(element, internalUUIDs);
}
}
@Override
public String toString() {
return s.toString();
}
@Override
public Boolean visitClass(org.eclipse.ocl.pivot.@NonNull Class object) {
if (Orphanage.isTypeOrphanage(object.getOwningPackage())) {
return false;
}
String name = object.getName();
if (PivotConstantsInternal.WILDCARD_NAME.equals(name)) {
if (Orphanage.isTypeOrphanage(PivotUtil.getContainingPackage(object))) {
return false;
}
}
s.append(TYPE_PREFIX);
appendParent(object);
appendName(name);
return true;
}
@Override
public @Nullable Boolean visitCollectionType(@NonNull CollectionType object) {
if (object.getOwnedBindings().isEmpty()) {
appendName(object.getName());
return true;
}
else {
return false;
}
}
@Override
public @Nullable Boolean visitConstraint(@NonNull Constraint object) {
String name = object.getName();
if ((name != null) && !"".equals(name)) {
EReference eContainmentFeature = object.eContainmentFeature();
if (eContainmentFeature == PivotPackage.Literals.OPERATION__OWNED_PRECONDITIONS) {
s.append(PRECONDITION_PREFIX);
}
else if (eContainmentFeature == PivotPackage.Literals.OPERATION__BODY_EXPRESSION) {
s.append(BODYCONDITION_PREFIX);
}
else if (eContainmentFeature == PivotPackage.Literals.OPERATION__OWNED_POSTCONDITIONS) {
s.append(POSTCONDITION_PREFIX);
}
else {
s.append(INVARIANT_PREFIX);
}
appendParent(object);
appendName(name);
return true;
}
return null;
}
@Override
public @Nullable Boolean visitElement(@NonNull Element object) {
return null;
}
/**
* @since 1.1
*/
@Override
public Boolean visitEnumerationLiteral(@NonNull EnumerationLiteral object) {
String name = object.getName();
if (name != null) {
s.append(ENUMERATION_LITERAL_PREFIX);
appendParent(object);
appendName(name);
return true;
}
return true;
}
@Override
public @Nullable Boolean visitLambdaType(@NonNull LambdaType object) {
return false;
}
@Override
public Boolean visitIteration(@NonNull Iteration object) {
s.append(ITERATION_PREFIX);
appendOperation(object);
return true;
}
@Override
public @Nullable Boolean visitMapType(@NonNull MapType object) {
if (object.getOwnedBindings().isEmpty()) {
appendName(object.getName());
return true;
}
else {
return false;
}
}
@Override
public @Nullable Boolean visitOperation(@NonNull Operation object) {
s.append(OPERATION_PREFIX);
appendOperation(object);
return true;
}
@Override
public @Nullable Boolean visitPackage(org.eclipse.ocl.pivot.@NonNull Package object) {
String name = object.getName();
if (name != null) {
s.append(PACKAGE_PREFIX);
appendParent(object);
appendName(name);
return true;
}
else if (object.getURI() != null) {
appendName(object.getURI());
return true;
}
else {
return false;
}
}
@Override
public @Nullable Boolean visitParameter(@NonNull Parameter object) {
Operation operation = (Operation)object.eContainer();
int index = operation.getOwnedParameters().indexOf(object);
if (index >= 0) {
s.append(PARAMETER_PREFIX);
s.append(index);
}
else if (operation instanceof Iteration) {
Iteration iteration = (Iteration)operation;
index = iteration.getOwnedIterators().indexOf(object);
if (index >= 0) {
s.append(ITERATOR_PREFIX);
s.append(index);
}
else {
index = iteration.getOwnedAccumulators().indexOf(object);
if (index >= 0) {
s.append(ACCUMULATOR_PREFIX);
s.append(index);
}
}
}
operation.accept(this);
return true;
}
@Override
public @Nullable Boolean visitPrecedence(@NonNull Precedence object) {
s.append(PRECEDENCE_PREFIX);
appendName(object.getName());
return true;
}
@Override
public @Nullable Boolean visitPrimitiveType(@NonNull PrimitiveType object) {
appendName(object.getName());
return true;
}
@Override
public @Nullable Boolean visitProperty(@NonNull Property object) {
EObject eContainer = object.eContainer();
if (eContainer instanceof TupleType) {
return false; // TupleParts of synthesized types use UUIDs
}
String name = object.getName();
if (object.isIsImplicit() && (eContainer instanceof org.eclipse.ocl.pivot.Class)) {
for (Property asProperty : ((org.eclipse.ocl.pivot.Class)eContainer).getOwnedProperties()) {
if ((asProperty != object) && name.equals(asProperty.getName())) {
return false; // Ambiguous implicit opposites must use UUIDs
}
}
}
s.append(PROPERTY_PREFIX);
appendParent(object);
appendName(name);
return true;
}
@Override
public @Nullable Boolean visitSelfType(@NonNull SelfType object) {
appendName(object.getName());
return true;
}
@Override
public @Nullable Boolean visitTemplateParameter(@NonNull TemplateParameter object) {
NamedElement template = (NamedElement) object.getOwningSignature().getOwningElement();
if ((template instanceof org.eclipse.ocl.pivot.Class) && Orphanage.isTypeOrphanage(((org.eclipse.ocl.pivot.Class)template).getOwningPackage())) {
return false;
}
s.append(TEMPLATE_PARAMETER_PREFIX);
appendParent(template);
s.append(SCOPE_SEPARATOR);
appendName(template.getName());
appendName(object.getName());
return true;
}
@Override
public @Nullable Boolean visitTemplateSignature(@NonNull TemplateSignature object) {
s.append(TEMPLATE_SIGNATURE_PREFIX);
TemplateableElement template = object.getOwningElement();
template.accept(this);
return true;
}
@Override
public @Nullable Boolean visitTupleType(@NonNull TupleType object) {
return false;
}
@Override
public @Nullable Boolean visitVariableDeclaration(@NonNull VariableDeclaration object) {
return null;
}
@Override
public @Nullable Boolean visiting(@NonNull Visitable visitable) {
throw new IllegalArgumentException("Unsupported " + visitable.eClass().getName() + " for " + getClass().getSimpleName());
}
}