/*******************************************************************************
* Copyright (c) 2010, 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.utilities;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.ExpressionInOCL;
import org.eclipse.ocl.pivot.Iteration;
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.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.Type;
import org.eclipse.ocl.pivot.TypedElement;
import org.eclipse.ocl.pivot.resource.ASResource;
import org.eclipse.ocl.pivot.utilities.AS2MonikerVisitor;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
public class AS2Moniker implements PivotConstantsInternal
{
public static @NonNull String toString(@NonNull Element pivotElement) {
AS2Moniker moniker = new AS2Moniker(pivotElement);
moniker.appendElement(pivotElement);
String string = moniker.toString();
assert !"".equals(string) : "Zero length moniker for '" + pivotElement.eClass().getName() + "'";
assert string != null;
return string;
}
private static final Logger logger = Logger.getLogger(AS2Moniker.class);
/**
* The CS element for which a moniker is required.
*/
protected final EObject target;
/**
* The working buffer for the result.
*/
private final StringBuilder s = new StringBuilder();
/**
* A pivot 2 moniker conversion visitor, if needed.
*/
// private AS2MonikerVisitor pivotVisitor = null;
/**
* TemplateParameters that already appear in the result and do not need re-qualification.
*/
private List<TemplateParameter> emittedParameters = null;
public AS2Moniker(@NonNull Element target) {
this.target = target;
}
public void append(char c) {
s.append(c);
}
public void append(int i) {
s.append(i);
}
public void append(String string) {
s.append(string != null ? string : "null"); //$NON-NLS-1$
}
public void appendElement(Element element) {
if (toString().length() >= MONIKER_OVERFLOW_LIMIT) {
append(OVERFLOW_MARKER);
}
else if (element == null) {
append(NULL_MARKER);
}
else if (element.eIsProxy()) {
append(UNRESOLVED_PROXY_MARKER);
}
else {
AS2MonikerVisitor as2MonikerVisitor = createAS2MonikerVisitor(element);
element.accept(as2MonikerVisitor);
}
}
public void appendElement(Element element, Map<TemplateParameter, Type> templateBindings) {
if (toString().length() >= MONIKER_OVERFLOW_LIMIT) {
append(OVERFLOW_MARKER);
}
else if (element == null) {
append(NULL_MARKER);
}
else if (templateBindings != null) { // FIXME is this needed
AS2MonikerVisitor nestedAS2MonikerVisitor = new AS2MonikerVisitor(this, templateBindings);
element.accept(nestedAS2MonikerVisitor);
}
else {
AS2MonikerVisitor as2MonikerVisitor = createAS2MonikerVisitor(element);
element.accept(as2MonikerVisitor);
}
}
public void appendIndex(EObject eObject) {
if (eObject != null) {
EObject parent = eObject.eContainer();
if (parent != null) {
Object objects = parent.eGet(eObject.eContainingFeature());
if (objects instanceof List<?>) {
append(((List<?>)objects).indexOf(eObject));
return;
}
}
}
append(0);
}
public void appendLambdaType(Type contextType, List<? extends Type> parameterTypes,
Type resultType, Map<TemplateParameter, Type> bindings) {
if (contextType != null) {
append(MONIKER_OPERATOR_SEPARATOR);
appendElement(contextType, bindings);
append(PARAMETER_PREFIX);
String prefix = ""; //$NON-NLS-1$
for (Type parameterType : parameterTypes) {
append(prefix);
appendElement(parameterType, bindings);
prefix = PARAMETER_SEPARATOR;
}
append(PARAMETER_SUFFIX);
if (resultType != null) {
appendElement(resultType, bindings);
}
}
}
/* protected void appendMultiplicity(MultiplicityElement multiplicityElement) {
int lower = multiplicityElement.getLower().intValue();
int upper = multiplicityElement.getUpper().intValue();
if (upper != 1) {
append("[");
append(lower);
// append(multiplicityElement.isOrdered() ? "S" : "s");
// append(multiplicityElement.isUnique() ? "U" : "u");
append(upper);
append("]");
}
} */
public void appendName(Element monikeredElement) {
if (monikeredElement instanceof TemplateableElement) { // FIXME migrate to more specific location
TemplateableElement unspecializedElement = ((TemplateableElement)monikeredElement).getUnspecializedElement();
if (unspecializedElement != null) {
appendName(unspecializedElement);
return;
}
}
if (monikeredElement instanceof NamedElement) {
append(((NamedElement) monikeredElement).getName());
}
else if (monikeredElement == null) {
// logger.warn("null for PivotMoniker.appendName()");
append("/null/");
}
else {
logger.warn("Unsupported PivotMoniker.appendName() for " + monikeredElement.eClass().getName());
append("/anon/");
}
}
public void appendParameters(Operation operation, Map<TemplateParameter, Type> templateBindings) {
s.append(PARAMETER_PREFIX);
String prefix = ""; //$NON-NLS-1$
if (operation instanceof Iteration) {
Iteration iteration = (Iteration)operation;
for (Parameter parameter : iteration.getOwnedIterators()) {
s.append(prefix);
appendElement(parameter.getType(), templateBindings);
// appendMultiplicity(parameter);
prefix = PARAMETER_SEPARATOR;
}
if (iteration.getOwnedAccumulators().size() > 0) {
prefix = ITERATOR_SEPARATOR;
for (Parameter parameter : iteration.getOwnedAccumulators()) {
s.append(prefix);
appendElement(parameter.getType(), templateBindings);
// appendMultiplicity(parameter);
prefix = PARAMETER_SEPARATOR;
}
}
prefix = ACCUMULATOR_SEPARATOR;
}
for (Parameter parameter : operation.getOwnedParameters()) {
s.append(prefix);
appendElement(parameter.getType(), templateBindings);
// appendMultiplicity(parameter);
prefix = PARAMETER_SEPARATOR;
}
s.append(PARAMETER_SUFFIX);
}
public void appendParent(Element element, String parentSeparator) {
if (toString().length() >= MONIKER_OVERFLOW_LIMIT) {
append(OVERFLOW_MARKER);
}
else if (element == null) {
append(NULL_MARKER);
}
else {
EObject parent = element.eContainer();
if (parent instanceof Element) {
appendElement((Element) parent);
if (parent instanceof TemplateableElement) {
TemplateSignature ownedTemplateSignature = ((TemplateableElement)parent).getOwnedSignature();
if (ownedTemplateSignature != null) {
for (TemplateParameter templateParameter : ownedTemplateSignature.getOwnedParameters()) {
emittedTemplateParameter(templateParameter);
}
}
}
}
else if (element.eIsProxy()) {
append("<<unresolved-proxy>>");
}
else {
assert element instanceof Model || element instanceof ExpressionInOCL : element.eClass().getName() + " has no parent";
}
}
append(parentSeparator);
}
public void appendRole(Element object) {
EStructuralFeature eFeature = object.eContainmentFeature();
if (eFeature != null) {
String roleName = roleNames.get(eFeature);
if (roleName == null) {
roleName = eFeature.getName();
}
append(roleName);
if (eFeature.isMany()) {
int index = ((List<?>)object.eContainer().eGet(object.eContainingFeature())).indexOf(object);
if (index != 0) {
append(index);
}
}
}
}
public void appendTemplateArguments(List<? extends Type> templateArguments, Map<TemplateParameter, Type> templateBindings) {
if (!templateArguments.isEmpty()) {
append(TEMPLATE_BINDING_PREFIX);
String prefix = ""; //$NON-NLS-1$
for (Type templateArgument : templateArguments) {
append(prefix);
appendElement(templateArgument, templateBindings);
prefix = TEMPLATE_BINDING_SEPARATOR;
}
append(TEMPLATE_BINDING_SUFFIX);
}
}
public void appendTemplateBindings(TemplateableElement templateableElement, Map<TemplateParameter, Type> bindings) {
List<TemplateBinding> templateBindings = templateableElement.getOwnedBindings();
if (!templateBindings.isEmpty()) {
boolean isSpecialized = isSpecialized(templateBindings, bindings);
if (!isSpecialized) {
s.append(TEMPLATE_SIGNATURE_PREFIX);
String prefix = ""; //$NON-NLS-1$
for (TemplateBinding templateBinding : templateBindings) {
List<TemplateParameterSubstitution> parameterSubstitutions = templateBinding.getOwnedSubstitutions();
if (parameterSubstitutions.size() > 1) {
parameterSubstitutions = new ArrayList<TemplateParameterSubstitution>(parameterSubstitutions);
Collections.sort(parameterSubstitutions, PivotUtil.TemplateParameterSubstitutionComparator.INSTANCE);
}
for (TemplateParameterSubstitution templateParameterSubstitution : parameterSubstitutions) {
s.append(prefix);
appendName(templateParameterSubstitution.getFormal());
prefix = TEMPLATE_SIGNATURE_SEPARATOR;
}
}
s.append(TEMPLATE_SIGNATURE_SUFFIX);
}
else {
s.append(TEMPLATE_BINDING_PREFIX);
String prefix = ""; //$NON-NLS-1$
for (TemplateBinding templateBinding : templateBindings) {
List<TemplateParameterSubstitution> parameterSubstitutions = templateBinding.getOwnedSubstitutions();
if (parameterSubstitutions.size() > 1) {
parameterSubstitutions = new ArrayList<TemplateParameterSubstitution>(parameterSubstitutions);
Collections.sort(parameterSubstitutions, PivotUtil.TemplateParameterSubstitutionComparator.INSTANCE);
}
for (TemplateParameterSubstitution templateParameterSubstitution : parameterSubstitutions) {
s.append(prefix);
appendElement(templateParameterSubstitution.getActual(), bindings);
prefix = TEMPLATE_BINDING_SEPARATOR;
}
}
s.append(TEMPLATE_BINDING_SUFFIX);
}
}
}
public void appendTemplateParameters(TemplateableElement templateableElement) {
TemplateSignature templateSignature = templateableElement.getOwnedSignature();
if (templateSignature != null) {
List<TemplateParameter> templateParameters = templateSignature.getOwnedParameters();
if (!templateParameters.isEmpty()) {
s.append(TEMPLATE_SIGNATURE_PREFIX);
String prefix = ""; //$NON-NLS-1$
for (TemplateParameter templateParameter : templateParameters) {
s.append(prefix);
emittedTemplateParameter(templateParameter);
appendName(templateParameter);
prefix = TEMPLATE_SIGNATURE_SEPARATOR;
}
s.append(TEMPLATE_SIGNATURE_SUFFIX);
}
}
}
public void appendTupleType(Collection<? extends TypedElement> tupleParts) {
List<TypedElement> parts = new ArrayList<TypedElement>(tupleParts);
Collections.sort(parts, new Comparator<TypedElement>()
{
@Override
public int compare(TypedElement o1, TypedElement o2) {
return o1.getName().compareTo(o2.getName());
}
});
append(TUPLE_SIGNATURE_PREFIX);
String prefix = "";
for (TypedElement part : parts) {
append(prefix);
appendName(part);
append(TUPLE_SIGNATURE_TYPE_SEPARATOR);
Type type = part.getType();
if (type != null) {
appendElement(type);
}
prefix = TUPLE_SIGNATURE_PART_SEPARATOR;
}
append(TUPLE_SIGNATURE_SUFFIX);
}
protected @NonNull AS2MonikerVisitor createAS2MonikerVisitor(@NonNull Element element) {
AS2MonikerVisitor as2MonikerVisitor;
Resource resource = element.eResource();
if (resource instanceof ASResource) {
as2MonikerVisitor = ((ASResource)resource).getASResourceFactory().createAS2MonikerVisitor(this);
}
else {
as2MonikerVisitor = new AS2MonikerVisitor(this);
}
return as2MonikerVisitor;
}
protected void emittedTemplateParameter(TemplateParameter templateParameter) {
if (emittedParameters == null) {
emittedParameters = new ArrayList<TemplateParameter>();
}
emittedParameters.add(templateParameter);
}
public boolean hasEmitted(TemplateParameter templateParameter) {
return (emittedParameters != null) && emittedParameters.contains(templateParameter);
}
protected boolean isSpecialized(List<TemplateBinding> templateBindings, Map<TemplateParameter, Type> bindings) {
if (bindings == null) {
return true;
}
for (TemplateBinding templateBinding : templateBindings) {
for (TemplateParameterSubstitution templateParameterSubstitution : templateBinding.getOwnedSubstitutions()) {
Type actual = templateParameterSubstitution.getActual();
if (actual == null) {
return true;
}
Type parameterableElement = bindings.get(actual);
if ((parameterableElement == null) || (parameterableElement != templateParameterSubstitution.getFormal())) {
return true;
}
}
}
return false;
}
/**
* Return the length of the moniker so far.
*/
protected int length() {
return s.length();
}
@Override
public String toString() {
return s.toString();
}
}