/*******************************************************************************
* Copyright (c) 2011, 2014 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.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.Library;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.Parameter;
import org.eclipse.ocl.pivot.PivotFactory;
import org.eclipse.ocl.pivot.Precedence;
import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal;
/**
* PrecedenceManager encapsulates the knowledge about known precedences.
*/
public class PrecedenceManager
{
public static @NonNull Precedence NULL_PRECEDENCE = PivotFactory.eINSTANCE.createPrecedence();
public static @NonNull Precedence NAVIGATION_PRECEDENCE = PivotFactory.eINSTANCE.createPrecedence();
public static @NonNull Precedence LEAF_PRECEDENCE = PivotFactory.eINSTANCE.createPrecedence();
static {
NULL_PRECEDENCE.setName("NULL");
NULL_PRECEDENCE.setOrder(Integer.MAX_VALUE/2); // Small enough to avoid wrap around during comparison
NAVIGATION_PRECEDENCE.setName("NAVIGATION");
NAVIGATION_PRECEDENCE.setOrder(Integer.valueOf(-1));
LEAF_PRECEDENCE.setName("LEAF");
LEAF_PRECEDENCE.setOrder(Integer.valueOf(-2));
}
/**
* Map of precedence name to precedence objects. Multiple precedence objects
* may be associated with a single name since alternate contributions
* provide independent lists that must be successfully interleaved so that
* all same-named precedence objects get the same compiled ordering.
* <p>
* e.g. <tt> precedence A B D</tt> and <tt>precedence B C D</tt> merge to
* <tt>A B C D</tt> with duplicate precedence objects for B and D.
*/
private Map<String, @NonNull List<Precedence>> nameToPrecedencesMap = null;
private Map<String, String> infixToPrecedenceNameMap = null;
private Map<String, String> prefixToPrecedenceNameMap = null;
/**
* Interleave the ownedPrecedences of the rootPackages to establish a merged
* ordering and assign the index in that ordering to each
* rootPackages.ownedPrecedences. Any inconsistent ordering and
* associativity is diagnosed.
*/
public @NonNull List<String> compilePrecedences(@NonNull Iterable<? extends Library> libraries) {
List<String> errors = new ArrayList<String>();
List<String> orderedPrecedences = new ArrayList<String>();
nameToPrecedencesMap = new HashMap<String, @NonNull List<Precedence>>();
infixToPrecedenceNameMap = new HashMap<String, String>();
prefixToPrecedenceNameMap = new HashMap<String, String>();
for (Library library : libraries) {
List<Precedence> precedences = library.getOwnedPrecedences();
if (precedences.size() > 0) {
compilePrecedencePackage(errors, library);
int prevIndex = -1;
List<Precedence> list = null;
String name = null;
for (Precedence precedence : precedences) {
name = precedence.getName();
int index = orderedPrecedences.indexOf(name);
if (index < 0) {
index = prevIndex < 0 ? orderedPrecedences.size() : prevIndex + 1;
orderedPrecedences.add(index, name);
list = new ArrayList<Precedence>();
nameToPrecedencesMap.put(name, list);
} else {
list = nameToPrecedencesMap.get(name);
assert list != null;
if (index <= prevIndex) {
errors.add("Inconsistent precedence ordering for '" + name + "'");
} else if ((prevIndex >= 0) && (index != prevIndex + 1)) {
errors.add("Ambiguous precedence ordering for '" + name + "'");
}
if (precedence.getAssociativity() != list.get(0).getAssociativity()) {
errors.add("Inconsistent precedence associativity for '" + name + "'");
}
}
prevIndex = index;
list.add(precedence);
}
if ((list != null) && (list.size() == 1) && (prevIndex != orderedPrecedences.size() - 1)) {
errors.add("Ambiguous precedence ordering for '" + name + "' at tail");
}
}
}
for (int i = 0; i < orderedPrecedences.size(); i++) {
String name = orderedPrecedences.get(i);
List<Precedence> precedences = nameToPrecedencesMap.get(name);
assert precedences != null;
for (Precedence precedence : precedences) {
precedence.setOrder(i);
}
}
return errors;
}
protected void compilePrecedenceOperation(@NonNull List<String> errors, @NonNull Operation operation) {
Precedence precedence = operation.getPrecedence();
if (precedence != null) {
List<Parameter> parameters = operation.getOwnedParameters();
if (parameters.size() == 0) {
String newName = precedence.getName();
String operatorName = operation.getName();
String oldName = prefixToPrecedenceNameMap.put(operatorName, newName);
if ((oldName != null) && !oldName.equals(newName)) {
errors.add("Conflicting precedences for prefix operation '" + operatorName + "'");
}
} else if (parameters.size() == 1) {
String newName = precedence.getName();
String operatorName = operation.getName();
String oldName = infixToPrecedenceNameMap.put(operatorName, newName);
if ((oldName != null) && !oldName.equals(newName)) {
errors.add("Conflicting precedences for infix operation '" + operatorName + "'");
}
}
}
}
protected void compilePrecedencePackage(@NonNull List<String> errors, @NonNull Library library) {
// for (org.eclipse.ocl.pivot.Package nestedPackage : pivotPackage.getNestedPackage()) {
// compilePrecedencePackage(errors, nestedPackage);
// }
for (org.eclipse.ocl.pivot.Class type : library.getOwnedClasses()) {
if ((type != null) && PivotUtilInternal.isLibraryType(type)) {
compilePrecedenceType(errors, type);
}
}
}
protected void compilePrecedenceType(@NonNull List<String> errors, org.eclipse.ocl.pivot.@NonNull Class pivotType) {
for (Operation operation : pivotType.getOwnedOperations()) {
if (operation != null) {
compilePrecedenceOperation(errors, operation);
}
}
}
public void dispose() {
nameToPrecedencesMap = null;
infixToPrecedenceNameMap = null;
prefixToPrecedenceNameMap = null;
}
public @Nullable Precedence getInfixPrecedence(@NonNull String operatorName) {
String precedenceName = infixToPrecedenceNameMap.get(operatorName);
if (precedenceName == null) {
return null;
}
List<Precedence> precedences = nameToPrecedencesMap.get(precedenceName);
if (precedences == null) {
return null;
}
return precedences.get(0);
}
public @Nullable Precedence getPrefixPrecedence(@NonNull String operatorName) {
String precedenceName = prefixToPrecedenceNameMap.get(operatorName);
if (precedenceName == null) {
return null;
}
List<Precedence> precedences = nameToPrecedencesMap.get(precedenceName);
if (precedences == null) {
return null;
}
return precedences.get(0);
}
}