Package: CollectionServices$SecondArgumentTypeInFirstArgumentCollectionType

CollectionServices$SecondArgumentTypeInFirstArgumentCollectionType

nameinstructionbranchcomplexitylinemethod
CollectionServices.SecondArgumentTypeInFirstArgumentCollectionType(Method, Object)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getType(Call, ValidationServices, IValidationResult, IReadOnlyQueryEnvironment, List)
M: 126 C: 0
0%
M: 18 C: 0
0%
M: 10 C: 0
0%
M: 19 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2015 Obeo.
3: * All rights reserved. This program and the accompanying materials
4: * are made available under the terms of the Eclipse Public License v1.0
5: * which accompanies this distribution, and is available at
6: * http://www.eclipse.org/legal/epl-v10.html
7: *
8: * Contributors:
9: * Obeo - initial API and implementation
10: *******************************************************************************/
11: package org.eclipse.acceleo.query.services;
12:
13: import static com.google.common.base.Preconditions.checkNotNull;
14:
15: import com.google.common.collect.ImmutableList;
16: import com.google.common.collect.Iterables;
17: import com.google.common.collect.Iterators;
18: import com.google.common.collect.Lists;
19: import com.google.common.collect.Sets;
20:
21: import java.lang.reflect.Method;
22: import java.util.ArrayList;
23: import java.util.Collection;
24: import java.util.Collections;
25: import java.util.Comparator;
26: import java.util.HashMap;
27: import java.util.Iterator;
28: import java.util.LinkedHashSet;
29: import java.util.List;
30: import java.util.ListIterator;
31: import java.util.Map;
32: import java.util.Map.Entry;
33: import java.util.Set;
34:
35: import org.eclipse.acceleo.annotations.api.documentation.Documentation;
36: import org.eclipse.acceleo.annotations.api.documentation.Example;
37: import org.eclipse.acceleo.annotations.api.documentation.Other;
38: import org.eclipse.acceleo.annotations.api.documentation.Param;
39: import org.eclipse.acceleo.annotations.api.documentation.ServiceProvider;
40: import org.eclipse.acceleo.annotations.api.documentation.Throw;
41: import org.eclipse.acceleo.query.ast.Call;
42: import org.eclipse.acceleo.query.ast.Expression;
43: import org.eclipse.acceleo.query.ast.Lambda;
44: import org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment;
45: import org.eclipse.acceleo.query.runtime.IService;
46: import org.eclipse.acceleo.query.runtime.IValidationResult;
47: import org.eclipse.acceleo.query.runtime.impl.AbstractServiceProvider;
48: import org.eclipse.acceleo.query.runtime.impl.JavaMethodService;
49: import org.eclipse.acceleo.query.runtime.impl.LambdaValue;
50: import org.eclipse.acceleo.query.runtime.impl.Nothing;
51: import org.eclipse.acceleo.query.runtime.impl.ValidationServices;
52: import org.eclipse.acceleo.query.validation.type.ClassType;
53: import org.eclipse.acceleo.query.validation.type.EClassifierSetLiteralType;
54: import org.eclipse.acceleo.query.validation.type.EClassifierType;
55: import org.eclipse.acceleo.query.validation.type.ICollectionType;
56: import org.eclipse.acceleo.query.validation.type.IType;
57: import org.eclipse.acceleo.query.validation.type.LambdaType;
58: import org.eclipse.acceleo.query.validation.type.NothingType;
59: import org.eclipse.acceleo.query.validation.type.SequenceType;
60: import org.eclipse.acceleo.query.validation.type.SetType;
61: import org.eclipse.emf.common.util.Diagnostic;
62: import org.eclipse.emf.ecore.EClassifier;
63:
64: //@formatter:off
65: @ServiceProvider(
66:         value = "Services available for Collections"
67: )
68: //@formatter:on
69: @SuppressWarnings({"checkstyle:javadocmethod", "checkstyle:javadoctype" })
70: public class CollectionServices extends AbstractServiceProvider {
71:
72:         /**
73:          * Message separator.
74:          */
75:         private static final String MESSAGE_SEPARATOR = "\n ";
76:
77:         /** Externalized here to avoid multiple uses. */
78:         private static final String SUM_ONLY_NUMERIC_ERROR = "Sum can only be used on a collection of numbers.";
79:
80:         /**
81:          * Exists {@link IService}.
82:          *
83:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
84:          */
85:         private static final class BooleanLambdaService extends JavaMethodService {
86:
87:                 /**
88:                  * Constructor.
89:                  *
90:                  * @param serviceMethod
91:                  * the method that realizes the service
92:                  * @param serviceInstance
93:                  * the instance on which the service must be called
94:                  */
95:                 private BooleanLambdaService(Method serviceMethod, Object serviceInstance) {
96:                         super(serviceMethod, serviceInstance);
97:                 }
98:
99:                 @Override
100:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
101:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
102:                         final Set<IType> result = new LinkedHashSet<IType>();
103:                         final LambdaType lambdaType = (LambdaType)argTypes.get(1);
104:                         final Object lambdaExpressionType = lambdaType.getLambdaExpressionType().getType();
105:                         if (isBooleanType(queryEnvironment, lambdaExpressionType)) {
106:                                 result.addAll(super.getType(call, services, validationResult, queryEnvironment, argTypes));
107:                         } else {
108:                                 result.add(services.nothing("expression in %s must return a boolean", getName()));
109:                         }
110:                         return result;
111:                 }
112:         }
113:
114:         /**
115:          * Any {@link IService}.
116:          *
117:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
118:          */
119:         private static final class AnyService extends JavaMethodService {
120:
121:                 /**
122:                  * Constructor.
123:                  *
124:                  * @param serviceMethod
125:                  * the method that realizes the service
126:                  * @param serviceInstance
127:                  * the instance on which the service must be called
128:                  */
129:                 private AnyService(Method serviceMethod, Object serviceInstance) {
130:                         super(serviceMethod, serviceInstance);
131:                 }
132:
133:                 @Override
134:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
135:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
136:                         final Set<IType> result = new LinkedHashSet<IType>();
137:                         final LambdaType lambdaType = (LambdaType)argTypes.get(1);
138:                         final Object lambdaExpressionType = lambdaType.getLambdaExpressionType().getType();
139:                         if (isBooleanType(queryEnvironment, lambdaExpressionType)) {
140:                                 final Expression expression;
141:                                 if (call != null) {
142:                                         expression = ((Lambda)call.getArguments().get(1)).getExpression();
143:                                 } else {
144:                                         expression = null;
145:                                 }
146:                                 final Set<IType> inferredTypes;
147:                                 if (validationResult != null) {
148:                                         inferredTypes = validationResult.getInferredVariableTypes(expression, Boolean.TRUE).get(
149:                                                         lambdaType.getLambdaEvaluatorName());
150:                                 } else {
151:                                         inferredTypes = null;
152:                                 }
153:                                 if (inferredTypes == null) {
154:                                         result.add(((ICollectionType)argTypes.get(0)).getCollectionType());
155:                                 } else {
156:                                         result.addAll(inferredTypes);
157:                                 }
158:                         } else {
159:                                 result.add(services.nothing("expression in an any must return a boolean"));
160:                         }
161:                         return result;
162:                 }
163:         }
164:
165:         /**
166:          * Including {@link IService}.
167:          *
168:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
169:          */
170:         private static class IncludingService extends JavaMethodService {
171:
172:                 /**
173:                  * Constructor.
174:                  *
175:                  * @param serviceMethod
176:                  * the method that realizes the service
177:                  * @param serviceInstance
178:                  * the instance on which the service must be called
179:                  */
180:                 protected IncludingService(Method serviceMethod, Object serviceInstance) {
181:                         super(serviceMethod, serviceInstance);
182:                 }
183:
184:                 @Override
185:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
186:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
187:                         final Set<IType> result = new LinkedHashSet<IType>();
188:                         result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes.get(0))
189:                                         .getCollectionType()));
190:                         result.add(createReturnCollectionWithType(queryEnvironment, argTypes.get(1)));
191:                         return result;
192:                 }
193:
194:                 @Override
195:                 public Set<IType> validateAllType(ValidationServices services,
196:                                 IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
197:                         final Set<IType> result = new LinkedHashSet<IType>();
198:                         final StringBuilder builder = new StringBuilder();
199:
200:                         for (Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
201:                                 for (IType type : entry.getValue()) {
202:                                         if (((ICollectionType)type).getCollectionType() instanceof NothingType) {
203:                                                 builder.append(MESSAGE_SEPARATOR);
204:                                                 builder.append(((NothingType)((ICollectionType)type).getCollectionType())
205:                                                                 .getMessage());
206:                                         } else {
207:                                                 result.add(type);
208:                                         }
209:                                 }
210:                         }
211:                         if (result.isEmpty()) {
212:                                 result.add(createReturnCollectionWithType(queryEnvironment, services.nothing(
213:                                                 "Nothing left after %s:" + builder.toString(), getName())));
214:                         }
215:
216:                         return result;
217:                 }
218:         }
219:
220:         /**
221:          * Collect {@link IService}.
222:          *
223:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
224:          */
225:         private static final class CollectService extends JavaMethodService {
226:                 /**
227:                  * Constructor.
228:                  *
229:                  * @param serviceMethod
230:                  * the method that realizes the service
231:                  * @param serviceInstance
232:                  * the instance on which the service must be called
233:                  */
234:                 private CollectService(Method serviceMethod, Object serviceInstance) {
235:                         super(serviceMethod, serviceInstance);
236:                 }
237:
238:                 @Override
239:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
240:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
241:                         final Set<IType> result = new LinkedHashSet<IType>();
242:
243:                         final IType receiverType = argTypes.get(0);
244:                         if (receiverType instanceof NothingType) {
245:                                 result.add(createReturnCollectionWithType(queryEnvironment, receiverType));
246:                         } else if (receiverType instanceof ICollectionType
247:                                         && ((ICollectionType)receiverType).getCollectionType() instanceof NothingType) {
248:                                 result.add(receiverType);
249:                         } else {
250:                                 final LambdaType lambdaType = (LambdaType)argTypes.get(1);
251:                                 // flatten if needed
252:                                 result.add(createReturnCollectionWithType(queryEnvironment, flatten(lambdaType
253:                                                 .getLambdaExpressionType())));
254:                         }
255:                         return result;
256:                 }
257:
258:                 private IType flatten(IType type) {
259:                         if (type instanceof ICollectionType) {
260:                                 return flatten(((ICollectionType)type).getCollectionType());
261:                         }
262:                         return type;
263:                 }
264:
265:                 /**
266:                  * {@inheritDoc}
267:                  *
268:                  * @see org.eclipse.acceleo.query.runtime.impl.AbstractService#validateAllType(org.eclipse.acceleo.query.runtime.impl.ValidationServices,
269:                  * org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment, java.util.Map)
270:                  */
271:                 @Override
272:                 public Set<IType> validateAllType(ValidationServices services,
273:                                 IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
274:                         final Set<IType> result = new LinkedHashSet<IType>();
275:                         for (Map.Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
276:                                 for (IType type : entry.getValue()) {
277:                                         final IType collectionType = ((ICollectionType)type).getCollectionType();
278:                                         if (collectionType instanceof ClassType && ((ClassType)collectionType).getType() == null) {
279:                                                 // This is the null literal, which we don't want in our result
280:                                                 // and will be stripped at runtime.
281:                                         } else {
282:                                                 result.add(type);
283:                                         }
284:                                 }
285:                         }
286:                         return result;
287:                 }
288:         }
289:
290:         /**
291:          * Closure {@link IService}.
292:          *
293:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
294:          */
295:         private static final class ClosureService extends JavaMethodService {
296:                 /**
297:                  * Constructor.
298:                  *
299:                  * @param serviceMethod
300:                  * the method that realizes the service
301:                  * @param serviceInstance
302:                  * the instance on which the service must be called
303:                  */
304:                 private ClosureService(Method serviceMethod, Object serviceInstance) {
305:                         super(serviceMethod, serviceInstance);
306:                 }
307:
308:                 /**
309:                  * {@inheritDoc}
310:                  *
311:                  * @see org.eclipse.acceleo.query.runtime.impl.JavaMethodService#getType(org.eclipse.acceleo.query.ast.Call,
312:                  * org.eclipse.acceleo.query.runtime.impl.ValidationServices,
313:                  * org.eclipse.acceleo.query.runtime.IValidationResult,
314:                  * org.eclipse.acceleo.query.runtime.IReadOnlyQueryEnvironment, java.util.List)
315:                  */
316:                 @Override
317:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
318:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
319:                         final Set<IType> result = new LinkedHashSet<IType>();
320:
321:                         final IType lambdaExpressionType = ((LambdaType)argTypes.get(1)).getLambdaExpressionType();
322:
323:                         // FIXME need to make the closure on type as well... it's not possible for the moment because we
324:                         // need variable types...
325:                         final IType receiverType = argTypes.get(0);
326:                         if (receiverType instanceof NothingType) {
327:                                 result.add(createReturnCollectionWithType(queryEnvironment, receiverType));
328:                         } else if (receiverType instanceof ICollectionType
329:                                         && ((ICollectionType)receiverType).getCollectionType() instanceof NothingType) {
330:                                 result.add(receiverType);
331:                         } else if (lambdaExpressionType instanceof ICollectionType) {
332:                                 result.add(new SetType(queryEnvironment, ((ICollectionType)lambdaExpressionType)
333:                                                 .getCollectionType()));
334:                         } else {
335:                                 result.add(new SetType(queryEnvironment, lambdaExpressionType));
336:                         }
337:
338:                         return result;
339:                 }
340:         }
341:
342:         /**
343:          * Sum {@link IService}.
344:          *
345:          * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
346:          */
347:         private static final class SumService extends JavaMethodService {
348:
349:                 /**
350:                  * Constructor.
351:                  *
352:                  * @param serviceMethod
353:                  * the method that realizes the service
354:                  * @param serviceInstance
355:                  * the instance on which the service must be called
356:                  */
357:                 private SumService(Method serviceMethod, Object serviceInstance) {
358:                         super(serviceMethod, serviceInstance);
359:                 }
360:
361:                 @Override
362:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
363:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
364:                         final Set<IType> result = new LinkedHashSet<IType>();
365:                         if (!argTypes.isEmpty() && argTypes.get(0) instanceof ICollectionType) {
366:                                 IType argType = ((ICollectionType)argTypes.get(0)).getCollectionType();
367:
368:                                 IType intType = new ClassType(queryEnvironment, Integer.class);
369:                                 IType longType = new ClassType(queryEnvironment, Long.class);
370:                                 IType numberType = new ClassType(queryEnvironment, Number.class);
371:
372:                                 if (intType.isAssignableFrom(argType) || longType.isAssignableFrom(argType)) {
373:                                         result.add(longType);
374:                                 } else if (numberType.isAssignableFrom(argType)) {
375:                                         // any number that is not an int or long is widened to Double
376:                                         result.add(new ClassType(queryEnvironment, Double.class));
377:                                 } else {
378:                                         result.add(services.nothing(SUM_ONLY_NUMERIC_ERROR));
379:                                 }
380:                         }
381:                         return result;
382:                 }
383:
384:                 @Override
385:                 public Set<IType> validateAllType(ValidationServices services,
386:                                 IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
387:                         IType currentResult = null;
388:
389:                         for (Map.Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
390:                                 IType longType = new ClassType(queryEnvironment, Long.class);
391:                                 IType doubleType = new ClassType(queryEnvironment, Double.class);
392:
393:                                 IType returnType = entry.getValue().iterator().next();
394:                                 if (currentResult == null) {
395:                                         currentResult = returnType;
396:                                 } else if (currentResult.equals(doubleType) || returnType.equals(doubleType)) {
397:                                         currentResult = doubleType;
398:                                 } else if (returnType.equals(longType)) {
399:                                         currentResult = longType;
400:                                 } else {
401:                                         currentResult = services.nothing(SUM_ONLY_NUMERIC_ERROR);
402:                                         break;
403:                                 }
404:                         }
405:
406:                         Set<IType> result = new LinkedHashSet<IType>();
407:                         result.add(currentResult);
408:                         return result;
409:                 }
410:         }
411:
412:         /**
413:          * Select {@link IService}.
414:          *
415:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
416:          */
417:         private static final class SelectService extends JavaMethodService {
418:
419:                 /**
420:                  * Constructor.
421:                  *
422:                  * @param serviceMethod
423:                  * the method that realizes the service
424:                  * @param serviceInstance
425:                  * the instance on which the service must be called
426:                  */
427:                 private SelectService(Method serviceMethod, Object serviceInstance) {
428:                         super(serviceMethod, serviceInstance);
429:                 }
430:
431:                 @Override
432:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
433:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
434:                         final Set<IType> result = new LinkedHashSet<IType>();
435:                         final LambdaType lambdaType = (LambdaType)argTypes.get(1);
436:                         final Object lambdaExpressionType = lambdaType.getLambdaExpressionType().getType();
437:                         if (isBooleanType(queryEnvironment, lambdaExpressionType)) {
438:                                 final Expression expression;
439:                                 if (call != null) {
440:                                         expression = ((Lambda)call.getArguments().get(1)).getExpression();
441:                                 } else {
442:                                         expression = null;
443:                                 }
444:                                 final Set<IType> inferredTypes;
445:                                 if (validationResult != null) {
446:                                         inferredTypes = validationResult.getInferredVariableTypes(expression, Boolean.TRUE).get(
447:                                                         lambdaType.getLambdaEvaluatorName());
448:                                 } else {
449:                                         inferredTypes = null;
450:                                 }
451:                                 if (inferredTypes == null) {
452:                                         result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes
453:                                                         .get(0)).getCollectionType()));
454:                                 } else {
455:                                         for (IType inferredType : inferredTypes) {
456:                                                 result.add(createReturnCollectionWithType(queryEnvironment, inferredType));
457:                                         }
458:                                 }
459:                         } else {
460:                                 result.add(createReturnCollectionWithType(queryEnvironment, services
461:                                                 .nothing("expression in a select must return a boolean")));
462:                         }
463:                         return result;
464:                 }
465:         }
466:
467:         /**
468:          * Reject {@link IService}.
469:          *
470:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
471:          */
472:         private static final class RejectService extends JavaMethodService {
473:
474:                 /**
475:                  * Constructor.
476:                  *
477:                  * @param serviceMethod
478:                  * the method that realizes the service
479:                  * @param serviceInstance
480:                  * the instance on which the service must be called
481:                  */
482:                 private RejectService(Method serviceMethod, Object serviceInstance) {
483:                         super(serviceMethod, serviceInstance);
484:                 }
485:
486:                 @Override
487:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
488:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
489:                         final Set<IType> result = new LinkedHashSet<IType>();
490:
491:                         final LambdaType lambdaType = (LambdaType)argTypes.get(1);
492:                         final Object lambdaExpressionType = lambdaType.getLambdaExpressionType().getType();
493:                         if (isBooleanType(queryEnvironment, lambdaExpressionType)) {
494:                                 final Expression expression;
495:                                 if (call != null) {
496:                                         expression = ((Lambda)call.getArguments().get(1)).getExpression();
497:                                 } else {
498:                                         expression = null;
499:                                 }
500:                                 final Set<IType> inferredTypes;
501:                                 if (validationResult != null) {
502:                                         inferredTypes = validationResult.getInferredVariableTypes(expression, Boolean.FALSE).get(
503:                                                         lambdaType.getLambdaEvaluatorName());
504:                                 } else {
505:                                         inferredTypes = null;
506:                                 }
507:                                 if (inferredTypes == null) {
508:                                         result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes
509:                                                         .get(0)).getCollectionType()));
510:                                 } else {
511:                                         for (IType inferredType : inferredTypes) {
512:                                                 result.add(createReturnCollectionWithType(queryEnvironment, inferredType));
513:                                         }
514:                                 }
515:                         } else {
516:                                 result.add(createReturnCollectionWithType(queryEnvironment, services
517:                                                 .nothing("expression in a reject must return a boolean")));
518:                         }
519:
520:                         return result;
521:                 }
522:         }
523:
524:         /**
525:          * A {@link Service} returning the raw collection type of the first argument.
526:          *
527:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
528:          */
529:         private static final class FirstArgumentRawCollectionType extends JavaMethodService {
530:
531:                 /**
532:                  * Constructor.
533:                  *
534:                  * @param serviceMethod
535:                  * the method that realizes the service
536:                  * @param serviceInstance
537:                  * the instance on which the service must be called
538:                  */
539:                 private FirstArgumentRawCollectionType(Method serviceMethod, Object serviceInstance) {
540:                         super(serviceMethod, serviceInstance);
541:                 }
542:
543:                 @Override
544:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
545:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
546:                         final Set<IType> result = new LinkedHashSet<IType>();
547:
548:                         result.add(((ICollectionType)argTypes.get(0)).getCollectionType());
549:
550:                         return result;
551:                 }
552:         }
553:
554:         /**
555:          * A {@link Service} returning the same collection type as the method with the raw collection type of
556:          * first argument.
557:          *
558:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
559:          */
560:         private static final class ReturnCollectionTypeWithFirstArgumentRawCollectionType extends JavaMethodService {
561:
562:                 /**
563:                  * Constructor.
564:                  *
565:                  * @param serviceMethod
566:                  * the method that realizes the service
567:                  * @param serviceInstance
568:                  * the instance on which the service must be called
569:                  */
570:                 private ReturnCollectionTypeWithFirstArgumentRawCollectionType(Method serviceMethod,
571:                                 Object serviceInstance) {
572:                         super(serviceMethod, serviceInstance);
573:                 }
574:
575:                 @Override
576:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
577:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
578:                         final Set<IType> result = new LinkedHashSet<IType>();
579:
580:                         result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes.get(0))
581:                                         .getCollectionType()));
582:
583:                         return result;
584:                 }
585:         }
586:
587:         /**
588:          * A {@link Service} returning the same collection type as the method with the raw collection type of
589:          * first and second arguments.
590:          *
591:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
592:          */
593:         private static final class ReturnCollectionTypeWithFirstAndSecondArgumentRawCollectionType extends JavaMethodService {
594:
595:                 /**
596:                  * Constructor.
597:                  *
598:                  * @param serviceMethod
599:                  * the method that realizes the service
600:                  * @param serviceInstance
601:                  * the instance on which the service must be called
602:                  */
603:                 private ReturnCollectionTypeWithFirstAndSecondArgumentRawCollectionType(Method serviceMethod,
604:                                 Object serviceInstance) {
605:                         super(serviceMethod, serviceInstance);
606:                 }
607:
608:                 @Override
609:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
610:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
611:                         final Set<IType> result = new LinkedHashSet<IType>();
612:
613:                         final IType arg1Type;
614:                         if (argTypes.get(0) instanceof ICollectionType) {
615:                                 arg1Type = ((ICollectionType)argTypes.get(0)).getCollectionType();
616:                         } else if (argTypes.get(0) instanceof NothingType) {
617:                                 arg1Type = argTypes.get(0);
618:                         } else {
619:                                 arg1Type = services.nothing(
620:                                                 "%s can only be called on collections, but %s was used as its receiver.", getName(),
621:                                                 argTypes.get(0));
622:                         }
623:                         final IType arg2Type;
624:                         if (argTypes.get(1) instanceof ICollectionType) {
625:                                 arg2Type = ((ICollectionType)argTypes.get(1)).getCollectionType();
626:                         } else if (argTypes.get(1) instanceof NothingType) {
627:                                 arg2Type = argTypes.get(1);
628:                         } else {
629:                                 arg2Type = services.nothing(
630:                                                 "%s can only be called on collections, but %s was used as its argument.", getName(),
631:                                                 argTypes.get(1));
632:                         }
633:
634:                         result.add(createReturnCollectionWithType(queryEnvironment, arg1Type));
635:                         result.add(createReturnCollectionWithType(queryEnvironment, arg2Type));
636:
637:                         return result;
638:                 }
639:
640:                 @Override
641:                 public Set<IType> validateAllType(ValidationServices services,
642:                                 IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
643:                         final Set<IType> result = new LinkedHashSet<IType>();
644:                         final StringBuilder builder = new StringBuilder();
645:
646:                         for (Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
647:                                 for (IType type : entry.getValue()) {
648:                                         if (((ICollectionType)type).getCollectionType() instanceof NothingType) {
649:                                                 builder.append(MESSAGE_SEPARATOR);
650:                                                 builder.append(((NothingType)((ICollectionType)type).getCollectionType())
651:                                                                 .getMessage());
652:                                         } else {
653:                                                 result.add(type);
654:                                         }
655:                                 }
656:                         }
657:
658:                         if (result.isEmpty()) {
659:                                 IType nothing = services.nothing("Nothing left after %s:" + builder.toString(), getName());
660:                                 result.add(createReturnCollectionWithType(queryEnvironment, nothing));
661:                         }
662:
663:                         return result;
664:                 }
665:
666:         }
667:
668:         /**
669:          * A {@link Service} returning the
670:          * {@link org.eclipse.acceleo.query.validation.type.EClassifierLiteralType#getType() classifier literal
671:          * type} of the second argument in the {@link org.eclipse.acceleo.query.validation.type.ICollectionType
672:          * ICollectionType} of the first argument.
673:          *
674:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
675:          */
676:         private static final class SecondArgumentTypeInFirstArgumentCollectionType extends FilterService {
677:
678:                 /**
679:                  * Constructor.
680:                  *
681:                  * @param serviceMethod
682:                  * the method that realizes the service
683:                  * @param serviceInstance
684:                  * the instance on which the service must be called
685:                  */
686:                 SecondArgumentTypeInFirstArgumentCollectionType(Method serviceMethod, Object serviceInstance) {
687:                         super(serviceMethod, serviceInstance);
688:                 }
689:
690:                 @Override
691:                 public java.util.Set<IType> getType(Call call, ValidationServices services,
692:                                 IValidationResult validationResult, IReadOnlyQueryEnvironment queryEnvironment,
693:                                 java.util.List<IType> argTypes) {
694:                         final Set<IType> result = new LinkedHashSet<IType>();
695:
696:                         final Set<EClassifierType> rawTypes = Sets.newLinkedHashSet();
697:
698:                         final IType receiverType = argTypes.get(0);
699:•                        if (receiverType instanceof NothingType) {
700:                                 result.add(createReturnCollectionWithType(queryEnvironment, receiverType));
701:•                        } else if (receiverType instanceof ICollectionType
702:•                                        && ((ICollectionType)receiverType).getCollectionType() instanceof NothingType) {
703:                                 result.add(receiverType);
704:•                        } else if (argTypes.get(1) instanceof ClassType && ((ClassType)argTypes.get(1)).getType() == null) {
705:                                 result.add(services.nothing("EClassifier on %s cannot be null.", getName()));
706:•                        } else if (argTypes.get(1) instanceof EClassifierType) {
707:                                 rawTypes.add(new EClassifierType(queryEnvironment, ((EClassifierType)argTypes.get(1))
708:                                                 .getType()));
709:•                        } else if (argTypes.get(1) instanceof EClassifierSetLiteralType) {
710:•                                for (EClassifier eCls : ((EClassifierSetLiteralType)argTypes.get(1)).getEClassifiers()) {
711:                                         rawTypes.add(new EClassifierType(queryEnvironment, eCls));
712:                                 }
713:                         }
714:•                        for (EClassifierType rawType : rawTypes) {
715:                                 result.add(createReturnCollectionWithType(queryEnvironment, rawType));
716:                         }
717:
718:                         return result;
719:
720:                 }
721:         }
722:
723:         /**
724:          * A {@link Service} returning the raw type of the first argument {@link ICollectionType}.
725:          *
726:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
727:          */
728:         private static final class FirstCollectionTypeService extends JavaMethodService {
729:
730:                 /**
731:                  * Creates a new service instance given a method and an instance.
732:                  *
733:                  * @param serviceMethod
734:                  * the method that realizes the service
735:                  * @param serviceInstance
736:                  * the instance on which the service must be called
737:                  */
738:                 public FirstCollectionTypeService(Method serviceMethod, Object serviceInstance) {
739:                         super(serviceMethod, serviceInstance);
740:                 }
741:
742:                 @Override
743:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
744:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
745:                         final Set<IType> result = new LinkedHashSet<IType>();
746:                         result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes.get(0))
747:                                         .getCollectionType()));
748:                         return result;
749:                 }
750:
751:         }
752:
753:         /**
754:          * InsertAt {@link IService}.
755:          *
756:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
757:          */
758:         private static final class InsertAtService extends IncludingService {
759:
760:                 /**
761:                  * Constructor.
762:                  *
763:                  * @param serviceMethod
764:                  * the method that realizes the service
765:                  * @param serviceInstance
766:                  * the instance on which the service must be called
767:                  */
768:                 private InsertAtService(Method serviceMethod, Object serviceInstance) {
769:                         super(serviceMethod, serviceInstance);
770:                 }
771:
772:                 @Override
773:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
774:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
775:                         final Set<IType> result = new LinkedHashSet<IType>();
776:                         result.add(createReturnCollectionWithType(queryEnvironment, ((ICollectionType)argTypes.get(0))
777:                                         .getCollectionType()));
778:                         result.add(createReturnCollectionWithType(queryEnvironment, argTypes.get(2)));
779:                         return result;
780:                 }
781:         }
782:
783:         /**
784:          * Intersection {@link IService}.
785:          *
786:          * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a>
787:          */
788:         private static final class IntersectionService extends JavaMethodService {
789:
790:                 /**
791:                  * Constructor.
792:                  *
793:                  * @param serviceMethod
794:                  * the method that realizes the service
795:                  * @param serviceInstance
796:                  * the instance on which the service must be called
797:                  */
798:                 private IntersectionService(Method serviceMethod, Object serviceInstance) {
799:                         super(serviceMethod, serviceInstance);
800:                 }
801:
802:                 @Override
803:                 public Set<IType> getType(Call call, ValidationServices services, IValidationResult validationResult,
804:                                 IReadOnlyQueryEnvironment queryEnvironment, List<IType> argTypes) {
805:                         final Set<IType> result = new LinkedHashSet<IType>();
806:
807:                         final IType arg1Type;
808:                         if (argTypes.get(0) instanceof ICollectionType) {
809:                                 arg1Type = ((ICollectionType)argTypes.get(0)).getCollectionType();
810:                         } else if (argTypes.get(0) instanceof NothingType) {
811:                                 arg1Type = argTypes.get(0);
812:                         } else {
813:                                 arg1Type = services.nothing(
814:                                                 "%s can only be called on collections, but %s was used as its receiver.", getName(),
815:                                                 argTypes.get(0));
816:                         }
817:                         final IType arg2Type;
818:                         if (argTypes.get(1) instanceof ICollectionType) {
819:                                 arg2Type = ((ICollectionType)argTypes.get(1)).getCollectionType();
820:                         } else if (argTypes.get(1) instanceof NothingType) {
821:                                 arg2Type = argTypes.get(1);
822:                         } else {
823:                                 arg2Type = services.nothing(
824:                                                 "%s can only be called on collections, but %s was used as its argument.", getName(),
825:                                                 argTypes.get(1));
826:                         }
827:
828:                         final Set<IType> resultRawTypes = services.intersection(arg1Type, arg2Type);
829:                         if (resultRawTypes.isEmpty()) {
830:                                 if (arg1Type instanceof NothingType) {
831:                                         resultRawTypes.add(arg1Type);
832:                                 }
833:                                 if (arg2Type instanceof NothingType) {
834:                                         resultRawTypes.add(arg2Type);
835:                                 }
836:                                 if (resultRawTypes.isEmpty()) {
837:                                         resultRawTypes.add(services.nothing("Nothing left after intersection of %s and %s",
838:                                                         argTypes.get(0), argTypes.get(1)));
839:                                 }
840:                         }
841:                         for (IType resultRawType : resultRawTypes) {
842:                                 result.add(createReturnCollectionWithType(queryEnvironment, resultRawType));
843:                         }
844:
845:                         return result;
846:                 }
847:
848:                 @Override
849:                 public Set<IType> validateAllType(ValidationServices services,
850:                                 IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
851:                         final Set<IType> result = new LinkedHashSet<IType>();
852:                         final StringBuilder builder = new StringBuilder();
853:
854:                         for (Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
855:                                 for (IType type : entry.getValue()) {
856:                                         if (((ICollectionType)type).getCollectionType() instanceof NothingType) {
857:                                                 builder.append(MESSAGE_SEPARATOR);
858:                                                 builder.append(((NothingType)((ICollectionType)type).getCollectionType())
859:                                                                 .getMessage());
860:                                         } else {
861:                                                 result.add(type);
862:                                         }
863:                                 }
864:                         }
865:
866:                         if (result.isEmpty()) {
867:                                 IType nothing = services.nothing("Nothing left after intersection:" + builder.toString());
868:                                 result.add(createReturnCollectionWithType(queryEnvironment, nothing));
869:                         }
870:
871:                         return result;
872:                 }
873:         }
874:
875:         @Override
876:         protected IService getService(Method publicMethod) {
877:                 final IService result;
878:
879:                 if ("filter".equals(publicMethod.getName())) {
880:                         result = new SecondArgumentTypeInFirstArgumentCollectionType(publicMethod, this);
881:                 } else if ("add".equals(publicMethod.getName()) || "concat".equals(publicMethod.getName())
882:                                 || "union".equals(publicMethod.getName())) {
883:                         result = new ReturnCollectionTypeWithFirstAndSecondArgumentRawCollectionType(publicMethod, this);
884:                 } else if ("asSequence".equals(publicMethod.getName()) || "asSet".equals(publicMethod.getName())
885:                                 || "asOrderedSet".equals(publicMethod.getName())) {
886:                         result = new ReturnCollectionTypeWithFirstArgumentRawCollectionType(publicMethod, this);
887:                 } else if ("subOrderedSet".equals(publicMethod.getName())
888:                                 || "subSequence".equals(publicMethod.getName())) {
889:                         result = new FirstCollectionTypeService(publicMethod, this);
890:                 } else if ("first".equals(publicMethod.getName()) || "at".equals(publicMethod.getName())
891:                                 || "last".equals(publicMethod.getName())) {
892:                         result = new FirstArgumentRawCollectionType(publicMethod, this);
893:                 } else if ("excluding".equals(publicMethod.getName()) || "sub".equals(publicMethod.getName())
894:                                 || "reverse".equals(publicMethod.getName())) {
895:                         result = new FirstCollectionTypeService(publicMethod, this);
896:                 } else if ("sortedBy".equals(publicMethod.getName())) {
897:                         result = new FirstCollectionTypeService(publicMethod, this);
898:                 } else if ("reject".equals(publicMethod.getName())) {
899:                         result = new RejectService(publicMethod, this);
900:                 } else if ("select".equals(publicMethod.getName())) {
901:                         result = new SelectService(publicMethod, this);
902:                 } else if ("collect".equals(publicMethod.getName())) {
903:                         result = new CollectService(publicMethod, this);
904:                 } else if ("closure".equals(publicMethod.getName())) {
905:                         result = new ClosureService(publicMethod, this);
906:                 } else if ("including".equals(publicMethod.getName()) || "prepend".equals(publicMethod.getName())) {
907:                         result = new IncludingService(publicMethod, this);
908:                 } else if ("sep".equals(publicMethod.getName())) {
909:                         if (publicMethod.getParameterTypes().length == 2) {
910:                                 result = new JavaMethodService(publicMethod, this) {
911:
912:                                         @Override
913:                                         public Set<IType> getType(Call call, ValidationServices services,
914:                                                         IValidationResult validationResult, IReadOnlyQueryEnvironment queryEnvironment,
915:                                                         List<IType> argTypes) {
916:                                                 final Set<IType> result = new LinkedHashSet<IType>();
917:
918:                                                 result.add(new SequenceType(queryEnvironment, ((ICollectionType)argTypes.get(0))
919:                                                                 .getCollectionType()));
920:                                                 result.add(new SequenceType(queryEnvironment, argTypes.get(1)));
921:
922:                                                 return result;
923:                                         }
924:                                 };
925:                         } else if (publicMethod.getParameterTypes().length == 4) {
926:                                 result = new JavaMethodService(publicMethod, this) {
927:
928:                                         @Override
929:                                         public Set<IType> getType(Call call, ValidationServices services,
930:                                                         IValidationResult validationResult, IReadOnlyQueryEnvironment queryEnvironment,
931:                                                         List<IType> argTypes) {
932:                                                 final Set<IType> result = new LinkedHashSet<IType>();
933:
934:                                                 result.add(new SequenceType(queryEnvironment, ((ICollectionType)argTypes.get(0))
935:                                                                 .getCollectionType()));
936:                                                 result.add(new SequenceType(queryEnvironment, argTypes.get(1)));
937:                                                 result.add(new SequenceType(queryEnvironment, argTypes.get(2)));
938:                                                 result.add(new SequenceType(queryEnvironment, argTypes.get(3)));
939:
940:                                                 return result;
941:                                         }
942:                                 };
943:                         } else {
944:                                 result = new JavaMethodService(publicMethod, this);
945:                         }
946:                 } else if ("any".equals(publicMethod.getName())) {
947:                         result = new AnyService(publicMethod, this);
948:                 } else if ("exists".equals(publicMethod.getName()) || "forAll".equals(publicMethod.getName())
949:                                 || "one".equals(publicMethod.getName())) {
950:                         result = new BooleanLambdaService(publicMethod, this);
951:                 } else if ("insertAt".equals(publicMethod.getName())) {
952:                         result = new InsertAtService(publicMethod, this);
953:                 } else if ("intersection".equals(publicMethod.getName())) {
954:                         result = new IntersectionService(publicMethod, this);
955:                 } else if ("sum".equals(publicMethod.getName())) {
956:                         result = new SumService(publicMethod, this);
957:                 } else {
958:                         result = new JavaMethodService(publicMethod, this);
959:                 }
960:                 return result;
961:         }
962:
963:         /**
964:          * Tells if the given {@link Object} is a is a boolean {@link org.eclipse.emf.ecore.EDataType EDataType}.
965:          *
966:          * @param queryEnvironment
967:          * the {@link IReadOnlyQueryEnvironment}
968:          * @param type
969:          * the {@link org.eclipse.emf.ecore.EDataType EDataType}
970:          * @return <code>true</code> if the given {@link Object} is a is a boolean
971:          * {@link org.eclipse.emf.ecore.EDataType EDataType}, <code>false</code> otherwise
972:          */
973:         private static boolean isBooleanType(IReadOnlyQueryEnvironment queryEnvironment, Object type) {
974:                 final Class<?> typeClass;
975:                 if (type instanceof EClassifier) {
976:                         typeClass = queryEnvironment.getEPackageProvider().getClass((EClassifier)type);
977:                 } else if (type instanceof ClassType) {
978:                         typeClass = ((ClassType)type).getType();
979:                 } else if (type instanceof Class<?>) {
980:                         typeClass = (Class<?>)type;
981:                 } else {
982:                         return false;
983:                 }
984:
985:                 return typeClass == Boolean.class || typeClass == boolean.class;
986:         }
987:
988:         // @formatter:off
989:         @SuppressWarnings("unchecked")
990:         @Documentation(
991:                 value = "Returns the concatenation of the current sequence with the given collection.",
992:                 params = {
993:                         @Param(name = "sequence", value = "The first operand"),
994:                         @Param(name = "collection", value = "The second operand")
995:                 },
996:                 result = "The concatenation of the two specified collections.",
997:                 examples = {
998:                         @Example(
999:                                 expression = "Sequence{'a', 'b', 'c'}.concat(Sequence{'d', 'e'})", result = "Sequence{'a', 'b', 'c', 'd', 'e'}",
1000:                                 others = {
1001:                                         @Other(
1002:                                                 language = Other.ACCELEO_3,
1003:                                                 expression = "Sequence{'a', 'b', 'c'}.addAll(Sequence{'d', 'e'})",
1004:                                                 result = "Sequence{'a', 'b', 'c', 'd', 'e'}"
1005:                                         )
1006:                                 }
1007:                         )
1008:                 }
1009:         )
1010:         // @formatter:on
1011:         public <T> List<T> concat(List<? extends T> sequence, Collection<? extends T> collection) {
1012:                 checkNotNull(sequence);
1013:                 final List<T> result;
1014:
1015:                 if (collection.isEmpty()) {
1016:                         result = (List<T>)sequence;
1017:                 } else {
1018:                         result = Lists.newArrayList(Iterables.concat(sequence, collection));
1019:                 }
1020:
1021:                 return result;
1022:         }
1023:
1024:         // @formatter:off
1025:         @SuppressWarnings("unchecked")
1026:         @Documentation(
1027:                 value = "Returns the concatenation of the current set with the given collection.",
1028:                 params = {
1029:                         @Param(name = "set", value = "The first operand"),
1030:                         @Param(name = "collection", value = "The second operand")
1031:                 },
1032:                 result = "The concatenation of the two specified collections.",
1033:                 examples = {
1034:                         @Example(
1035:                                 expression = "OrderedSet{'a', 'b', 'c'}.concat(Sequence{'d', 'e'})", result = "OrderedSet{'a', 'b', 'c', 'd', 'e'}",
1036:                                 others = {
1037:                                         @Other(
1038:                                                 language = Other.ACCELEO_3,
1039:                                                 expression = "OrderedSet{'a', 'b', 'c'}.addAll(Sequence{'d', 'e'})",
1040:                                                 result = "OrderedSet{'a', 'b', 'c', 'd', 'e'}"
1041:                                         )
1042:                                 }
1043:                         )
1044:                 }
1045:         )
1046:         // @formatter:on
1047:         public <T> Set<T> concat(Set<? extends T> set, Collection<? extends T> collection) {
1048:                 checkNotNull(set);
1049:                 final Set<T> result;
1050:
1051:                 if (collection.isEmpty()) {
1052:                         result = (Set<T>)set;
1053:                 } else {
1054:                         result = Sets.newLinkedHashSet(Iterables.concat(set, collection));
1055:                 }
1056:
1057:                 return result;
1058:         }
1059:
1060:         // @formatter:off
1061:         @Documentation(
1062:                 value = "Returns the concatenation of the given collection into the given sequence.",
1063:                 params = {
1064:                         @Param(name = "sequence", value = "The first operand"),
1065:                         @Param(name = "collection", value = "The second operand")
1066:                 },
1067:                 result = "The current sequence including the elements of the given collection.",
1068:                 examples = {
1069:                         @Example(
1070:                                 expression = "Sequence{'a', 'b', 'c'}.add(Sequence{'d', 'e'})", result = "Sequence{'a', 'b', 'c', 'd', 'e'}",
1071:                                 others = {
1072:                                         @Other(
1073:                                                 language = Other.ACCELEO_3,
1074:                                                 expression = "Sequence{'a', 'b', 'c'}.addAll(Sequence{'d', 'e'})",
1075:                                                 result = "Sequence{'a', 'b', 'c', 'd', 'e'}"
1076:                                         )
1077:                                 }
1078:                         ),
1079:                         @Example(
1080:                                 expression = "Sequence{'a', 'b', 'c'}.add(OrderedSet{'c', 'e'})", result = "Sequence{'a', 'b', 'c', 'c', 'e'}",
1081:                                 others = {
1082:                                         @Other(
1083:                                                 language = Other.ACCELEO_3,
1084:                                                 expression = "Sequence{'a', 'b', 'c'}.addAll(OrderedSet{'c', 'e'})",
1085:                                                 result = "Sequence{'a', 'b', 'c', 'c', 'e'}"
1086:                                         )
1087:                                 }
1088:                         )
1089:                 },
1090:                 comment = "The service addAll has been replaced by \"add\" in order to have access to the operator \"+\" between to sequences"
1091:         )
1092:         // @formatter:on
1093:         public <T> List<T> add(List<? extends T> sequence, Collection<? extends T> collection) {
1094:                 return concat(sequence, collection);
1095:         }
1096:
1097:         // @formatter:off
1098:         @Documentation(
1099:                 value = "Returns the concatenation of the given collection into the current set.",
1100:                 params = {
1101:                         @Param(name = "set", value = "The first operand"),
1102:                         @Param(name = "collection", value = "The second operand")
1103:                 },
1104:                 result = "The current set including the elements of the given collection.",
1105:                 examples = {
1106:                         @Example(
1107:                                 expression = "OrderedSet{'a', 'b', 'c'}.add(OrderedSet{'c', 'b', 'f'})", result = "OrderedSet{'a', 'b', 'c', 'c', 'b', 'f'}",
1108:                                 others = {
1109:                                         @Other(
1110:                                                 language = Other.ACCELEO_3,
1111:                                                 expression = "OrderedSet{'a', 'b', 'c'}.addAll(OrderedSet{'c', 'b', 'f'})",
1112:                                                 result = "OrderedSet{'a', 'b', 'c', 'c', 'b', 'f'}"
1113:                                         )
1114:                                 }
1115:                         )
1116:                 },
1117:                 comment = "The service addAll has been replaced by \"add\" in order to have access to the operator \"+\" between to sets"
1118:         )
1119:         // @formatter:on
1120:         public <T> Set<T> add(Set<? extends T> set, Collection<? extends T> collection) {
1121:                 return concat(set, collection);
1122:         }
1123:
1124:         // @formatter:off
1125:         @Documentation(
1126:                 value = "Returns the difference of the current sequence and the given collection.",
1127:                 params = {
1128:                         @Param(name = "sequence", value = "The first operand"),
1129:                         @Param(name = "collection", value = "The second operand")
1130:                 },
1131:                 result = "The sequence that contains elements from sequence1 that are not in collection2.",
1132:                 examples = {
1133:                         @Example(
1134:                                 expression = "Sequence{'a', 'b', 'c'}.sub(Sequence{'c', 'b', 'f'})", result = "Sequence{'a'}",
1135:                                 others = {
1136:                                         @Other(
1137:                                                 language = Other.ACCELEO_3,
1138:                                                 expression = "Sequence{'a', 'b', 'c'}.removeAll(Sequence{'c', 'b', 'f'})",
1139:                                                 result = "Sequence{'a'}"
1140:                                         )
1141:                                 }
1142:                         ),
1143:                         @Example(
1144:                                 expression = "Sequence{'a', 'b', 'c'}.sub(OrderedSet{'c', 'b', 'f'})", result = "Sequence{'a'}",
1145:                                 others = {
1146:                                         @Other(
1147:                                                 language = Other.ACCELEO_3,
1148:                                                 expression = "Sequence{'a', 'b', 'c'}.removeAll(OrderedSet{'c', 'b', 'f'})",
1149:                                                 result = "Sequence{'a'}"
1150:                                         )
1151:                                 }
1152:                         )
1153:                 },
1154:                 comment = "The service removeAll has been replaced by \"sub\" in order to have access to the operator \"-\" between to sequences"
1155:         )
1156:         // @formatter:on
1157:         public <T> List<T> sub(List<T> sequence, Collection<?> collection) {
1158:                 checkNotNull(sequence);
1159:                 if (collection.isEmpty()) {
1160:                         return sequence;
1161:                 } else {
1162:                         List<T> result = Lists.newArrayList(sequence);
1163:                         result.removeAll(collection);
1164:                         return result;
1165:                 }
1166:         }
1167:
1168:         // @formatter:off
1169:         @Documentation(
1170:                 value = "Returns the difference of the current set and the given collection.",
1171:                 params = {
1172:                         @Param(name = "set", value = "The first operand"),
1173:                         @Param(name = "collection", value = "The second operand")
1174:                 },
1175:                 result = "The set that contains elements from set1 that are not in collection2.",
1176:                 examples = {
1177:                         @Example(
1178:                                 expression = "OrderedSet{'a', 'b', 'c'}.sub(OrderedSet{'c', 'b', 'f'})", result = "OrderedSet{'a'}",
1179:                                 others = {
1180:                                         @Other(
1181:                                                 language = Other.ACCELEO_3,
1182:                                                 expression = "OrderedSet{'a', 'b', 'c'}.removeAll(OrderedSet{'c', 'b', 'f'})",
1183:                                                 result = "OrderedSet{'a'}"
1184:                                         )
1185:                                 }
1186:                         )
1187:                 },
1188:                 comment = "The service removeAll has been replaced by \"sub\" in order to have access to the operator \"-\" between to sets"
1189:         )
1190:         // @formatter:on
1191:         public <T> Set<T> sub(Set<T> set, Collection<?> collection) {
1192:                 checkNotNull(set);
1193:                 if (collection.isEmpty()) {
1194:                         return set;
1195:                 } else {
1196:                         Set<T> result = Sets.newLinkedHashSet(set);
1197:                         result.removeAll(collection);
1198:                         return result;
1199:                 }
1200:         }
1201:
1202:         // @formatter:off
1203:         @Documentation(
1204:                 value = "Select returns a filtered version of the specified sequence. Only elements for which the given \"lambda\" evaluates " +
1205:                                 "to true will be present in the returned sequence.",
1206:                 params = {
1207:                         @Param(name = "sequence", value = "The original sequence"),
1208:                         @Param(name = "lambda", value = "The filtering expression")
1209:                 },
1210:                 result = "A filtered version of the specified sequence",
1211:                 examples = {
1212:                         @Example(expression = "Sequence{'a', 'b', 'c'}->select(str | str.equals('a'))", result = "Sequence{'a'}")
1213:                 }
1214:         )
1215:         // @formatter:on
1216:         public <T> List<T> select(List<T> sequence, LambdaValue lambda) {
1217:                 final List<T> newList;
1218:
1219:                 if (lambda == null) {
1220:                         newList = Lists.newArrayList();
1221:                 } else {
1222:                         newList = Lists.newArrayList();
1223:                         for (T elt : sequence) {
1224:                                 try {
1225:                                         Object value = lambda.eval(new Object[] {elt });
1226:                                         if (Boolean.TRUE.equals(value)) {
1227:                                                 newList.add(elt);
1228:                                         }
1229:                                         // CHECKSTYLE:OFF
1230:                                 } catch (Exception e) {
1231:                                         // CHECKSTYLE:ON
1232:                                         lambda.logException(Diagnostic.WARNING, e);
1233:                                 }
1234:                         }
1235:                 }
1236:
1237:                 return newList;
1238:         }
1239:
1240:         // @formatter:off
1241:         @Documentation(
1242:                 value = "Select returns a filtered version of the specified set. Only elements for which the given \"lambda\" evaluates " +
1243:                                 "to true will be present in the returned set.",
1244:                 params = {
1245:                         @Param(name = "set", value = "The original set"),
1246:                         @Param(name = "lambda", value = "The filtering expression")
1247:                 },
1248:                 result = "A filtered version of the specified set",
1249:                 examples = {
1250:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->select(str | str.equals('a'))", result = "OrderedSet{'a'}")
1251:                 }
1252:         )
1253:         // @formatter:on
1254:         public <T> Set<T> select(Set<T> set, LambdaValue lambda) {
1255:                 final Set<T> newSet;
1256:
1257:                 if (lambda == null) {
1258:                         newSet = Sets.newLinkedHashSet();
1259:                 } else {
1260:                         newSet = Sets.newLinkedHashSet();
1261:                         for (T elt : set) {
1262:                                 try {
1263:                                         Object value = lambda.eval(new Object[] {elt });
1264:                                         if (Boolean.TRUE.equals(value)) {
1265:                                                 newSet.add(elt);
1266:                                         }
1267:                                         // CHECKSTYLE:OFF
1268:                                 } catch (Exception e) {
1269:                                         // CHECKSTYLE:ON
1270:                                         lambda.logException(Diagnostic.WARNING, e);
1271:                                 }
1272:                         }
1273:                 }
1274:
1275:                 return newSet;
1276:         }
1277:
1278:         // @formatter:off
1279:         @Documentation(
1280:                 value = "Reject returns a filtered version of the specified set. Only elements for which the given \"lambda\" evaluates " +
1281:                                 "to false will be present in the returned set",
1282:                 params = {
1283:                         @Param(name = "set", value = "The original set"),
1284:                         @Param(name = "lambda", value = "The filtering expression")
1285:                 },
1286:                 result = "A filtered version of the specified set",
1287:                 examples = {
1288:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->reject(str | str.equals('a'))", result = "OrderedSet{'b', 'c'}")
1289:                 }
1290:         )
1291:         // @formatter:on
1292:         public <T> Set<T> reject(Set<T> set, LambdaValue lambda) {
1293:                 final Set<T> newSet;
1294:
1295:                 if (lambda == null) {
1296:                         newSet = Sets.newLinkedHashSet();
1297:                 } else {
1298:                         newSet = Sets.newLinkedHashSet();
1299:                         for (T elt : set) {
1300:                                 try {
1301:                                         Object value = lambda.eval(new Object[] {elt });
1302:                                         if (Boolean.FALSE.equals(value)) {
1303:                                                 newSet.add(elt);
1304:                                         }
1305:                                         // CHECKSTYLE:OFF
1306:                                 } catch (Exception e) {
1307:                                         // CHECKSTYLE:ON
1308:                                         lambda.logException(Diagnostic.WARNING, e);
1309:                                 }
1310:                         }
1311:                 }
1312:
1313:                 return newSet;
1314:         }
1315:
1316:         // @formatter:off
1317:         @Documentation(
1318:                 value = "Reject returns a filtered version of the specified sequence. Only elements for which the given \"lambda\" evaluates " +
1319:                                 "to false will be present in the returned sequence",
1320:                 params = {
1321:                         @Param(name = "sequence", value = "The original sequence"),
1322:                         @Param(name = "lambda", value = "The filtering expression")
1323:                 },
1324:                 result = "A filtered version of the specified sequence",
1325:                 examples = {
1326:                         @Example(expression = "Sequence{'a', 'b', 'c'}->reject(str | str.equals('a'))", result = "Sequence{'b', 'c'}")
1327:                 }
1328:         )
1329:         // @formatter:on
1330:         public <T> List<T> reject(List<T> sequence, LambdaValue lambda) {
1331:                 final List<T> newList;
1332:
1333:                 if (lambda == null) {
1334:                         newList = Lists.newArrayList();
1335:                 } else {
1336:                         newList = Lists.newArrayList();
1337:                         for (T elt : sequence) {
1338:                                 try {
1339:                                         Object value = lambda.eval(new Object[] {elt });
1340:                                         if (Boolean.FALSE.equals(value)) {
1341:                                                 newList.add(elt);
1342:                                         }
1343:                                         // CHECKSTYLE:OFF
1344:                                 } catch (Exception e) {
1345:                                         // CHECKSTYLE:ON
1346:                                         lambda.logException(Diagnostic.WARNING, e);
1347:                                 }
1348:                         }
1349:                 }
1350:
1351:                 return newList;
1352:         }
1353:
1354:         // @formatter:off
1355:         @Documentation(
1356:                 value = "Returns a set containing the result of applying \"lambda\" on all elements contained in the current set, " +
1357:                                 "maintaining order.",
1358:                 params = {
1359:                         @Param(name = "set", value = "The original set"),
1360:                         @Param(name = "lambda", value = "The lambda expression")
1361:                 },
1362:                 result = "A transformed version of the specified set using the given lamba",
1363:                 examples = {
1364:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->collect(str | str.toUpper())", result = "OrderedSet{'A', 'B', 'C'}")
1365:                 }
1366:         )
1367:         // @formatter:on
1368:         public Set<Object> collect(Set<?> set, LambdaValue lambda) {
1369:                 final Set<Object> result;
1370:
1371:                 if (lambda == null) {
1372:                         result = Collections.emptySet();
1373:                 } else {
1374:                         result = Sets.newLinkedHashSet();
1375:                         for (Object elt : set) {
1376:                                 try {
1377:                                         Object lambdaResult = lambda.eval(new Object[] {elt });
1378:                                         if (!(lambdaResult instanceof Nothing)) {
1379:                                                 if (lambdaResult instanceof Collection) {
1380:                                                         result.addAll((Collection<?>)lambdaResult);
1381:                                                 } else if (lambdaResult != null) {
1382:                                                         result.add(lambdaResult);
1383:                                                 }
1384:                                         }
1385:                                         // CHECKSTYLE:OFF
1386:                                 } catch (Exception e) {
1387:                                         lambda.logException(Diagnostic.WARNING, e);
1388:                                 }
1389:                                 // CHECKSTYLE:ON
1390:                         }
1391:                 }
1392:
1393:                 return result;
1394:         }
1395:
1396:         // @formatter:off
1397:         @Documentation(
1398:                 value = "Returns a sequence containing the result of applying \"lambda\" on all elements contained in the current sequence, " +
1399:                                 "maintaining order.",
1400:                 params = {
1401:                         @Param(name = "sequence", value = "The original sequence"),
1402:                         @Param(name = "lambda", value = "The lambda expression")
1403:                 },
1404:                 result = "A transformed version of the specified sequence using the given lamba",
1405:                 examples = {
1406:                         @Example(expression = "Sequence{'a', 'b', 'c'}->collect(str | str.toUpper())", result = "Sequence{'A', 'B', 'C'}")
1407:                 }
1408:         )
1409:         // @formatter:on
1410:         public List<Object> collect(List<?> sequence, LambdaValue lambda) {
1411:                 final List<Object> result;
1412:
1413:                 if (lambda == null) {
1414:                         result = Collections.emptyList();
1415:                 } else {
1416:                         result = Lists.newArrayList();
1417:                         for (Object elt : sequence) {
1418:                                 try {
1419:                                         Object lambdaResult = lambda.eval(new Object[] {elt });
1420:                                         if (!(lambdaResult instanceof Nothing)) {
1421:                                                 if (lambdaResult instanceof Collection) {
1422:                                                         result.addAll((Collection<?>)lambdaResult);
1423:                                                 } else if (lambdaResult != null) {
1424:                                                         result.add(lambdaResult);
1425:                                                 }
1426:                                         }
1427:                                         // CHECKSTYLE:OFF
1428:                                 } catch (Exception e) {
1429:                                         lambda.logException(Diagnostic.WARNING, e);
1430:                                 }
1431:                                 // CHECKSTYLE:ON
1432:                         }
1433:                 }
1434:
1435:                 return result;
1436:         }
1437:
1438:         public Set<Object> closure(Collection<?> collection, LambdaValue lambda) {
1439:                 final Set<Object> result;
1440:
1441:                 if (lambda == null) {
1442:                         result = Collections.emptySet();
1443:                 } else {
1444:                         result = Sets.newLinkedHashSet(collection);
1445:                         Set<Object> currentSet = Sets.newLinkedHashSet(collection);
1446:                         Set<Object> added;
1447:                         do {
1448:                                 added = Sets.newLinkedHashSet();
1449:                                 for (Object current : currentSet) {
1450:                                         try {
1451:                                                 final Object lambdaResult = lambda.eval(new Object[] {current });
1452:                                                 if (lambdaResult instanceof Collection) {
1453:                                                         for (Object child : (Collection<?>)lambdaResult) {
1454:                                                                 if (result.add(child)) {
1455:                                                                         added.add(child);
1456:                                                                 }
1457:                                                         }
1458:                                                 } else if (lambdaResult != null && !(lambdaResult instanceof Nothing)) {
1459:                                                         if (result.add(lambdaResult)) {
1460:                                                                 added.add(lambdaResult);
1461:                                                         }
1462:                                                 }
1463:                                                 // CHECKSTYLE:OFF
1464:                                         } catch (Exception e) {
1465:                                                 // CHECKSTYLE:ON
1466:                                                 lambda.logException(Diagnostic.WARNING, e);
1467:                                         }
1468:                                 }
1469:                                 currentSet = added;
1470:                         } while (!added.isEmpty());
1471:                 }
1472:
1473:                 return result;
1474:         }
1475:
1476:         // @formatter:off
1477:         @Documentation(
1478:                 value = "Returns a sequence containing the elements of the original sequence ordered by the result of the given lamba",
1479:                 params = {
1480:                         @Param(name = "sequence", value = "The original sequence"),
1481:                         @Param(name = "lambda", value = "The lambda expression")
1482:                 },
1483:                 result = "An ordered version of the given sequence",
1484:                 examples = {
1485:                         @Example(expression = "Sequence{'aa', 'bbb', 'c'}->sortedBy(str | str.size())", result = "Sequence{'c', 'aa', 'bbb'}")
1486:                 }
1487:         )
1488:         // @formatter:on
1489:         public <T> List<T> sortedBy(List<T> sequence, final LambdaValue lambda) {
1490:                 final List<T> result;
1491:
1492:                 if (sequence == null) {
1493:                         result = null;
1494:                 } else if (lambda == null) {
1495:                         return sequence;
1496:                 } else {
1497:                         result = Lists.newArrayList(sequence);
1498:
1499:                         final Map<T, Object> values = new HashMap<T, Object>();
1500:                         for (T object : result) {
1501:                                 try {
1502:                                         values.put(object, lambda.eval(new Object[] {object }));
1503:                                         // CHECKSTYLE:OFF
1504:                                 } catch (Exception e) {
1505:                                         lambda.logException(Diagnostic.WARNING, e);
1506:                                 }
1507:                                 // CHECKSTYLE:ON
1508:                         }
1509:
1510:                         Collections.sort(result, new LambdaComparator<T>(values));
1511:                 }
1512:
1513:                 return result;
1514:         }
1515:
1516:         // @formatter:off
1517:         @Documentation(
1518:                 value = "Returns a set containing the elements of the original set ordered by the result of the given lamba",
1519:                 params = {
1520:                         @Param(name = "set", value = "The original set"),
1521:                         @Param(name = "lambda", value = "The lambda expression")
1522:                 },
1523:                 result = "An ordered version of the given set",
1524:                 examples = {
1525:                         @Example(expression = "OrderedSet{'aa', 'bbb', 'c'}->sortedBy(str | str.size())", result = "OrderedSet{'c', 'aa', 'bbb'}")
1526:                 }
1527:         )
1528:         // @formatter:on
1529:         public <T> Set<T> sortedBy(Set<T> set, final LambdaValue lambda) {
1530:                 final Set<T> result;
1531:
1532:                 if (set == null) {
1533:                         result = null;
1534:                 } else if (lambda == null) {
1535:                         return set;
1536:                 } else {
1537:                         List<T> sorted = Lists.newArrayList(set);
1538:
1539:                         final Map<T, Object> values = new HashMap<T, Object>();
1540:                         for (T object : sorted) {
1541:                                 try {
1542:                                         values.put(object, lambda.eval(new Object[] {object }));
1543:                                         // CHECKSTYLE:OFF
1544:                                 } catch (Exception e) {
1545:                                         lambda.logException(Diagnostic.WARNING, e);
1546:                                 }
1547:                                 // CHECKSTYLE:ON
1548:                         }
1549:
1550:                         Collections.sort(sorted, new LambdaComparator<T>(values));
1551:                         result = Sets.newLinkedHashSet(sorted);
1552:                 }
1553:
1554:                 return result;
1555:         }
1556:
1557:         // @formatter:off
1558:         @Documentation(
1559:                 value = "Returns the size of the specified collection",
1560:                 params = {
1561:                         @Param(name = "collection", value = "The input collection")
1562:                 },
1563:                 result = "The size of the specified collection",
1564:                 examples = {
1565:                         @Example(expression = "Sequence{'a', 'b', 'c'}->size()", result = "3"),
1566:                         @Example(expression = "OrderedSet{'a', 'b', 'c', 'd'}->size()", result = "4")
1567:                 }
1568:         )
1569:         // @formatter:on
1570:         public Integer size(Collection<?> collection) {
1571:                 return collection.size();
1572:         }
1573:
1574:         // @formatter:off
1575:         @Documentation(
1576:                 value = "Adds the given object to the current set.",
1577:                 params = {
1578:                         @Param(name = "set", value = "The source set"),
1579:                         @Param(name = "object", value = "The object to add")
1580:                 },
1581:                 result = "A set containing all elements of source set plus the given object",
1582:                 examples = {
1583:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->including('d')", result = "OrderedSet{'a', 'b', 'c', 'd'}")
1584:                 }
1585:         )
1586:         // @formatter:on
1587:         public <T> Set<T> including(Set<T> set, T object) {
1588:                 if (set.contains(object)) {
1589:                         return set;
1590:                 } else {
1591:                         Set<T> result = Sets.newLinkedHashSet(set);
1592:                         result.add(object);
1593:                         return result;
1594:                 }
1595:         }
1596:
1597:         // @formatter:off
1598:         @Documentation(
1599:                 value = "Removes the given object from the current set.",
1600:                 params = {
1601:                         @Param(name = "set", value = "The source set"),
1602:                         @Param(name = "object", value = "The object to remove")
1603:                 },
1604:                 result = "A set containing all elements of source set minus the given object",
1605:                 examples = {
1606:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->excluding('c')", result = "OrderedSet{'a', 'b'}")
1607:                 }
1608:         )
1609:         // @formatter:on
1610:         public <T> Set<T> excluding(Set<T> set, Object object) {
1611:                 if (!set.contains(object)) {
1612:                         return set;
1613:                 } else {
1614:                         Set<T> result = Sets.newLinkedHashSet(set);
1615:                         result.remove(object);
1616:                         return result;
1617:                 }
1618:         }
1619:
1620:         // @formatter:off
1621:         @Documentation(
1622:                 value = "Adds the given object to the current sequence.",
1623:                 params = {
1624:                         @Param(name = "sequence", value = "The source sequence"),
1625:                         @Param(name = "object", value = "The object to add")
1626:                 },
1627:                 result = "A sequence containing all elements of the source sequence plus the given object",
1628:                 examples = {
1629:                         @Example(expression = "Sequence{'a', 'b', 'c'}->including('d')", result = "Sequence{'a', 'b', 'c', 'd'}")
1630:                 }
1631:         )
1632:         // @formatter:on
1633:         public <T> List<T> including(List<T> sequence, T object) {
1634:                 List<T> result = Lists.newArrayList(sequence);
1635:                 result.add(object);
1636:                 return result;
1637:         }
1638:
1639:         // @formatter:off
1640:         @Documentation(
1641:                 value = "Removes the given object from the current sequence.",
1642:                 params = {
1643:                         @Param(name = "sequence", value = "The source sequence"),
1644:                         @Param(name = "object", value = "The object to remove")
1645:                 },
1646:                 result = "A sequence containing all elements of source sequence minus the given object",
1647:                 examples = {
1648:                         @Example(expression = "Sequence{'a', 'b', 'c'}->excluding('c')", result = "Sequence{'a', 'b'}")
1649:                 }
1650:         )
1651:         // @formatter:on
1652:         public <T> List<T> excluding(List<T> sequence, Object object) {
1653:                 List<T> result = Lists.newArrayList(sequence);
1654:                 result.removeAll(Collections.singleton(object));
1655:                 return result;
1656:         }
1657:
1658:         // @formatter:off
1659:         @Documentation(
1660:                 value = "Returns a sequence representation of the specified collection. Returns the same object if " +
1661:                                 "it is already a sequence.",
1662:                 params = {
1663:                         @Param(name = "collection", value = "The input collection")
1664:                 },
1665:                 result = "A sequence with all the elements of the input collection",
1666:                 examples = {
1667:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->asSequence()", result = "Sequence{'a', 'b', 'c'}"),
1668:                         @Example(expression = "Sequence{'a', 'b', 'c'}->asSequence()", result = "Sequence{'a', 'b', 'c'}")
1669:                 }
1670:         )
1671:         // @formatter:on
1672:         public <T> List<T> asSequence(Collection<T> collection) {
1673:                 if (collection instanceof List) {
1674:                         return (List<T>)collection;
1675:                 } else {
1676:                         return Lists.newArrayList(collection);
1677:                 }
1678:         }
1679:
1680:         // @formatter:off
1681:         @Documentation(
1682:                 value = "Returns a set representation of the specified collection. Returns the same object if " +
1683:                                 "it is already a set.",
1684:                 params = {
1685:                         @Param(name = "collection", value = "The input collection")
1686:                 },
1687:                 result = "A set with all the elements of the input collection",
1688:                 examples = {
1689:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->asSet()", result = "OrderedSet{'a', 'b', 'c'}"),
1690:                         @Example(expression = "Sequence{'a', 'b', 'c', 'c', 'a'}->asSet()", result = "OrderedSet{'a', 'b', 'c'}")
1691:                 }
1692:         )
1693:         // @formatter:on
1694:         public <T> Set<T> asSet(Collection<T> collection) {
1695:                 if (collection instanceof Set) {
1696:                         return (Set<T>)collection;
1697:                 } else {
1698:                         return Sets.newLinkedHashSet(collection);
1699:                 }
1700:         }
1701:
1702:         // @formatter:off
1703:         @Documentation(
1704:                 value = "Returns a set representation of the specified collection. Returns the same object if " +
1705:                                 "it is a set already. This operation has the same behavior as \"asSet()\"",
1706:                 params = {
1707:                         @Param(name = "collection", value = "The input collection")
1708:                 },
1709:                 result = "A set with all the elements of the input collection",
1710:                 examples = {
1711:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->asOrderedSet()", result = "OrderedSet{'a', 'b', 'c'}"),
1712:                         @Example(expression = "Sequence{'a', 'b', 'c'}->asOrderedSet()", result = "OrderedSet{'a', 'b', 'c'}")
1713:                 }
1714:         )
1715:         // @formatter:on
1716:         public <T> Set<T> asOrderedSet(Collection<T> collection) {
1717:                 return asSet(collection);
1718:         }
1719:
1720:         // @formatter:off
1721:         @Documentation(
1722:                 value = "Returns the first element of the specified Collection.",
1723:                 params = {
1724:                         @Param(name = "collection", value = "The input collection")
1725:                 },
1726:                 result = "The first element of the collection",
1727:                 examples = {
1728:                         @Example(expression = "Sequence{'a', 'b', 'c'}->first()", result = "'a'")
1729:                 }
1730:         )
1731:         // @formatter:on
1732:         public <T> T first(Collection<T> collection) {
1733:                 return Iterators.getNext(collection.iterator(), null);
1734:         }
1735:
1736:         // @formatter:off
1737:         @Documentation(
1738:                 value = "Returns the given sequence in reversed order.",
1739:                 params = {
1740:                         @Param(name = "sequence", value = "The input sequence")
1741:                 },
1742:                 result = "The sequence in reserved order",
1743:                 examples = {
1744:                         @Example(expression = "Sequence{'a', 'b', 'c'}->reverse()", result = "Sequence{'c', 'b', 'a'}")
1745:                 }
1746:         )
1747:         // @formatter:on
1748:         public <T> List<T> reverse(List<T> sequence) {
1749:                 return Lists.newArrayList(Lists.reverse(sequence));
1750:         }
1751:
1752:         // @formatter:off
1753:         @Documentation(
1754:                 value = "Returns the given set in reversed order.",
1755:                 params = {
1756:                         @Param(name = "set", value = "The input set")
1757:                 },
1758:                 result = "The set in reserved order",
1759:                 examples = {
1760:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->reverse()", result = "OrderedSet{'c', 'b', 'a'}")
1761:                 }
1762:         )
1763:         // @formatter:on
1764:         public <T> Set<T> reverse(Set<T> set) {
1765:                 return Sets.newLinkedHashSet(ImmutableList.copyOf(set).reverse());
1766:         }
1767:
1768:         // @formatter:off
1769:         @Documentation(
1770:                 value = "Returns \"true\" when the input collection is empty.",
1771:                 params = {
1772:                         @Param(name = "collection", value = "The input collection")
1773:                 },
1774:                 result = "\"true\" when the input collection is empty.",
1775:                 examples = {
1776:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->isEmpty()", result = "false"),
1777:                         @Example(expression = "Sequence{}->isEmpty()", result = "true"),
1778:                 }
1779:         )
1780:         // @formatter:on
1781:         public Boolean isEmpty(Collection<?> collection) {
1782:                 return collection.isEmpty();
1783:         }
1784:
1785:         // @formatter:off
1786:         @Documentation(
1787:                 value = "Returns \"true\" when the input collection is not empty.",
1788:                 params = {
1789:                         @Param(name = "collection", value = "The input collection")
1790:                 },
1791:                 result = "\"true\" when the input collection is not empty.",
1792:                 examples = {
1793:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->notEmpty()", result = "true"),
1794:                         @Example(expression = "Sequence{}->notEmpty()", result = "false"),
1795:                 }
1796:         )
1797:         // @formatter:on
1798:         public Boolean notEmpty(Collection<?> collection) {
1799:                 return !collection.isEmpty();
1800:         }
1801:
1802:         // @formatter:off
1803:         @Documentation(
1804:                 value = "Returns the element at the specified position in the sequence.",
1805:                 params = {
1806:                         @Param(name = "sequence", value = "The input sequence"),
1807:                         @Param(name = "position", value = "The position of the element to return ([1..size])")
1808:                 },
1809:                 result = "The element at the specified position in the list",
1810:                 examples = {
1811:                         @Example(expression = "Sequence{'a', 'b', 'c'}->at(1)", result = "'a'"),
1812:                         @Example(expression = "Sequence{'a', 'b', 'c'}->at(2)", result = "'b'")
1813:                 }
1814:         )
1815:         // @formatter:on
1816:         public <T> T at(List<T> sequence, Integer position) {
1817:                 return sequence.get(position - 1);
1818:         }
1819:
1820:         // @formatter:off
1821:         @Documentation(
1822:                 value = "Keeps only instances of the given EClassifier from the given set.",
1823:                 params = {
1824:                         @Param(name = "set", value = "The input set to filter"),
1825:                         @Param(name = "eClassifier", value = "The type used to filters element in the set")
1826:                 },
1827:                 result = "A new set containing instances of the given EClassifier or null if the given set is null",
1828:                 examples = {
1829:                         @Example(expression = "OrderedSet{anEClass, anEAttribute, anEReference}->filter(ecore::EClass)", result = "OrederedSet{anEClass}"),
1830:                         @Example(expression = "OrderedSet{anEClass, anEAttribute}->filter(ecore::EStructuralFeature)", result = "OrederedSet{anEAttribute}"),
1831:                 }
1832:         )
1833:         public <T> Set<T> filter(Set<T> set, final EClassifier eClassifier) {
1834:                 final Set<T> result;
1835:
1836:                 if (set == null) {
1837:                         result = null;
1838:                 } else if (eClassifier != null) {
1839:                         final Set<EClassifier> eClassifiers = new LinkedHashSet<EClassifier>();
1840:                         eClassifiers.add(eClassifier);
1841:                         result = filter(set, eClassifiers);
1842:                 } else {
1843:                         result = Sets.newLinkedHashSet();
1844:                 }
1845:
1846:                 return result;
1847:         }
1848:
1849:         // @formatter:off
1850:         @Documentation(
1851:                 value = "Keeps only instances of the given set of EClassifier from the given set.",
1852:                 params = {
1853:                         @Param(name = "set", value = "The input set to filter"),
1854:                         @Param(name = "eClassifiers", value = "The set of type used to filters element in the set")
1855:                 },
1856:                 result = "A new set containing instances of the given set of EClassifiers or null if the given set is null",
1857:                 examples = {
1858:                                 @Example(expression = "OrderedSet{anEClass, anEAttribute, anEReference}->filter({ecore::EClass | ecore::EReference})", result = "OrderedSet{anEClass, anEReference}"),
1859:                                 @Example(expression = "OrderedSet{anEClass, anEAttribute, anEPackage}->filter({ecore::EStructuralFeature | ecore::EPacakge})", result = "OrderedSet{anEAttribute, anEPackage}"),
1860:                 }
1861:         )
1862:         // @formatter:on
1863:         public <T> Set<T> filter(Set<T> set, final Set<EClassifier> eClassifiers) {
1864:                 final Set<T> result;
1865:
1866:                 if (set == null) {
1867:                         result = null;
1868:                 } else if (eClassifiers == null || eClassifiers.isEmpty()) {
1869:                         result = Sets.newLinkedHashSet();
1870:                 } else {
1871:                         result = Sets.newLinkedHashSet();
1872:                         for (T object : set) {
1873:                                 for (EClassifier eClassifier : eClassifiers) {
1874:                                         if (eClassifier.isInstance(object)) {
1875:                                                 result.add(object);
1876:                                                 break;
1877:                                         }
1878:                                 }
1879:                         }
1880:                 }
1881:
1882:                 return result;
1883:         }
1884:
1885:         // @formatter:off
1886:         @Documentation(
1887:                 value = "Keeps only instances of the given EClassifier in the given sequence.",
1888:                 params = {
1889:                         @Param(name = "sequence", value = "The input sequence to filter"),
1890:                         @Param(name = "eClassifier", value = "The type used to filters element in the sequence")
1891:                 },
1892:                 result = "A new sequence containing instances of the given EClassifier or null if the given sequence is null",
1893:                 examples = {
1894:                         @Example(expression = "Sequence{anEClass, anEAttribute, anEReference}->filter(ecore::EClass)", result = "Sequence{anEClass}"),
1895:                         @Example(expression = "Sequence{anEClass, anEAttribute}->filter(ecore::EStructuralFeature)", result = "Sequence{anEAttribute}"),
1896:                 }
1897:         )
1898:         // @formatter:on
1899:         public <T> List<T> filter(List<T> sequence, final EClassifier eClassifier) {
1900:                 final List<T> result;
1901:
1902:                 if (sequence == null) {
1903:                         result = null;
1904:                 } else if (eClassifier != null) {
1905:                         final Set<EClassifier> eClassifiers = new LinkedHashSet<EClassifier>();
1906:                         eClassifiers.add(eClassifier);
1907:                         result = filter(sequence, eClassifiers);
1908:                 } else {
1909:                         result = Lists.newArrayList();
1910:                 }
1911:
1912:                 return result;
1913:         }
1914:
1915:         // @formatter:off
1916:         @Documentation(
1917:                 value = "Keeps only instances of the given EClassifier in the given sequence.",
1918:                 params = {
1919:                         @Param(name = "sequence", value = "The input sequence to filter"),
1920:                         @Param(name = "eClassifiers", value = "The set of types used to filters element in the sequence")
1921:                 },
1922:                 result = "A new sequence containing instances of the given EClassifiers or null if the given sequence is null",
1923:                 examples = {
1924:                         @Example(expression = "Sequence{anEClass, anEAttribute, anEReference}->filter({ecore::EClass | ecore::EReference})", result = "Sequence{anEClass, anEReference}"),
1925:                         @Example(expression = "Sequence{anEClass, anEAttribute, anEPackage}->filter({ecore::EStructuralFeature | ecore::EPacakge})", result = "Sequence{anEAttribute, anEPackage}"),
1926:                 }
1927:         )
1928:         // @formatter:on
1929:         public <T> List<T> filter(List<T> sequence, final Set<EClassifier> eClassifiers) {
1930:                 final List<T> result;
1931:
1932:                 if (sequence == null) {
1933:                         result = null;
1934:                 } else if (eClassifiers == null || eClassifiers.isEmpty()) {
1935:                         result = Lists.newArrayList();
1936:                 } else {
1937:                         result = Lists.newArrayList();
1938:                         for (T object : sequence) {
1939:                                 for (EClassifier eClassifier : eClassifiers) {
1940:                                         if (eClassifier.isInstance(object)) {
1941:                                                 result.add(object);
1942:                                                 break;
1943:                                         }
1944:                                 }
1945:                         }
1946:                 }
1947:
1948:                 return result;
1949:         }
1950:
1951:         // @formatter:off
1952:         @Documentation(
1953:                 value = "Inserts the given separator between each elements of the given collection.",
1954:                 params = {
1955:                         @Param(name = "collection", value = "The input collection"),
1956:                         @Param(name = "separator", value = "The separator to insert")
1957:                 },
1958:                 result = "A new sequence, or null if the given collection is null",
1959:                 examples = {
1960:                         @Example(expression = "Sequence{'a', 'b', 'c'}->sep('-')", result = "Sequence{'a', '-', 'b', '-', 'c'}"),
1961:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->sep('-')", result = "Sequence{'a', '-', 'b', '-', 'c'}")
1962:                 }
1963:         )
1964:         // @formatter:on
1965:         public List<Object> sep(Collection<?> collection, Object separator) {
1966:                 final List<Object> result;
1967:
1968:                 if (collection == null) {
1969:                         result = null;
1970:                 } else {
1971:                         result = Lists.newArrayList();
1972:
1973:                         final Iterator<?> it = collection.iterator();
1974:                         if (it.hasNext()) {
1975:                                 result.add(it.next());
1976:                                 while (it.hasNext()) {
1977:                                         result.add(separator);
1978:                                         result.add(it.next());
1979:                                 }
1980:                         }
1981:                 }
1982:
1983:                 return result;
1984:         }
1985:
1986:         // @formatter:off
1987:         @Documentation(
1988:                 value = "Inserts the given separator between each elements of the given collection, the given prefix " +
1989:                                 "before the first element, and the given suffix after the last element.",
1990:                 params = {
1991:                         @Param(name = "collection", value = "The input collection"),
1992:                         @Param(name = "prefix", value = "The prefix"),
1993:                         @Param(name = "separator", value = "The separator to insert"),
1994:                         @Param(name = "suffix", value = "The suffix")
1995:                 },
1996:                 result = "A new sequence, or null if the given collection is null",
1997:                 examples = {
1998:                         @Example(expression = "Sequence{'a', 'b', 'c'}->sep('[', '-', ']')", result = "Sequence{'[', 'a', '-', 'b', '-', 'c', ']'}")
1999:                 }
2000:         )
2001:         // @formatter:on
2002:         public List<Object> sep(Collection<?> collection, Object prefix, Object separator, Object suffix) {
2003:                 final List<Object> result = Lists.newArrayList();
2004:
2005:                 result.add(prefix);
2006:                 if (collection != null) {
2007:                         result.addAll(sep(collection, separator));
2008:                 }
2009:                 result.add(suffix);
2010:
2011:                 return result;
2012:         }
2013:
2014:         // @formatter:off
2015:         @Documentation(
2016:                 value = "Returns the last element of the given sequence.",
2017:                 params = {
2018:                         @Param(name = "sequence", value = "The sequence")
2019:                 },
2020:                 result = "The last element of the given sequence.",
2021:                 examples = {
2022:                         @Example(expression = "Sequence{'a', 'b', 'c'}->last()", result = "'c'")
2023:                 }
2024:         )
2025:         // @formatter:on
2026:         public <T> T last(List<T> sequence) {
2027:                 ListIterator<T> it = sequence.listIterator(sequence.size());
2028:                 if (it.hasPrevious()) {
2029:                         return it.previous();
2030:                 }
2031:                 return null;
2032:         }
2033:
2034:         // @formatter:off
2035:         @Documentation(
2036:                 value = "Returns the last element of the given set.",
2037:                 params = {
2038:                         @Param(name = "set", value = "The set")
2039:                 },
2040:                 result = "The last element of the given set.",
2041:                 examples = {
2042:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->last()", result = "'c'")
2043:                 }
2044:         )
2045:         // @formatter:on
2046:         public <T> T last(Set<T> set) {
2047:                 return Iterators.getLast(set.iterator(), null);
2048:         }
2049:
2050:         // @formatter:off
2051:         @Documentation(
2052:                 value = "Indicates if the given collection doesn't contain the given object.",
2053:                 params = {
2054:                         @Param(name = "collection", value = "The input collection"),
2055:                         @Param(name = "object", value = "The object"),
2056:                 },
2057:                 result = "\"true\" if the given collection doesn't contain the given object, \"false\" otherwise",
2058:                 examples = {
2059:                         @Example(expression = "Sequence{'a', 'b', 'c'}->excludes('a')", result = "false"),
2060:                         @Example(expression = "Sequence{'a', 'b', 'c'}->excludes('d')", result = "true")
2061:                 }
2062:         )
2063:         // @formatter:on
2064:         public Boolean excludes(Collection<?> collection, Object object) {
2065:                 return Boolean.valueOf(!collection.contains(object));
2066:         }
2067:
2068:         // @formatter:off
2069:         @Documentation(
2070:                 value = "Indicates if the given collection contains the given object.",
2071:                 params = {
2072:                         @Param(name = "collection", value = "The input collection"),
2073:                         @Param(name = "object", value = "The object"),
2074:                 },
2075:                 result = "\"true\" if the given collection contains the given object, \"false\" otherwise",
2076:                 examples = {
2077:                         @Example(expression = "Sequence{'a', 'b', 'c'}->includes('a')", result = "true"),
2078:                         @Example(expression = "Sequence{'a', 'b', 'c'}->includes('d')", result = "false")
2079:                 }
2080:         )
2081:         // @formatter:on
2082:         public Boolean includes(Collection<?> collection, Object object) {
2083:                 return Boolean.valueOf(collection.contains(object));
2084:         }
2085:
2086:         // @formatter:off
2087:         @Documentation(
2088:                 value = "Returns a set containing all the elements of the first and second sets",
2089:                 params = {
2090:                         @Param(name = "set1", value = "The first set"),
2091:                         @Param(name = "set2", value = "The second set"),
2092:                 },
2093:                 result = "A set containing all the elements of the first and second sets",
2094:                 examples = {
2095:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->union(OrderedSet{'d', 'c'})", result = "OrderedSet{'a', 'b', 'c', 'd'}")
2096:                 }
2097:         )
2098:         // @formatter:on
2099:         public <T> Set<T> union(Set<? extends T> set1, Set<? extends T> set2) {
2100:                 return concat(set1, set2);
2101:         }
2102:
2103:         // @formatter:off
2104:         @Documentation(
2105:                 value = "Returns a sequence containing all the elements of the first and second sequences",
2106:                 params = {
2107:                         @Param(name = "sequence1", value = "The first sequence"),
2108:                         @Param(name = "sequence2", value = "The second sequence"),
2109:                 },
2110:                 result = "A sequence containing all the elements of the first and second sequences",
2111:                 examples = {
2112:                         @Example(expression = "Sequence{'a', 'b', 'c'}->union(Sequence{'d', 'c'})", result = "Sequence{'a', 'b', 'c', 'd'}")
2113:                 }
2114:         )
2115:         // @formatter:on
2116:         public <T> List<T> union(List<? extends T> sequence1, List<? extends T> sequence2) {
2117:                 return concat(sequence1, sequence2);
2118:         }
2119:
2120:         // @formatter:off
2121:         @Documentation(
2122:                 value = "Gets the first element in the current collection for which the value returned by the lambda evaluates " +
2123:                                 "to \"true\".",
2124:                 params = {
2125:                         @Param(name = "collection", value = "The input collection"),
2126:                         @Param(name = "lambda", value = "The lamba"),
2127:                 },
2128:                 result = "The first element in the given collection for which the value returned by the lambda is \"true\"",
2129:                 examples = {
2130:                         @Example(expression = "Sequence{'a', 'b', 'c'}->any(str | str.size() = 1)", result = "'a'")
2131:                 }
2132:         )
2133:         // @formatter:on
2134:         public <T> T any(Collection<T> collection, LambdaValue lambda) {
2135:                 T result = null;
2136:
2137:                 if (collection != null && lambda == null) {
2138:                         result = null;
2139:                 } else {
2140:                         for (T input : collection) {
2141:                                 try {
2142:                                         Object value = lambda.eval(new Object[] {input });
2143:                                         if (Boolean.TRUE.equals(value)) {
2144:                                                 result = input;
2145:                                                 break;
2146:                                         }
2147:                                         // CHECKSTYLE:OFF
2148:                                 } catch (Exception e) {
2149:                                         // CHECKSTYLE:ON
2150:                                         lambda.logException(Diagnostic.WARNING, e);
2151:                                 }
2152:                         }
2153:                 }
2154:
2155:                 return result;
2156:         }
2157:
2158:         // @formatter:off
2159:         @Documentation(
2160:                 value = "Returns \"1\" if the current set contains the given object, \"0\" otherwise.",
2161:                 params = {
2162:                         @Param(name = "set", value = "The set"),
2163:                         @Param(name = "object", value = "The object"),
2164:                 },
2165:                 result = "\"1\" if the current set contains the given object, \"0\" otherwise.",
2166:                 examples = {
2167:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->count('d')", result = "0"),
2168:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->count('a')", result = "1")
2169:                 }
2170:         )
2171:         // @formatter:on
2172:         public Integer count(Set<?> set, Object object) {
2173:                 final Integer result;
2174:
2175:                 if (set.contains(object)) {
2176:                         result = Integer.valueOf(1);
2177:                 } else {
2178:                         result = Integer.valueOf(0);
2179:                 }
2180:
2181:                 return result;
2182:         }
2183:
2184:         // @formatter:off
2185:         @Documentation(
2186:                 value = "Counts the number of occurrences of the given object in the given sequence",
2187:                 params = {
2188:                         @Param(name = "sequence", value = "The sequence"),
2189:                         @Param(name = "object", value = "The object"),
2190:                 },
2191:                 result = "The number of times the given object is present in the given sequence",
2192:                 examples = {
2193:                         @Example(expression = "Sequence{'a', 'b', 'c'}->count('d')", result = "0"),
2194:                         @Example(expression = "Sequence{'a', 'b', 'c'}->count('a')", result = "1")
2195:                 }
2196:         )
2197:         // @formatter:on
2198:         public Integer count(List<Object> sequence, Object object) {
2199:                 return Integer.valueOf(Collections.frequency(sequence, object));
2200:         }
2201:
2202:         // @formatter:off
2203:         @Documentation(
2204:                 value = "Indicates if it exists an object from the given collection for which the given lambda evaluates " +
2205:                                 "to \"true\"",
2206:                 params = {
2207:                         @Param(name = "collection", value = "The collection"),
2208:                         @Param(name = "lambda", value = "The lambda"),
2209:                 },
2210:                 result = "\"true\" if it exists an object from the given collection that validate the given lamba " +
2211:                                  "\"false\" otherwise.",
2212:                 examples = {
2213:                         @Example(expression = "Sequence{'a', 'b', 'c'}->exists(str | str.size() > 5)", result = "false")
2214:                 }
2215:         )
2216:         // @formatter:on
2217:         public Boolean exists(Collection<?> collection, LambdaValue lambda) {
2218:                 Boolean result = Boolean.FALSE;
2219:
2220:                 if (collection != null && lambda != null) {
2221:                         for (Object input : collection) {
2222:                                 try {
2223:                                         Object value = lambda.eval(new Object[] {input });
2224:                                         if (Boolean.TRUE.equals(value)) {
2225:                                                 result = Boolean.TRUE;
2226:                                                 break;
2227:                                         }
2228:                                         // CHECKSTYLE:OFF
2229:                                 } catch (Exception e) {
2230:                                         // CHECKSTYLE:ON
2231:                                         lambda.logException(Diagnostic.WARNING, e);
2232:                                 }
2233:                         }
2234:                 }
2235:
2236:                 return result;
2237:         }
2238:
2239:         // @formatter:off
2240:         @Documentation(
2241:                 value = "Indicates if all the objects from the given collection validate the given lamba",
2242:                 params = {
2243:                         @Param(name = "collection", value = "The collection"),
2244:                         @Param(name = "lambda", value = "The lambda"),
2245:                 },
2246:                 result = "\"true\" if all the objects from the given collection validate the given lamba " +
2247:                                  "\"false\" otherwise.",
2248:                 examples = {
2249:                         @Example(expression = "Sequence{'a', 'b', 'ccc'}->forAll(str | str.size() = 1)", result = "false"),
2250:                         @Example(expression = "Sequence{'a', 'b', 'c'}->forAll(str | str.size() = 1)", result = "false")
2251:                 }
2252:         )
2253:         // @formatter:on
2254:         public Boolean forAll(Collection<?> collection, LambdaValue lambda) {
2255:                 Boolean result = Boolean.TRUE;
2256:
2257:                 if (collection == null || lambda == null) {
2258:                         result = Boolean.FALSE;
2259:                 } else {
2260:                         for (Object input : collection) {
2261:                                 try {
2262:                                         Object value = lambda.eval(new Object[] {input });
2263:                                         if (!(value instanceof Boolean) || !Boolean.TRUE.equals(value)) {
2264:                                                 result = Boolean.FALSE;
2265:                                                 break;
2266:                                         }
2267:                                         // CHECKSTYLE:OFF
2268:                                 } catch (Exception e) {
2269:                                         // CHECKSTYLE:ON
2270:                                         lambda.logException(Diagnostic.WARNING, e);
2271:                                         result = Boolean.FALSE;
2272:                                         break;
2273:                                 }
2274:                         }
2275:                 }
2276:
2277:                 return result;
2278:         }
2279:
2280:         // @formatter:off
2281:         @Documentation(
2282:                 value = "Indicates if no elements from the second collection are contained in the first collection",
2283:                 params = {
2284:                         @Param(name = "collection1", value = "The first collection"),
2285:                         @Param(name = "collection2", value = "The second collection")
2286:                 },
2287:                 result = "\"true\" if no elements of the second collection are contained in the first one " +
2288:                                  "\"false\" otherwise.",
2289:                 examples = {
2290:                         @Example(expression = "Sequence{'a', 'b'}->excludesAll(OrderedSet{'f'})", result = "true"),
2291:                         @Example(expression = "Sequence{'a', 'b'}->excludesAll(OrderedSet{'a', 'f'})", result = "false")
2292:                 }
2293:         )
2294:         // @formatter:on
2295:         public Boolean excludesAll(Collection<?> collection1, Collection<?> collection2) {
2296:                 return Boolean.valueOf(Collections.disjoint(collection1, collection2));
2297:         }
2298:
2299:         // @formatter:off
2300:         @Documentation(
2301:                 value = "Indicates if all elements from the second collection are contained in the first collection",
2302:                 params = {
2303:                         @Param(name = "collection1", value = "The first collection"),
2304:                         @Param(name = "collection2", value = "The second collection")
2305:                 },
2306:                 result = "\"true\" if all elements of the second collection are contained in the first one " +
2307:                                  "\"false\" otherwise.",
2308:                 examples = {
2309:                         @Example(expression = "Sequence{'a', 'b', 'c'}->includesAll(OrderedSet{'a'})", result = "true"),
2310:                         @Example(expression = "Sequence{'a', 'b', 'c'}->includesAll(OrderedSet{'a', 'f'})", result = "false"),
2311:                 }
2312:         )
2313:         // @formatter:on
2314:         public Boolean includesAll(Collection<?> collection1, Collection<?> collection2) {
2315:                 return Boolean.valueOf(collection1.containsAll(collection2));
2316:         }
2317:
2318:         // @formatter:off
2319:         @Documentation(
2320:                 value = "Indicates if the evaluation of the given lambda gives a different value for all elements of the " +
2321:                                 "given collection.",
2322:                 params = {
2323:                         @Param(name = "collection", value = "The collection"),
2324:                         @Param(name = "lambda", value = "The lambda")
2325:                 },
2326:                 result = "\"true\" if the evaluation of the lamba gives a different value for all elements of the " +
2327:                                  "given collection, \"false\" otherwise.",
2328:                 examples = {
2329:                         @Example(expression = "Sequence{'a', 'b', 'c'}->isUnique(str | str.size())", result = "false"),
2330:                         @Example(expression = "Sequence{'a', 'bb', 'ccc'}->isUnique(str | str.size())", result = "true")
2331:                 }
2332:         )
2333:         // @formatter:on
2334:         public Boolean isUnique(Collection<?> collection, LambdaValue lambda) {
2335:                 boolean result = true;
2336:                 final Set<Object> evaluated = Sets.newHashSet();
2337:
2338:                 if (collection != null && lambda == null) {
2339:                         result = false;
2340:                 } else {
2341:                         for (Object input : collection) {
2342:                                 try {
2343:                                         if (!evaluated.add(lambda.eval(new Object[] {input }))) {
2344:                                                 result = false;
2345:                                                 break;
2346:                                         }
2347:                                         // CHECKSTYLE:OFF
2348:                                 } catch (Exception e) {
2349:                                         lambda.logException(Diagnostic.WARNING, e);
2350:                                 }
2351:                                 // CHECKSTYLE:ON
2352:                         }
2353:                 }
2354:
2355:                 return Boolean.valueOf(result);
2356:         }
2357:
2358:         // @formatter:off
2359:         @Documentation(
2360:                 value = "Indicates if one and only one element of the given collection validates the given lambda.",
2361:                 params = {
2362:                         @Param(name = "collection", value = "The collection"),
2363:                         @Param(name = "lambda", value = "The lambda")
2364:                 },
2365:                 result = "\"true\" if one and only one element of the given collection validates the given lambda, " +
2366:                                  "\"false\" otherwise.",
2367:                 examples = {
2368:                         @Example(expression = "Sequence{'a', 'b', 'c'}->one(str | str.equals('a'))", result = "true"),
2369:                         @Example(expression = "Sequence{'a', 'a', 'c'}->one(str | str.equals('a'))", result = "false")
2370:                 }
2371:         )
2372:         // @formatter:on
2373:         public Boolean one(Collection<?> self, LambdaValue lambda) {
2374:                 boolean result = false;
2375:
2376:                 if (self != null && lambda == null) {
2377:                         result = false;
2378:                 } else {
2379:                         for (Object input : self) {
2380:                                 try {
2381:                                         Object value = lambda.eval(new Object[] {input });
2382:                                         if (Boolean.TRUE.equals(value)) {
2383:                                                 result = !result;
2384:                                                 if (!result) {
2385:                                                         break;
2386:                                                 }
2387:                                         }
2388:                                         // CHECKSTYLE:OFF
2389:                                 } catch (Exception e) {
2390:                                         // CHECKSTYLE:ON
2391:                                         lambda.logException(Diagnostic.WARNING, e);
2392:                                 }
2393:                         }
2394:                 }
2395:
2396:                 return Boolean.valueOf(result);
2397:         }
2398:
2399:         // @formatter:off
2400:         @Documentation(
2401:                 value = "Sums elements of the given collection if possible.",
2402:                 params = {
2403:                         @Param(name = "collection", value = "The collection")
2404:                 },
2405:                 result = "The sum of elements of the given collection if possible",
2406:                 exceptions = {
2407:                         @Throw(type = IllegalArgumentException.class, value = "If an element of the collection cannot be processed")
2408:                 },
2409:                 examples = {
2410:                         @Example(expression = "Sequence{1, 2, 3, 4}->sum()", result = "10")
2411:                 }
2412:         )
2413:         // @formatter:on
2414:         public Number sum(Collection<?> collection) {
2415:                 Number result = Long.valueOf(0);
2416:
2417:                 for (Object input : collection) {
2418:                         if (!(input instanceof Number)) {
2419:                                 throw new IllegalArgumentException(SUM_ONLY_NUMERIC_ERROR);
2420:                         }
2421:
2422:                         if (result instanceof Long && (input instanceof Long || input instanceof Integer)) {
2423:                                 result = result.longValue() + ((Number)input).longValue();
2424:                         } else {
2425:                                 // widen anything that is not a long or int to a double
2426:                                 result = result.doubleValue() + ((Number)input).doubleValue();
2427:                         }
2428:                 }
2429:
2430:                 return result;
2431:         }
2432:
2433:         // @formatter:off
2434:         @Documentation(
2435:                 value = "Returns the index of the given object in the given sequence ([1..size]).",
2436:                 params = {
2437:                         @Param(name = "sequence", value = "The sequence"),
2438:                         @Param(name = "object", value = "The object")
2439:                 },
2440:                 result = "The index of the given object",
2441:                 examples = {
2442:                         @Example(expression = "Sequence{1, 2, 3, 4}->indexOf(3)", result = "3")
2443:                 }
2444:         )
2445:         // @formatter:on
2446:         public Integer indexOf(List<?> sequence, Object object) {
2447:                 return Integer.valueOf(sequence.indexOf(object) + 1);
2448:         }
2449:
2450:         // @formatter:off
2451:         @Documentation(
2452:                 value = "Returns the index of the given object in the given set ([1..size]).",
2453:                 params = {
2454:                         @Param(name = "set", value = "The set"),
2455:                         @Param(name = "object", value = "The object")
2456:                 },
2457:                 result = "The index of the given object",
2458:                 examples = {
2459:                         @Example(expression = "OrderedSet{1, 2, 3, 4}->indexOf(3)", result = "3")
2460:                 }
2461:         )
2462:         // @formatter:on
2463:         public Integer indexOf(Set<?> set, Object object) {
2464:                 int index = 1;
2465:                 for (Object o : set) {
2466:                         if (o == object || (o != null && o.equals(object))) {
2467:                                 return index;
2468:                         }
2469:                         index++;
2470:                 }
2471:                 return 0;
2472:         }
2473:
2474:         // @formatter:off
2475:         @Documentation(
2476:                 value = "Inserts the given object in a copy of the given sequence at the given position ([1..size]).",
2477:                 params = {
2478:                         @Param(name = "sequence", value = "The sequence"),
2479:                         @Param(name = "position", value = "The position"),
2480:                         @Param(name = "object", value = "The object")
2481:                 },
2482:                 result = "A copy of the given sequence including the object at the given position",
2483:                 examples = {
2484:                         @Example(expression = "Sequence{'a', 'b', 'c'}->insertAt(2, 'f')", result = "Sequence{'a', 'f', 'b', 'c'}")
2485:                 }
2486:         )
2487:         // @formatter:on
2488:         public <T> List<T> insertAt(List<T> sequence, Integer position, T object) {
2489:                 final int initialSize = sequence.size();
2490:                 if (position < 1 || position > initialSize + 1) {
2491:                         throw new IndexOutOfBoundsException();
2492:                 }
2493:                 final List<T> result = new ArrayList<T>(initialSize + 1);
2494:
2495:                 result.addAll(sequence.subList(0, position - 1));
2496:                 result.add(object);
2497:                 result.addAll(sequence.subList(position - 1, initialSize));
2498:
2499:                 return result;
2500:         }
2501:
2502:         // @formatter:off
2503:         @Documentation(
2504:                 value = "Inserts the given object in a copy of the given set at the given position ([1..size]). "
2505:                                 + "If the given set already contains this object, it will be moved to the accurate position.",
2506:                 params = {
2507:                         @Param(name = "set", value = "The set"),
2508:                         @Param(name = "position", value = "The position"),
2509:                         @Param(name = "object", value = "The object")
2510:                 },
2511:                 result = "A copy of the given set including the object at the given position if it didn't already contain that object.",
2512:                 examples = {
2513:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->insertAt(2, 'f')", result = "Sequence{'a', 'f', 'b', 'c'}")
2514:                 }
2515:         )
2516:         // @formatter:on
2517:         public <T> Set<T> insertAt(Set<T> set, Integer position, T object) {
2518:                 final int initialSize = set.size();
2519:                 if (position < 1 || position > initialSize + 1) {
2520:                         throw new IndexOutOfBoundsException();
2521:                 }
2522:                 final Set<T> result = new LinkedHashSet<T>(initialSize + 1);
2523:
2524:                 int current = 1;
2525:                 Iterator<T> iterator = set.iterator();
2526:                 while (iterator.hasNext()) {
2527:                         if (current == position.intValue()) {
2528:                                 result.add(object);
2529:                                 current++;
2530:                         } else {
2531:                                 T value = iterator.next();
2532:                                 if (object == value || (object != null && object.equals(value))) {
2533:                                         // Do not add our target object here, wait until we reach its demanded position
2534:                                 } else {
2535:                                         result.add(value);
2536:                                         current++;
2537:                                 }
2538:                         }
2539:                 }
2540:                 if (current <= position.intValue()) {
2541:                         result.add(object);
2542:                 }
2543:
2544:                 return result;
2545:         }
2546:
2547:         // @formatter:off
2548:         @Documentation(
2549:                 value = "Inserts the given object in a copy of the given sequence at the first position.",
2550:                 params = {
2551:                         @Param(name = "sequence", value = "The sequence"),
2552:                         @Param(name = "object", value = "The object")
2553:                 },
2554:                 result = "A copy of the given sequence including the object at the first position",
2555:                 examples = {
2556:                         @Example(expression = "Sequence{'a', 'b', 'c'}->prepend('f')", result = "Sequence{'f', 'a', 'b', 'c'}")
2557:                 }
2558:         )
2559:         // @formatter:on
2560:         public <T> List<T> prepend(List<T> sequence, T object) {
2561:                 final List<T> result = new ArrayList<T>(sequence.size() + 1);
2562:
2563:                 result.add(object);
2564:                 result.addAll(sequence);
2565:
2566:                 return result;
2567:         }
2568:
2569:         // @formatter:off
2570:         @Documentation(
2571:                 value = "Inserts the given object in a copy of the given set at the first position. "
2572:                                 + "If the set already contained the given object, it is moved to the first position.",
2573:                 params = {
2574:                         @Param(name = "set", value = "The sequence"),
2575:                         @Param(name = "object", value = "The object")
2576:                 },
2577:                 result = "A copy of the given set including the object at the first position",
2578:                 examples = {
2579:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->prepend('f')", result = "OrderedSet{'f', 'a', 'b', 'c'}")
2580:                 }
2581:         )
2582:         // @formatter:on
2583:         public <T> Set<T> prepend(Set<T> set, T object) {
2584:                 final Set<T> result = new LinkedHashSet<T>(set.size() + 1);
2585:
2586:                 result.add(object);
2587:                 result.addAll(set);
2588:
2589:                 return result;
2590:         }
2591:
2592:         // @formatter:off
2593:         @Documentation(
2594:                 value = "Creates a set with the elements from the given set that are also present in the given collection.",
2595:                 params = {
2596:                         @Param(name = "set", value = "The set"),
2597:                         @Param(name = "collection", value = "The collection")
2598:                 },
2599:                 result = "The created set with elements from the given set that are also present in the given collection",
2600:                 examples = {
2601:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->intersection(OrderedSet{'a', 'f'})", result = "OrderedSet{'a'}")
2602:                 }
2603:         )
2604:         // @formatter:on
2605:         public <T> Set<T> intersection(Set<T> set, Collection<?> collection) {
2606:                 if (collection instanceof Set<?>) {
2607:                         return Sets.intersection(set, (Set<?>)collection);
2608:                 }
2609:                 final Set<T> result = Sets.newLinkedHashSet(set);
2610:                 result.retainAll(collection);
2611:                 return result;
2612:         }
2613:
2614:         // @formatter:off
2615:         @Documentation(
2616:                 value = "Creates a sequence with elements from the given sequence that are present in both the current sequence " +
2617:                                 "and the given other {@code Collection}. Iteration order will match that of the current sequence. Duplicates " +
2618:                                 "from the first list will all be kept in the result if they also are in the second one, but duplicates " +
2619:                                 "from the second list will be dumped even if they are present in the first.",
2620:                 params = {
2621:                         @Param(name = "sequence", value = "The sequence"),
2622:                         @Param(name = "collection", value = "The collection")
2623:                 },
2624:                 result = "The intersection of both collections",
2625:                 examples = {
2626:                         @Example(expression = "Sequence{'a', 'b', 'c'}->intersection(OrderedSet{'a', 'f'})", result = "Sequence{'a'}")
2627:                 }
2628:         )
2629:         // @formatter:on
2630:         public <T> List<T> intersection(List<T> sequence, Collection<?> collection) {
2631:                 final List<T> result = Lists.newArrayList(sequence);
2632:                 result.retainAll(collection);
2633:                 return result;
2634:         }
2635:
2636:         // @formatter:off
2637:         @Documentation(
2638:                 value = "Returns a subset of the given set",
2639:                 params = {
2640:                         @Param(name = "set", value = "The set"),
2641:                         @Param(name = "startIndex", value = "The low end point (inclusive) of the subset. Must not be less than 1."),
2642:                         @Param(name = "startIndex", value = "The high end point (inclusive) of the subset. Must not be greater than the current set's size.")
2643:                 },
2644:                 result = "A subset of the given set",
2645:                 exceptions = {
2646:                         @Throw(type = IndexOutOfBoundsException.class, value = "If we have an illegal end point value (startIndex < 1 || " +
2647:                                                                                                                                  "endIndex > set.size() || startIndex > endIndex).")
2648:                 },
2649:                 examples = {
2650:                         @Example(expression = "OrderedSet{'a', 'b', 'c'}->subOrderedSet(1, 2)", result = "OrderedSet{'a', 'b'}")
2651:                 }
2652:         )
2653:         // @formatter:on
2654:         public <T> Set<T> subOrderedSet(Set<T> set, Integer startIndex, Integer endIndex) {
2655:                 if (startIndex < 1 || endIndex > set.size() || startIndex > endIndex) {
2656:                         throw new IndexOutOfBoundsException();
2657:                 }
2658:                 final Set<T> result = new LinkedHashSet<T>(endIndex - startIndex + 1);
2659:
2660:                 int index = 1;
2661:                 for (T input : set) {
2662:                         if (index >= startIndex) {
2663:                                 if (index <= endIndex) {
2664:                                         result.add(input);
2665:                                 } else {
2666:                                         break;
2667:                                 }
2668:                         }
2669:                         ++index;
2670:                 }
2671:
2672:                 return result;
2673:         }
2674:
2675:         // @formatter:off
2676:         @Documentation(
2677:                 value = "Returns a subset of the given sequence",
2678:                 params = {
2679:                         @Param(name = "sequence", value = "The sequence"),
2680:                         @Param(name = "startIndex", value = "The low end point (inclusive) of the subsequence"),
2681:                         @Param(name = "startIndex", value = "The high end point (inclusive) of the subsequence")
2682:                 },
2683:                 result = "A subset of the given sequence",
2684:                 exceptions = {
2685:                         @Throw(type = IndexOutOfBoundsException.class, value = "If we have an illegal end point value (startIndex < 1 || " +
2686:                                                                                                                                  "endIndex > set.size() || startIndex > endIndex).")
2687:                         },
2688:                 examples = {
2689:                         @Example(expression = "Sequence{'a', 'b', 'c'}->subSequence(1, 2)", result = "Sequence{'a', 'b'}")
2690:                 }
2691:         )
2692:         // @formatter:on
2693:         public <T> List<T> subSequence(List<T> sequence, Integer startIndex, Integer endIndex) {
2694:                 if (startIndex < 1 || endIndex > sequence.size() || startIndex > endIndex) {
2695:                         throw new IndexOutOfBoundsException();
2696:                 }
2697:                 return Lists.newArrayList(sequence.subList(startIndex - 1, endIndex));
2698:         }
2699:
2700:         /**
2701:          * Evaluates a lambda then uses the result as comparables.
2702:          */
2703:         private static final class LambdaComparator<T> implements Comparator<T> {
2704:                 /**
2705:                  * Pre-populated map linking the objects from the collection we're sorting with the values calculated
2706:                  * by this lambda.
2707:                  */
2708:                 private final Map<T, Object> preComputedValues;
2709:
2710:                 /**
2711:                  * Constructs a comparator given its lambda.
2712:                  *
2713:                  * @param lambda
2714:                  * the lambda providing our comparables.
2715:                  */
2716:                 public LambdaComparator(Map<T, Object> preComputedValues) {
2717:                         this.preComputedValues = preComputedValues;
2718:                 }
2719:
2720:                 @Override
2721:                 public int compare(T o1, T o2) {
2722:                         final int result;
2723:
2724:                         Object o1Result = preComputedValues.get(o1);
2725:                         Object o2Result = preComputedValues.get(o2);
2726:                         try {
2727:                                 if (o1Result instanceof Comparable<?>) {
2728:                                         @SuppressWarnings("unchecked")
2729:                                         Comparable<Object> c1 = (Comparable<Object>)o1Result;
2730:                                         result = c1.compareTo(o2Result);
2731:                                 } else if (o2Result instanceof Comparable<?>) {
2732:                                         @SuppressWarnings("unchecked")
2733:                                         Comparable<Object> c2 = (Comparable<Object>)o2Result;
2734:                                         result = -c2.compareTo(o1Result);
2735:                                 } else {
2736:                                         result = 0;
2737:                                 }
2738:                                 // CHECKSTYLE:OFF
2739:                         } catch (Exception e) {
2740:                                 // CHECKSTYLE:ON
2741:                                 throw new IllegalArgumentException("Cannot compare " + o1 + " with " + o2, e);
2742:                         }
2743:
2744:                         return result;
2745:                 }
2746:         }
2747: }