/*******************************************************************************
* Copyright (c) 2012, 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.manager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.CompleteInheritance;
import org.eclipse.ocl.pivot.CompletePackage;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.ids.ParametersId;
import org.eclipse.ocl.pivot.internal.complete.CompleteModelInternal;
import org.eclipse.ocl.pivot.internal.complete.StandardLibraryInternal;
import org.eclipse.ocl.pivot.library.LibraryFeature;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.NameUtil;
public class FinalAnalysis
{
protected final @NonNull CompleteModelInternal completeModel;
/* @Deprecated - not needed - compute from completeModel */
@Deprecated
protected final @NonNull PivotMetamodelManager metamodelManager;
private final @NonNull Map<@NonNull CompleteClass, @NonNull Set<@NonNull CompleteClass>> superCompleteClass2subCompleteClasses = new HashMap<@NonNull CompleteClass, @NonNull Set<@NonNull CompleteClass>>();
private final @NonNull Map<@NonNull Operation, @Nullable Set<@NonNull Operation>> operation2overrides = new HashMap<@NonNull Operation, @Nullable Set<@NonNull Operation>>();
public FinalAnalysis(@NonNull CompleteModelInternal completeModel) {
this.completeModel = completeModel;
this.metamodelManager = completeModel.getMetamodelManager();
for (@NonNull CompletePackage completePackage : completeModel.getAllCompletePackages()) {
for (@NonNull CompleteClass subCompleteClass : ClassUtil.nullFree(completePackage.getOwnedCompleteClasses())) {
for (@NonNull CompleteClass superCompleteClass : subCompleteClass.getSuperCompleteClasses()) {
Set<@NonNull CompleteClass> subCompleteClasses = superCompleteClass2subCompleteClasses.get(superCompleteClass);
if (subCompleteClasses == null) {
subCompleteClasses = new HashSet<@NonNull CompleteClass>();
superCompleteClass2subCompleteClasses.put(superCompleteClass, subCompleteClasses);
}
subCompleteClasses.add(subCompleteClass);
}
}
}
for (@NonNull CompleteClass superCompleteClass : superCompleteClass2subCompleteClasses.keySet()) {
Set<@NonNull CompleteClass> subCompleteClasses = superCompleteClass2subCompleteClasses.get(superCompleteClass);
assert subCompleteClasses != null;
for (@NonNull Operation domainOperation : superCompleteClass.getOperations(null)) {
String opName = domainOperation.getName();
ParametersId parametersId = domainOperation.getParametersId();
LibraryFeature domainImplementation = metamodelManager.getImplementation(domainOperation);
Set<@NonNull Operation> overrides = operation2overrides.get(domainOperation);
for (@NonNull CompleteClass subCompleteClass : subCompleteClasses) {
if (subCompleteClass != superCompleteClass) {
for (@NonNull Operation subOperation : subCompleteClass.getOperations(null)) {
if (opName.equals(subOperation.getName()) && parametersId.equals(subOperation.getParametersId())) {
LibraryFeature subImplementation = metamodelManager.getImplementation(subOperation);
if ((domainImplementation != subImplementation)
|| (domainOperation.getBodyExpression() != subOperation.getBodyExpression())
|| (domainOperation.getTypeId() != subOperation.getTypeId())) {
if (overrides == null) {
overrides = new HashSet<@NonNull Operation>();
overrides.add(domainOperation);
}
overrides.add(subOperation);
}
}
}
}
}
operation2overrides.put(domainOperation, overrides);
}
}
}
/**
* Return the overrides of operation. If there are no overrides the original operation is returned.
*
* @since 1.1
*/
public @NonNull Iterable<@NonNull Operation> getOverrides(@NonNull Operation operation) {
Set<@NonNull Operation> overrides = operation2overrides.get(operation);
return overrides != null ? overrides : Collections.singletonList(operation);
}
/**
* Return the overrides of operation that are applicable to a source type of completeClass. If there are no overrides
* the original operation is returned.
*
* @since 1.1
*/
public @NonNull Iterable<@NonNull Operation> getOverrides(@NonNull Operation operation, @NonNull CompleteClass completeClass) {
Set<@NonNull Operation> overrides = operation2overrides.get(operation);
if (overrides == null) {
return Collections.singletonList(operation);
}
List<@NonNull Operation> results = new ArrayList<@NonNull Operation>();
StandardLibraryInternal standardLibrary = completeModel.getStandardLibrary();
CompleteInheritance requiredInheritance = completeClass.getCompleteInheritance();
for (@NonNull Operation override : overrides) {
CompleteInheritance overrideInheritance = override.getInheritance(standardLibrary);
if ((overrideInheritance != null) && overrideInheritance.isSuperInheritanceOf(requiredInheritance)) {
results.add(override);
}
}
if (results.size() <= 0) {
results.add(operation);
}
return results;
}
/**
* Return true if no classes derive from completeClass.
*
* @param completeClass
* @return
*/
public boolean isFinal(@NonNull CompleteClass completeClass) {
Set<@NonNull CompleteClass> subCompleteClasses = superCompleteClass2subCompleteClasses.get(completeClass);
return (subCompleteClasses != null) && (subCompleteClasses.size() <= 1);
}
/**
* Return true if no derived classes override operation.
*
* @param completeClass
* @return
*/
public boolean isFinal(@NonNull Operation operation) {
Set<@NonNull Operation> overrides = operation2overrides.get(operation);
return overrides == null;
}
public @Nullable Operation isFinal(@NonNull Operation operation, @NonNull CompleteClass completeClass) {
Set<@NonNull Operation> overrides = operation2overrides.get(operation);
if (overrides == null) {
return operation;
}
Operation candidate = null;
StandardLibraryInternal standardLibrary = completeModel.getStandardLibrary();
CompleteInheritance requiredInheritance = completeClass.getCompleteInheritance();
for (@NonNull Operation override : overrides) {
CompleteInheritance overrideInheritance = override.getInheritance(standardLibrary);
if ((overrideInheritance != null) && overrideInheritance.isSuperInheritanceOf(requiredInheritance)) {
if (candidate != null) {
return null;
}
candidate = override;
}
}
return candidate;
}
public void print(@NonNull StringBuilder s) {
List<@NonNull CompleteClass> completeClasses = new ArrayList<@NonNull CompleteClass>(superCompleteClass2subCompleteClasses.keySet());
Collections.sort(completeClasses, NameUtil.NAMEABLE_COMPARATOR);
s.append("Final types");
for (@NonNull CompleteClass completeClass : completeClasses) {
assert completeClass != null;
if (isFinal(completeClass)) {
s.append("\n\t");
s.append(completeClass.getName());
}
}
s.append("\nNon-final types");
for (@NonNull CompleteClass completeClass : completeClasses) {
assert completeClass != null;
if (!isFinal(completeClass)) {
s.append("\n\t");
s.append(completeClass.getName());
}
}
}
}