blob: f99feb232d951e78767450076bbb639f17d7dbe7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2020 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.acceleo.query.services;
import java.lang.reflect.Method;
import java.security.Provider.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.acceleo.annotations.api.documentation.Documentation;
import org.eclipse.acceleo.annotations.api.documentation.Example;
import org.eclipse.acceleo.annotations.api.documentation.Other;
import org.eclipse.acceleo.annotations.api.documentation.Param;
import org.eclipse.acceleo.annotations.api.documentation.ServiceProvider;
import org.eclipse.acceleo.annotations.api.documentation.Throw;
import org.eclipse.acceleo.query.ast.Call;
import org.eclipse.acceleo.query.ast.Expression;
import org.eclipse.acceleo.query.ast.Lambda;
import org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment;
import org.eclipse.acceleo.query.runtime.IService;
import org.eclipse.acceleo.query.runtime.IValidationResult;
import org.eclipse.acceleo.query.runtime.impl.AbstractServiceProvider;
import org.eclipse.acceleo.query.runtime.impl.JavaMethodService;
import org.eclipse.acceleo.query.runtime.impl.LambdaValue;
import org.eclipse.acceleo.query.runtime.impl.Nothing;
import org.eclipse.acceleo.query.runtime.impl.ValidationServices;
import org.eclipse.acceleo.query.validation.type.ClassLiteralType;
import org.eclipse.acceleo.query.validation.type.ClassType;
import org.eclipse.acceleo.query.validation.type.EClassifierSetLiteralType;
import org.eclipse.acceleo.query.validation.type.EClassifierType;
import org.eclipse.acceleo.query.validation.type.ICollectionType;
import org.eclipse.acceleo.query.validation.type.IType;
import org.eclipse.acceleo.query.validation.type.LambdaType;
import org.eclipse.acceleo.query.validation.type.NothingType;
import org.eclipse.acceleo.query.validation.type.SequenceType;
import org.eclipse.acceleo.query.validation.type.SetType;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EClassifier;
//@formatter:off
@ServiceProvider(
value = "Services available for Collections"
)
//@formatter:on
@SuppressWarnings({"checkstyle:javadocmethod", "checkstyle:javadoctype" })
public class CollectionServices extends AbstractServiceProvider {
/**
* Message separator.
*/
private static final String MESSAGE_SEPARATOR = "\n ";
/** Externalized here to avoid multiple uses. */
private static final String ONLY_NUMERIC_ERROR = "%s can only be used on a collection of numbers.";
/**
* Exists {@link IService}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class BooleanLambdaService extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private BooleanLambdaService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final LambdaType lambdaType = (LambdaType)argTypes.get(1);
final Object lambdaExpressionType = lambdaType.getLambdaExpressionType().getType();
if (isBooleanType(queryEnvironment, lambdaExpressionType)) {
result.addAll(super.getType(call, services, validationResult, queryEnvironment, argTypes));
} else {
result.add(services.nothing("expression in %s must return a boolean", getName()));
}
return result;
}
}
/**
* Any {@link IService}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class AnyService extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private AnyService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final LambdaType lambdaType = (LambdaType)argTypes.get(1);
final Object lambdaExpressionType = lambdaType.getLambdaExpressionType().getType();
if (isBooleanType(queryEnvironment, lambdaExpressionType)) {
final Expression expression;
if (call != null) {
expression = ((Lambda)call.getArguments().get(1)).getExpression();
} else {
expression = null;
}
final Set<IType> inferredTypes;
if (validationResult != null) {
inferredTypes = validationResult.getInferredVariableTypes(expression, Boolean.TRUE).get(
lambdaType.getLambdaEvaluatorName());
} else {
inferredTypes = null;
}
if (inferredTypes == null) {
result.add(((ICollectionType)argTypes.get(0)).getCollectionType());
} else {
result.addAll(inferredTypes);
}
} else {
result.add(services.nothing("expression in an any must return a boolean"));
}
return result;
}
}
/**
* Including {@link IService}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static class IncludingService extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
protected IncludingService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes.get(0))
.getCollectionType()));
result.add(createReturnCollectionWithType(queryEnvironment, argTypes.get(1)));
return result;
}
@Override
public Set<IType> validateAllType(ValidationServices services,
IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final StringBuilder builder = new StringBuilder();
for (Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
for (IType type : entry.getValue()) {
if (((ICollectionType)type).getCollectionType() instanceof NothingType) {
builder.append(MESSAGE_SEPARATOR);
builder.append(((NothingType)((ICollectionType)type).getCollectionType())
.getMessage());
} else {
result.add(type);
}
}
}
if (result.isEmpty()) {
result.add(createReturnCollectionWithType(queryEnvironment, services.nothing(
"Nothing left after %s:" + builder.toString(), getName())));
}
return result;
}
}
/**
* Collect {@link IService}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class CollectService extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private CollectService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final IType receiverType = argTypes.get(0);
if (receiverType instanceof NothingType) {
result.add(createReturnCollectionWithType(queryEnvironment, receiverType));
} else if (receiverType instanceof ICollectionType && ((ICollectionType)receiverType)
.getCollectionType() instanceof NothingType) {
result.add(receiverType);
} else {
final LambdaType lambdaType = (LambdaType)argTypes.get(1);
// flatten if needed
result.add(createReturnCollectionWithType(queryEnvironment, flatten(lambdaType
.getLambdaExpressionType())));
}
return result;
}
private IType flatten(IType type) {
if (type instanceof ICollectionType) {
return flatten(((ICollectionType)type).getCollectionType());
}
return type;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.impl.AbstractService#validateAllType(org.eclipse.acceleo.query.runtime.impl.ValidationServices,
* org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment, java.util.Map)
*/
@Override
public Set<IType> validateAllType(ValidationServices services,
IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
for (Map.Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
for (IType type : entry.getValue()) {
final IType collectionType = ((ICollectionType)type).getCollectionType();
if (collectionType instanceof ClassType && ((ClassType)collectionType)
.getType() == null) {
// This is the null literal, which we don't want in our result
// and will be stripped at runtime.
} else {
result.add(type);
}
}
}
return result;
}
}
/**
* Closure {@link IService}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class ClosureService extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private ClosureService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.impl.JavaMethodService#getType(org.eclipse.acceleo.query.ast.Call,
* org.eclipse.acceleo.query.runtime.impl.ValidationServices,
* org.eclipse.acceleo.query.runtime.IValidationResult,
* org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment, java.util.List)
*/
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final IType lambdaExpressionType = ((LambdaType)argTypes.get(1)).getLambdaExpressionType();
// FIXME need to make the closure on type as well... it's not possible for the moment because we
// need variable types...
final IType receiverType = argTypes.get(0);
if (receiverType instanceof NothingType) {
result.add(createReturnCollectionWithType(queryEnvironment, receiverType));
} else if (receiverType instanceof ICollectionType && ((ICollectionType)receiverType)
.getCollectionType() instanceof NothingType) {
result.add(receiverType);
} else if (lambdaExpressionType instanceof ICollectionType) {
result.add(new SetType(queryEnvironment, ((ICollectionType)lambdaExpressionType)
.getCollectionType()));
} else {
result.add(new SetType(queryEnvironment, lambdaExpressionType));
}
return result;
}
}
/**
* Sum, min, max {@link IService}.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
private static final class NumberService extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private NumberService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
if (!argTypes.isEmpty() && argTypes.get(0) instanceof ICollectionType) {
IType argType = ((ICollectionType)argTypes.get(0)).getCollectionType();
IType intType = new ClassType(queryEnvironment, Integer.class);
IType longType = new ClassType(queryEnvironment, Long.class);
IType numberType = new ClassType(queryEnvironment, Number.class);
if (intType.isAssignableFrom(argType) || longType.isAssignableFrom(argType)) {
result.add(longType);
} else if (numberType.isAssignableFrom(argType)) {
// any number that is not an int or long is widened to Double
result.add(new ClassType(queryEnvironment, Double.class));
} else {
result.add(services.nothing(String.format(ONLY_NUMERIC_ERROR, getName())));
}
}
return result;
}
@Override
public Set<IType> validateAllType(ValidationServices services,
IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
IType currentResult = null;
final IType longType = new ClassType(queryEnvironment, Long.class);
final IType doubleType = new ClassType(queryEnvironment, Double.class);
for (Map.Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
IType returnType = entry.getValue().iterator().next();
if (currentResult == null) {
currentResult = returnType;
} else if (currentResult.equals(doubleType) || returnType.equals(doubleType)) {
currentResult = new ClassType(queryEnvironment, Double.class);
} else if (returnType.equals(longType)) {
currentResult = new ClassType(queryEnvironment, Long.class);
} else {
currentResult = services.nothing(String.format(ONLY_NUMERIC_ERROR, getName()));
break;
}
}
Set<IType> result = new LinkedHashSet<IType>();
result.add(currentResult);
return result;
}
}
/**
* Select {@link IService}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class SelectService extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private SelectService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final LambdaType lambdaType = (LambdaType)argTypes.get(1);
final Object lambdaExpressionType = lambdaType.getLambdaExpressionType().getType();
if (isBooleanType(queryEnvironment, lambdaExpressionType)) {
final Expression expression;
if (call != null) {
expression = ((Lambda)call.getArguments().get(1)).getExpression();
} else {
expression = null;
}
final Set<IType> inferredTypes;
if (validationResult != null) {
inferredTypes = validationResult.getInferredVariableTypes(expression, Boolean.TRUE).get(
lambdaType.getLambdaEvaluatorName());
} else {
inferredTypes = null;
}
if (inferredTypes == null) {
result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes
.get(0)).getCollectionType()));
} else {
for (IType inferredType : inferredTypes) {
result.add(createReturnCollectionWithType(queryEnvironment, inferredType));
}
}
} else {
result.add(createReturnCollectionWithType(queryEnvironment, services.nothing(
"expression in a select must return a boolean")));
}
return result;
}
}
/**
* Reject {@link IService}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class RejectService extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private RejectService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final LambdaType lambdaType = (LambdaType)argTypes.get(1);
final Object lambdaExpressionType = lambdaType.getLambdaExpressionType().getType();
if (isBooleanType(queryEnvironment, lambdaExpressionType)) {
final Expression expression;
if (call != null) {
expression = ((Lambda)call.getArguments().get(1)).getExpression();
} else {
expression = null;
}
final Set<IType> inferredTypes;
if (validationResult != null) {
inferredTypes = validationResult.getInferredVariableTypes(expression, Boolean.FALSE).get(
lambdaType.getLambdaEvaluatorName());
} else {
inferredTypes = null;
}
if (inferredTypes == null) {
result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes
.get(0)).getCollectionType()));
} else {
for (IType inferredType : inferredTypes) {
result.add(createReturnCollectionWithType(queryEnvironment, inferredType));
}
}
} else {
result.add(createReturnCollectionWithType(queryEnvironment, services.nothing(
"expression in a reject must return a boolean")));
}
return result;
}
}
/**
* A {@link Service} returning the raw collection type of the first argument.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class FirstArgumentRawCollectionType extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private FirstArgumentRawCollectionType(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
result.add(((ICollectionType)argTypes.get(0)).getCollectionType());
return result;
}
}
/**
* A {@link Service} returning the same collection type as the method with the raw collection type of
* first argument.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class ReturnCollectionTypeWithFirstArgumentRawCollectionType extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private ReturnCollectionTypeWithFirstArgumentRawCollectionType(Method serviceMethod,
Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes.get(0))
.getCollectionType()));
return result;
}
}
/**
* A {@link Service} returning the same collection type as the method with the raw collection type of
* first and second arguments.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class ReturnCollectionTypeWithFirstAndSecondArgumentRawCollectionType extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private ReturnCollectionTypeWithFirstAndSecondArgumentRawCollectionType(Method serviceMethod,
Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final IType arg1Type;
if (argTypes.get(0) instanceof ICollectionType) {
arg1Type = ((ICollectionType)argTypes.get(0)).getCollectionType();
} else if (argTypes.get(0) instanceof NothingType) {
arg1Type = argTypes.get(0);
} else {
arg1Type = services.nothing(
"%s can only be called on collections, but %s was used as its receiver.", getName(),
argTypes.get(0));
}
final IType arg2Type;
if (argTypes.get(1) instanceof ICollectionType) {
arg2Type = ((ICollectionType)argTypes.get(1)).getCollectionType();
} else if (argTypes.get(1) instanceof NothingType) {
arg2Type = argTypes.get(1);
} else {
arg2Type = services.nothing(
"%s can only be called on collections, but %s was used as its argument.", getName(),
argTypes.get(1));
}
result.add(createReturnCollectionWithType(queryEnvironment, arg1Type));
result.add(createReturnCollectionWithType(queryEnvironment, arg2Type));
return result;
}
@Override
public Set<IType> validateAllType(ValidationServices services,
IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final StringBuilder builder = new StringBuilder();
for (Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
for (IType type : entry.getValue()) {
if (((ICollectionType)type).getCollectionType() instanceof NothingType) {
builder.append(MESSAGE_SEPARATOR);
builder.append(((NothingType)((ICollectionType)type).getCollectionType())
.getMessage());
} else {
result.add(type);
}
}
}
if (result.isEmpty()) {
IType nothing = services.nothing("Nothing left after %s:" + builder.toString(), getName());
result.add(createReturnCollectionWithType(queryEnvironment, nothing));
}
return result;
}
}
/**
* A {@link Service} returning the
* {@link org.eclipse.acceleo.query.validation.type.EClassifierLiteralType#getType() classifier literal
* type} of the second argument in the {@link org.eclipse.acceleo.query.validation.type.ICollectionType
* ICollectionType} of the first argument.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class SecondArgumentTypeInFirstArgumentCollectionType extends FilterService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
SecondArgumentTypeInFirstArgumentCollectionType(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public java.util.Set<IType> getType(Call call, ValidationServices services,
IValidationResult validationResult, IReadOnlyQueryEnvironment queryEnvironment,
java.util.List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final Set<IType> rawTypes = new LinkedHashSet<IType>();
final IType receiverType = argTypes.get(0);
if (receiverType instanceof NothingType) {
result.add(createReturnCollectionWithType(queryEnvironment, receiverType));
} else if (receiverType instanceof ICollectionType && ((ICollectionType)receiverType)
.getCollectionType() instanceof NothingType) {
result.add(receiverType);
} else if (argTypes.get(1) instanceof ClassLiteralType) {
rawTypes.add(new ClassType(queryEnvironment, ((ClassLiteralType)argTypes.get(1)).getType()));
} else if (argTypes.get(1) instanceof ClassType && ((ClassType)argTypes.get(1))
.getType() == null) {
result.add(services.nothing("EClassifier on %s cannot be null.", getName()));
} else if (argTypes.get(1) instanceof EClassifierType) {
rawTypes.add(new EClassifierType(queryEnvironment, ((EClassifierType)argTypes.get(1))
.getType()));
} else if (argTypes.get(1) instanceof EClassifierSetLiteralType) {
for (EClassifier eCls : ((EClassifierSetLiteralType)argTypes.get(1)).getEClassifiers()) {
rawTypes.add(new EClassifierType(queryEnvironment, eCls));
}
}
for (IType rawType : rawTypes) {
result.add(createReturnCollectionWithType(queryEnvironment, rawType));
}
return result;
}
}
/**
* A {@link Service} returning the raw type of the first argument {@link ICollectionType}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class FirstCollectionTypeService extends JavaMethodService {
/**
* Creates a new service instance given a method and an instance.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
FirstCollectionTypeService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes.get(0))
.getCollectionType()));
return result;
}
}
/**
* InsertAt {@link IService}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class InsertAtService extends IncludingService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private InsertAtService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes.get(0))
.getCollectionType()));
result.add(createReturnCollectionWithType(queryEnvironment, argTypes.get(2)));
return result;
}
}
/**
* Intersection {@link IService}.
*
* @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
*/
private static final class IntersectionService extends JavaMethodService {
/**
* Constructor.
*
* @param serviceMethod
* the method that realizes the service
* @param serviceInstance
* the instance on which the service must be called
*/
private IntersectionService(Method serviceMethod, Object serviceInstance) {
super(serviceMethod, serviceInstance);
}
@Override
public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final IType arg1Type;
if (argTypes.get(0) instanceof ICollectionType) {
arg1Type = ((ICollectionType)argTypes.get(0)).getCollectionType();
} else if (argTypes.get(0) instanceof NothingType) {
arg1Type = argTypes.get(0);
} else {
arg1Type = services.nothing(
"%s can only be called on collections, but %s was used as its receiver.", getName(),
argTypes.get(0));
}
final IType arg2Type;
if (argTypes.get(1) instanceof ICollectionType) {
arg2Type = ((ICollectionType)argTypes.get(1)).getCollectionType();
} else if (argTypes.get(1) instanceof NothingType) {
arg2Type = argTypes.get(1);
} else {
arg2Type = services.nothing(
"%s can only be called on collections, but %s was used as its argument.", getName(),
argTypes.get(1));
}
final Set<IType> resultRawTypes = services.intersection(arg1Type, arg2Type);
if (resultRawTypes.isEmpty()) {
if (arg1Type instanceof NothingType) {
resultRawTypes.add(arg1Type);
}
if (arg2Type instanceof NothingType) {
resultRawTypes.add(arg2Type);
}
if (resultRawTypes.isEmpty()) {
resultRawTypes.add(services.nothing("Nothing left after intersection of %s and %s",
argTypes.get(0), argTypes.get(1)));
}
}
for (IType resultRawType : resultRawTypes) {
result.add(createReturnCollectionWithType(queryEnvironment, resultRawType));
}
return result;
}
@Override
public Set<IType> validateAllType(ValidationServices services,
IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
final StringBuilder builder = new StringBuilder();
for (Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
for (IType type : entry.getValue()) {
if (((ICollectionType)type).getCollectionType() instanceof NothingType) {
builder.append(MESSAGE_SEPARATOR);
builder.append(((NothingType)((ICollectionType)type).getCollectionType())
.getMessage());
} else {
result.add(type);
}
}
}
if (result.isEmpty()) {
IType nothing = services.nothing("Nothing left after intersection:" + builder.toString());
result.add(createReturnCollectionWithType(queryEnvironment, nothing));
}
return result;
}
}
@Override
protected IService<Method> getService(Method publicMethod) {
final IService<Method> result;
if ("filter".equals(publicMethod.getName())) {
result = new SecondArgumentTypeInFirstArgumentCollectionType(publicMethod, this);
} else if ("add".equals(publicMethod.getName()) || "concat".equals(publicMethod.getName()) || "union"
.equals(publicMethod.getName())) {
result = new ReturnCollectionTypeWithFirstAndSecondArgumentRawCollectionType(publicMethod, this);
} else if ("asSequence".equals(publicMethod.getName()) || "asSet".equals(publicMethod.getName())
|| "asOrderedSet".equals(publicMethod.getName())) {
result = new ReturnCollectionTypeWithFirstArgumentRawCollectionType(publicMethod, this);
} else if ("subOrderedSet".equals(publicMethod.getName()) || "subSequence".equals(publicMethod
.getName())) {
result = new FirstCollectionTypeService(publicMethod, this);
} else if ("drop".equals(publicMethod.getName()) || "dropRight".equals(publicMethod.getName())) {
result = new FirstCollectionTypeService(publicMethod, this);
} else if ("first".equals(publicMethod.getName()) || "at".equals(publicMethod.getName()) || "last"
.equals(publicMethod.getName())) {
result = new FirstArgumentRawCollectionType(publicMethod, this);
} else if ("excluding".equals(publicMethod.getName()) || "sub".equals(publicMethod.getName())
|| "reverse".equals(publicMethod.getName())) {
result = new FirstCollectionTypeService(publicMethod, this);
} else if ("sortedBy".equals(publicMethod.getName())) {
result = new FirstCollectionTypeService(publicMethod, this);
} else if ("reject".equals(publicMethod.getName())) {
result = new RejectService(publicMethod, this);
} else if ("select".equals(publicMethod.getName())) {
result = new SelectService(publicMethod, this);
} else if ("collect".equals(publicMethod.getName())) {
result = new CollectService(publicMethod, this);
} else if ("closure".equals(publicMethod.getName())) {
result = new ClosureService(publicMethod, this);
} else if ("including".equals(publicMethod.getName()) || "prepend".equals(publicMethod.getName())
|| "append".equals(publicMethod.getName())) {
result = new IncludingService(publicMethod, this);
} else if ("sep".equals(publicMethod.getName())) {
if (publicMethod.getParameterTypes().length == 2) {
result = new JavaMethodService(publicMethod, this) {
@Override
public Set<IType> getType(Call call, ValidationServices services,
IValidationResult validationResult, IReadOnlyQueryEnvironment queryEnvironment,
List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
result.add(new SequenceType(queryEnvironment, ((ICollectionType)argTypes.get(0))
.getCollectionType()));
result.add(new SequenceType(queryEnvironment, argTypes.get(1)));
return result;
}
};
} else if (publicMethod.getParameterTypes().length == 4) {
result = new JavaMethodService(publicMethod, this) {
@Override
public Set<IType> getType(Call call, ValidationServices services,
IValidationResult validationResult, IReadOnlyQueryEnvironment queryEnvironment,
List<IType> argTypes) {
final Set<IType> result = new LinkedHashSet<IType>();
result.add(new SequenceType(queryEnvironment, ((ICollectionType)argTypes.get(0))
.getCollectionType()));
result.add(new SequenceType(queryEnvironment, argTypes.get(1)));
result.add(new SequenceType(queryEnvironment, argTypes.get(2)));
result.add(new SequenceType(queryEnvironment, argTypes.get(3)));
return result;
}
};
} else {
result = new JavaMethodService(publicMethod, this);
}
} else if ("any".equals(publicMethod.getName())) {
result = new AnyService(publicMethod, this);
} else if ("exists".equals(publicMethod.getName()) || "forAll".equals(publicMethod.getName()) || "one"
.equals(publicMethod.getName())) {
result = new BooleanLambdaService(publicMethod, this);
} else if ("insertAt".equals(publicMethod.getName())) {
result = new InsertAtService(publicMethod, this);
} else if ("intersection".equals(publicMethod.getName())) {
result = new IntersectionService(publicMethod, this);
} else if ("sum".equals(publicMethod.getName()) || "min".equals(publicMethod.getName()) || "max"
.equals(publicMethod.getName())) {
result = new NumberService(publicMethod, this);
} else {
result = new JavaMethodService(publicMethod, this);
}
return result;
}
/**
* Tells if the given {@link Object} is a is a boolean {@link org.eclipse.emf.ecore.EDataType EDataType}.
*
* @param queryEnvironment
* the {@link IReadOnlyQueryEnvironment}
* @param type
* the {@link org.eclipse.emf.ecore.EDataType EDataType}
* @return <code>true</code> if the given {@link Object} is a is a boolean
* {@link org.eclipse.emf.ecore.EDataType EDataType}, <code>false</code> otherwise
*/
private static boolean isBooleanType(IReadOnlyQueryEnvironment queryEnvironment, Object type) {
final Class<?> typeClass;
if (type instanceof EClassifier) {
typeClass = queryEnvironment.getEPackageProvider().getClass((EClassifier)type);
} else if (type instanceof ClassType) {
typeClass = ((ClassType)type).getType();
} else if (type instanceof Class<?>) {
typeClass = (Class<?>)type;
} else {
return false;
}
return typeClass == Boolean.class || typeClass == boolean.class;
}
// @formatter:off
@SuppressWarnings("unchecked")
@Documentation(
value = "Returns the concatenation of the current sequence with the given collection.",
params = {
@Param(name = "sequence", value = "The first operand"),
@Param(name = "collection", value = "The second operand")
},
result = "The concatenation of the two specified collections.",
examples = {
@Example(
expression = "Sequence{'a', 'b', 'c'}.concat(Sequence{'d', 'e'})", result = "Sequence{'a', 'b', 'c', 'd', 'e'}",
others = {
@Other(
language = Other.ACCELEO_3,
expression = "Sequence{'a', 'b', 'c'}.addAll(Sequence{'d', 'e'})",
result = "Sequence{'a', 'b', 'c', 'd', 'e'}"
)
}
)
}
)
// @formatter:on
public <T> List<T> concat(List<? extends T> sequence, Collection<? extends T> collection) {
if (sequence == null) {
throw new NullPointerException();
}
final List<T> result;
if (collection.isEmpty()) {
result = (List<T>)sequence;
} else {
result = new ArrayList<T>(sequence);
result.addAll(collection);
}
return result;
}
// @formatter:off
@SuppressWarnings("unchecked")
@Documentation(
value = "Returns the concatenation of the current set with the given collection.",
params = {
@Param(name = "set", value = "The first operand"),
@Param(name = "collection", value = "The second operand")
},
result = "The concatenation of the two specified collections.",
examples = {
@Example(
expression = "OrderedSet{'a', 'b', 'c'}.concat(Sequence{'d', 'e'})", result = "OrderedSet{'a', 'b', 'c', 'd', 'e'}",
others = {
@Other(
language = Other.ACCELEO_3,
expression = "OrderedSet{'a', 'b', 'c'}.addAll(Sequence{'d', 'e'})",
result = "OrderedSet{'a', 'b', 'c', 'd', 'e'}"
)
}
)
}
)
// @formatter:on
public <T> Set<T> concat(Set<? extends T> set, Collection<? extends T> collection) {
if (set == null) {
throw new NullPointerException();
}
final Set<T> result;
if (collection.isEmpty()) {
result = (Set<T>)set;
} else {
result = new LinkedHashSet<T>(set);
result.addAll(collection);
}
return result;
}
// @formatter:off
@Documentation(
value = "Returns the concatenation of the given collection into the given sequence.",
params = {
@Param(name = "sequence", value = "The first operand"),
@Param(name = "collection", value = "The second operand")
},
result = "The current sequence including the elements of the given collection.",
examples = {
@Example(
expression = "Sequence{'a', 'b', 'c'}.add(Sequence{'d', 'e'})", result = "Sequence{'a', 'b', 'c', 'd', 'e'}",
others = {
@Other(
language = Other.ACCELEO_3,
expression = "Sequence{'a', 'b', 'c'} + Sequence{'d', 'e'}",
result = "Sequence{'a', 'b', 'c', 'd', 'e'}"
)
}
),
@Example(
expression = "Sequence{'a', 'b', 'c'} + OrderedSet{'c', 'e'}", result = "Sequence{'a', 'b', 'c', 'c', 'e'}",
others = {
@Other(
language = Other.ACCELEO_3,
expression = "Sequence{'a', 'b', 'c'}.addAll(OrderedSet{'c', 'e'})",
result = "Sequence{'a', 'b', 'c', 'c', 'e'}"
)
}
)
},
comment = "The service addAll has been replaced by \"add\" in order to have access to the operator \"+\" between to sequences"
)
// @formatter:on
public <T> List<T> add(List<? extends T> sequence, Collection<? extends T> collection) {
return concat(sequence, collection);
}
// @formatter:off
@Documentation(
value = "Returns the concatenation of the given collection into the current set.",
params = {
@Param(name = "set", value = "The first operand"),
@Param(name = "collection", value = "The second operand")
},
result = "The current set including the elements of the given collection.",
examples = {
@Example(
expression = "OrderedSet{'a', 'b', 'c'} + OrderedSet{'c', 'b', 'f'}", result = "OrderedSet{'a', 'b', 'c', 'f'}",
others = {
@Other(
language = Other.ACCELEO_3,
expression = "OrderedSet{'a', 'b', 'c'}.addAll(OrderedSet{'c', 'b', 'f'})",
result = "OrderedSet{'a', 'b', 'c', 'f'}"
)
}
)
},
comment = "The service addAll has been replaced by \"add\" in order to have access to the operator \"+\" between to sets"
)
// @formatter:on
public <T> Set<T> add(Set<? extends T> set, Collection<? extends T> collection) {
return concat(set, collection);
}
// @formatter:off
@Documentation(
value = "Returns the difference of the current sequence and the given collection.",
params = {
@Param(name = "sequence", value = "The first operand"),
@Param(name = "collection", value = "The second operand")
},
result = "The sequence that contains elements from sequence1 that are not in collection2.",
examples = {
@Example(
expression = "Sequence{'a', 'b', 'c'} - Sequence{'c', 'b', 'f'}", result = "Sequence{'a'}",
others = {
@Other(
language = Other.ACCELEO_3,
expression = "Sequence{'a', 'b', 'c'}.removeAll(Sequence{'c', 'b', 'f'})",
result = "Sequence{'a'}"
)
}
),
@Example(
expression = "Sequence{'a', 'b', 'c'} - OrderedSet{'c', 'b', 'f'}", result = "Sequence{'a'}",
others = {
@Other(
language = Other.ACCELEO_3,
expression = "Sequence{'a', 'b', 'c'}.removeAll(OrderedSet{'c', 'b', 'f'})",
result = "Sequence{'a'}"
)
}
)
},
comment = "The service removeAll has been replaced by \"sub\" in order to have access to the operator \"-\" between to sequences"
)
// @formatter:on
public <T> List<T> sub(List<T> sequence, Collection<?> collection) {
if (sequence == null) {
throw new NullPointerException();
}
if (collection.isEmpty()) {
return sequence;
} else {
List<T> result = new ArrayList<T>(sequence);
result.removeAll(collection);
return result;
}
}
// @formatter:off
@Documentation(
value = "Returns the difference of the current set and the given collection.",
params = {
@Param(name = "set", value = "The first operand"),
@Param(name = "collection", value = "The second operand")
},
result = "The set that contains elements from set1 that are not in collection2.",
examples = {
@Example(
expression = "OrderedSet{'a', 'b', 'c'} - OrderedSet{'c', 'b', 'f'}", result = "OrderedSet{'a'}",
others = {
@Other(
language = Other.ACCELEO_3,
expression = "OrderedSet{'a', 'b', 'c'}.removeAll(OrderedSet{'c', 'b', 'f'})",
result = "OrderedSet{'a'}"
)
}
)
},
comment = "The service removeAll has been replaced by \"sub\" in order to have access to the operator \"-\" between to sets"
)
// @formatter:on
public <T> Set<T> sub(Set<T> set, Collection<?> collection) {
if (set == null) {
throw new NullPointerException();
}
if (collection.isEmpty()) {
return set;
} else {
Set<T> result = new LinkedHashSet<T>(set);
result.removeAll(collection);
return result;
}
}
// @formatter:off
@Documentation(
value = "Select returns a filtered version of the specified sequence. Only elements for which the given \"lambda\" evaluates " +
"to true will be present in the returned sequence.",
params = {
@Param(name = "sequence", value = "The original sequence"),
@Param(name = "lambda", value = "The filtering expression")
},
result = "A filtered version of the specified sequence",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->select(str | str.equals('a'))", result = "Sequence{'a'}")
}
)
// @formatter:on
public <T> List<T> select(List<T> sequence, LambdaValue lambda) {
final List<T> newList;
if (lambda == null) {
newList = new ArrayList<T>();
} else {
newList = new ArrayList<T>();
for (T elt : sequence) {
try {
Object value = lambda.eval(new Object[] {elt });
if (Boolean.TRUE.equals(value)) {
newList.add(elt);
}
// CHECKSTYLE:OFF
} catch (Exception e) {
// CHECKSTYLE:ON
lambda.logException(Diagnostic.WARNING, e);
}
}
}
return newList;
}
// @formatter:off
@Documentation(
value = "Select returns a filtered version of the specified set. Only elements for which the given \"lambda\" evaluates " +
"to true will be present in the returned set.",
params = {
@Param(name = "set", value = "The original set"),
@Param(name = "lambda", value = "The filtering expression")
},
result = "A filtered version of the specified set",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->select(str | str.equals('a'))", result = "OrderedSet{'a'}")
}
)
// @formatter:on
public <T> Set<T> select(Set<T> set, LambdaValue lambda) {
final Set<T> newSet;
if (lambda == null) {
newSet = new LinkedHashSet<T>();
} else {
newSet = new LinkedHashSet<T>();
for (T elt : set) {
try {
Object value = lambda.eval(new Object[] {elt });
if (Boolean.TRUE.equals(value)) {
newSet.add(elt);
}
// CHECKSTYLE:OFF
} catch (Exception e) {
// CHECKSTYLE:ON
lambda.logException(Diagnostic.WARNING, e);
}
}
}
return newSet;
}
// @formatter:off
@Documentation(
value = "Reject returns a filtered version of the specified set. Only elements for which the given \"lambda\" evaluates " +
"to false will be present in the returned set",
params = {
@Param(name = "set", value = "The original set"),
@Param(name = "lambda", value = "The filtering expression")
},
result = "A filtered version of the specified set",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->reject(str | str.equals('a'))", result = "OrderedSet{'b', 'c'}")
}
)
// @formatter:on
public <T> Set<T> reject(Set<T> set, LambdaValue lambda) {
final Set<T> newSet;
if (lambda == null) {
newSet = new LinkedHashSet<T>();
} else {
newSet = new LinkedHashSet<T>();
for (T elt : set) {
try {
Object value = lambda.eval(new Object[] {elt });
if (Boolean.FALSE.equals(value)) {
newSet.add(elt);
}
// CHECKSTYLE:OFF
} catch (Exception e) {
// CHECKSTYLE:ON
lambda.logException(Diagnostic.WARNING, e);
}
}
}
return newSet;
}
// @formatter:off
@Documentation(
value = "Reject returns a filtered version of the specified sequence. Only elements for which the given \"lambda\" evaluates " +
"to false will be present in the returned sequence",
params = {
@Param(name = "sequence", value = "The original sequence"),
@Param(name = "lambda", value = "The filtering expression")
},
result = "A filtered version of the specified sequence",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->reject(str | str.equals('a'))", result = "Sequence{'b', 'c'}")
}
)
// @formatter:on
public <T> List<T> reject(List<T> sequence, LambdaValue lambda) {
final List<T> newList;
if (lambda == null) {
newList = new ArrayList<T>();
} else {
newList = new ArrayList<T>();
for (T elt : sequence) {
try {
Object value = lambda.eval(new Object[] {elt });
if (Boolean.FALSE.equals(value)) {
newList.add(elt);
}
// CHECKSTYLE:OFF
} catch (Exception e) {
// CHECKSTYLE:ON
lambda.logException(Diagnostic.WARNING, e);
}
}
}
return newList;
}
// @formatter:off
@Documentation(
value = "Returns a set containing the result of applying \"lambda\" on all elements contained in the current set, " +
"maintaining order.",
params = {
@Param(name = "set", value = "The original set"),
@Param(name = "lambda", value = "The lambda expression")
},
result = "A transformed version of the specified set using the given lamba",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->collect(str | str.toUpper())", result = "OrderedSet{'A', 'B', 'C'}")
}
)
// @formatter:on
public Set<Object> collect(Set<?> set, LambdaValue lambda) {
final Set<Object> result;
if (lambda == null) {
result = Collections.emptySet();
} else {
result = new LinkedHashSet<Object>();
for (Object elt : set) {
try {
Object lambdaResult = lambda.eval(new Object[] {elt });
if (!(lambdaResult instanceof Nothing)) {
if (lambdaResult instanceof Collection) {
result.addAll((Collection<?>)lambdaResult);
} else if (lambdaResult != null) {
result.add(lambdaResult);
}
}
// CHECKSTYLE:OFF
} catch (Exception e) {
lambda.logException(Diagnostic.WARNING, e);
}
// CHECKSTYLE:ON
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Returns a sequence containing the result of applying \"lambda\" on all elements contained in the current sequence, " +
"maintaining order.",
params = {
@Param(name = "sequence", value = "The original sequence"),
@Param(name = "lambda", value = "The lambda expression")
},
result = "A transformed version of the specified sequence using the given lamba",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->collect(str | str.toUpper())", result = "Sequence{'A', 'B', 'C'}")
}
)
// @formatter:on
public List<Object> collect(List<?> sequence, LambdaValue lambda) {
final List<Object> result;
if (lambda == null) {
result = Collections.emptyList();
} else {
result = new ArrayList<Object>();
for (Object elt : sequence) {
try {
Object lambdaResult = lambda.eval(new Object[] {elt });
if (!(lambdaResult instanceof Nothing)) {
if (lambdaResult instanceof Collection) {
result.addAll((Collection<?>)lambdaResult);
} else if (lambdaResult != null) {
result.add(lambdaResult);
}
}
// CHECKSTYLE:OFF
} catch (Exception e) {
lambda.logException(Diagnostic.WARNING, e);
}
// CHECKSTYLE:ON
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Returns a set containing the result of applying \"lambda\" recursivly.",
params = {
@Param(name = "collection", value = "The original collection"),
@Param(name = "lambda", value = "The lambda expression")
},
result = "a set containing the result of applying \"lambda\" recursivly",
examples = {
@Example(expression = "Sequence{eCls}->closure(e | e.eContainer())", result = "Sequence{subEPkg, ePkg, rootEPkg}")
}
)
// @formatter:on
public Set<Object> closure(Collection<?> collection, LambdaValue lambda) {
final Set<Object> result;
if (lambda == null) {
result = Collections.emptySet();
} else {
result = new LinkedHashSet<Object>();
Set<Object> currentSet = new LinkedHashSet<Object>(collection);
Set<Object> added;
do {
added = new LinkedHashSet<Object>();
for (Object current : currentSet) {
try {
final Object lambdaResult = lambda.eval(new Object[] {current });
if (lambdaResult instanceof Collection) {
for (Object child : (Collection<?>)lambdaResult) {
if (result.add(child)) {
added.add(child);
}
}
} else if (lambdaResult != null && !(lambdaResult instanceof Nothing)) {
if (result.add(lambdaResult)) {
added.add(lambdaResult);
}
}
// CHECKSTYLE:OFF
} catch (Exception e) {
// CHECKSTYLE:ON
lambda.logException(Diagnostic.WARNING, e);
}
}
currentSet = added;
} while (!added.isEmpty());
}
return result;
}
// @formatter:off
@Documentation(
value = "Returns a sequence containing the elements of the original sequence ordered by the result of the given lamba",
params = {
@Param(name = "sequence", value = "The original sequence"),
@Param(name = "lambda", value = "The lambda expression")
},
result = "An ordered version of the given sequence",
examples = {
@Example(expression = "Sequence{'aa', 'bbb', 'c'}->sortedBy(str | str.size())", result = "Sequence{'c', 'aa', 'bbb'}")
}
)
// @formatter:on
public <T> List<T> sortedBy(List<T> sequence, final LambdaValue lambda) {
final List<T> result;
if (sequence == null) {
result = null;
} else if (lambda == null) {
return sequence;
} else {
result = new ArrayList<T>(sequence);
final Map<T, Object> values = new HashMap<T, Object>();
for (T object : result) {
try {
values.put(object, lambda.eval(new Object[] {object }));
// CHECKSTYLE:OFF
} catch (Exception e) {
lambda.logException(Diagnostic.WARNING, e);
}
// CHECKSTYLE:ON
}
Collections.sort(result, new LambdaComparator<T>(values));
}
return result;
}
// @formatter:off
@Documentation(
value = "Returns a set containing the elements of the original set ordered by the result of the given lamba",
params = {
@Param(name = "set", value = "The original set"),
@Param(name = "lambda", value = "The lambda expression")
},
result = "An ordered version of the given set",
examples = {
@Example(expression = "OrderedSet{'aa', 'bbb', 'c'}->sortedBy(str | str.size())", result = "OrderedSet{'c', 'aa', 'bbb'}")
}
)
// @formatter:on
public <T> Set<T> sortedBy(Set<T> set, final LambdaValue lambda) {
final Set<T> result;
if (set == null) {
result = null;
} else if (lambda == null) {
return set;
} else {
List<T> sorted = new ArrayList<T>(set);
final Map<T, Object> values = new HashMap<T, Object>();
for (T object : sorted) {
try {
values.put(object, lambda.eval(new Object[] {object }));
// CHECKSTYLE:OFF
} catch (Exception e) {
lambda.logException(Diagnostic.WARNING, e);
}
// CHECKSTYLE:ON
}
Collections.sort(sorted, new LambdaComparator<T>(values));
result = new LinkedHashSet<T>(sorted);
}
return result;
}
// @formatter:off
@Documentation(
value = "Returns the size of the specified collection",
params = {
@Param(name = "collection", value = "The input collection")
},
result = "The size of the specified collection",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->size()", result = "3"),
@Example(expression = "OrderedSet{'a', 'b', 'c', 'd'}->size()", result = "4")
}
)
// @formatter:on
public Integer size(Collection<?> collection) {
return collection.size();
}
// @formatter:off
@Documentation(
value = "Adds the given object to the current set.",
params = {
@Param(name = "set", value = "The source set"),
@Param(name = "object", value = "The object to add")
},
result = "A set containing all elements of source set plus the given object",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->including('d')", result = "OrderedSet{'a', 'b', 'c', 'd'}")
}
)
// @formatter:on
public <T> Set<T> including(Set<T> set, T object) {
if (set.contains(object)) {
return set;
} else {
Set<T> result = new LinkedHashSet<T>(set);
result.add(object);
return result;
}
}
// @formatter:off
@Documentation(
value = "Removes the given object from the current set.",
params = {
@Param(name = "set", value = "The source set"),
@Param(name = "object", value = "The object to remove")
},
result = "A set containing all elements of source set minus the given object",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->excluding('c')", result = "OrderedSet{'a', 'b'}")
}
)
// @formatter:on
public <T> Set<T> excluding(Set<T> set, Object object) {
if (!set.contains(object)) {
return set;
} else {
Set<T> result = new LinkedHashSet<T>(set);
result.remove(object);
return result;
}
}
// @formatter:off
@Documentation(
value = "Adds the given object to the current sequence.",
params = {
@Param(name = "sequence", value = "The source sequence"),
@Param(name = "object", value = "The object to add")
},
result = "A sequence containing all elements of the source sequence plus the given object",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->including('d')", result = "Sequence{'a', 'b', 'c', 'd'}")
}
)
// @formatter:on
public <T> List<T> including(List<T> sequence, T object) {
List<T> result = new ArrayList<T>(sequence);
result.add(object);
return result;
}
// @formatter:off
@Documentation(
value = "Removes the given object from the current sequence.",
params = {
@Param(name = "sequence", value = "The source sequence"),
@Param(name = "object", value = "The object to remove")
},
result = "A sequence containing all elements of source sequence minus the given object",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->excluding('c')", result = "Sequence{'a', 'b'}")
}
)
// @formatter:on
public <T> List<T> excluding(List<T> sequence, Object object) {
List<T> result = new ArrayList<T>(sequence);
result.removeAll(Collections.singleton(object));
return result;
}
// @formatter:off
@Documentation(
value = "Returns a sequence representation of the specified collection. Returns the same object if " +
"it is already a sequence.",
params = {
@Param(name = "collection", value = "The input collection")
},
result = "A sequence with all the elements of the input collection",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->asSequence()", result = "Sequence{'a', 'b', 'c'}"),
@Example(expression = "Sequence{'a', 'b', 'c'}->asSequence()", result = "Sequence{'a', 'b', 'c'}")
}
)
// @formatter:on
public <T> List<T> asSequence(Collection<T> collection) {
if (collection instanceof List) {
return (List<T>)collection;
} else {
return new ArrayList<T>(collection);
}
}
// @formatter:off
@Documentation(
value = "Returns a set representation of the specified collection. Returns the same object if " +
"it is already a set.",
params = {
@Param(name = "collection", value = "The input collection")
},
result = "A set with all the elements of the input collection",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->asSet()", result = "OrderedSet{'a', 'b', 'c'}"),
@Example(expression = "Sequence{'a', 'b', 'c', 'c', 'a'}->asSet()", result = "OrderedSet{'a', 'b', 'c'}")
}
)
// @formatter:on
public <T> Set<T> asSet(Collection<T> collection) {
if (collection instanceof Set) {
return (Set<T>)collection;
} else {
return new LinkedHashSet<T>(collection);
}
}
// @formatter:off
@Documentation(
value = "Returns a set representation of the specified collection. Returns the same object if " +
"it is a set already. This operation has the same behavior as \"asSet()\"",
params = {
@Param(name = "collection", value = "The input collection")
},
result = "A set with all the elements of the input collection",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->asOrderedSet()", result = "OrderedSet{'a', 'b', 'c'}"),
@Example(expression = "Sequence{'a', 'b', 'c'}->asOrderedSet()", result = "OrderedSet{'a', 'b', 'c'}")
}
)
// @formatter:on
public <T> Set<T> asOrderedSet(Collection<T> collection) {
return asSet(collection);
}
// @formatter:off
@Documentation(
value = "Returns the first element of the specified Collection.",
params = {
@Param(name = "collection", value = "The input collection")
},
result = "The first element of the collection",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->first()", result = "'a'")
}
)
// @formatter:on
public <T> T first(Collection<T> collection) {
final T res;
final Iterator<T> it = collection.iterator();
if (it.hasNext()) {
res = it.next();
} else {
res = null;
}
return res;
}
// @formatter:off
@Documentation(
value = "Returns the given sequence in reversed order.",
params = {
@Param(name = "sequence", value = "The input sequence")
},
result = "The sequence in reserved order",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->reverse()", result = "Sequence{'c', 'b', 'a'}")
}
)
// @formatter:on
public <T> List<T> reverse(List<T> sequence) {
final ArrayList<T> res = new ArrayList<T>(sequence);
Collections.reverse(res);
return res;
}
// @formatter:off
@Documentation(
value = "Returns the given set in reversed order.",
params = {
@Param(name = "set", value = "The input set")
},
result = "The set in reserved order",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->reverse()", result = "OrderedSet{'c', 'b', 'a'}")
}
)
// @formatter:on
public <T> Set<T> reverse(Set<T> set) {
final ArrayList<T> res = new ArrayList<T>(set);
Collections.reverse(res);
return new LinkedHashSet<T>(res);
}
// @formatter:off
@Documentation(
value = "Returns \"true\" when the input collection is empty.",
params = {
@Param(name = "collection", value = "The input collection")
},
result = "\"true\" when the input collection is empty.",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->isEmpty()", result = "false"),
@Example(expression = "Sequence{}->isEmpty()", result = "true"),
}
)
// @formatter:on
public Boolean isEmpty(Collection<?> collection) {
return collection.isEmpty();
}
// @formatter:off
@Documentation(
value = "Returns \"true\" when the input collection is not empty.",
params = {
@Param(name = "collection", value = "The input collection")
},
result = "\"true\" when the input collection is not empty.",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->notEmpty()", result = "true"),
@Example(expression = "Sequence{}->notEmpty()", result = "false"),
}
)
// @formatter:on
public Boolean notEmpty(Collection<?> collection) {
return !collection.isEmpty();
}
// @formatter:off
@Documentation(
value = "Returns the element at the specified position in the sequence.",
params = {
@Param(name = "sequence", value = "The input sequence"),
@Param(name = "position", value = "The position of the element to return ([1..size])")
},
result = "The element at the specified position in the list",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->at(1)", result = "'a'"),
@Example(expression = "Sequence{'a', 'b', 'c'}->at(2)", result = "'b'")
}
)
// @formatter:on
public <T> T at(List<T> sequence, Integer position) {
return sequence.get(position - 1);
}
// @formatter:off
@Documentation(
value = "Returns the element at the specified position in the set.",
params = {
@Param(name = "set", value = "The input set"),
@Param(name = "position", value = "The position of the element to return ([1..size])")
},
result = "The element at the specified position in the set",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->at(1)", result = "'a'"),
@Example(expression = "OrderedSet{'a', 'b', 'c'}->at(2)", result = "'b'")
}
)
// @formatter:on
public <T> T at(Set<T> set, Integer position) {
return new ArrayList<T>(set).get(position - 1);
}
// @formatter:off
@Documentation(
value = "Keeps only instances of the given primitive type (String, Integer, Real, Boolean) from the given set.",
params = {
@Param(name = "set", value = "The input set to filter"),
@Param(name = "cls", value = "The type used to filters element in the set")
},
result = "A new set containing instances of the given primitive type or null if the given set is null",
examples = {
@Example(expression = "OrderedSet{'a', 1, 3.14}->filter(String)", result = "OrederedSet{'a'}")
}
)
// @formatter:on
public <T> Set<T> filter(Set<T> set, Class<?> cls) {
final Set<T> result;
if (set == null) {
result = null;
} else if (cls != null) {
result = new LinkedHashSet<T>();
for (T value : set) {
if (cls.isInstance(value)) {
result.add(value);
}
}
} else {
result = new LinkedHashSet<T>();
}
return result;
}
// @formatter:off
@Documentation(
value = "Keeps only instances of the given EClassifier from the given set.",
params = {
@Param(name = "set", value = "The input set to filter"),
@Param(name = "eClassifier", value = "The type used to filters element in the set")
},
result = "A new set containing instances of the given EClassifier or null if the given set is null",
examples = {
@Example(expression = "OrderedSet{anEClass, anEAttribute, anEReference}->filter(ecore::EClass)", result = "OrederedSet{anEClass}"),
@Example(expression = "OrderedSet{anEClass, anEAttribute}->filter(ecore::EStructuralFeature)", result = "OrederedSet{anEAttribute}"),
}
)
// @formatter:on
public <T> Set<T> filter(Set<T> set, EClassifier eClassifier) {
final Set<T> result;
if (set == null) {
result = null;
} else if (eClassifier != null) {
final Set<EClassifier> eClassifiers = new LinkedHashSet<EClassifier>();
eClassifiers.add(eClassifier);
result = filter(set, eClassifiers);
} else {
result = new LinkedHashSet<T>();
}
return result;
}
// @formatter:off
@Documentation(
value = "Keeps only instances of the given set of EClassifier from the given set.",
params = {
@Param(name = "set", value = "The input set to filter"),
@Param(name = "eClassifiers", value = "The set of type used to filters element in the set")
},
result = "A new set containing instances of the given set of EClassifiers or null if the given set is null",
examples = {
@Example(expression = "OrderedSet{anEClass, anEAttribute, anEReference}->filter({ecore::EClass | ecore::EReference})", result = "OrderedSet{anEClass, anEReference}"),
@Example(expression = "OrderedSet{anEClass, anEAttribute, anEPackage}->filter({ecore::EStructuralFeature | ecore::EPacakge})", result = "OrderedSet{anEAttribute, anEPackage}"),
}
)
// @formatter:on
public <T> Set<T> filter(Set<T> set, Set<EClassifier> eClassifiers) {
final Set<T> result;
if (set == null) {
result = null;
} else if (eClassifiers == null || eClassifiers.isEmpty()) {
result = new LinkedHashSet<T>();
} else {
result = new LinkedHashSet<T>();
for (T object : set) {
for (EClassifier eClassifier : eClassifiers) {
if (eClassifier.isInstance(object)) {
result.add(object);
break;
}
}
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Keeps only instances of the given primitive type (String, Integer, Real, Boolean) from the given sequence.",
params = {
@Param(name = "sequence", value = "The input sequence to filter"),
@Param(name = "cls", value = "The type used to filters element in the sequence")
},
result = "A new sequence containing instances of the given primitive type or null if the given sequence is null",
examples = {
@Example(expression = "Sequence{'a', 1, 3.14}->filter(String)", result = "Sequence{'a'}")
}
)
// @formatter:on
public <T> List<T> filter(List<T> sequence, Class<?> cls) {
final List<T> result;
if (sequence == null) {
result = null;
} else if (cls != null) {
result = new ArrayList<T>();
for (T value : sequence) {
if (cls.isInstance(value)) {
result.add(value);
}
}
} else {
result = new ArrayList<T>();
}
return result;
}
// @formatter:off
@Documentation(
value = "Keeps only instances of the given EClassifier in the given sequence.",
params = {
@Param(name = "sequence", value = "The input sequence to filter"),
@Param(name = "eClassifier", value = "The type used to filters element in the sequence")
},
result = "A new sequence containing instances of the given EClassifier or null if the given sequence is null",
examples = {
@Example(expression = "Sequence{anEClass, anEAttribute, anEReference}->filter(ecore::EClass)", result = "Sequence{anEClass}"),
@Example(expression = "Sequence{anEClass, anEAttribute}->filter(ecore::EStructuralFeature)", result = "Sequence{anEAttribute}"),
}
)
// @formatter:on
public <T> List<T> filter(List<T> sequence, EClassifier eClassifier) {
final List<T> result;
if (sequence == null) {
result = null;
} else if (eClassifier != null) {
final Set<EClassifier> eClassifiers = new LinkedHashSet<EClassifier>();
eClassifiers.add(eClassifier);
result = filter(sequence, eClassifiers);
} else {
result = new ArrayList<T>();
}
return result;
}
// @formatter:off
@Documentation(
value = "Keeps only instances of the given EClassifier in the given sequence.",
params = {
@Param(name = "sequence", value = "The input sequence to filter"),
@Param(name = "eClassifiers", value = "The set of types used to filters element in the sequence")
},
result = "A new sequence containing instances of the given EClassifiers or null if the given sequence is null",
examples = {
@Example(expression = "Sequence{anEClass, anEAttribute, anEReference}->filter({ecore::EClass | ecore::EReference})", result = "Sequence{anEClass, anEReference}"),
@Example(expression = "Sequence{anEClass, anEAttribute, anEPackage}->filter({ecore::EStructuralFeature | ecore::EPacakge})", result = "Sequence{anEAttribute, anEPackage}"),
}
)
// @formatter:on
public <T> List<T> filter(List<T> sequence, Set<EClassifier> eClassifiers) {
final List<T> result;
if (sequence == null) {
result = null;
} else if (eClassifiers == null || eClassifiers.isEmpty()) {
result = new ArrayList<T>();
} else {
result = new ArrayList<T>();
for (T object : sequence) {
for (EClassifier eClassifier : eClassifiers) {
if (eClassifier.isInstance(object)) {
result.add(object);
break;
}
}
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Inserts the given separator between each elements of the given collection.",
params = {
@Param(name = "collection", value = "The input collection"),
@Param(name = "separator", value = "The separator to insert")
},
result = "A new sequence, or null if the given collection is null",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->sep('-')", result = "Sequence{'a', '-', 'b', '-', 'c'}"),
@Example(expression = "OrderedSet{'a', 'b', 'c'}->sep('-')", result = "Sequence{'a', '-', 'b', '-', 'c'}")
}
)
// @formatter:on
public List<Object> sep(Collection<?> collection, Object separator) {
final List<Object> result;
if (collection == null) {
result = null;
} else {
result = new ArrayList<Object>();
final Iterator<?> it = collection.iterator();
if (it.hasNext()) {
result.add(it.next());
while (it.hasNext()) {
result.add(separator);
result.add(it.next());
}
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Inserts the given separator between each elements of the given collection, the given prefix " +
"before the first element, and the given suffix after the last element.",
params = {
@Param(name = "collection", value = "The input collection"),
@Param(name = "prefix", value = "The prefix"),
@Param(name = "separator", value = "The separator to insert"),
@Param(name = "suffix", value = "The suffix")
},
result = "A new sequence, or null if the given collection is null",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->sep('[', '-', ']')", result = "Sequence{'[', 'a', '-', 'b', '-', 'c', ']'}")
}
)
// @formatter:on
public List<Object> sep(Collection<?> collection, Object prefix, Object separator, Object suffix) {
final List<Object> result = new ArrayList<Object>();
result.add(prefix);
if (collection != null) {
result.addAll(sep(collection, separator));
}
result.add(suffix);
return result;
}
// @formatter:off
@Documentation(
value = "Returns the last element of the given sequence.",
params = {
@Param(name = "sequence", value = "The sequence")
},
result = "The last element of the given sequence.",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->last()", result = "'c'")
}
)
// @formatter:on
public <T> T last(List<T> sequence) {
ListIterator<T> it = sequence.listIterator(sequence.size());
if (it.hasPrevious()) {
return it.previous();
}
return null;
}
// @formatter:off
@Documentation(
value = "Returns the last element of the given set.",
params = {
@Param(name = "set", value = "The set")
},
result = "The last element of the given set.",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->last()", result = "'c'")
}
)
// @formatter:on
public <T> T last(Set<T> set) {
T res = null;
final Iterator<T> it = set.iterator();
while (it.hasNext()) {
res = it.next();
}
return res;
}
// @formatter:off
@Documentation(
value = "Indicates if the given collection doesn't contain the given object.",
params = {
@Param(name = "collection", value = "The input collection"),
@Param(name = "object", value = "The object"),
},
result = "\"true\" if the given collection doesn't contain the given object, \"false\" otherwise",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->excludes('a')", result = "false"),
@Example(expression = "Sequence{'a', 'b', 'c'}->excludes('d')", result = "true")
}
)
// @formatter:on
public Boolean excludes(Collection<?> collection, Object object) {
return Boolean.valueOf(!collection.contains(object));
}
// @formatter:off
@Documentation(
value = "Indicates if the given collection contains the given object.",
params = {
@Param(name = "collection", value = "The input collection"),
@Param(name = "object", value = "The object"),
},
result = "\"true\" if the given collection contains the given object, \"false\" otherwise",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->includes('a')", result = "true"),
@Example(expression = "Sequence{'a', 'b', 'c'}->includes('d')", result = "false")
}
)
// @formatter:on
public Boolean includes(Collection<?> collection, Object object) {
return Boolean.valueOf(collection.contains(object));
}
// @formatter:off
@Documentation(
value = "Returns a set containing all the elements of the first and second sets",
params = {
@Param(name = "set1", value = "The first set"),
@Param(name = "set2", value = "The second set"),
},
result = "A set containing all the elements of the first and second sets",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->union(OrderedSet{'d', 'c'})", result = "OrderedSet{'a', 'b', 'c', 'd'}")
}
)
// @formatter:on
public <T> Set<T> union(Set<? extends T> set1, Set<? extends T> set2) {
return concat(set1, set2);
}
// @formatter:off
@Documentation(
value = "Returns a sequence containing all the elements of the first and second sequences",
params = {
@Param(name = "sequence1", value = "The first sequence"),
@Param(name = "sequence2", value = "The second sequence"),
},
result = "A sequence containing all the elements of the first and second sequences",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->union(Sequence{'d', 'c'})", result = "Sequence{'a', 'b', 'c', 'd'}")
}
)
// @formatter:on
public <T> List<T> union(List<? extends T> sequence1, List<? extends T> sequence2) {
return concat(sequence1, sequence2);
}
// @formatter:off
@Documentation(
value = "Gets the first element in the current collection for which the value returned by the lambda evaluates " +
"to \"true\".",
params = {
@Param(name = "collection", value = "The input collection"),
@Param(name = "lambda", value = "The lamba"),
},
result = "The first element in the given collection for which the value returned by the lambda is \"true\"",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->any(str | str.size() = 1)", result = "'a'")
}
)
// @formatter:on
public <T> T any(Collection<T> collection, LambdaValue lambda) {
T result = null;
if (collection != null && lambda == null) {
result = null;
} else {
for (T input : collection) {
try {
Object value = lambda.eval(new Object[] {input });
if (Boolean.TRUE.equals(value)) {
result = input;
break;
}
// CHECKSTYLE:OFF
} catch (Exception e) {
// CHECKSTYLE:ON
lambda.logException(Diagnostic.WARNING, e);
}
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Returns \"1\" if the current set contains the given object, \"0\" otherwise.",
params = {
@Param(name = "set", value = "The set"),
@Param(name = "object", value = "The object"),
},
result = "\"1\" if the current set contains the given object, \"0\" otherwise.",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->count('d')", result = "0"),
@Example(expression = "OrderedSet{'a', 'b', 'c'}->count('a')", result = "1")
}
)
// @formatter:on
public Integer count(Set<?> set, Object object) {
final Integer result;
if (set.contains(object)) {
result = Integer.valueOf(1);
} else {
result = Integer.valueOf(0);
}
return result;
}
// @formatter:off
@Documentation(
value = "Counts the number of occurrences of the given object in the given sequence",
params = {
@Param(name = "sequence", value = "The sequence"),
@Param(name = "object", value = "The object"),
},
result = "The number of times the given object is present in the given sequence",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->count('d')", result = "0"),
@Example(expression = "Sequence{'a', 'b', 'c'}->count('a')", result = "1")
}
)
// @formatter:on
public Integer count(List<Object> sequence, Object object) {
return Integer.valueOf(Collections.frequency(sequence, object));
}
// @formatter:off
@Documentation(
value = "Indicates if it exists an object from the given collection for which the given lambda evaluates " +
"to \"true\"",
params = {
@Param(name = "collection", value = "The collection"),
@Param(name = "lambda", value = "The lambda"),
},
result = "\"true\" if it exists an object from the given collection that validate the given lamba " +
"\"false\" otherwise.",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->exists(str | str.size() > 5)", result = "false")
}
)
// @formatter:on
public Boolean exists(Collection<?> collection, LambdaValue lambda) {
Boolean result = Boolean.FALSE;
if (collection != null && lambda != null) {
for (Object input : collection) {
try {
Object value = lambda.eval(new Object[] {input });
if (Boolean.TRUE.equals(value)) {
result = Boolean.TRUE;
break;
}
// CHECKSTYLE:OFF
} catch (Exception e) {
// CHECKSTYLE:ON
lambda.logException(Diagnostic.WARNING, e);
}
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Indicates if all the objects from the given collection validate the given lamba",
params = {
@Param(name = "collection", value = "The collection"),
@Param(name = "lambda", value = "The lambda"),
},
result = "\"true\" if all the objects from the given collection validate the given lamba " +
"\"false\" otherwise.",
examples = {
@Example(expression = "Sequence{'a', 'b', 'ccc'}->forAll(str | str.size() = 1)", result = "false"),
@Example(expression = "Sequence{'a', 'b', 'c'}->forAll(str | str.size() = 1)", result = "false")
}
)
// @formatter:on
public Boolean forAll(Collection<?> collection, LambdaValue lambda) {
Boolean result = Boolean.TRUE;
if (collection == null || lambda == null) {
result = Boolean.FALSE;
} else {
for (Object input : collection) {
try {
Object value = lambda.eval(new Object[] {input });
if (!(value instanceof Boolean) || !Boolean.TRUE.equals(value)) {
result = Boolean.FALSE;
break;
}
// CHECKSTYLE:OFF
} catch (Exception e) {
// CHECKSTYLE:ON
lambda.logException(Diagnostic.WARNING, e);
result = Boolean.FALSE;
break;
}
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Indicates if no elements from the second collection are contained in the first collection",
params = {
@Param(name = "collection1", value = "The first collection"),
@Param(name = "collection2", value = "The second collection")
},
result = "\"true\" if no elements of the second collection are contained in the first one " +
"\"false\" otherwise.",
examples = {
@Example(expression = "Sequence{'a', 'b'}->excludesAll(OrderedSet{'f'})", result = "true"),
@Example(expression = "Sequence{'a', 'b'}->excludesAll(OrderedSet{'a', 'f'})", result = "false")
}
)
// @formatter:on
public Boolean excludesAll(Collection<?> collection1, Collection<?> collection2) {
return Boolean.valueOf(Collections.disjoint(collection1, collection2));
}
// @formatter:off
@Documentation(
value = "Indicates if all elements from the second collection are contained in the first collection",
params = {
@Param(name = "collection1", value = "The first collection"),
@Param(name = "collection2", value = "The second collection")
},
result = "\"true\" if all elements of the second collection are contained in the first one " +
"\"false\" otherwise.",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->includesAll(OrderedSet{'a'})", result = "true"),
@Example(expression = "Sequence{'a', 'b', 'c'}->includesAll(OrderedSet{'a', 'f'})", result = "false"),
}
)
// @formatter:on
public Boolean includesAll(Collection<?> collection1, Collection<?> collection2) {
return Boolean.valueOf(collection1.containsAll(collection2));
}
// @formatter:off
@Documentation(
value = "Indicates if the evaluation of the given lambda gives a different value for all elements of the " +
"given collection.",
params = {
@Param(name = "collection", value = "The collection"),
@Param(name = "lambda", value = "The lambda")
},
result = "\"true\" if the evaluation of the lamba gives a different value for all elements of the " +
"given collection, \"false\" otherwise.",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->isUnique(str | str.size())", result = "false"),
@Example(expression = "Sequence{'a', 'bb', 'ccc'}->isUnique(str | str.size())", result = "true")
}
)
// @formatter:on
public Boolean isUnique(Collection<?> collection, LambdaValue lambda) {
boolean result = true;
final Set<Object> evaluated = new HashSet<Object>();
if (collection != null && lambda == null) {
result = false;
} else {
for (Object input : collection) {
try {
if (!evaluated.add(lambda.eval(new Object[] {input }))) {
result = false;
break;
}
// CHECKSTYLE:OFF
} catch (Exception e) {
lambda.logException(Diagnostic.WARNING, e);
}
// CHECKSTYLE:ON
}
}
return Boolean.valueOf(result);
}
// @formatter:off
@Documentation(
value = "Indicates if one and only one element of the given collection validates the given lambda.",
params = {
@Param(name = "collection", value = "The collection"),
@Param(name = "lambda", value = "The lambda")
},
result = "\"true\" if one and only one element of the given collection validates the given lambda, " +
"\"false\" otherwise.",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->one(str | str.equals('a'))", result = "true"),
@Example(expression = "Sequence{'a', 'a', 'c'}->one(str | str.equals('a'))", result = "false")
}
)
// @formatter:on
public Boolean one(Collection<?> self, LambdaValue lambda) {
boolean result = false;
if (self != null && lambda == null) {
result = false;
} else {
for (Object input : self) {
try {
Object value = lambda.eval(new Object[] {input });
if (Boolean.TRUE.equals(value)) {
result = !result;
if (!result) {
break;
}
}
// CHECKSTYLE:OFF
} catch (Exception e) {
// CHECKSTYLE:ON
lambda.logException(Diagnostic.WARNING, e);
}
}
}
return Boolean.valueOf(result);
}
// @formatter:off
@Documentation(
value = "Sums elements of the given collection if possible.",
params = {
@Param(name = "collection", value = "The collection")
},
result = "The sum of elements of the given collection if possible",
exceptions = {
@Throw(type = IllegalArgumentException.class, value = "If an element of the collection cannot be processed")
},
examples = {
@Example(expression = "Sequence{1, 2, 3, 4}->sum()", result = "10")
}
)
// @formatter:on
public Number sum(Collection<?> collection) {
Number result = Long.valueOf(0);
for (Object input : collection) {
if (!(input instanceof Number)) {
throw new IllegalArgumentException(String.format(ONLY_NUMERIC_ERROR, "sum"));
}
if (result instanceof Long && (input instanceof Long || input instanceof Integer)) {
result = result.longValue() + ((Number)input).longValue();
} else {
// widen anything that is not a long or int to a double
result = result.doubleValue() + ((Number)input).doubleValue();
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Min element of the given collection if possible.",
params = {
@Param(name = "collection", value = "The collection")
},
result = "The min element of the given collection if possible",
exceptions = {
@Throw(type = IllegalArgumentException.class, value = "If an element of the collection cannot be processed")
},
examples = {
@Example(expression = "Sequence{1, 2, 3, 4}->min()", result = "1"),
@Example(expression = "Sequence{1, 2, 3.14, 4}->min()", result = "1.0")
}
)
// @formatter:on
public Number min(Collection<?> collection) {
Number result = Long.valueOf(Long.MAX_VALUE);
for (Object input : collection) {
if (!(input instanceof Number)) {
throw new IllegalArgumentException(String.format(ONLY_NUMERIC_ERROR, "min"));
}
if (result instanceof Long && (input instanceof Long || input instanceof Integer)) {
result = Long.min(result.longValue(), ((Number)input).longValue());
} else {
// widen anything that is not a long or int to a double
result = Double.min(result.doubleValue(), ((Number)input).doubleValue());
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Max element of the given collection if possible.",
params = {
@Param(name = "collection", value = "The collection")
},
result = "The max element of the given collection if possible",
exceptions = {
@Throw(type = IllegalArgumentException.class, value = "If an element of the collection cannot be processed")
},
examples = {
@Example(expression = "Sequence{1, 2, 3, 4}->max()", result = "4"),
@Example(expression = "Sequence{1, 2, 3.14, 4}->max()", result = "4")
}
)
// @formatter:on
public Number max(Collection<?> collection) {
Number result = Long.valueOf(Long.MIN_VALUE);
for (Object input : collection) {
if (!(input instanceof Number)) {
throw new IllegalArgumentException(String.format(ONLY_NUMERIC_ERROR, "max"));
}
if (result instanceof Long && (input instanceof Long || input instanceof Integer)) {
result = Long.max(result.longValue(), ((Number)input).longValue());
} else {
// widen anything that is not a long or int to a double
result = Double.max(result.doubleValue(), ((Number)input).doubleValue());
}
}
return result;
}
// @formatter:off
@Documentation(
value = "Returns the index of the given object in the given sequence ([1..size]).",
params = {
@Param(name = "sequence", value = "The sequence"),
@Param(name = "object", value = "The object")
},
result = "The index of the given object",
examples = {
@Example(expression = "Sequence{1, 2, 3, 4}->indexOf(3)", result = "3")
}
)
// @formatter:on
public Integer indexOf(List<?> sequence, Object object) {
return Integer.valueOf(sequence.indexOf(object) + 1);
}
// @formatter:off
@Documentation(
value = "Returns the index of the given object in the given set ([1..size]).",
params = {
@Param(name = "set", value = "The set"),
@Param(name = "object", value = "The object")
},
result = "The index of the given object",
examples = {
@Example(expression = "OrderedSet{1, 2, 3, 4}->indexOf(3)", result = "3")
}
)
// @formatter:on
public Integer indexOf(Set<?> set, Object object) {
int index = 1;
for (Object o : set) {
if (o == object || (o != null && o.equals(object))) {
return index;
}
index++;
}
return 0;
}
// @formatter:off
@Documentation(
value = "Returns the last index of the given object in the given sequence ([1..size]).",
params = {
@Param(name = "sequence", value = "The sequence"),
@Param(name = "object", value = "The object")
},
result = "The last index of the given object",
examples = {
@Example(expression = "Sequence{1, 2, 3, 4, 3}->lastIndexOf(3)", result = "5")
}
)
// @formatter:on
public Integer lastIndexOf(List<?> sequence, Object object) {
return Integer.valueOf(sequence.lastIndexOf(object) + 1);
}
// @formatter:off
@Documentation(
value = "Returns the last index of the given object in the given set ([1..size]).",
params = {
@Param(name = "set", value = "The set"),
@Param(name = "object", value = "The object")
},
result = "The last index of the given object",
examples = {
@Example(expression = "OrderedSet{1, 2, 3, 4}->lastIndexOf(3)", result = "3")
}
)
// @formatter:on
public Integer lastIndexOf(Set<?> set, Object object) {
return indexOf(set, object);
}
// @formatter:off
@Documentation(
value = "Inserts the given object in a copy of the given sequence at the given position ([1..size]).",
params = {
@Param(name = "sequence", value = "The sequence"),
@Param(name = "position", value = "The position"),
@Param(name = "object", value = "The object")
},
result = "A copy of the given sequence including the object at the given position",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->insertAt(2, 'f')", result = "Sequence{'a', 'f', 'b', 'c'}")
}
)
// @formatter:on
public <T> List<T> insertAt(List<T> sequence, Integer position, T object) {
final int initialSize = sequence.size();
if (position < 1 || position > initialSize + 1) {
throw new IndexOutOfBoundsException();
}
final List<T> result = new ArrayList<T>(initialSize + 1);
result.addAll(sequence.subList(0, position - 1));
result.add(object);
result.addAll(sequence.subList(position - 1, initialSize));
return result;
}
// @formatter:off
@Documentation(
value = "Inserts the given object in a copy of the given set at the given position ([1..size]). "
+ "If the given set already contains this object, it will be moved to the accurate position.",
params = {
@Param(name = "set", value = "The set"),
@Param(name = "position", value = "The position"),
@Param(name = "object", value = "The object")
},
result = "A copy of the given set including the object at the given position if it didn't already contain that object.",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->insertAt(2, 'f')", result = "Sequence{'a', 'f', 'b', 'c'}")
}
)
// @formatter:on
public <T> Set<T> insertAt(Set<T> set, Integer position, T object) {
final int initialSize = set.size();
if (position < 1 || position > initialSize + 1) {
throw new IndexOutOfBoundsException();
}
final Set<T> result = new LinkedHashSet<T>(initialSize + 1);
int current = 1;
Iterator<T> iterator = set.iterator();
while (iterator.hasNext()) {
if (current == position.intValue()) {
result.add(object);
current++;
} else {
T value = iterator.next();
if (object == value || (object != null && object.equals(value))) {
// Do not add our target object here, wait until we reach its demanded position
} else {
result.add(value);
current++;
}
}
}
if (current <= position.intValue()) {
result.add(object);
}
return result;
}
// @formatter:off
@Documentation(
value = "Inserts the given object in a copy of the given sequence at the last position.",
params = {
@Param(name = "sequence", value = "The sequence"),
@Param(name = "object", value = "The object")
},
result = "A copy of the given sequence including the object at the last position",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->append('f')", result = "Sequence{'a', 'b', 'c', 'f'}")
}
)
// @formatter:on
public <T> List<T> append(List<T> sequence, T object) {
final List<T> result = new ArrayList<T>(sequence);
result.add(object);
return result;
}
// @formatter:off
@Documentation(
value = "Inserts the given object in a copy of the given set at the last position. "
+ "If the set already contained the given object, it is moved to the last position.",
params = {
@Param(name = "set", value = "The sequence"),
@Param(name = "object", value = "The object")
},
result = "A copy of the given set including the object at the last position",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->append('f')", result = "OrderedSet{'a', 'b', 'c', 'f'}"),
@Example(expression = "OrderedSet{'a', 'b', 'c'}->append('b')", result = "OrderedSet{'a', 'c', 'b'}")
}
)
// @formatter:on
public <T> Set<T> append(Set<T> set, T object) {
final Set<T> result = new LinkedHashSet<T>(set);
result.remove(object);
result.add(object);
return result;
}
// @formatter:off
@Documentation(
value = "Inserts the given object in a copy of the given sequence at the first position.",
params = {
@Param(name = "sequence", value = "The sequence"),
@Param(name = "object", value = "The object")
},
result = "A copy of the given sequence including the object at the first position",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->prepend('f')", result = "Sequence{'f', 'a', 'b', 'c'}")
}
)
// @formatter:on
public <T> List<T> prepend(List<T> sequence, T object) {
final List<T> result = new ArrayList<T>(sequence.size() + 1);
result.add(object);
result.addAll(sequence);
return result;
}
// @formatter:off
@Documentation(
value = "Inserts the given object in a copy of the given set at the first position. "
+ "If the set already contained the given object, it is moved to the first position.",
params = {
@Param(name = "set", value = "The sequence"),
@Param(name = "object", value = "The object")
},
result = "A copy of the given set including the object at the first position",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->prepend('f')", result = "OrderedSet{'f', 'a', 'b', 'c'}")
}
)
// @formatter:on
public <T> Set<T> prepend(Set<T> set, T object) {
final Set<T> result = new LinkedHashSet<T>(set.size() + 1);
result.add(object);
result.addAll(set);
return result;
}
// @formatter:off
@Documentation(
value = "Creates a set with the elements from the given set that are also present in the given collection.",
params = {
@Param(name = "set", value = "The set"),
@Param(name = "collection", value = "The collection")
},
result = "The created set with elements from the given set that are also present in the given collection",
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->intersection(OrderedSet{'a', 'f'})", result = "OrderedSet{'a'}")
}
)
// @formatter:on
public <T> Set<T> intersection(Set<T> set, Collection<?> collection) {
final Set<T> result = new LinkedHashSet<T>(set);
result.retainAll(collection);
return result;
}
// @formatter:off
@Documentation(
value = "Creates a sequence with elements from the given sequence that are present in both the current sequence " +
"and the given other {@code Collection}. Iteration order will match that of the current sequence. Duplicates " +
"from the first list will all be kept in the result if they also are in the second one, but duplicates " +
"from the second list will be dumped even if they are present in the first.",
params = {
@Param(name = "sequence", value = "The sequence"),
@Param(name = "collection", value = "The collection")
},
result = "The intersection of both collections",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->intersection(OrderedSet{'a', 'f'})", result = "Sequence{'a'}")
}
)
// @formatter:on
public <T> List<T> intersection(List<T> sequence, Collection<?> collection) {
final List<T> result = new ArrayList<T>(sequence);
result.retainAll(collection);
return result;
}
// @formatter:off
@Documentation(
value = "Returns a subset of the given set",
params = {
@Param(name = "set", value = "The set"),
@Param(name = "startIndex", value = "The low end point (inclusive) of the subset. Must not be less than 1."),
@Param(name = "startIndex", value = "The high end point (inclusive) of the subset. Must not be greater than the current set's size.")
},
result = "A subset of the given set",
exceptions = {
@Throw(type = IndexOutOfBoundsException.class, value = "If we have an illegal end point value (startIndex < 1 || " +
"endIndex > set.size() || startIndex > endIndex).")
},
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->subOrderedSet(1, 2)", result = "OrderedSet{'a', 'b'}")
}
)
// @formatter:on
public <T> Set<T> subOrderedSet(Set<T> set, Integer startIndex, Integer endIndex) {
if (startIndex < 1 || endIndex > set.size() || startIndex > endIndex) {
throw new IndexOutOfBoundsException();
}
final Set<T> result = new LinkedHashSet<T>(endIndex - startIndex + 1);
int index = 1;
for (T input : set) {
if (index >= startIndex) {
if (index <= endIndex) {
result.add(input);
} else {
break;
}
}
++index;
}
return result;
}
// @formatter:off
@Documentation(
value = "Returns a subset of the given sequence",
params = {
@Param(name = "sequence", value = "The sequence"),
@Param(name = "startIndex", value = "The low end point (inclusive) of the subsequence"),
@Param(name = "startIndex", value = "The high end point (inclusive) of the subsequence")
},
result = "A subset of the given sequence",
exceptions = {
@Throw(type = IndexOutOfBoundsException.class, value = "If we have an illegal end point value (startIndex < 1 || " +
"endIndex > sequence.size() || startIndex > endIndex).")
},
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->subSequence(1, 2)", result = "Sequence{'a', 'b'}")
}
)
// @formatter:on
public <T> List<T> subSequence(List<T> sequence, Integer startIndex, Integer endIndex) {
if (startIndex < 1 || endIndex > sequence.size() || startIndex > endIndex) {
throw new IndexOutOfBoundsException();
}
return new ArrayList<T>(sequence.subList(startIndex - 1, endIndex));
}
// @formatter:off
@Documentation(
value = "Returns a sequence of elements after the given index in the given sequence",
params = {
@Param(name = "sequence", value = "The sequence"),
@Param(name = "index", value = "The low start point (exclusive) of the subsequence")
},
result = "A subset of the given sequence",
exceptions = {
@Throw(type = IndexOutOfBoundsException.class, value = "If we have an illegal start point value (index < 1 || " +
"index > sequence.size()).")
},
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->drop(2)", result = "Sequence{'c'}")
}
)
// @formatter:on
public <T> List<T> drop(List<T> sequence, Integer index) {
return subSequence(sequence, index + 1, sequence.size());
}
// @formatter:off
@Documentation(
value = "Returns a set of elements after the given index in the given set",
params = {
@Param(name = "set", value = "The set"),
@Param(name = "index", value = "The low start point (exclusive) of the subsequence")
},
result = "A subset of the given set",
exceptions = {
@Throw(type = IndexOutOfBoundsException.class, value = "If we have an illegal start point value (index < 1 || " +
"index > set.size()).")
},
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->drop(2)", result = "OrderedSet{'c'}")
}
)
// @formatter:on
public <T> Set<T> drop(Set<T> set, Integer index) {
return subOrderedSet(set, index + 1, set.size());
}
// @formatter:off
@Documentation(
value = "Returns a sequence of elements before the given index in the given sequence",
params = {
@Param(name = "sequence", value = "The sequence"),
@Param(name = "index", value = "The high end point (exclusive) of the subsequence")
},
result = "A subset of the given sequence",
exceptions = {
@Throw(type = IndexOutOfBoundsException.class, value = "If we have an illegal end point value (index < 1 || " +
"index > sequence.size()).")
},
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->dropRight(2)", result = "Sequence{'a'}")
}
)
// @formatter:on
public <T> List<T> dropRight(List<T> sequence, Integer index) {
return subSequence(sequence, 1, index - 1);
}
// @formatter:off
@Documentation(
value = "Returns a set of elements before the given index in the given set",
params = {
@Param(name = "set", value = "The set"),
@Param(name = "index", value = "The high end point (exclusive) of the subsequence")
},
result = "A subset of the given set",
exceptions = {
@Throw(type = IndexOutOfBoundsException.class, value = "If we have an illegal end point value (index < 1 || " +
"index > set.size()).")
},
examples = {
@Example(expression = "OrderedSet{'a', 'b', 'c'}->dropRight(2)", result = "OrderedSet{'a'}")
}
)
// @formatter:on
public <T> Set<T> dropRight(Set<T> set, Integer index) {
return subOrderedSet(set, 1, index - 1);
}
// @formatter:off
@Documentation(
value = "Returns \"true\" if the sequence starts with other, \"false\" otherwise",
params = {
@Param(name = "sequence", value = "The Sequence or OrderedSet"),
@Param(name = "other", value = "The other Sequence or OrderedSet")
},
result = "\\\"true\\\" if the sequence starts with other, \\\"false\\\" otherwise",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->startsWith(Sequence{'a', 'b'})", result = "true")
}
)
// @formatter:on
public <T> Boolean startsWith(Collection<T> collection, Collection<T> other) {
final boolean res;
if (other.size() > collection.size()) {
res = false;
} else {
final Iterator<T> collectionIt = collection.iterator();
final Iterator<T> otherIt = other.iterator();
res = equalsIterator(collectionIt, otherIt);
}
return res;
}
// @formatter:off
@Documentation(
value = "Returns \"true\" if the sequence ends with other, \"false\" otherwise",
params = {
@Param(name = "sequence", value = "The Sequence or OrderedSet"),
@Param(name = "other", value = "The other Sequence or OrderedSet")
},
result = "\\\"true\\\" if the sequence ends with other, \\\"false\\\" otherwise",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->endsWith(Sequence{'b', 'c'})", result = "true")
}
)
// @formatter:on
public <T> Boolean endsWith(Collection<T> collection, Collection<T> other) {
final boolean res;
if (other.size() > collection.size()) {
res = false;
} else {
final Iterator<T> collectionIt = collection.iterator();
final Iterator<T> otherIt = other.iterator();
for (int i = 0; i < collection.size() - other.size(); i++) {
collectionIt.next();
}
res = equalsIterator(collectionIt, otherIt);
}
return res;
}
// @formatter:off
@Documentation(
value = "Returns the index of the other collection in the given collection",
params = {
@Param(name = "sequence", value = "The Sequence or OrderedSet"),
@Param(name = "other", value = "The other Sequence or OrderedSet")
},
result = "the index of of the other collection in the given collection",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c'}->indexOfSlice(Sequence{'b', 'c'})", result = "2")
}
)
// @formatter:on
public <T> Integer indexOfSlice(Collection<T> collection, Collection<T> other) {
Integer res = -1;
boolean found = false;
if (other.size() <= collection.size()) {
for (int i = 0; i < collection.size() - other.size() + 1; i++) {
final Iterator<T> collectionIt = collection.iterator();
res = i;
for (int j = 0; j < i; j++) {
collectionIt.next();
}
if (equalsIterator(collectionIt, other.iterator())) {
found = true;
break;
}
}
}
if (!found) {
res = -1;
}
return res + 1;
}
// @formatter:off
@Documentation(
value = "Returns the last index of the other collection in the given collection",
params = {
@Param(name = "sequence", value = "The Sequence or OrderedSet"),
@Param(name = "other", value = "The other Sequence or OrderedSet")
},
result = "the last index of of the other collection in the given collection",
examples = {
@Example(expression = "Sequence{'a', 'b', 'c', 'a', 'b', 'c'}->lastIndexOfSlice(Sequence{'b', 'c'})", result = "5")
}
)
// @formatter:on
public <T> Integer lastIndexOfSlice(Collection<T> collection, Collection<T> other) {
Integer res = -1;
boolean found = false;
if (other.size() <= collection.size()) {
for (int i = collection.size() - other.size(); i >= 0; i--) {
final Iterator<T> collectionIt = collection.iterator();
res = i;
for (int j = 0; j < i; j++) {
collectionIt.next();
}
if (equalsIterator(collectionIt, other.iterator())) {
found = true;
break;
}
}
}
if (!found) {
res = -1;
}
return res + 1;
}
/**
* Tells if both given {@link Iterator} contains the same elements.
*
* @param <T>
* collections elements type
* @param collectionIt
* the first {@link Iterator}
* @param otherIt
* the second {@link Iterator}
* @return <code>true</code> if both given {@link Iterator} contains the same elements, <code>false</code>
* otherwise
*/
private <T> boolean equalsIterator(final Iterator<T> collectionIt, final Iterator<T> otherIt) {
boolean res = true;
while (otherIt.hasNext()) {
final T element = collectionIt.next();
final T elementOther = otherIt.next();
if ((element != null && !element.equals(elementOther)) || (element == null
&& elementOther != null)) {
res = false;
break;
}
}
return res;
}
}