/******************************************************************************* * Copyright (c) 2012, 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.internal.evaluation; 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.ocl.Environment; import org.eclipse.ocl.TypeChecker; import org.eclipse.ocl.utilities.TypedElement; import org.eclipse.ocl.utilities.UMLReflection; public class CachedTypeChecker<C, O, P, PM> extends BasicTypeChecker<C, O, P, PM> implements TypeChecker.Cached<C, O, P> { private boolean bypass = false; private final UMLReflection<?, C, O, P, ?, PM, ?, ?, ?, ?> uml; /** * Lazily created maps of operation name to an operation or a list of operations for each type. */ private final Map<C, Map<String, Object>> type2name2operationOrOperations = new HashMap<C, Map<String, Object>>(); /** * Lazily created maps of static operation to dynamic operation for each dispatch type. */ private final Map<C, Map<O, O>> type2static2dynamic = new HashMap<C, Map<O, O>>(); /** * Lazily created maps of property name to a property for each type. */ private final Map<C, Map<String, P>> type2name2property = new HashMap<C, Map<String, P>>(); public CachedTypeChecker(Environment<?, C, O, P, ?, PM, ?, ?, ?, ?, ?, ?> environment) { super(environment); this.uml = environment.getUMLReflection(); } /** * Return true if requiredParameters is an exact match for candidateParameters. */ protected boolean exactlyMatches(List<? extends PM> requiredParameters, List<? extends PM> candidateParameters) { int iMax = requiredParameters.size(); if (iMax != candidateParameters.size()) { return false; } for (int i = 0; i < iMax; i++) { PM requiredParameter = requiredParameters.get(i); PM candidateParameter = candidateParameters.get(i); C requiredType = uml.getOCLType(requiredParameter); C candidateType = uml.getOCLType(candidateParameter); if (requiredType != candidateType) { return false; } } return true; } @Override public P findAttribute(C owner, String name) { if (bypass) { return super.findAttribute(owner, name); } Map<String, P> name2property = getName2Property(owner); return name2property.get(name); } @Override protected List<O> getBestMatchingOperations(C owner, String name, List<? extends TypedElement<C>> args) { if (bypass) { return super.getBestMatchingOperations(owner, name, args); } Map<String, Object> name2operationOrOperations = getName2OperationOrOperations(owner); Object candidateOperationOrOperations = name2operationOrOperations.get(name); if (candidateOperationOrOperations instanceof List<?>) { List<O> matches = null; @SuppressWarnings("unchecked") List<O> candidateOperations = (List<O>) candidateOperationOrOperations; int bestExactitude = 0; for (O oper : candidateOperations) { int exactitude = matchArgsWithExactitude(owner, uml.getParameters(oper), args); if (exactitude >= bestExactitude) { if (exactitude > bestExactitude) { if (matches != null) { matches.clear(); } bestExactitude = exactitude; } if (matches == null) { // assume a small number of redefinitions matches = new java.util.ArrayList<O>(3); } matches.add(oper); } } return matches; } else if (candidateOperationOrOperations != null) { @SuppressWarnings("unchecked") O candidateOperation = (O) candidateOperationOrOperations; int exactitude = matchArgsWithExactitude(owner, uml.getParameters(candidateOperation), args); if (exactitude >= 0) { return Collections.singletonList(candidateOperation); } else { return null; } } else { return null; } } /** * Return the operation of dynamicType that exactly match the staticOperation, or null * if there is none or more than one. */ public O getDynamicOperation(C dynamicType, O staticOperation) { // // Use class2dynamic2static to return known dynamic operation for static operation on a source type. // Map<O, O> typeOperations = type2static2dynamic.get(dynamicType); if (typeOperations == null) { typeOperations = new HashMap<O, O>(); type2static2dynamic.put(dynamicType, typeOperations); } else { O dynamicOperation = typeOperations.get(staticOperation); if (dynamicOperation != null) { return dynamicOperation; } if (typeOperations.containsKey(staticOperation)) { return null; // Cached miss or ambiguity } } // // Prepare the type-specific name2operationOrOperations lazily. // Map<String, Object> name2operationOrOperations = getName2OperationOrOperations(dynamicType); // // Pick and cache the matching unambiguous overload. // O dynamicOperation = getExactMatchingOperation(name2operationOrOperations, staticOperation); typeOperations.put(staticOperation, dynamicOperation); return dynamicOperation; } /** * Return the operation from the name2operationOrOperations cache that matches operation. * Returns null if there is no operation or more than one. * * @param name2operationOrOperations * @param operation */ protected O getExactMatchingOperation(Map<String, Object> name2operationOrOperations, O operation) { String requiredName = uml.getName(operation); List<PM> requiredParameters = uml.getParameters(operation); Object candidateOperationOrOperations = name2operationOrOperations.get(requiredName); if (candidateOperationOrOperations instanceof List<?>) { O matchingOperation = null; @SuppressWarnings("unchecked") List<O> candidateOperations = (List<O>) candidateOperationOrOperations; for (O candidateOperation : candidateOperations) { List<PM> candidateParameters = uml.getParameters(candidateOperation); if (exactlyMatches(requiredParameters, candidateParameters)) { if (matchingOperation == null) { matchingOperation = candidateOperation; } else { return null; // Ambiguity detected } } } return matchingOperation; } else if (candidateOperationOrOperations != null) { @SuppressWarnings("unchecked") O candidateOperation = (O) candidateOperationOrOperations; List<PM> candidateParameters = uml.getParameters(candidateOperation); if (exactlyMatches(requiredParameters, candidateParameters)) { return candidateOperation; } else { return null; } } else { return null; } } /** * Return the map of name to operator or list of operations for a type. */ protected Map<String, Object> getName2OperationOrOperations(C type) { Map<String, Object> name2operationOrOperations = type2name2operationOrOperations.get(type); if (name2operationOrOperations == null) { name2operationOrOperations = new HashMap<String, Object>(); type2name2operationOrOperations.put(type, name2operationOrOperations); List<O> allOperations = getOperations(type); for (O candidateOperation : allOperations) { String name = uml.getName(candidateOperation); Object overloadOrOverloads = name2operationOrOperations.get(name); if (overloadOrOverloads == null) { name2operationOrOperations.put(name, candidateOperation); } else { List<O> overloads; if (overloadOrOverloads instanceof List<?>) { @SuppressWarnings("unchecked") List<O> castOperations = (List<O>) overloadOrOverloads; overloads = castOperations; } else { overloads = new ArrayList<O>(); name2operationOrOperations.put(name, overloads); @SuppressWarnings("unchecked") O castOperation = (O)overloadOrOverloads; overloads.add(castOperation); } C candidateOwner = uml.getOwningClassifier(candidateOperation); Collection<? extends C> candidateSupertypes = uml.getAllSupertypes(candidateOwner); List<PM> candidateParameters = uml.getParameters(candidateOperation); int iMax = candidateParameters.size(); int j = overloads.size(); while (--j >= 0) { // Reverse count to allow remove() O oldOperation = overloads.get(j); List<PM> oldParameters = uml.getParameters(oldOperation); if (iMax == oldParameters.size()) { int i = 0; for ( ; i < iMax; i++) { PM candidateParameter = candidateParameters.get(i); PM oldParameter = oldParameters.get(i); C oldType = uml.getOCLType(oldParameter); C candidateType = uml.getOCLType(candidateParameter); if (oldType != candidateType) { break; } } if (i >= iMax) { C oldOwner = uml.getOwningClassifier(oldOperation); if (candidateSupertypes.contains(oldOwner)) { overloads.remove(j); } else { Collection<? extends C> oldSupertypes = uml.getAllSupertypes(oldOwner); if (oldSupertypes.contains(candidateOwner)) { break; } } } } } if (j < 0) { overloads.add(candidateOperation); } } } } return name2operationOrOperations; } /** * Return the map of name to property for a type. */ protected Map<String, P> getName2Property(C type) { Map<String, P> name2property = type2name2property.get(type); if (name2property == null) { name2property = new HashMap<String, P>(); type2name2property.put(type, name2property); List<P> allProperties = getAttributes(type); for (P candidateProperty : allProperties) { String name = uml.getName(candidateProperty); P oldProperty = name2property.get(name); if (oldProperty == null) { name2property.put(name, candidateProperty); } else { C candidateOwner = uml.getOwningClassifier(candidateProperty); Collection<? extends C> candidateSupertypes = uml.getAllSupertypes(candidateOwner); C oldOwner = uml.getOwningClassifier(oldProperty); if (candidateSupertypes.contains(oldOwner)) { name2property.put(name, candidateProperty); } else { Collection<? extends C> oldSupertypes = uml.getAllSupertypes(oldOwner); if (!oldSupertypes.contains(candidateOwner)) { ; // This should not happen } } } } } return name2property; } public void reset() { type2name2operationOrOperations.clear(); type2static2dynamic.clear(); type2name2property.clear(); } public void setBypass(boolean bypass) { reset(); this.bypass = bypass; } }