blob: 8e74f6db528fadb1759e8f77d2d62cfb5f229aef [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2017 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.library.iterator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
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.CompleteInheritance;
import org.eclipse.ocl.pivot.Operation;
import org.eclipse.ocl.pivot.StandardLibrary;
import org.eclipse.ocl.pivot.evaluation.Evaluator;
import org.eclipse.ocl.pivot.evaluation.Executor;
import org.eclipse.ocl.pivot.evaluation.IterationManager;
import org.eclipse.ocl.pivot.ids.CollectionTypeId;
import org.eclipse.ocl.pivot.ids.TypeId;
import org.eclipse.ocl.pivot.internal.values.ValueImpl;
import org.eclipse.ocl.pivot.library.AbstractIteration;
import org.eclipse.ocl.pivot.library.LibraryBinaryOperation;
import org.eclipse.ocl.pivot.library.LibraryConstants;
import org.eclipse.ocl.pivot.library.LibraryFeature;
import org.eclipse.ocl.pivot.messages.PivotMessages;
import org.eclipse.ocl.pivot.utilities.ValueUtil;
import org.eclipse.ocl.pivot.values.IntegerValue;
import org.eclipse.ocl.pivot.values.InvalidValueException;
import org.eclipse.ocl.pivot.values.Value;
/**
* SelectIteration realizes the Collection::sortedBy() library iteration.
*/
public class SortedByIteration extends AbstractIteration
{
protected static class SortingValue extends ValueImpl implements Comparator<Object>
{
protected final @NonNull CollectionTypeId typeId;
private final @NonNull Executor executor;
private final boolean isUnique;
private final LibraryBinaryOperation.@NonNull LibraryBinaryOperationExtension implementation;
private final @NonNull Map<Object, Object> content = new HashMap<Object, Object>(); // User object to sortedBy value
private Map<Object, Integer> repeatCounts = null; // Repeat counts for non-unique content
/** @deprecated use Executor */
@Deprecated
public SortingValue(@NonNull Evaluator evaluator, @NonNull CollectionTypeId returnTypeId, @NonNull LibraryBinaryOperation implementation) {
this(getExecutor(evaluator), returnTypeId, implementation);
}
/**
* @since 1.1
*/
public SortingValue(@NonNull Executor executor, @NonNull CollectionTypeId returnTypeId, @NonNull LibraryBinaryOperation implementation) {
this.typeId = returnTypeId;
this.executor = executor;
this.implementation = (LibraryBinaryOperation.LibraryBinaryOperationExtension)implementation;
CollectionTypeId generalizedId = typeId.getGeneralizedId();
isUnique = (generalizedId == TypeId.SET) || (generalizedId == TypeId.ORDERED_SET);
}
@Override
public @NonNull Object asObject() {
return content;
}
@Override
public int compare(Object o1, Object o2) {
if (o1 == o2) {
return 0;
}
Object v1 = content.get(o1);
Object v2 = content.get(o2);
if (v1 == v2) {
return 0;
}
else if (v1 == null) {
return -1;
}
else if (v2 == null) {
return 1;
}
try {
IntegerValue comparison = ValueUtil.asIntegerValue(implementation.evaluate(executor, TypeId.INTEGER, v1, v2));
return comparison.signum();
} catch (InvalidValueException e) {
throw e;
} catch (Exception e) {
throw new InvalidValueException(e);
}
}
public @NonNull Value createSortedValue() {
List<Object> result = new ArrayList<Object>(content.keySet());
Collections.sort(result, this);
if (isUnique || (repeatCounts == null)) {
return executor.getIdResolver().createCollectionOfAll(true, isUnique, typeId, result);
}
else {
List<Object> nonUniqueResult = new ArrayList<Object>();
for (Object resultValue : result) {
nonUniqueResult.add(resultValue);
Integer repeatCount = repeatCounts.get(resultValue);
if (repeatCount != null) {
for (int i = repeatCount; i > 0; i--) {
nonUniqueResult.add(resultValue);
}
}
}
return executor.getIdResolver().createCollectionOfAll(true, false, typeId, nonUniqueResult);
}
}
// public @NonNull DomainType getType(@NonNull DomainStandardLibrary standardLibrary) {
// return type;
// }
@Override
public @NonNull TypeId getTypeId() {
return typeId;
}
public void put(@Nullable Object iterVal, @Nullable Object comparable) {
if (content.put(iterVal, comparable) != null) {
if (!isUnique) {
if (repeatCounts == null) {
repeatCounts = new HashMap<Object, Integer>();
}
Integer repeatCount = repeatCounts.get(iterVal);
if (repeatCount == null) {
repeatCount = 1;
}
else {
repeatCount++;
}
repeatCounts.put(iterVal, repeatCount);
}
}
}
@Override
public void toString(@NonNull StringBuilder s, int sizeLimit) {
s.append(content.toString());
}
}
public static final @NonNull SortedByIteration INSTANCE = new SortedByIteration();
/** @deprecated use Executor */
@Deprecated
@Override
public SortedByIteration.@NonNull SortingValue createAccumulatorValue(@NonNull Evaluator evaluator, @NonNull TypeId accumulatorTypeId, @NonNull TypeId bodyTypeId) {
return createAccumulatorValue(ValueUtil.getExecutor(evaluator), accumulatorTypeId, bodyTypeId);
}
/**
* @since 1.1
*/
@Override
public SortedByIteration.@NonNull SortingValue createAccumulatorValue(@NonNull Executor executor, @NonNull TypeId accumulatorTypeId, @NonNull TypeId bodyTypeId) {
StandardLibrary standardLibrary = executor.getStandardLibrary();
CompleteInheritance comparableType = standardLibrary.getOclComparableType().getInheritance(standardLibrary);
CompleteInheritance selfType = standardLibrary.getOclSelfType().getInheritance(standardLibrary);
Operation staticOperation = comparableType.lookupLocalOperation(standardLibrary, LibraryConstants.COMPARE_TO, selfType);
if (staticOperation != null) {
org.eclipse.ocl.pivot.Class bodyType = executor.getIdResolver().getClass(bodyTypeId, null);
LibraryFeature implementation = bodyType.lookupImplementation(standardLibrary, staticOperation);
return new SortingValue(executor, (CollectionTypeId)accumulatorTypeId, (LibraryBinaryOperation) implementation);
}
throw new InvalidValueException(PivotMessages.UndefinedOperation, String.valueOf(comparableType) + "::" + LibraryConstants.COMPARE_TO); //$NON-NLS-1$
}
@Override
protected @NonNull Object resolveTerminalValue(@NonNull IterationManager iterationManager) {
SortingValue accumulatorValue = (SortingValue) iterationManager.getAccumulatorValue();
assert accumulatorValue != null;
return accumulatorValue.createSortedValue();
}
@Override
protected @Nullable Object updateAccumulator(@NonNull IterationManager iterationManager) {
Object bodyVal = iterationManager.evaluateBody();
if (bodyVal == null) {
throw new InvalidValueException(PivotMessages.UndefinedBody, "sortedBy"); // Null body is invalid //$NON-NLS-1$
}
Object iterValue = iterationManager.get();
SortingValue accumulatorValue = (SortingValue) iterationManager.getAccumulatorValue();
assert accumulatorValue != null;
accumulatorValue.put(iterValue, bodyVal);
return CARRY_ON; // Carry on
}
}