From 0aab4688cf57ce2febdb0abbade5e31198d10bc0 Mon Sep 17 00:00:00 2001 From: till Date: Wed, 22 Nov 2023 10:37:15 +0100 Subject: [PATCH 01/10] Added auto completion in the EvalOCLDialog for Collection operations and class members and autocompletion for use-commands (but not integrated into shell) --- .../org/tzi/use/uml/ocl/expr/ParamInfo.java | 35 + .../org/tzi/use/uml/ocl/expr/ParamMethod.java | 21 + .../use/uml/ocl/expr/ParamNamesAndTypes.java | 47 + .../uml/ocl/expr/operations/OpGeneric.java | 68 +- .../operations/StandardOperationsBag.java | 576 +++---- .../StandardOperationsCollection.java | 1434 +++++++++-------- .../StandardOperationsOrderedSet.java | 904 ++++++----- .../StandardOperationsSequence.java | 897 ++++++----- .../operations/StandardOperationsSet.java | 705 ++++---- .../java/org/tzi/use/util/OperationType.java | 14 + .../use/autocompletion/AutoCompletion.java | 834 ++++++++++ .../autocompletion/AutoCompletionParser.java | 724 +++++++++ .../tzi/use/autocompletion/ParserResult.java | 53 + .../SuggestionForClassName.java | 78 + .../use/autocompletion/SuggestionResult.java | 12 + .../parserResultTypes/ResultTypeRoot.java | 8 + .../parserResultTypes/ocl/ResultTypeOCL.java | 10 + .../ocl/ResultTypeOCLAttributePrefix.java | 39 + .../ocl/ResultTypeOCLCollections.java | 8 + ...ResultTypeOCLCollectionsContainsColon.java | 35 + .../ocl/ResultTypeOCLCollectionsDefault.java | 30 + .../ResultTypeOCLCollectionsEndWithPipe.java | 30 + ...CLCollectionsEndsWithCommaAndIsForAll.java | 14 + ...llectionsEndsWithPipeAndContainsColon.java | 31 + .../ocl/ResultTypeOCLObjectPrefix.java | 30 + .../ocl/ResultTypeOCLObjects.java | 36 + .../useCommands/ResultTypeUseCheck.java | 13 + .../useCommands/ResultTypeUseCommands.java | 10 + .../useCommands/ResultTypeUseCreate.java | 9 + .../ResultTypeUseCreateAssociation.java | 33 + .../ResultTypeUseCreateDefault.java | 13 + .../useCommands/ResultTypeUseDelete.java | 8 + .../ResultTypeUseDeleteAssociation.java | 13 + .../ResultTypeUseDeleteObjects.java | 30 + .../useCommands/ResultTypeUseDestroy.java | 31 + .../useCommands/ResultTypeUseInfo.java | 9 + .../useCommands/ResultTypeUseInfoClass.java | 13 + .../useCommands/ResultTypeUseInfoDefault.java | 13 + .../useCommands/ResultTypeUseInsert.java | 9 + .../ResultTypeUseInsertAssociation.java | 13 + .../ResultTypeUseInsertObjects.java | 30 + .../useCommands/ResultTypeUseOpen.java | 30 + .../useCommands/ResultTypeUseOpenter.java | 9 + .../ResultTypeUseOpenterObject.java | 30 + .../ResultTypeUseOpenterOperation.java | 34 + .../useCommands/ResultTypeUseSet.java | 9 + .../useCommands/ResultTypeUseSetAttr.java | 30 + .../useCommands/ResultTypeUseSetObject.java | 13 + .../useCommands/ResultTypeUseStep.java | 13 + .../tzi/use/gui/main/CreateObjectDialog.java | 1 + .../org/tzi/use/gui/main/EvalOCLDialog.java | 143 ++ .../java/org/tzi/use/gui/main/MainWindow.java | 10 + .../util/input/AutoCompletionOperation.java | 20 + .../src/test/java/org/tzi/use/TestSystem.java | 173 ++ .../org/tzi/use/autocompletion/AllTests.java | 41 + .../AutoCompletionParserTest.java | 805 +++++++++ .../AutoCompletionSuggestionTest.java | 258 +++ 57 files changed, 6412 insertions(+), 2127 deletions(-) create mode 100644 use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamInfo.java create mode 100644 use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamMethod.java create mode 100644 use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamNamesAndTypes.java create mode 100644 use-core/src/main/java/org/tzi/use/util/OperationType.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletionParser.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/ParserResult.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionForClassName.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionResult.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ResultTypeRoot.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCL.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLAttributePrefix.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollections.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsContainsColon.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsDefault.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndWithPipe.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithCommaAndIsForAll.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithPipeAndContainsColon.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjectPrefix.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjects.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCheck.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCommands.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreate.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateAssociation.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateDefault.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDelete.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteAssociation.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteObjects.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDestroy.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfo.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoClass.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoDefault.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsert.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertAssociation.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertObjects.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpen.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenter.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterObject.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterOperation.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSet.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetAttr.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetObject.java create mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseStep.java create mode 100644 use-gui/src/main/java/org/tzi/use/util/input/AutoCompletionOperation.java create mode 100644 use-gui/src/test/java/org/tzi/use/TestSystem.java create mode 100644 use-gui/src/test/java/org/tzi/use/autocompletion/AllTests.java create mode 100644 use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionParserTest.java create mode 100644 use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java diff --git a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamInfo.java b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamInfo.java new file mode 100644 index 000000000..0957a5e7b --- /dev/null +++ b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamInfo.java @@ -0,0 +1,35 @@ +package org.tzi.use.uml.ocl.expr; + +import java.util.List; + +/** + * The ParamInfo class is responsible for managing parameter information of operations + *

+ * Each operation has its own ParamManager object. + * It consists of a {@link ParamInfo#paramMethodList list of functional interfaces} used in OpGeneric's matches method + * and a {@link ParamNamesAndTypes}. + */ +public class ParamInfo { + /** + * Parameter information containing types and names. + */ + public ParamNamesAndTypes paramNamesAndTypes; + + /** + * List of parameter methods. + */ + public List paramMethodList; + + /** + * Constructs a new ParamManager with the provided parameter methods, + * types, and names. + * + * @param paramMethods A list of ParamMethod objects. + * @param paramTypes A list of parameter types. + * @param paramNames A list of parameter names. + */ + public ParamInfo(List paramMethods, List paramTypes, List paramNames) { + this.paramNamesAndTypes = new ParamNamesAndTypes(paramTypes, paramNames); + this.paramMethodList = paramMethods; + } +} diff --git a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamMethod.java b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamMethod.java new file mode 100644 index 000000000..a739de22d --- /dev/null +++ b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamMethod.java @@ -0,0 +1,21 @@ +package org.tzi.use.uml.ocl.expr; + +import org.tzi.use.uml.ocl.type.Type; + +/** + * The ParamMethod interface is a functional interface. + */ +public interface ParamMethod { + /** + * The SAM implemented when a new operation is defined + *

+ * This method is called in the context of {@link org.tzi.use.uml.ocl.expr.operations.OpGeneric OpGeneric}'s matches method. + * + * @implSpec + * The implementation typically checks whether the parameter is a subtype of a certain type. + * + * @param param The parameter on which the method is executed. + * @return true if the method execution is successful; false otherwise. + */ + boolean method(Type param); +} diff --git a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamNamesAndTypes.java b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamNamesAndTypes.java new file mode 100644 index 000000000..c1744675a --- /dev/null +++ b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/ParamNamesAndTypes.java @@ -0,0 +1,47 @@ +package org.tzi.use.uml.ocl.expr; + +import java.util.List; + +/** + * The ParamNamesAndTypes class represents information about method parameters, including + * their types and names. + */ +public class ParamNamesAndTypes { + /** + * List of parameter names. + */ + List names; + + /** + * List of parameter types. + */ + List types; + + /** + * Constructs a new ParamInfo with the provided parameter types and names. + * + * @param types List of parameter types. + * @param names List of parameter names. + */ + public ParamNamesAndTypes(List types, List names) { + this.types = types; + this.names = names; + } + + /** + * Generates a formatted string representation of parameter types and names. + * + * @return A string representing parameter types and names. + */ + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 1; i < names.size(); i++) {//ignore first param since it is the collection itself + stringBuilder.append(types.get(i)).append(" ").append(names.get(i)); + if (i < names.size() - 1) { + stringBuilder.append(", "); + } + } + return stringBuilder.toString(); + } +} \ No newline at end of file diff --git a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/OpGeneric.java b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/OpGeneric.java index 823c7bb59..ec3b164a2 100644 --- a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/OpGeneric.java +++ b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/OpGeneric.java @@ -1,14 +1,16 @@ package org.tzi.use.uml.ocl.expr.operations; -import org.tzi.use.uml.ocl.expr.EvalContext; -import org.tzi.use.uml.ocl.expr.Expression; +import org.tzi.use.uml.ocl.expr.*; import org.tzi.use.uml.ocl.type.Type; import org.tzi.use.uml.ocl.type.Type.VoidHandling; import org.tzi.use.uml.ocl.value.Value; +import org.tzi.use.util.OperationType; import org.tzi.use.util.StringUtil; import com.google.common.collect.Multimap; +import java.util.stream.IntStream; + /** * OpGeneric is the base class of a large group of individual operations. Each * operation is implemented by its own class deriving from OpGeneric. New @@ -35,8 +37,64 @@ public abstract class OpGeneric { public static final int SPECIAL = 3; + /** + * Parameter information containing type checks, types and names. + */ + private ParamInfo paramInfo; + + /** + * Checks if the given array of parameters matches the expected method parameters. + *

+ * Uses the List of functions located in {@link this#paramInfo} + * + * @param params An array of Type objects representing the method parameters to be checked. + * @return true if the parameters match the expected method parameters, false otherwise. + */ + public boolean match(Type[] params) { + if (paramInfo.paramMethodList.size() != params.length) { + return false; + } + + return IntStream.range(0, params.length) + .allMatch(i -> paramInfo.paramMethodList.get(i).method(params[i])); + } + + public abstract Type matches(Type[] params); + public abstract String name(); + + /** + * Retrieves information about the operation type and returns an OperationType object. + * + * @return An OperationType object containing information about the operation. + */ + public final OperationType getOperationType() { + // Extracting class name information + String[] fullClassName = getClass().toString().split("\\."); + String[] className = fullClassName[fullClassName.length - 1].split("_"); + + // Creating OperationType object + OperationType operationType = new OperationType(); + + // Setting operationName and collectionType based on class name + if (className.length > 2) { // collections + operationType.operationName = className[2]; + operationType.collectionType = className[1]; + } else { // everything else + operationType.operationName = className[1]; + } + + // Setting param information if ParamManager is not null + if (paramInfo != null) { + operationType.param = paramInfo.paramNamesAndTypes.toString(); + } else { + operationType.param = ""; + } + + return operationType; + } + public boolean isBooleanOperation() { return false; } @@ -45,8 +103,6 @@ public boolean isBooleanOperation() { public abstract boolean isInfixOrPrefix(); - public abstract Type matches(Type params[]); - public String checkWarningUnrelatedTypes(Expression args[]) { return null; } public abstract Value eval(EvalContext ctx, Value args[], Type resultType); @@ -115,4 +171,8 @@ public static void registerOperation(OpGeneric op, Multimap o public static void registerOperation(String name, OpGeneric op, Multimap opmap) { opmap.put(name, op); } + + public void setParamInfo(ParamInfo paramInfo) { + this.paramInfo = paramInfo; + } } diff --git a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsBag.java b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsBag.java index 8b513c321..e5f81c9ca 100644 --- a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsBag.java +++ b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsBag.java @@ -2,10 +2,10 @@ import org.tzi.use.uml.ocl.expr.EvalContext; import org.tzi.use.uml.ocl.expr.Expression; +import org.tzi.use.uml.ocl.expr.ParamInfo; import org.tzi.use.uml.ocl.type.BagType; import org.tzi.use.uml.ocl.type.SetType; import org.tzi.use.uml.ocl.type.Type; -import org.tzi.use.uml.ocl.type.Type.VoidHandling; import org.tzi.use.uml.ocl.type.TypeFactory; import org.tzi.use.uml.ocl.value.BagValue; import org.tzi.use.uml.ocl.value.SetValue; @@ -15,21 +15,23 @@ import com.google.common.collect.Multimap; +import java.util.List; + public class StandardOperationsBag { - public static void registerTypeOperations(Multimap opmap) { - // operations on Bag - OpGeneric.registerOperation(new Op_bag_union(), opmap); - OpGeneric.registerOperation(new Op_bag_union_set(), opmap); - OpGeneric.registerOperation(new Op_bag_intersection(), opmap); - OpGeneric.registerOperation(new Op_bag_intersection_set(), opmap); - OpGeneric.registerOperation(new Op_bag_including(), opmap); - OpGeneric.registerOperation(new Op_bag_excluding(), opmap); - // the following three are special expressions: - // select - // reject - // collect - // count: inherited from Collection - } + public static void registerTypeOperations(Multimap opmap) { + // operations on Bag + OpGeneric.registerOperation(new Op_bag_union(), opmap); + OpGeneric.registerOperation(new Op_bag_union_set(), opmap); + OpGeneric.registerOperation(new Op_bag_intersection(), opmap); + OpGeneric.registerOperation(new Op_bag_intersection_set(), opmap); + OpGeneric.registerOperation(new Op_bag_including(), opmap); + OpGeneric.registerOperation(new Op_bag_excluding(), opmap); + // the following three are special expressions: + // select + // reject + // collect + // count: inherited from Collection + } } // -------------------------------------------------------- @@ -40,290 +42,322 @@ public static void registerTypeOperations(Multimap opmap) { /* union : Bag(T1) x Bag(T2) -> Bag(T1), with T2 <= T1 */ final class Op_bag_union extends OpGeneric { - public String name() { - return "union"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfBag() && - params[1].isTypeOfBag()) { - - BagType bag1 = (BagType) params[0]; - BagType bag2 = (BagType) params[1]; - - Type commonElementType = bag1.elemType().getLeastCommonSupertype( - bag2.elemType()); - - if (commonElementType != null) - return TypeFactory.mkBag(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - BagValue bag1 = (BagValue) args[0]; - BagValue bag2 = (BagValue) args[1]; - return bag1.union(resultType,bag2); - } + + public Op_bag_union() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfBag, Type::isTypeOfBag), List.of("Bag(T1)", "Bag(T2)"), List.of("self", "other"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + + BagType bag1 = (BagType) params[0]; + BagType bag2 = (BagType) params[1]; + + Type commonElementType = bag1.elemType().getLeastCommonSupertype( + bag2.elemType()); + + if (commonElementType != null) { + return TypeFactory.mkBag(commonElementType); + } + } + return null; + } + + + public String name() { + return "union"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + BagValue bag1 = (BagValue) args[0]; + BagValue bag2 = (BagValue) args[1]; + return bag1.union(resultType, bag2); + } } // -------------------------------------------------------- /* union : Bag(T1) x Set(T2) -> Bag(T1), with T2 <= T1 */ final class Op_bag_union_set extends OpGeneric { - public String name() { - return "union"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfBag() && - params[1].isTypeOfSet()) { - BagType bag = (BagType) params[0]; - SetType set = (SetType) params[1]; - - Type commonElementType = bag.elemType().getLeastCommonSupertype( - set.elemType()); - - if (commonElementType != null) - return TypeFactory.mkBag(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - BagValue bag = (BagValue) args[0]; - SetValue set = (SetValue) args[1]; - return bag.union(resultType, set.asBag()); - } + + public Op_bag_union_set() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfBag, Type::isTypeOfSet), List.of("Bag(T1)", "Set(T2)"), List.of("self", "other"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + BagType bag = (BagType) params[0]; + SetType set = (SetType) params[1]; + + Type commonElementType = bag.elemType().getLeastCommonSupertype( + set.elemType()); + + if (commonElementType != null) { + return TypeFactory.mkBag(commonElementType); + } + } + return null; + } + + + public String name() { + return "union"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + BagValue bag = (BagValue) args[0]; + SetValue set = (SetValue) args[1]; + return bag.union(resultType, set.asBag()); + } } // -------------------------------------------------------- /* intersection : Bag(T1) x Bag(T2) -> Bag(T1), with T2 <= T1 */ final class Op_bag_intersection extends OpGeneric { - public String name() { - return "intersection"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length != 2) return null; - - if (params[0].isTypeOfBag() && - params[1].isKindOfBag(VoidHandling.INCLUDE_VOID)) { - BagType bag1 = (BagType) params[0]; - - if (params[1].isTypeOfVoidType()) return bag1; - - BagType bag2 = (BagType) params[1]; - - Type commonElementType = bag1.elemType().getLeastCommonSupertype( - bag2.elemType()); - - if (commonElementType != null) - return TypeFactory.mkBag(commonElementType); - - } - - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - BagValue bag1 = (BagValue) args[0]; - //FIXME: Handle null-value - BagValue bag2 = (BagValue) args[1]; - return bag1.intersection(resultType, bag2); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - BagType bag1 = (BagType) args[0].type(); - BagType bag2 = (BagType) args[1].type(); - - Type elemType1 = bag1.elemType(); - Type elemType2 = bag2.elemType(); - - Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); - - if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " can never evaluate to more than an empty bag, " + StringUtil.NEWLINE + - "because the element types " + StringUtil.inQuotes(elemType1) + - " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; - } - - return null; - } + + public Op_bag_intersection() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfBag, Type::isTypeOfBag), List.of("Bag(T1)", "Bag(T2)"), List.of("self", "other"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + BagType bag1 = (BagType) params[0]; + + if (params[1].isTypeOfVoidType()) { + return bag1; + } + + BagType bag2 = (BagType) params[1]; + + Type commonElementType = bag1.elemType().getLeastCommonSupertype( + bag2.elemType()); + + if (commonElementType != null) { + return TypeFactory.mkBag(commonElementType); + } + + } + + return null; + } + + + public String name() { + return "intersection"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + BagValue bag1 = (BagValue) args[0]; + //FIXME: Handle null-value + BagValue bag2 = (BagValue) args[1]; + return bag1.intersection(resultType, bag2); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + BagType bag1 = (BagType) args[0].type(); + BagType bag2 = (BagType) args[1].type(); + + Type elemType1 = bag1.elemType(); + Type elemType2 = bag2.elemType(); + + Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); + + if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " can never evaluate to more than an empty bag, " + StringUtil.NEWLINE + + "because the element types " + StringUtil.inQuotes(elemType1) + + " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* intersection : Bag(T1) x Set(T2) -> Set(T1), with T2 <= T1 */ final class Op_bag_intersection_set extends OpGeneric { - public String name() { - return "intersection"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfBag() && - params[1].isTypeOfSet()) { - - BagType bag = (BagType) params[0]; - SetType set = (SetType) params[1]; - - Type commonElementType = bag.elemType().getLeastCommonSupertype( - set.elemType()); - - if (commonElementType != null) - return TypeFactory.mkSet(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - BagValue bag = (BagValue) args[0]; - SetValue set = (SetValue) args[1]; - return bag.asSet().intersection(resultType, set); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - BagType bag = (BagType) args[0].type(); - SetType set = (SetType) args[1].type(); - - Type elemType1 = bag.elemType(); - Type elemType2 = set.elemType(); - - Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); - - if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " can never evaluate to more then an empty set, " + StringUtil.NEWLINE + - "because the element types " + StringUtil.inQuotes(elemType1) + - " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; - } - - return null; - } + + public Op_bag_intersection_set() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfBag, Type::isTypeOfSet), List.of("Bag(T1)", "Set(T2)"), List.of("self", "other"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + + BagType bag = (BagType) params[0]; + SetType set = (SetType) params[1]; + + Type commonElementType = bag.elemType().getLeastCommonSupertype( + set.elemType()); + + if (commonElementType != null) { + return TypeFactory.mkSet(commonElementType); + } + } + return null; + } + + public String name() { + return "intersection"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + BagValue bag = (BagValue) args[0]; + SetValue set = (SetValue) args[1]; + return bag.asSet().intersection(resultType, set); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + BagType bag = (BagType) args[0].type(); + SetType set = (SetType) args[1].type(); + + Type elemType1 = bag.elemType(); + Type elemType2 = set.elemType(); + + Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); + + if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " can never evaluate to more then an empty set, " + StringUtil.NEWLINE + + "because the element types " + StringUtil.inQuotes(elemType1) + + " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* including : Bag(T1) x T2 -> Bag(T1), with T2 <= T1 */ final class Op_bag_including extends OpGeneric { - public String name() { - return "including"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isTypeOfBag()) { - BagType bag = (BagType) params[0]; - Type commonElementType = bag.elemType().getLeastCommonSupertype( - params[1]); - - if (commonElementType != null) - return TypeFactory.mkBag(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return UndefinedValue.instance; - BagValue bag = (BagValue) args[0]; - return bag.including(resultType, args[1]); - } + + public Op_bag_including() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfBag, param -> true), List.of("Bag(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + BagType bag = (BagType) params[0]; + Type commonElementType = bag.elemType().getLeastCommonSupertype( + params[1]); + + if (commonElementType != null) { + return TypeFactory.mkBag(commonElementType); + } + } + return null; + } + + public String name() { + return "including"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return UndefinedValue.instance; + BagValue bag = (BagValue) args[0]; + return bag.including(resultType, args[1]); + } } // -------------------------------------------------------- /* excluding : Bag(T1) x T2 -> Bag(T1), with T2 <= T1 */ final class Op_bag_excluding extends OpGeneric { - public String name() { - return "excluding"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isTypeOfBag()) { - BagType bag = (BagType) params[0]; - Type commonElementType = bag.elemType().getLeastCommonSupertype( - params[1]); - - if (commonElementType != null) - return TypeFactory.mkBag(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return UndefinedValue.instance; - BagValue bag = (BagValue) args[0]; - return bag.excluding(resultType, args[1]); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - BagType bag = (BagType) args[0].type(); - Type commonElementType = bag.elemType().getLeastCommonSupertype(args[1].type()); - - if (!(bag.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to the same bag, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(bag.elemType()) + - " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; - } - - return null; - } + + public Op_bag_excluding() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfBag, param -> true), List.of("Bag(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + BagType bag = (BagType) params[0]; + Type commonElementType = bag.elemType().getLeastCommonSupertype( + params[1]); + + if (commonElementType != null) { + return TypeFactory.mkBag(commonElementType); + } + } + return null; + } + + public String name() { + return "excluding"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return UndefinedValue.instance; + BagValue bag = (BagValue) args[0]; + return bag.excluding(resultType, args[1]); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + BagType bag = (BagType) args[0].type(); + Type commonElementType = bag.elemType().getLeastCommonSupertype(args[1].type()); + + if (!(bag.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to the same bag, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(bag.elemType()) + + " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; + } + + return null; + } } \ No newline at end of file diff --git a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsCollection.java b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsCollection.java index d44fc693d..974f20c6d 100644 --- a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsCollection.java +++ b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsCollection.java @@ -1,10 +1,6 @@ package org.tzi.use.uml.ocl.expr.operations; -import org.tzi.use.uml.ocl.expr.EvalContext; -import org.tzi.use.uml.ocl.expr.ExpInvalidException; -import org.tzi.use.uml.ocl.expr.ExpStdOp; -import org.tzi.use.uml.ocl.expr.Expression; -import org.tzi.use.uml.ocl.expr.ExpressionWithValue; +import org.tzi.use.uml.ocl.expr.*; import org.tzi.use.uml.ocl.type.CollectionType; import org.tzi.use.uml.ocl.type.TupleType; import org.tzi.use.uml.ocl.type.TupleType.Part; @@ -22,30 +18,32 @@ import com.google.common.collect.Multimap; +import java.util.List; + public class StandardOperationsCollection { - public static void registerTypeOperations(Multimap opmap) { - // operations on Collection - OpGeneric.registerOperation(new Op_collection_size(), opmap); - OpGeneric.registerOperation(new Op_collection_includes(), opmap); - OpGeneric.registerOperation(new Op_collection_excludes(), opmap); - OpGeneric.registerOperation(new Op_collection_count(), opmap); - OpGeneric.registerOperation(new Op_collection_includesAll(), opmap); - OpGeneric.registerOperation(new Op_collection_excludesAll(), opmap); - OpGeneric.registerOperation(new Op_collection_isEmpty(), opmap); - OpGeneric.registerOperation(new Op_collection_notEmpty(), opmap); - OpGeneric.registerOperation(new Op_collection_max(), opmap); - OpGeneric.registerOperation(new Op_collection_min(), opmap); - OpGeneric.registerOperation(new Op_collection_sum(), opmap); - OpGeneric.registerOperation(new Op_collection_product(), opmap); - OpGeneric.registerOperation(new Op_collection_asSet(), opmap); - OpGeneric.registerOperation(new Op_collection_asSequence(), opmap); - OpGeneric.registerOperation(new Op_collection_asOrderedSet(), opmap); - OpGeneric.registerOperation(new Op_collection_asBag(), opmap); - OpGeneric.registerOperation(new Op_collection_flatten(), opmap); - - OpGeneric.registerOperation(new Op_collection_single(), opmap); // USE specific - - } + public static void registerTypeOperations(Multimap opmap) { + // operations on Collection + OpGeneric.registerOperation(new Op_collection_size(), opmap); + OpGeneric.registerOperation(new Op_collection_includes(), opmap); + OpGeneric.registerOperation(new Op_collection_excludes(), opmap); + OpGeneric.registerOperation(new Op_collection_count(), opmap); + OpGeneric.registerOperation(new Op_collection_includesAll(), opmap); + OpGeneric.registerOperation(new Op_collection_excludesAll(), opmap); + OpGeneric.registerOperation(new Op_collection_isEmpty(), opmap); + OpGeneric.registerOperation(new Op_collection_notEmpty(), opmap); + OpGeneric.registerOperation(new Op_collection_max(), opmap); + OpGeneric.registerOperation(new Op_collection_min(), opmap); + OpGeneric.registerOperation(new Op_collection_sum(), opmap); + OpGeneric.registerOperation(new Op_collection_product(), opmap); + OpGeneric.registerOperation(new Op_collection_asSet(), opmap); + OpGeneric.registerOperation(new Op_collection_asSequence(), opmap); + OpGeneric.registerOperation(new Op_collection_asOrderedSet(), opmap); + OpGeneric.registerOperation(new Op_collection_asBag(), opmap); + OpGeneric.registerOperation(new Op_collection_flatten(), opmap); + + OpGeneric.registerOperation(new Op_collection_single(), opmap); // USE specific + + } } // -------------------------------------------------------- @@ -56,733 +54,825 @@ public static void registerTypeOperations(Multimap opmap) { /* size : Collection(T) -> Integer */ final class Op_collection_size extends OpGeneric { - public String name() { - return "size"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - return (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) ? TypeFactory - .mkInteger() : null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) return UndefinedValue.instance; - - CollectionValue coll = (CollectionValue) args[0]; - return IntegerValue.valueOf(coll.size()); - } + + public Op_collection_size() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + return (match(params)) ? TypeFactory.mkInteger() : null; + } + + public String name() { + return "size"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) return UndefinedValue.instance; + + CollectionValue coll = (CollectionValue) args[0]; + return IntegerValue.valueOf(coll.size()); + } } // -------------------------------------------------------- /* includes : Collection(T2) x T1 -> Boolean, with T2 <= T1 */ final class Op_collection_includes extends OpGeneric { - public String name() { - return "includes"; - } - - // may test for undefined being an element of the collection - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType coll = (CollectionType) params[0]; - if (params[1].getLeastCommonSupertype(coll.elemType()) != null) - return TypeFactory.mkBoolean(); - - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return BooleanValue.FALSE; - - CollectionValue coll = (CollectionValue) args[0]; - boolean res = coll.includes(args[1]); - return BooleanValue.get(res); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col = (CollectionType) args[0].type(); - Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); - - if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to false, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(col.elemType()) + - " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; - } - - return null; - } + + public Op_collection_includes() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID), param -> true), List.of("Collection(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType coll = (CollectionType) params[0]; + if (params[1].getLeastCommonSupertype(coll.elemType()) != null) { + return TypeFactory.mkBoolean(); + } + } + return null; + } + + public String name() { + return "includes"; + } + + // may test for undefined being an element of the collection + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return BooleanValue.FALSE; + + CollectionValue coll = (CollectionValue) args[0]; + boolean res = coll.includes(args[1]); + return BooleanValue.get(res); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col = (CollectionType) args[0].type(); + Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); + + if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to false, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(col.elemType()) + + " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* excludes : Collection(T2) x T1 -> Boolean, with T2 <= T1 */ final class Op_collection_excludes extends OpGeneric { - public String name() { - return "excludes"; - } - - // may test for undefined being an element of the collection - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType coll = (CollectionType) params[0]; - if (params[1].getLeastCommonSupertype(coll.elemType()) != null) - return TypeFactory.mkBoolean(); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return BooleanValue.FALSE; - CollectionValue coll = (CollectionValue) args[0]; - boolean res = !coll.includes(args[1]); - return BooleanValue.get(res); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col = (CollectionType) args[0].type(); - Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); - - if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to true, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(col.elemType()) + - " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; - } - - return null; - } + + public Op_collection_excludes() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID), param -> true), List.of("Collection(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType coll = (CollectionType) params[0]; + if (params[1].getLeastCommonSupertype(coll.elemType()) != null) { + return TypeFactory.mkBoolean(); + } + } + return null; + } + + public String name() { + return "excludes"; + } + + // may test for undefined being an element of the collection + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return BooleanValue.FALSE; + CollectionValue coll = (CollectionValue) args[0]; + boolean res = !coll.includes(args[1]); + return BooleanValue.get(res); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col = (CollectionType) args[0].type(); + Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); + + if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to true, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(col.elemType()) + + " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* count : Collection(T) x T -> Integer */ final class Op_collection_count extends OpGeneric { - public String name() { - return "count"; - } - - // may count occurrences of undefined in the collection - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType coll = (CollectionType) params[0]; - if (params[1].getLeastCommonSupertype(coll.elemType()) != null) - return TypeFactory.mkInteger(); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return IntegerValue.valueOf(0); - CollectionValue coll = (CollectionValue) args[0]; - int res = coll.count(args[1]); - return IntegerValue.valueOf(res); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col = (CollectionType) args[0].type(); - Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); - - if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to true, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(col.elemType()) + - " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; - } - - return null; - } + + public Op_collection_count() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID), param -> true), List.of("Collection(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType coll = (CollectionType) params[0]; + if (params[1].getLeastCommonSupertype(coll.elemType()) != null) { + return TypeFactory.mkInteger(); + } + } + return null; + } + + + public String name() { + return "count"; + } + + // may count occurrences of undefined in the collection + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return IntegerValue.valueOf(0); + CollectionValue coll = (CollectionValue) args[0]; + int res = coll.count(args[1]); + return IntegerValue.valueOf(res); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col = (CollectionType) args[0].type(); + Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); + + if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to true, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(col.elemType()) + + " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* includesAll : Collection(T) x Collection(T) -> Boolean */ final class Op_collection_includesAll extends OpGeneric { - public String name() { - return "includesAll"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID) && - params[1].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType coll1 = (CollectionType) params[0]; - CollectionType coll2 = (CollectionType) params[1]; - - if (coll2.getLeastCommonSupertype(coll1) != null) - return TypeFactory.mkBoolean(); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue coll1 = (CollectionValue) args[0]; - CollectionValue coll2 = (CollectionValue) args[1]; - boolean res = coll1.includesAll(coll2); - return BooleanValue.get(res); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col1 = (CollectionType) args[0].type(); - CollectionType col2 = (CollectionType) args[1].type(); - - Type elemType1 = col1.elemType(); - Type elemType2 = col2.elemType(); - - Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); - - if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to false, " + StringUtil.NEWLINE + - "because the element types " + StringUtil.inQuotes(elemType1) + - " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; - } - - return null; - } + + public Op_collection_includesAll() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID), param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)", "Collection(T2)"), List.of("self", "other"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType coll1 = (CollectionType) params[0]; + CollectionType coll2 = (CollectionType) params[1]; + + if (coll2.getLeastCommonSupertype(coll1) != null) { + return TypeFactory.mkBoolean(); + } + } + return null; + } + + public String name() { + return "includesAll"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue coll1 = (CollectionValue) args[0]; + CollectionValue coll2 = (CollectionValue) args[1]; + boolean res = coll1.includesAll(coll2); + return BooleanValue.get(res); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col1 = (CollectionType) args[0].type(); + CollectionType col2 = (CollectionType) args[1].type(); + + Type elemType1 = col1.elemType(); + Type elemType2 = col2.elemType(); + + Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); + + if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to false, " + StringUtil.NEWLINE + + "because the element types " + StringUtil.inQuotes(elemType1) + + " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* excludesAll : Collection(T) x Collection(T) -> Boolean */ final class Op_collection_excludesAll extends OpGeneric { - public String name() { - return "excludesAll"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID) && - params[1].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - - CollectionType coll1 = (CollectionType) params[0]; - CollectionType coll2 = (CollectionType) params[1]; - - if (coll2.getLeastCommonSupertype(coll1) != null) - return TypeFactory.mkBoolean(); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue coll1 = (CollectionValue) args[0]; - CollectionValue coll2 = (CollectionValue) args[1]; - boolean res = coll1.excludesAll(coll2); - return BooleanValue.get(res); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col1 = (CollectionType) args[0].type(); - CollectionType col2 = (CollectionType) args[1].type(); - - Type elemType1 = col1.elemType(); - Type elemType2 = col2.elemType(); - - Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); - - if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to true, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(elemType1) + - " and the parameter type " + StringUtil.inQuotes(elemType2) + " are unrelated."; - } - - return null; - } + + public Op_collection_excludesAll() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID), param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)", "Collection(T2)"), List.of("self", "other"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType coll1 = (CollectionType) params[0]; + CollectionType coll2 = (CollectionType) params[1]; + + if (coll2.getLeastCommonSupertype(coll1) != null) { + return TypeFactory.mkBoolean(); + } + } + return null; + } + + public String name() { + return "excludesAll"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue coll1 = (CollectionValue) args[0]; + CollectionValue coll2 = (CollectionValue) args[1]; + boolean res = coll1.excludesAll(coll2); + return BooleanValue.get(res); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col1 = (CollectionType) args[0].type(); + CollectionType col2 = (CollectionType) args[1].type(); + + Type elemType1 = col1.elemType(); + Type elemType2 = col2.elemType(); + + Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); + + if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to true, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(elemType1) + + " and the parameter type " + StringUtil.inQuotes(elemType2) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* isEmpty : Collection(T) -> Boolean */ final class Op_collection_isEmpty extends OpGeneric { - public String name() { - return "isEmpty"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - return (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) ? TypeFactory - .mkBoolean() : null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) return UndefinedValue.instance; - - CollectionValue coll = (CollectionValue) args[0]; - return BooleanValue.get(coll.isEmpty()); - } + + public Op_collection_isEmpty() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + return (match(params)) ? TypeFactory.mkBoolean() : null; + } + + public String name() { + return "isEmpty"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) return UndefinedValue.instance; + + CollectionValue coll = (CollectionValue) args[0]; + return BooleanValue.get(coll.isEmpty()); + } } // -------------------------------------------------------- /* notEmpty : Collection(T) -> Boolean */ final class Op_collection_notEmpty extends OpGeneric { - public String name() { - return "notEmpty"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - return (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) ? TypeFactory - .mkBoolean() : null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) return UndefinedValue.instance; - CollectionValue coll = (CollectionValue) args[0]; - return BooleanValue.get(!coll.isEmpty()); - } + + public Op_collection_notEmpty() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + return (match(params)) ? TypeFactory.mkBoolean() : null; + } + + public String name() { + return "notEmpty"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) return UndefinedValue.instance; + CollectionValue coll = (CollectionValue) args[0]; + return BooleanValue.get(!coll.isEmpty()); + } } // -------------------------------------------------------- /* sum : Collection(T) -> T */ final class Op_collection_sum extends OpGeneric { - public String name() { - return "sum"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType c = (CollectionType) params[0]; - if (c.elemType().isTypeOfInteger()) - return TypeFactory.mkInteger(); - else if (c.elemType().isTypeOfReal()) - return TypeFactory.mkReal(); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue coll = (CollectionValue) args[0]; - boolean isIntegerCollection = coll.elemType().isTypeOfInteger(); - - if (isIntegerCollection) { - int isum = 0; - for (Value v : coll) { - if (v.isUndefined()) - return UndefinedValue.instance; - isum += ((IntegerValue) v).value(); - } - return IntegerValue.valueOf(isum); - } else { - double rsum = 0.0; - - for (Value v : coll) { - if (v.isUndefined()) - return UndefinedValue.instance; - if (v.isInteger()) - rsum += ((IntegerValue) v).value(); - else - rsum += ((RealValue) v).value(); - } - return new RealValue(rsum); - } - } + + public Op_collection_sum() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType c = (CollectionType) params[0]; + if (c.elemType().isTypeOfInteger()) { + return TypeFactory.mkInteger(); + } else if (c.elemType().isTypeOfReal()) { + return TypeFactory.mkReal(); + } + } + return null; + } + + public String name() { + return "sum"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue coll = (CollectionValue) args[0]; + boolean isIntegerCollection = coll.elemType().isTypeOfInteger(); + + if (isIntegerCollection) { + int isum = 0; + for (Value v : coll) { + if (v.isUndefined()) + return UndefinedValue.instance; + isum += ((IntegerValue) v).value(); + } + return IntegerValue.valueOf(isum); + } else { + double rsum = 0.0; + + for (Value v : coll) { + if (v.isUndefined()) + return UndefinedValue.instance; + if (v.isInteger()) + rsum += ((IntegerValue) v).value(); + else + rsum += ((RealValue) v).value(); + } + return new RealValue(rsum); + } + } } /* product : Collection(T) x Collection(T2) -> Set(Tuple(first: T, second : T2)) */ final class Op_collection_product extends OpGeneric { - public String name() { - return "product"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID) - && params[1].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType c = (CollectionType) params[0]; - CollectionType c2 = (CollectionType) params[1]; - - Part[] parts = new Part[2]; - parts[0] = new Part(0, "first", c.elemType()); - parts[1] = new Part(1, "second", c2.elemType()); - - TupleType tupleType = TypeFactory.mkTuple(parts); - return TypeFactory.mkSet(tupleType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined() || args[1].isUndefined()) - return UndefinedValue.instance; - - CollectionValue col1 = (CollectionValue) args[0]; - CollectionValue col2 = (CollectionValue) args[1]; - - return col1.product(col2); - } + + public Op_collection_product() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID), param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)", "Collection(T2)"), List.of("self", "other"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType c = (CollectionType) params[0]; + CollectionType c2 = (CollectionType) params[1]; + + Part[] parts = new Part[2]; + parts[0] = new Part(0, "first", c.elemType()); + parts[1] = new Part(1, "second", c2.elemType()); + + TupleType tupleType = TypeFactory.mkTuple(parts); + return TypeFactory.mkSet(tupleType); + } + return null; + } + + + public String name() { + return "product"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined() || args[1].isUndefined()) + return UndefinedValue.instance; + + CollectionValue col1 = (CollectionValue) args[0]; + CollectionValue col2 = (CollectionValue) args[1]; + + return col1.product(col2); + } } // -------------------------------------------------------- /* flatten : C1(C2(T)) -> C1(T) */ final class Op_collection_flatten extends OpGeneric { - public String name() { - return "flatten"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType c1 = (CollectionType) params[0]; - if (c1.elemType().isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType c2 = (CollectionType) c1.elemType(); - return c1.createCollectionType(c2.elemType()); - } else { - return c1; - } - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue coll = (CollectionValue) args[0]; - return coll.flatten(resultType); - } + + public Op_collection_flatten() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(Collection(T1))"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType c1 = (CollectionType) params[0]; + if (c1.elemType().isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { + CollectionType c2 = (CollectionType) c1.elemType(); + return c1.createCollectionType(c2.elemType()); + } else { + return c1; + } + } + return null; + } + + public String name() { + return "flatten"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue coll = (CollectionValue) args[0]; + return coll.flatten(resultType); + } } /* asBag : Collection(T) -> Bag(T) */ final class Op_collection_asBag extends OpGeneric { - public String name() { - return "asBag"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType col = (CollectionType) params[0]; - return TypeFactory.mkBag(col.elemType()); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue col = (CollectionValue) args[0]; - return col.asBag(); - } + + public Op_collection_asBag() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType col = (CollectionType) params[0]; + return TypeFactory.mkBag(col.elemType()); + } + return null; + } + + public String name() { + return "asBag"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue col = (CollectionValue) args[0]; + return col.asBag(); + } } /* asSet : Collection(T) -> Set(T) */ final class Op_collection_asSet extends OpGeneric { - public String name() { - return "asSet"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType col = (CollectionType) params[0]; - return TypeFactory.mkSet(col.elemType()); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue col = (CollectionValue) args[0]; - return col.asSet(); - } + + public Op_collection_asSet() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType col = (CollectionType) params[0]; + return TypeFactory.mkSet(col.elemType()); + } + return null; + } + + public String name() { + return "asSet"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue col = (CollectionValue) args[0]; + return col.asSet(); + } } /* asSequence : Collection(T) -> Sequence(T) */ final class Op_collection_asSequence extends OpGeneric { - public String name() { - return "asSequence"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType col = (CollectionType) params[0]; - return TypeFactory.mkSequence(col.elemType()); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue col = (CollectionValue) args[0]; - return col.asSequence(); - } + + public Op_collection_asSequence() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType col = (CollectionType) params[0]; + return TypeFactory.mkSequence(col.elemType()); + } + return null; + } + + public String name() { + return "asSequence"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue col = (CollectionValue) args[0]; + return col.asSequence(); + } } /* asOrderedSet : Collection(T) -> OrderedSet(T) */ final class Op_collection_asOrderedSet extends OpGeneric { - public String name() { - return "asOrderedSet"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType col = (CollectionType) params[0]; - return TypeFactory.mkOrderedSet(col.elemType()); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue col = (CollectionValue) args[0]; - return col.asOrderedSet(); - } + + public Op_collection_asOrderedSet() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType col = (CollectionType) params[0]; + return TypeFactory.mkOrderedSet(col.elemType()); + } + return null; + } + + public String name() { + return "asOrderedSet"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue col = (CollectionValue) args[0]; + return col.asOrderedSet(); + } } /* max : Collection(T) -> T */ final class Op_collection_max extends OpGeneric { - public String name() { - return "max"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType t = (CollectionType)params[0]; - - // Check if basic type supports max operation - if (ExpStdOp.exists("max", new Type[]{t.elemType(), t.elemType()})) - return t.elemType(); - } - - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue col = (CollectionValue) args[0]; - - Value max = UndefinedValue.instance; - boolean first = true; - - for (Value v : col) { - if (first) { - max = v; - first = false; - } else { - try { - ExpStdOp op = ExpStdOp.create("max", - new Expression[]{new ExpressionWithValue(max), new ExpressionWithValue(v)}); - max = op.eval(ctx); - } catch (ExpInvalidException e) { - Log.error(e); - return UndefinedValue.instance; - } - } - } - - return max; - } + + public Op_collection_max() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType t = (CollectionType) params[0]; + + // Check if basic type supports max operation + if (ExpStdOp.exists("max", new Type[]{t.elemType(), t.elemType()})) { + return t.elemType(); + } + } + + return null; + } + + public String name() { + return "max"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue col = (CollectionValue) args[0]; + + Value max = UndefinedValue.instance; + boolean first = true; + + for (Value v : col) { + if (first) { + max = v; + first = false; + } else { + try { + ExpStdOp op = ExpStdOp.create("max", + new Expression[]{new ExpressionWithValue(max), new ExpressionWithValue(v)}); + max = op.eval(ctx); + } catch (ExpInvalidException e) { + Log.error(e); + return UndefinedValue.instance; + } + } + } + + return max; + } } /* min : Collection(T) -> T */ final class Op_collection_min extends OpGeneric { - public String name() { - return "min"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType t = (CollectionType)params[0]; - - // Check if basic type supports min operation - if (ExpStdOp.exists("min", new Type[]{t.elemType(), t.elemType()})) - return t.elemType(); - } - - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue col = (CollectionValue) args[0]; - - Value max = UndefinedValue.instance; - boolean first = true; - - for (Value v : col) { - if (first) { - max = v; - first = false; - } else { - try { - ExpStdOp op = ExpStdOp.create("min", - new Expression[]{new ExpressionWithValue(max), new ExpressionWithValue(v)}); - max = op.eval(ctx); - } catch (ExpInvalidException e) { - Log.error(e); - return UndefinedValue.instance; - } - } - } - - return max; - } + + public Op_collection_min() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType t = (CollectionType) params[0]; + + // Check if basic type supports min operation + if (ExpStdOp.exists("min", new Type[]{t.elemType(), t.elemType()})) { + return t.elemType(); + } + } + + return null; + } + + public String name() { + return "min"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue col = (CollectionValue) args[0]; + + Value max = UndefinedValue.instance; + boolean first = true; + + for (Value v : col) { + if (first) { + max = v; + first = false; + } else { + try { + ExpStdOp op = ExpStdOp.create("min", + new Expression[]{new ExpressionWithValue(max), new ExpressionWithValue(v)}); + max = op.eval(ctx); + } catch (ExpInvalidException e) { + Log.error(e); + return UndefinedValue.instance; + } + } + } + + return max; + } } + /* single : Collection(T) -> T */ final class Op_collection_single extends OpGeneric { - public String name() { - return "single"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isKindOfCollection(VoidHandling.EXCLUDE_VOID)) { - CollectionType t = (CollectionType)params[0]; - return t.elemType(); - } - - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - CollectionValue col = (CollectionValue) args[0]; - - Value res = UndefinedValue.instance; - - if (col.size() == 1) { - res = col.collection().iterator().next(); - } - - return res; - } + + public Op_collection_single() { + setParamInfo(new ParamInfo(List.of(param -> param.isKindOfCollection(VoidHandling.EXCLUDE_VOID)), List.of("Collection(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + CollectionType t = (CollectionType) params[0]; + return t.elemType(); + } + return null; + } + + + public String name() { + return "single"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + CollectionValue col = (CollectionValue) args[0]; + + Value res = UndefinedValue.instance; + + if (col.size() == 1) { + res = col.collection().iterator().next(); + } + + return res; + } } \ No newline at end of file diff --git a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsOrderedSet.java b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsOrderedSet.java index bc4bbf682..d2774a9c3 100644 --- a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsOrderedSet.java +++ b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsOrderedSet.java @@ -2,9 +2,11 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.List; import org.tzi.use.uml.ocl.expr.EvalContext; import org.tzi.use.uml.ocl.expr.Expression; +import org.tzi.use.uml.ocl.expr.ParamInfo; import org.tzi.use.uml.ocl.type.CollectionType; import org.tzi.use.uml.ocl.type.OrderedSetType; import org.tzi.use.uml.ocl.type.Type; @@ -18,23 +20,23 @@ import com.google.common.collect.Multimap; public class StandardOperationsOrderedSet { - public static void registerTypeOperations(Multimap opmap) { - // operations on OrderedSet - OpGeneric.registerOperation(new Op_orderedSet_append(), opmap); - OpGeneric.registerOperation(new Op_orderedSet_prepend(), opmap); - OpGeneric.registerOperation(new Op_orderedSet_insertAt(), opmap); - OpGeneric.registerOperation(new Op_orderedSet_subOrderedSet(), opmap); - OpGeneric.registerOperation(new Op_orderedSet_at(), opmap); - OpGeneric.registerOperation(new Op_orderedSet_indexOf(), opmap); - OpGeneric.registerOperation(new Op_orderedSet_first(), opmap); - OpGeneric.registerOperation(new Op_orderedSet_last(), opmap); - OpGeneric.registerOperation(new Op_orderedSet_reverse(), opmap); - - // Not mentioned in OCL 2.2 specification - OpGeneric.registerOperation(new Op_orderedSet_union(), opmap); - OpGeneric.registerOperation(new Op_orderedSet_including(), opmap); - OpGeneric.registerOperation(new Op_orderedSet_excluding(), opmap); - } + public static void registerTypeOperations(Multimap opmap) { + // operations on OrderedSet + OpGeneric.registerOperation(new Op_orderedSet_append(), opmap); + OpGeneric.registerOperation(new Op_orderedSet_prepend(), opmap); + OpGeneric.registerOperation(new Op_orderedSet_insertAt(), opmap); + OpGeneric.registerOperation(new Op_orderedSet_subOrderedSet(), opmap); + OpGeneric.registerOperation(new Op_orderedSet_at(), opmap); + OpGeneric.registerOperation(new Op_orderedSet_indexOf(), opmap); + OpGeneric.registerOperation(new Op_orderedSet_first(), opmap); + OpGeneric.registerOperation(new Op_orderedSet_last(), opmap); + OpGeneric.registerOperation(new Op_orderedSet_reverse(), opmap); + + // Not mentioned in OCL 2.2 specification + OpGeneric.registerOperation(new Op_orderedSet_union(), opmap); + OpGeneric.registerOperation(new Op_orderedSet_including(), opmap); + OpGeneric.registerOperation(new Op_orderedSet_excluding(), opmap); + } } // -------------------------------------------------------- @@ -45,470 +47,530 @@ public static void registerTypeOperations(Multimap opmap) { /* union : OrderedSet(T1) x OrderedSet(T2) -> OrderedSet(T1), with T2 <= T1 */ final class Op_orderedSet_union extends OpGeneric { - public String name() { - return "union"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isTypeOfOrderedSet() - && params[1].isTypeOfOrderedSet()) { - OrderedSetType oset1 = (OrderedSetType) params[0]; - OrderedSetType oset2 = (OrderedSetType) params[1]; - - Type commonElementType = oset1.elemType().getLeastCommonSupertype( - oset2.elemType()); - - if (commonElementType != null) - return TypeFactory.mkOrderedSet(commonElementType); - - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - OrderedSetValue oset1 = (OrderedSetValue) args[0]; - OrderedSetValue oset2 = (OrderedSetValue) args[1]; - return oset1.union(resultType, oset2); - } + + public Op_orderedSet_union() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet, Type::isTypeOfOrderedSet), List.of("OrderedSet(T1)", "OrderedSet(T2)"), List.of("self", "other"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + OrderedSetType oset1 = (OrderedSetType) params[0]; + OrderedSetType oset2 = (OrderedSetType) params[1]; + + Type commonElementType = oset1.elemType().getLeastCommonSupertype( + oset2.elemType()); + + if (commonElementType != null) { + return TypeFactory.mkOrderedSet(commonElementType); + } + } + return null; + } + + public String name() { + return "union"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + OrderedSetValue oset1 = (OrderedSetValue) args[0]; + OrderedSetValue oset2 = (OrderedSetValue) args[1]; + return oset1.union(resultType, oset2); + } } // -------------------------------------------------------- /* append : OrderedSet(T) x T -> OrderedSet(T) */ final class Op_orderedSet_append extends OpGeneric { - public String name() { - return "append"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isTypeOfOrderedSet()) { - OrderedSetType osetType = (OrderedSetType) params[0]; - - Type commonElementType = osetType.elemType() - .getLeastCommonSupertype(params[1]); - - if (commonElementType != null) - return TypeFactory.mkOrderedSet(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - OrderedSetValue oset = (OrderedSetValue) args[0]; - return oset.append(resultType, args[1]); - } + + public Op_orderedSet_append() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet, param -> true), List.of("OrderedSet(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + OrderedSetType osetType = (OrderedSetType) params[0]; + + Type commonElementType = osetType.elemType() + .getLeastCommonSupertype(params[1]); + + if (commonElementType != null) { + return TypeFactory.mkOrderedSet(commonElementType); + } + } + return null; + } + + public String name() { + return "append"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + OrderedSetValue oset = (OrderedSetValue) args[0]; + return oset.append(resultType, args[1]); + } } // -------------------------------------------------------- /* prepend : OrderedSet(T) x T -> OrderedSet(T) */ final class Op_orderedSet_prepend extends OpGeneric { - public String name() { - return "prepend"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isTypeOfOrderedSet()) { - OrderedSetType osetType = (OrderedSetType) params[0]; - - Type commonElementType = osetType.elemType() - .getLeastCommonSupertype(params[1]); - - if (commonElementType != null) - return TypeFactory.mkOrderedSet(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - OrderedSetValue oset = (OrderedSetValue) args[0]; - return oset.prepend(resultType, args[1]); - } + + public Op_orderedSet_prepend() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet, param -> true), List.of("OrderedSet(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + OrderedSetType osetType = (OrderedSetType) params[0]; + + Type commonElementType = osetType.elemType() + .getLeastCommonSupertype(params[1]); + + if (commonElementType != null) { + return TypeFactory.mkOrderedSet(commonElementType); + } + } + return null; + } + + public String name() { + return "prepend"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + OrderedSetValue oset = (OrderedSetValue) args[0]; + return oset.prepend(resultType, args[1]); + } } // -------------------------------------------------------- /* insertAt : OrderedSet(T) x Integer x T -> OrderedSet(T) */ final class Op_orderedSet_insertAt extends OpGeneric { - public String name() { - return "insertAt"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 3 && params[0].isTypeOfOrderedSet() - && params[1].isTypeOfInteger()) { - OrderedSetType osetType = (OrderedSetType) params[0]; - - Type commonElementType = osetType.elemType() - .getLeastCommonSupertype(params[2]); - - if (commonElementType != null) - return TypeFactory.mkOrderedSet(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - OrderedSetValue oset = (OrderedSetValue) args[0]; - OrderedSetValue res = oset.insertAt(resultType, (IntegerValue) args[1], args[2]); - - if (res == null) - return UndefinedValue.instance; - else - return res; - } + + public Op_orderedSet_insertAt() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet, Type::isTypeOfInteger, param -> true), List.of("OrderedSet(T1)", "Integer", "T2"), List.of("self", "index", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + OrderedSetType osetType = (OrderedSetType) params[0]; + + Type commonElementType = osetType.elemType() + .getLeastCommonSupertype(params[2]); + + if (commonElementType != null) { + return TypeFactory.mkOrderedSet(commonElementType); + } + } + return null; + } + + public String name() { + return "insertAt"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + OrderedSetValue oset = (OrderedSetValue) args[0]; + OrderedSetValue res = oset.insertAt(resultType, (IntegerValue) args[1], args[2]); + + if (res == null) + return UndefinedValue.instance; + else + return res; + } } // -------------------------------------------------------- /* subOrderedSet : OrderedSet(T) x Integer x Integer -> OrderedSet(T) */ final class Op_orderedSet_subOrderedSet extends OpGeneric { - public String name() { - return "subOrderedSet"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - return (params.length == 3 && params[0].isTypeOfOrderedSet() - && params[1].isTypeOfInteger() && params[2].isTypeOfInteger()) ? params[0] - : null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - OrderedSetValue seq = (OrderedSetValue) args[0]; - int lower = ((IntegerValue) args[1]).value(); - int upper = ((IntegerValue) args[2]).value(); - if (lower > upper) - return UndefinedValue.instance; - - Value res = null; - try { - res = seq.subOrderedSet(resultType, lower - 1, upper); - } catch (IndexOutOfBoundsException e) { - res = UndefinedValue.instance; - } - return res; - } + + public Op_orderedSet_subOrderedSet() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet, Type::isTypeOfInteger, Type::isTypeOfInteger), List.of("OrderedSet(T1)", "Integer", "Integer"), List.of("self", "indexStart", "IndexEnd"))); + } + + public Type matches(Type params[]) { + return (match(params)) ? params[0] : null; + } + + + public String name() { + return "subOrderedSet"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + OrderedSetValue seq = (OrderedSetValue) args[0]; + int lower = ((IntegerValue) args[1]).value(); + int upper = ((IntegerValue) args[2]).value(); + if (lower > upper) + return UndefinedValue.instance; + + Value res = null; + try { + res = seq.subOrderedSet(resultType, lower - 1, upper); + } catch (IndexOutOfBoundsException e) { + res = UndefinedValue.instance; + } + return res; + } } // -------------------------------------------------------- /* at : OrderedSet(T) x Integer -> T */ final class Op_orderedSet_at extends OpGeneric { - public String name() { - return "at"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isTypeOfOrderedSet() - && params[1].isTypeOfInteger()) { - OrderedSetType oset = (OrderedSetType) params[0]; - return oset.elemType(); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - OrderedSetValue oset = (OrderedSetValue) args[0]; - IntegerValue n = (IntegerValue) args[1]; - Value res = null; - try { - res = oset.get(n.value() - 1); - } catch (IndexOutOfBoundsException e) { - res = UndefinedValue.instance; - } - return res; - } + + public Op_orderedSet_at() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet, Type::isTypeOfInteger), List.of("OrderedSet(T1)", "Integer"), List.of("self", "index"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + OrderedSetType oset = (OrderedSetType) params[0]; + return oset.elemType(); + } + return null; + } + + public String name() { + return "at"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + OrderedSetValue oset = (OrderedSetValue) args[0]; + IntegerValue n = (IntegerValue) args[1]; + Value res = null; + try { + res = oset.get(n.value() - 1); + } catch (IndexOutOfBoundsException e) { + res = UndefinedValue.instance; + } + return res; + } } // -------------------------------------------------------- /* first : OrderedSet(T) -> T */ final class Op_orderedSet_first extends OpGeneric { - public String name() { - return "first"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isTypeOfOrderedSet()) { - OrderedSetType oset = (OrderedSetType) params[0]; - return oset.elemType(); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - OrderedSetValue oset = (OrderedSetValue) args[0]; - if (oset.isEmpty()) - return UndefinedValue.instance; - return oset.get(0); - } + + public Op_orderedSet_first() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet), List.of("OrderedSet(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + OrderedSetType oset = (OrderedSetType) params[0]; + return oset.elemType(); + } + return null; + } + + public String name() { + return "first"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + OrderedSetValue oset = (OrderedSetValue) args[0]; + if (oset.isEmpty()) + return UndefinedValue.instance; + return oset.get(0); + } } // -------------------------------------------------------- /* last : OrderedSet(T) -> T */ final class Op_orderedSet_last extends OpGeneric { - public String name() { - return "last"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isTypeOfOrderedSet()) { - OrderedSetType oset = (OrderedSetType) params[0]; - return oset.elemType(); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - OrderedSetValue oset = (OrderedSetValue) args[0]; - if (oset.isEmpty()) - return UndefinedValue.instance; - return oset.get(oset.size() - 1); - } + + public Op_orderedSet_last() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet), List.of("OrderedSet(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + OrderedSetType oset = (OrderedSetType) params[0]; + return oset.elemType(); + } + return null; + } + + public String name() { + return "last"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + OrderedSetValue oset = (OrderedSetValue) args[0]; + if (oset.isEmpty()) + return UndefinedValue.instance; + return oset.get(oset.size() - 1); + } } // -------------------------------------------------------- /* including : OrderedSet(T) x T -> OrderedSet(T) */ final class Op_orderedSet_including extends OpGeneric { - public String name() { - return "including"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isTypeOfOrderedSet()) { - OrderedSetType osetType = (OrderedSetType) params[0]; - - Type commonElementType = osetType.elemType() - .getLeastCommonSupertype(params[1]); - - if (commonElementType != null) - return TypeFactory.mkOrderedSet(commonElementType); - - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return UndefinedValue.instance; - OrderedSetValue oset = (OrderedSetValue) args[0]; - return oset.append(resultType, args[1]); - } + + public Op_orderedSet_including() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet, param -> true), List.of("OrderedSet(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + OrderedSetType osetType = (OrderedSetType) params[0]; + + Type commonElementType = osetType.elemType() + .getLeastCommonSupertype(params[1]); + + if (commonElementType != null) { + return TypeFactory.mkOrderedSet(commonElementType); + } + } + return null; + } + + public String name() { + return "including"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return UndefinedValue.instance; + OrderedSetValue oset = (OrderedSetValue) args[0]; + return oset.append(resultType, args[1]); + } } // -------------------------------------------------------- /* excluding : OrderedSet(T) x T -> OrderedSet(T) */ final class Op_orderedSet_excluding extends OpGeneric { - public String name() { - return "excluding"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isTypeOfOrderedSet()) { - OrderedSetType osetType = (OrderedSetType) params[0]; - - Type commonElementType = osetType.elemType() - .getLeastCommonSupertype(params[1]); - - if (commonElementType != null) - return TypeFactory.mkOrderedSet(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return UndefinedValue.instance; - OrderedSetValue oset = (OrderedSetValue) args[0]; - return oset.excluding(resultType, args[1]); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col = (CollectionType) args[0].type(); - - Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); - - if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to the same ordered set, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(col.elemType()) + - " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; - } - - return null; - } + + public Op_orderedSet_excluding() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet, param -> true), List.of("OrderedSet(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + OrderedSetType osetType = (OrderedSetType) params[0]; + + Type commonElementType = osetType.elemType() + .getLeastCommonSupertype(params[1]); + + if (commonElementType != null) { + return TypeFactory.mkOrderedSet(commonElementType); + } + } + return null; + } + + public String name() { + return "excluding"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return UndefinedValue.instance; + OrderedSetValue oset = (OrderedSetValue) args[0]; + return oset.excluding(resultType, args[1]); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col = (CollectionType) args[0].type(); + + Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); + + if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to the same ordered set, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(col.elemType()) + + " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; + } + + return null; + } } /* indexOf : OrderedSet(T) x T -> Integer */ final class Op_orderedSet_indexOf extends OpGeneric { - public String name() { - return "indexOf"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && params[0].isTypeOfOrderedSet()) { - OrderedSetType osetType = (OrderedSetType) params[0]; - - Type commonElementType = osetType.elemType() - .getLeastCommonSupertype(params[1]); - - if (commonElementType != null) - return TypeFactory.mkInteger(); - - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return UndefinedValue.instance; - - OrderedSetValue oset = (OrderedSetValue) args[0]; - - int index = oset.indexOf(args[1]); - if (index == -1) - return UndefinedValue.instance; - else - return IntegerValue.valueOf(index + 1); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col = (CollectionType) args[0].type(); - - Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); - - if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to undefined, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(col.elemType()) + - " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; - } - - return null; - } + + public Op_orderedSet_indexOf() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet, param -> true), List.of("OrderedSet(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + OrderedSetType osetType = (OrderedSetType) params[0]; + + Type commonElementType = osetType.elemType() + .getLeastCommonSupertype(params[1]); + + if (commonElementType != null) { + return TypeFactory.mkInteger(); + } + } + return null; + } + + public String name() { + return "indexOf"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return UndefinedValue.instance; + + OrderedSetValue oset = (OrderedSetValue) args[0]; + + int index = oset.indexOf(args[1]); + if (index == -1) + return UndefinedValue.instance; + else + return IntegerValue.valueOf(index + 1); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col = (CollectionType) args[0].type(); + + Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); + + if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to undefined, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(col.elemType()) + + " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; + } + + return null; + } } /* reverse : OrderedSet(T) -> OrderedSet(T) */ final class Op_orderedSet_reverse extends OpGeneric { - public String name() { - return "reverse"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 1 && params[0].isTypeOfOrderedSet()) { - return params[0]; - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) return UndefinedValue.instance; - - OrderedSetValue col = (OrderedSetValue)args[0]; - ArrayList elements = new ArrayList(col.collection()); - Collections.reverse(elements); - - return new OrderedSetValue(col.elemType(), elements); - } + + public Op_orderedSet_reverse() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfOrderedSet), List.of("OrderedSet(T1)"), List.of("self"))); + } + + public Type matches(Type params[]) { + if (match(params)) { + return params[0]; + } + return null; + } + + public String name() { + return "reverse"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) return UndefinedValue.instance; + + OrderedSetValue col = (OrderedSetValue) args[0]; + ArrayList elements = new ArrayList(col.collection()); + Collections.reverse(elements); + + return new OrderedSetValue(col.elemType(), elements); + } } \ No newline at end of file diff --git a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsSequence.java b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsSequence.java index 6cc79531b..096dd4f19 100644 --- a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsSequence.java +++ b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsSequence.java @@ -3,10 +3,8 @@ import com.google.common.collect.Multimap; import org.tzi.use.uml.ocl.expr.EvalContext; import org.tzi.use.uml.ocl.expr.Expression; -import org.tzi.use.uml.ocl.type.CollectionType; -import org.tzi.use.uml.ocl.type.SequenceType; -import org.tzi.use.uml.ocl.type.Type; -import org.tzi.use.uml.ocl.type.TypeFactory; +import org.tzi.use.uml.ocl.expr.ParamInfo; +import org.tzi.use.uml.ocl.type.*; import org.tzi.use.uml.ocl.value.IntegerValue; import org.tzi.use.uml.ocl.value.SequenceValue; import org.tzi.use.uml.ocl.value.UndefinedValue; @@ -15,6 +13,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.List; public class StandardOperationsSequence { public static void registerTypeOperations(Multimap opMap) { @@ -44,510 +43,578 @@ public static void registerTypeOperations(Multimap opMap) { /* union : Sequence(T1) x Sequence(T2) -> Sequence(T1), with T2 <= T1 */ final class Op_sequence_union extends OpGeneric { - public String name() { - return "union"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type[] params) { - if (params.length == 2 && params[0].isTypeOfSequence() - && params[1].isTypeOfSequence()) { - SequenceType sequence1 = (SequenceType) params[0]; - SequenceType sequence2 = (SequenceType) params[1]; - - Type commonElementType = sequence1.elemType() - .getLeastCommonSupertype(sequence2.elemType()); - - if (commonElementType != null) - return TypeFactory.mkSequence(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SequenceValue sequence1 = (SequenceValue) args[0]; - SequenceValue sequence2 = (SequenceValue) args[1]; - return sequence1.union(resultType, sequence2); - } + public Op_sequence_union() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence, Type::isTypeOfSequence), List.of("Sequence(T1)", "Sequence(T2)"), List.of("self", "other"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SequenceType sequence1 = (SequenceType) params[0]; + SequenceType sequence2 = (SequenceType) params[1]; + + Type commonElementType = sequence1.elemType() + .getLeastCommonSupertype(sequence2.elemType()); + + if (commonElementType != null) { + return TypeFactory.mkSequence(commonElementType); + } + } + return null; + } + + public String name() { + return "union"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SequenceValue sequence1 = (SequenceValue) args[0]; + SequenceValue sequence2 = (SequenceValue) args[1]; + return sequence1.union(resultType, sequence2); + } } // -------------------------------------------------------- /* append : Sequence(T) x T -> Sequence(T) */ final class Op_sequence_append extends OpGeneric { - public String name() { - return "append"; - } - - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - public Type matches(Type[] params) { - if (params.length == 2 && params[0].isTypeOfSequence()) { - SequenceType seqType = (SequenceType) params[0]; - - Type commonElementType = seqType.elemType() - .getLeastCommonSupertype(params[1]); - - if (commonElementType != null) - return TypeFactory.mkSequence(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if(args[0].isUndefined()){ - return UndefinedValue.instance; - } - SequenceValue seq = (SequenceValue) args[0]; - return seq.append(resultType, args[1]); - } + public Op_sequence_append() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence, param -> true), List.of("Sequence(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SequenceType seqType = (SequenceType) params[0]; + Type commonElementType = seqType.elemType() + .getLeastCommonSupertype(params[1]); + + if (commonElementType != null) { + return TypeFactory.mkSequence(commonElementType); + } + } + return null; + } + + + public String name() { + return "append"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) { + return UndefinedValue.instance; + } + SequenceValue seq = (SequenceValue) args[0]; + return seq.append(resultType, args[1]); + } } // -------------------------------------------------------- /* prepend : Sequence(T) x T -> Sequence(T) */ final class Op_sequence_prepend extends OpGeneric { - public String name() { - return "prepend"; - } - - public int kind() { - return SPECIAL; - } - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type[] params) { - if (params.length == 2 && params[0].isTypeOfSequence()) { - SequenceType seqType = (SequenceType) params[0]; - - Type commonElementType = seqType.elemType() - .getLeastCommonSupertype(params[1]); - - if (commonElementType != null) - return TypeFactory.mkSequence(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if(args[0].isUndefined()){ - return UndefinedValue.instance; - } - SequenceValue seq = (SequenceValue) args[0]; - return seq.prepend(resultType, args[1]); - } + public Op_sequence_prepend() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence, param -> true), List.of("Sequence(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SequenceType seqType = (SequenceType) params[0]; + Type commonElementType = seqType.elemType() + .getLeastCommonSupertype(params[1]); + + if (commonElementType != null) { + return TypeFactory.mkSequence(commonElementType); + } + } + return null; + } + + + public String name() { + return "prepend"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) { + return UndefinedValue.instance; + } + SequenceValue seq = (SequenceValue) args[0]; + return seq.prepend(resultType, args[1]); + } } // -------------------------------------------------------- /* insertAt : Sequence(T) x Integer x T -> Sequence(T) */ final class Op_sequence_insertAt extends OpGeneric { - public String name() { - return "insertAt"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type[] params) { - if (params.length == 3 && params[0].isTypeOfSequence() - && params[1].isTypeOfInteger()) { - SequenceType seqType = (SequenceType) params[0]; - - Type commonElementType = seqType.elemType() - .getLeastCommonSupertype(params[2]); - - if (commonElementType != null) - return TypeFactory.mkSequence(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SequenceValue seq = (SequenceValue) args[0]; - SequenceValue res = seq.insertAt(resultType, (IntegerValue) args[1], args[2]); - if (res == null) - return UndefinedValue.instance; - else - return res; - } + public Op_sequence_insertAt() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence, Type::isTypeOfInteger, param -> true), List.of("Sequence(T1)", "Integer", "T2"), List.of("self", "index", "element"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SequenceType seqType = (SequenceType) params[0]; + Type commonElementType = seqType.elemType() + .getLeastCommonSupertype(params[2]); + + if (commonElementType != null) { + return TypeFactory.mkSequence(commonElementType); + } + } + return null; + } + + + public String name() { + return "insertAt"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SequenceValue seq = (SequenceValue) args[0]; + SequenceValue res = seq.insertAt(resultType, (IntegerValue) args[1], args[2]); + + if (res == null) + return UndefinedValue.instance; + else + return res; + } } // -------------------------------------------------------- /* subSequence : Sequence(T) x Integer x Integer -> Sequence(T) */ final class Op_sequence_subSequence extends OpGeneric { - public String name() { - return "subSequence"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - public Type matches(Type[] params) { - return (params.length == 3 && params[0].isTypeOfSequence() - && params[1].isTypeOfInteger() && params[2].isTypeOfInteger()) ? params[0] - : null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SequenceValue seq = (SequenceValue) args[0]; - int lower = ((IntegerValue) args[1]).value(); - int upper = ((IntegerValue) args[2]).value(); - - if (lower > upper) - return UndefinedValue.instance; - - Value res; - try { - res = seq.subSequence(resultType, lower - 1, upper); - } catch (IndexOutOfBoundsException e) { - res = UndefinedValue.instance; - } - return res; - } + public Op_sequence_subSequence() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence, Type::isTypeOfInteger, Type::isTypeOfInteger), List.of("Sequence(T1)", "Integer", "Integer"), List.of("self", "start", "end"))); + } + + public Type matches(Type[] params) { + return (match(params)) ? params[0] : null; + } + + public String name() { + return "subSequence"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SequenceValue seq = (SequenceValue) args[0]; + int lower = ((IntegerValue) args[1]).value(); + int upper = ((IntegerValue) args[2]).value(); + + if (lower > upper) + return UndefinedValue.instance; + + Value res; + try { + res = seq.subSequence(resultType, lower - 1, upper); + } catch (IndexOutOfBoundsException e) { + res = UndefinedValue.instance; + } + return res; + } } // -------------------------------------------------------- /* at : Sequence(T) x Integer -> T */ final class Op_sequence_at extends OpGeneric { - public String name() { - return "at"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - public Type matches(Type[] params) { - if (params.length == 2 && params[0].isTypeOfSequence() - && params[1].isTypeOfInteger()) { - SequenceType seq = (SequenceType) params[0]; - return seq.elemType(); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SequenceValue seq = (SequenceValue) args[0]; - IntegerValue n = (IntegerValue) args[1]; - - Value res; - try { - res = seq.get(n.value() - 1); - } catch (IndexOutOfBoundsException e) { - res = UndefinedValue.instance; - } - return res; - } + public Op_sequence_at() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence, Type::isTypeOfInteger), List.of("Sequence(T1)", "Integer"), List.of("self", "Index"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SequenceType seq = (SequenceType) params[0]; + return seq.elemType(); + } + return null; + } + + public String name() { + return "at"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SequenceValue seq = (SequenceValue) args[0]; + IntegerValue n = (IntegerValue) args[1]; + + Value res; + try { + res = seq.get(n.value() - 1); + } catch (IndexOutOfBoundsException e) { + res = UndefinedValue.instance; + } + return res; + } } // -------------------------------------------------------- /* first : Sequence(T) -> T */ final class Op_sequence_first extends OpGeneric { - public String name() { - return "first"; - } - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type[] params) { - if (params.length == 1 && params[0].isTypeOfSequence()) { - SequenceType seq = (SequenceType) params[0]; - return seq.elemType(); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SequenceValue seq = (SequenceValue) args[0]; - if (seq.isEmpty()) - return UndefinedValue.instance; - return seq.get(0); - } + public Op_sequence_first() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence), List.of("Sequence(T1)"), List.of("self"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SequenceType seq = (SequenceType) params[0]; + return seq.elemType(); + } + return null; + } + + + public String name() { + return "first"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SequenceValue seq = (SequenceValue) args[0]; + if (seq.isEmpty()) + return UndefinedValue.instance; + return seq.get(0); + } } // -------------------------------------------------------- /* last : Sequence(T) -> T */ final class Op_sequence_last extends OpGeneric { - public String name() { - return "last"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type[] params) { - if (params.length == 1 && params[0].isTypeOfSequence()) { - SequenceType seq = (SequenceType) params[0]; - return seq.elemType(); - } - return null; - } - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SequenceValue seq = (SequenceValue) args[0]; - if (seq.isEmpty()) - return UndefinedValue.instance; - return seq.get(seq.size() - 1); - } + public Op_sequence_last() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence), List.of("Sequence(T1)"), List.of("self"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SequenceType seq = (SequenceType) params[0]; + return seq.elemType(); + } + return null; + } + + + public String name() { + return "last"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SequenceValue seq = (SequenceValue) args[0]; + if (seq.isEmpty()) + return UndefinedValue.instance; + return seq.get(seq.size() - 1); + } } // -------------------------------------------------------- /* including : Sequence(T) x T -> Sequence(T) */ final class Op_sequence_including extends OpGeneric { - public String name() { - return "including"; - } - - public int kind() { - return SPECIAL; - } - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type[] params) { - if (params.length == 2 && params[0].isTypeOfSequence()) { - SequenceType seqType = (SequenceType) params[0]; - - Type commonElementType = seqType.elemType() - .getLeastCommonSupertype(params[1]); - - if (commonElementType != null) - return TypeFactory.mkSequence(commonElementType); - - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return UndefinedValue.instance; - SequenceValue seq = (SequenceValue) args[0]; - return seq.append(resultType, args[1]); - } + public Op_sequence_including() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence, param -> true), List.of("Sequence(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SequenceType seqType = (SequenceType) params[0]; + + Type commonElementType = seqType.elemType() + .getLeastCommonSupertype(params[1]); + + if (commonElementType != null) { + return TypeFactory.mkSequence(commonElementType); + } + } + return null; + } + + public String name() { + return "including"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return UndefinedValue.instance; + SequenceValue seq = (SequenceValue) args[0]; + return seq.append(resultType, args[1]); + } } // -------------------------------------------------------- /* excluding : Sequence(T) x T -> Sequence(T) */ final class Op_sequence_excluding extends OpGeneric { - public String name() { - return "excluding"; - } - public int kind() { - return SPECIAL; - } + public Op_sequence_excluding() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence, param -> true), List.of("Sequence(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SequenceType seqType = (SequenceType) params[0]; + Type commonElementType = seqType.elemType() + .getLeastCommonSupertype(params[1]); + + if (commonElementType != null) { + return TypeFactory.mkSequence(commonElementType); + } + } + return null; + } + + + public String name() { + return "excluding"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return UndefinedValue.instance; + SequenceValue seq = (SequenceValue) args[0]; + return seq.excluding(resultType, args[1]); + } + + @Override + public String checkWarningUnrelatedTypes(Expression[] args) { + CollectionType col = (CollectionType) args[0].type(); + + Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); + + if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to the same sequence, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(col.elemType()) + + " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; + } + + return null; + } +} - public boolean isInfixOrPrefix() { - return false; - } +/* indexOf : Sequence(T) x T -> Integer */ +final class Op_sequence_indexOf extends OpGeneric { - public Type matches(Type[] params) { - if (params.length == 2 && params[0].isTypeOfSequence()) { - SequenceType seqType = (SequenceType) params[0]; + public Op_sequence_indexOf() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence, param -> true), List.of("Sequence(T1)", "T2"), List.of("self", "element"))); + } - Type commonElementType = seqType.elemType() - .getLeastCommonSupertype(params[1]); + public Type matches(Type[] params) { + if (match(params)) { + SequenceType seqType = (SequenceType) params[0]; - if (commonElementType != null) - return TypeFactory.mkSequence(commonElementType); - } - return null; - } + Type commonElementType = seqType.elemType() + .getLeastCommonSupertype(params[1]); - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return UndefinedValue.instance; - SequenceValue seq = (SequenceValue) args[0]; - return seq.excluding(resultType, args[1]); - } - - @Override - public String checkWarningUnrelatedTypes(Expression[] args) { - CollectionType col = (CollectionType) args[0].type(); - - Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); - - if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to the same sequence, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(col.elemType()) + - " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; - } - - return null; - } -} + if (commonElementType != null) { + return TypeFactory.mkInteger(); + } + } + return null; + } -/* indexOf : Sequence(T) x T -> Integer */ -final class Op_sequence_indexOf extends OpGeneric { - public String name() { - return "indexOf"; - } - public int kind() { - return SPECIAL; - } + public String name() { + return "indexOf"; + } - public boolean isInfixOrPrefix() { - return false; - } + public int kind() { + return SPECIAL; + } - public Type matches(Type[] params) { - if (params.length == 2 && params[0].isTypeOfSequence()) { - SequenceType seqType = (SequenceType) params[0]; + public boolean isInfixOrPrefix() { + return false; + } - Type commonElementType = seqType.elemType() - .getLeastCommonSupertype(params[1]); + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return UndefinedValue.instance; - if (commonElementType != null) - return TypeFactory.mkInteger(); + SequenceValue seq = (SequenceValue) args[0]; - } - return null; - } + int index = seq.indexOf(args[1]); + if (index == -1) + return UndefinedValue.instance; + else + return IntegerValue.valueOf(index + 1); + } - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return UndefinedValue.instance; + @Override + public String checkWarningUnrelatedTypes(Expression[] args) { + CollectionType col = (CollectionType) args[0].type(); - SequenceValue seq = (SequenceValue) args[0]; + Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); - int index = seq.indexOf(args[1]); - if (index == -1) - return UndefinedValue.instance; - else - return IntegerValue.valueOf(index + 1); - } - - @Override - public String checkWarningUnrelatedTypes(Expression[] args) { - CollectionType col = (CollectionType) args[0].type(); - - Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); - - if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to undefined, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(col.elemType()) + - " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; - } - - return null; - } + if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to undefined, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(col.elemType()) + + " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; + } + + return null; + } } /* reverse : Sequence(T) -> Sequence(T) */ final class Op_sequence_reverse extends OpGeneric { - public String name() { - return "reverse"; - } - public int kind() { - return SPECIAL; - } + public Op_sequence_reverse() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence), List.of("Sequence(T1)"), List.of("self"))); + } - public boolean isInfixOrPrefix() { - return false; - } + public Type matches(Type[] params) { + if (match(params)) { + return params[0]; + } + return null; + } - public Type matches(Type[] params) { - if (params.length == 1 && params[0].isTypeOfSequence()) { - return params[0]; - } - return null; - } - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) return UndefinedValue.instance; - - SequenceValue col = (SequenceValue)args[0]; - ArrayList elements = new ArrayList<>(col.collection()); - Collections.reverse(elements); - - return new SequenceValue(col.elemType(), elements); - } + public String name() { + return "reverse"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) return UndefinedValue.instance; + + SequenceValue col = (SequenceValue) args[0]; + ArrayList elements = new ArrayList<>(col.collection()); + Collections.reverse(elements); + + return new SequenceValue(col.elemType(), elements); + } } /* Not OCL: shuffle : Sequence(T) -> Sequence(T) */ final class Op_sequence_shuffle extends OpGeneric { - public String name() { - return "shuffle"; - } - public int kind() { - return SPECIAL; - } + public Op_sequence_shuffle() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSequence), List.of("Sequence(T1)"), List.of("self"))); + } - public boolean isInfixOrPrefix() { - return false; - } + public Type matches(Type[] params) { + if (match(params)) { + return params[0]; + } + return null; + } - public Type matches(Type[] params) { - if (params.length == 1 && params[0].isTypeOfSequence()) { - return params[0]; - } - return null; - } + public String name() { + return "shuffle"; + } - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) return UndefinedValue.instance; - - SequenceValue col = (SequenceValue)args[0]; - ArrayList elements = new ArrayList<>(col.collection()); - Collections.shuffle(elements); - - return new SequenceValue(col.elemType(), elements); - } + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) return UndefinedValue.instance; + + SequenceValue col = (SequenceValue) args[0]; + ArrayList elements = new ArrayList<>(col.collection()); + Collections.shuffle(elements); + + return new SequenceValue(col.elemType(), elements); + } } \ No newline at end of file diff --git a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsSet.java b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsSet.java index 9830307d0..75807af2e 100644 --- a/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsSet.java +++ b/use-core/src/main/java/org/tzi/use/uml/ocl/expr/operations/StandardOperationsSet.java @@ -1,12 +1,7 @@ package org.tzi.use.uml.ocl.expr.operations; -import org.tzi.use.uml.ocl.expr.EvalContext; -import org.tzi.use.uml.ocl.expr.Expression; -import org.tzi.use.uml.ocl.type.BagType; -import org.tzi.use.uml.ocl.type.CollectionType; -import org.tzi.use.uml.ocl.type.SetType; -import org.tzi.use.uml.ocl.type.Type; -import org.tzi.use.uml.ocl.type.TypeFactory; +import org.tzi.use.uml.ocl.expr.*; +import org.tzi.use.uml.ocl.type.*; import org.tzi.use.uml.ocl.value.BagValue; import org.tzi.use.uml.ocl.value.SetValue; import org.tzi.use.uml.ocl.value.UndefinedValue; @@ -15,6 +10,8 @@ import com.google.common.collect.Multimap; +import java.util.List; + public class StandardOperationsSet { public static void registerTypeOperations(Multimap opmap) { // operations on Set @@ -42,401 +39,421 @@ public static void registerTypeOperations(Multimap opmap) { /* union : Set(T1) x Set(T2) -> Set(T1), with T2 <= T1 */ final class Op_set_union extends OpGeneric { - public String name() { - return "union"; - } - public int kind() { - return OPERATION; - } + public Op_set_union() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSet, Type::isTypeOfSet), List.of("Set(T1)", "Set(T2)"), List.of("self", "other"))); + } - public boolean isInfixOrPrefix() { - return false; - } + public Type matches(Type[] params) { + return match(params) ? params[0].getLeastCommonSupertype(params[1]) : null; + } - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfSet() && - params[1].isTypeOfSet()) { - - return params[0].getLeastCommonSupertype(params[1]); - } + public String name() { + return "union"; + } - return null; - } + public int kind() { + return OPERATION; + } - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SetValue set1 = (SetValue) args[0]; - SetValue set2 = (SetValue) args[1]; - return set1.union(resultType, set2); - } + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SetValue set1 = (SetValue) args[0]; + SetValue set2 = (SetValue) args[1]; + return set1.union(resultType, set2); + } } // -------------------------------------------------------- /* union : Set(T1) x Bag(T2) -> Bag(T1), with T2 <= T1 */ final class Op_set_union_bag extends OpGeneric { - public String name() { - return "union"; - } - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfSet() && - params[1].isTypeOfBag()) { - SetType set = (SetType) params[0]; - BagType bag = (BagType) params[1]; - Type newElementType = set.elemType().getLeastCommonSupertype( - bag.elemType()); - - if (newElementType != null) - return TypeFactory.mkBag(newElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SetValue set = (SetValue) args[0]; - BagValue bag = (BagValue) args[1]; - return set.union(resultType, bag); - } + public Op_set_union_bag() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSet, Type::isTypeOfBag), List.of("Set(T1)", "Bag(T2)"), List.of("self", "other"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SetType set = (SetType) params[0]; + BagType bag = (BagType) params[1]; + Type newElementType = set.elemType().getLeastCommonSupertype( + bag.elemType()); + + if (newElementType != null) { + return TypeFactory.mkBag(newElementType); + } + } + return null; + } + + public String name() { + return "union"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SetValue set = (SetValue) args[0]; + BagValue bag = (BagValue) args[1]; + return set.union(resultType, bag); + } } // -------------------------------------------------------- /* intersection : Set(T1) x Set(T2) -> Set(T1), with T2 <= T1 */ final class Op_set_intersection extends OpGeneric { - public String name() { - return "intersection"; - } - - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfSet() && - params[1].isTypeOfSet()) { - - SetType set1 = (SetType) params[0]; - SetType set2 = (SetType) params[1]; - Type commonElementType = set1.elemType().getLeastCommonSupertype( - set2.elemType()); - - if (commonElementType != null) - return TypeFactory.mkSet(commonElementType); - } - return null; - } - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SetValue set1 = (SetValue) args[0]; - SetValue set2 = (SetValue) args[1]; - return set1.intersection(resultType, set2); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col1 = (CollectionType) args[0].type(); - CollectionType col2 = (CollectionType) args[1].type(); - - Type elemType1 = col1.elemType(); - Type elemType2 = col2.elemType(); - - Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); - - if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to an empty set, " + StringUtil.NEWLINE + - "because the element types " + StringUtil.inQuotes(elemType1) + - " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; - } - - return null; - } + public Op_set_intersection() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSet, Type::isTypeOfSet), List.of("Set(T1)", "Set(T2)"), List.of("self", "other"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SetType set1 = (SetType) params[0]; + SetType set2 = (SetType) params[1]; + Type commonElementType = set1.elemType().getLeastCommonSupertype( + set2.elemType()); + + if (commonElementType != null) { + return TypeFactory.mkSet(commonElementType); + } + } + return null; + } + + public String name() { + return "intersection"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SetValue set1 = (SetValue) args[0]; + SetValue set2 = (SetValue) args[1]; + return set1.intersection(resultType, set2); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col1 = (CollectionType) args[0].type(); + CollectionType col2 = (CollectionType) args[1].type(); + + Type elemType1 = col1.elemType(); + Type elemType2 = col2.elemType(); + + Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); + + if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to an empty set, " + StringUtil.NEWLINE + + "because the element types " + StringUtil.inQuotes(elemType1) + + " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* intersection : Set(T1) x Bag(T2) -> Set(T1), with T2 <= T1 */ final class Op_set_intersection_bag extends OpGeneric { - public String name() { - return "intersection"; - } - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfSet() && - params[1].isTypeOfBag()) { - - SetType set = (SetType) params[0]; - BagType bag = (BagType) params[1]; - - Type commonElementType = set.elemType().getLeastCommonSupertype( - bag.elemType()); - - if (commonElementType != null) - return TypeFactory.mkSet(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SetValue set = (SetValue) args[0]; - BagValue bag = (BagValue) args[1]; - return set.intersection(resultType, bag); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col1 = (CollectionType) args[0].type(); - CollectionType col2 = (CollectionType) args[1].type(); - - Type elemType1 = col1.elemType(); - Type elemType2 = col2.elemType(); - - Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); - - if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to an empty, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(elemType1) + - " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; - } - - return null; - } + public Op_set_intersection_bag() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSet, Type::isTypeOfBag), List.of("Set(T1)", "Bag(T2)"), List.of("self", "other"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SetType set = (SetType) params[0]; + BagType bag = (BagType) params[1]; + + Type commonElementType = set.elemType().getLeastCommonSupertype( + bag.elemType()); + + if (commonElementType != null) { + return TypeFactory.mkSet(commonElementType); + } + } + return null; + } + + public String name() { + return "intersection"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SetValue set = (SetValue) args[0]; + BagValue bag = (BagValue) args[1]; + return set.intersection(resultType, bag); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col1 = (CollectionType) args[0].type(); + CollectionType col2 = (CollectionType) args[1].type(); + + Type elemType1 = col1.elemType(); + Type elemType2 = col2.elemType(); + + Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); + + if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to an empty, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(elemType1) + + " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* - : Set(T1) x Set(T2) -> Set(T1), with T2 <= T1 */ final class Op_set_difference extends OpGeneric { - public String name() { - return "-"; - } - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return true; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfSet() && - params[1].isTypeOfSet()) { - - SetType set1 = (SetType) params[0]; - SetType set2 = (SetType) params[1]; - Type commonElementType = set1.elemType().getLeastCommonSupertype( - set2.elemType()); - - if (commonElementType != null) - return TypeFactory.mkSet(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SetValue set1 = (SetValue) args[0]; - SetValue set2 = (SetValue) args[1]; - return set1.difference(resultType, set2); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col1 = (CollectionType) args[0].type(); - CollectionType col2 = (CollectionType) args[1].type(); - - Type elemType1 = col1.elemType(); - Type elemType2 = col2.elemType(); - - Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); - - if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to the same set, " + StringUtil.NEWLINE + - "because the element types " + StringUtil.inQuotes(elemType1) + - " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; - } - - return null; - } + public Op_set_difference() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSet, Type::isTypeOfSet), List.of("Set(T1)", "Set(T2)"), List.of("self", "other"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SetType set1 = (SetType) params[0]; + SetType set2 = (SetType) params[1]; + Type commonElementType = set1.elemType().getLeastCommonSupertype( + set2.elemType()); + + if (commonElementType != null) { + return TypeFactory.mkSet(commonElementType); + } + } + return null; + } + + public String name() { + return "-"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return true; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SetValue set1 = (SetValue) args[0]; + SetValue set2 = (SetValue) args[1]; + return set1.difference(resultType, set2); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col1 = (CollectionType) args[0].type(); + CollectionType col2 = (CollectionType) args[1].type(); + + Type elemType1 = col1.elemType(); + Type elemType2 = col2.elemType(); + + Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); + + if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to the same set, " + StringUtil.NEWLINE + + "because the element types " + StringUtil.inQuotes(elemType1) + + " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* including : Set(T1) x T2 -> Set(T1), with T2 <= T1 */ final class Op_set_including extends OpGeneric { - public String name() { - return "including"; - } - public int kind() { - return SPECIAL; - } + public Op_set_including() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSet, param -> true), List.of("Set(T1)", "T2"), List.of("self", "element"))); + } - public boolean isInfixOrPrefix() { - return false; - } + public Type matches(Type params[]) { + if (match(params)) { + SetType set1 = (SetType) params[0]; + Type commonElementType = set1.elemType().getLeastCommonSupertype(params[1]); - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfSet()) { - SetType set1 = (SetType) params[0]; + if (commonElementType != null) { + return TypeFactory.mkSet(commonElementType); + } + } + return null; + } - Type commonElementType = set1.elemType().getLeastCommonSupertype(params[1]); - if (commonElementType != null) - return TypeFactory.mkSet(commonElementType); + public String name() { + return "including"; + } - } - return null; - } + public int kind() { + return SPECIAL; + } - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return UndefinedValue.instance; - SetValue set1 = (SetValue) args[0]; - - return set1.including(resultType, args[1]); - } + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return UndefinedValue.instance; + SetValue set1 = (SetValue) args[0]; + + return set1.including(resultType, args[1]); + } } // -------------------------------------------------------- /* excluding : Set(T1) x T2 -> Set(T1), with T2 <= T1 */ final class Op_set_excluding extends OpGeneric { - public String name() { - return "excluding"; - } - public int kind() { - return SPECIAL; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfSet()) { - - SetType set1 = (SetType) params[0]; - Type commonElementType = set1.elemType().getLeastCommonSupertype( - params[1]); - - if (commonElementType != null) - return TypeFactory.mkSet(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - if (args[0].isUndefined()) - return UndefinedValue.instance; - SetValue set1 = (SetValue) args[0]; - return set1.excluding(resultType, args[1]); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col = (CollectionType) args[0].type(); - - Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); - - if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to the same set, " + StringUtil.NEWLINE + - "because the element type " + StringUtil.inQuotes(col.elemType()) + - " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; - } - - return null; - } + public Op_set_excluding() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSet, param -> true), List.of("Set(T1)", "T2"), List.of("self", "element"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SetType set1 = (SetType) params[0]; + Type commonElementType = set1.elemType().getLeastCommonSupertype(params[1]); + if (commonElementType != null) { + return TypeFactory.mkSet(commonElementType); + } + } + return null; + } + + public String name() { + return "excluding"; + } + + public int kind() { + return SPECIAL; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + if (args[0].isUndefined()) + return UndefinedValue.instance; + SetValue set1 = (SetValue) args[0]; + return set1.excluding(resultType, args[1]); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col = (CollectionType) args[0].type(); + + Type commonElementType = col.elemType().getLeastCommonSupertype(args[1].type()); + + if (!(col.elemType().isTypeOfOclAny() || args[1].type().isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to the same set, " + StringUtil.NEWLINE + + "because the element type " + StringUtil.inQuotes(col.elemType()) + + " and the parameter type " + StringUtil.inQuotes(args[1].type()) + " are unrelated."; + } + + return null; + } } // -------------------------------------------------------- /* symmetricDifference : Set(T1) x Set(T2) -> Set(T1) with T2 <= T1 */ final class Op_set_symmetricDifference extends OpGeneric { - public String name() { - return "symmetricDifference"; - } - public int kind() { - return OPERATION; - } - - public boolean isInfixOrPrefix() { - return false; - } - - public Type matches(Type params[]) { - if (params.length == 2 && - params[0].isTypeOfSet() && - params[1].isTypeOfSet()) { - - SetType set1 = (SetType) params[0]; - SetType set2 = (SetType) params[1]; - - Type commonElementType = set1.elemType().getLeastCommonSupertype( - set2.elemType()); - - if (commonElementType != null) - return TypeFactory.mkSet(commonElementType); - } - return null; - } - - public Value eval(EvalContext ctx, Value[] args, Type resultType) { - SetValue set1 = (SetValue) args[0]; - SetValue set2 = (SetValue) args[1]; - return set1.symmetricDifference(resultType, set2); - } - - @Override - public String checkWarningUnrelatedTypes(Expression args[]) { - CollectionType col1 = (CollectionType) args[0].type(); - CollectionType col2 = (CollectionType) args[1].type(); - - Type elemType1 = col1.elemType(); - Type elemType2 = col2.elemType(); - - Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); - - if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { - return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + - " will always evaluate to the union of both sets, " + StringUtil.NEWLINE + - "because the element types " + StringUtil.inQuotes(elemType1) + - " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; - } - - return null; - } + public Op_set_symmetricDifference() { + setParamInfo(new ParamInfo(List.of(Type::isTypeOfSet, Type::isTypeOfSet), List.of("Set(T1)", "Set(T2)"), List.of("self", "other"))); + } + + public Type matches(Type[] params) { + if (match(params)) { + SetType set1 = (SetType) params[0]; + SetType set2 = (SetType) params[1]; + + Type commonElementType = set1.elemType().getLeastCommonSupertype( + set2.elemType()); + + if (commonElementType != null) { + return TypeFactory.mkSet(commonElementType); + } + } + return null; + } + + public String name() { + return "symmetricDifference"; + } + + public int kind() { + return OPERATION; + } + + public boolean isInfixOrPrefix() { + return false; + } + + public Value eval(EvalContext ctx, Value[] args, Type resultType) { + SetValue set1 = (SetValue) args[0]; + SetValue set2 = (SetValue) args[1]; + return set1.symmetricDifference(resultType, set2); + } + + @Override + public String checkWarningUnrelatedTypes(Expression args[]) { + CollectionType col1 = (CollectionType) args[0].type(); + CollectionType col2 = (CollectionType) args[1].type(); + + Type elemType1 = col1.elemType(); + Type elemType2 = col2.elemType(); + + Type commonElementType = elemType1.getLeastCommonSupertype(elemType2); + + if (!(elemType1.isTypeOfOclAny() || elemType2.isTypeOfOclAny()) && commonElementType.isTypeOfOclAny()) { + return "Expression " + StringUtil.inQuotes(this.stringRep(args, "")) + + " will always evaluate to the union of both sets, " + StringUtil.NEWLINE + + "because the element types " + StringUtil.inQuotes(elemType1) + + " and " + StringUtil.inQuotes(elemType2) + " are unrelated."; + } + + return null; + } } \ No newline at end of file diff --git a/use-core/src/main/java/org/tzi/use/util/OperationType.java b/use-core/src/main/java/org/tzi/use/util/OperationType.java new file mode 100644 index 000000000..2cef124fe --- /dev/null +++ b/use-core/src/main/java/org/tzi/use/util/OperationType.java @@ -0,0 +1,14 @@ +package org.tzi.use.util; + +/** + * The OperationType class represents an operation. + * It includes information about the collection type (might be null), operation name, and a parameter (might be null). + */ +public class OperationType { + + public String collectionType; + + public String operationName; + + public String param; +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java new file mode 100644 index 000000000..51296825b --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java @@ -0,0 +1,834 @@ +package org.tzi.use.autocompletion; + +import org.tzi.use.autocompletion.parserResultTypes.ResultTypeRoot; +import org.tzi.use.autocompletion.parserResultTypes.ocl.*; +import org.tzi.use.autocompletion.parserResultTypes.useCommands.*; +import org.tzi.use.main.ChangeListener; +import org.tzi.use.main.Session; +import org.tzi.use.parser.base.ParserHelper; +import org.tzi.use.uml.mm.*; +import org.tzi.use.uml.ocl.expr.ExpStdOp; +import org.tzi.use.uml.ocl.expr.operations.OpGeneric; +import org.tzi.use.util.OperationType; +import org.tzi.use.util.input.AutoCompletionOperation; + +import java.io.File; +import java.util.*; +import java.util.stream.Collectors; + +/** + * The AutoCompletion class provides support for auto-completion for USE-Commands, collection operations and class members. + * It maps class names to a list of their attributes, object names to their (inherited) classes, + * collections to associated operations, classes to available operations, associations to related class names, + * and classes to their instantiated objects. The auto-completion is based on the provided MModel. + *

+ * This class is initialized with an MModel and a Session, and it monitors the session for changes + * to update the auto-completion accordingly. + *

+ * + * + * @author [Till Aul] + */ +public class AutoCompletion { + //Maps classnames to a list their attributes (seperated according to their type) + private final Map classToAttributeMap; + + //Maps object names to their (inherited) classes + private final Map> objectsToClassMap; + + private final Map> collectionToOperationsMap; + + private final Map> classToOperationMap; + + private final Map> associationToClassNamesMap; + + private final Map> classToObjectsMap; + + private final MModel model; + + /** + * Initializes the AutoCompletion instance with the given MModel and Session. + * + * @param model The MModel to be used for AutoCompletion. + * @param session The Session to monitor for changes and update AutoCompletion. + */ + public AutoCompletion(MModel model, Session session) { + classToAttributeMap = new HashMap<>(); + objectsToClassMap = new HashMap<>(); + collectionToOperationsMap = new HashMap<>(); + classToOperationMap = new HashMap<>(); + associationToClassNamesMap = new HashMap<>(); + classToObjectsMap = new HashMap<>(); + this.model = model; + + //initialize autoCompletion after session is changed + ChangeListener sessionChangeListener = e -> initializeAutocompletion(session); + session.addChangeListener(sessionChangeListener); + } + + /** + * Initializes auto-completion by populating data structures with classes, attributes, + * operations, associations, and iteration operations from the provided Session. + * + * @param session The Session from which to gather information for auto-completion. + */ + private void initializeAutocompletion(Session session) { + Collection mClasses = session.system().model().classes(); + //Add all classes, attributes and operations to autocompletion + //addOperations(String className, List operations); + for (MClass mClass : mClasses) { + addClass(mClass.name()); + + List operations = mClass.operations().stream() + .map(MOperation::toString) + .collect(Collectors.toList()); + addOperations(mClass.name(), operations); + List attributes = mClass.allAttributes(); + for (MAttribute mAttribute : attributes) { + addAttributeToExistingClass(mClass.name(), mAttribute.name(), mAttribute.type().toString()); + } + } + + //add associations + Collection associations = session.system().model().associations(); + for (MAssociation mAssociation : associations) { + List classNames = new LinkedList<>(); + for (MClass mClass : mAssociation.associatedClasses()) { + classNames.add(mClass.name()); + } + addAssociation(mAssociation.name(), classNames); + } + + //operations + for (OpGeneric op : ExpStdOp.opmap.values()) { + OperationType operationType = op.getOperationType(); + + addOperationToMap(operationType); + } + for (String opName : ParserHelper.queryIdentMap.keySet()) { + addIterationOp(opName); + } + + } + + /** + * Retrieves a list of suggestions based on the parsed input and a flag indicating OCL-only mode. + *

+ * This method takes the parsed input and a boolean flag indicating whether it is in OCL-only mode. + * In OCL-only mode, it provides suggestions for OCL commands only, excluding USE-commands. + * In non-OCL-only mode, it supports both OCL and USE-commands. + * + * @param input The parsed input string. + * @param OCLOnly A boolean flag indicating whether the mode is OCL-only. + * @return A list of suggestions based on the parsed input and mode. + */ + public SuggestionResult getSuggestions(String input, boolean OCLOnly) { + SuggestionResult suggestions = new SuggestionResult(); + suggestions.suggestions = new LinkedList<>(); + + AutoCompletionParser parser = new AutoCompletionParser(model, input); + ParserResult parserResult = parser.getResult(); + + if (parserResult == null) { + return null; + } else if (!OCLOnly && parserResult.result instanceof ResultTypeUseCommands) { + return handleUseCommands(parserResult.result, suggestions); + } else { + return handleOCLTypes(parserResult.result, suggestions); + } + } + + /** + * Processes and provides suggestions based on the specified 'type' for OCL expressions. + * + * @param result The result of parsing the OCL expression. + * @param suggestions The existing SuggestionResult object to update with suggestions. + * @return A SuggestionResult object containing suggestions based on the provided 'type'. + */ + private SuggestionResult handleOCLTypes(ResultTypeRoot result, SuggestionResult suggestions) { + if (result instanceof ResultTypeOCLObjects) { + suggestions.suggestions = getSuggestionsObjects((ResultTypeOCLObjects) result); + } else if (result instanceof ResultTypeOCLAttributePrefix) { + suggestions.suggestions = getSuggestionsWithAttributePrefix((ResultTypeOCLAttributePrefix) result); + suggestions.prefix = ((ResultTypeOCLAttributePrefix) result).attributePrefix; + } else if (result instanceof ResultTypeOCLObjectPrefix) { + suggestions.suggestions = getSuggestionsWithObjectPrefix((ResultTypeOCLObjectPrefix) result); + suggestions.prefix = ((ResultTypeOCLObjectPrefix) result).objectPrefix; + } else if (result instanceof ResultTypeOCLCollections) { + suggestions.suggestions = getSuggestionCollections((ResultTypeOCLCollections) result); + if (result instanceof ResultTypeOCLCollectionsDefault) { + suggestions.prefix = ((ResultTypeOCLCollectionsDefault) result).prefix; + } + } + return suggestions; + } + + /** + * Handles USE command cases. + * + * @param result The result of parsing the USE command. + * @param suggestions The SuggestionResult object to update with suggestions. + * @return A SuggestionResult object with USE command suggestions. + */ + private SuggestionResult handleUseCommands(ResultTypeRoot result, SuggestionResult suggestions) { + if (result instanceof ResultTypeUseDelete) { + suggestions.suggestions = getSuggestionsDelete((ResultTypeUseDelete) result); + } else if (result instanceof ResultTypeUseInsert) { + suggestions.suggestions = getSuggestionInsert((ResultTypeUseInsert) result); + } else if (result instanceof ResultTypeUseCreate) { + suggestions.suggestions = getSuggestionCreate((ResultTypeUseCreate) result); + } else if (result instanceof ResultTypeUseDestroy) { + suggestions.suggestions = getAllObjectsWithException((ResultTypeUseDestroy) result); + } else if (result instanceof ResultTypeUseSet) { + suggestions.suggestions = getSuggestionSet((ResultTypeUseSet) result); + } else if (result instanceof ResultTypeUseOpenter) { + suggestions.suggestions = getSuggestionOpenter((ResultTypeUseOpenter) result); + } else if (result instanceof ResultTypeUseCheck) { + suggestions.suggestions = getSuggestionCheck(); + } else if (result instanceof ResultTypeUseStep) { + suggestions.suggestions = getSuggestionStep(); + } else if (result instanceof ResultTypeUseOpen) { + suggestions.suggestions = getSuggestionOpen((ResultTypeUseOpen) result); + } else if (result instanceof ResultTypeUseInfo) { + suggestions.suggestions = getSuggestionInfo((ResultTypeUseInfo) result); + }else{ + suggestions.suggestions = List.of(); + } + return suggestions; + } + + /** + * Adds the specified association and associated class names to the auto-completion map. + * + * @param association The name of the association. + * @param classNames The list of class names associated with the given association. + */ + public void addAssociation(String association, List classNames) { + associationToClassNamesMap.put(association, classNames); + } + + /** + * Adds a new class with an empty attribute map to the class-to-attribute mapping. + * + * @param className The name of the class to be added. + */ + public void addClass(String className) { + classToAttributeMap.put(className, new SuggestionForClassName()); + classToObjectsMap.put(className, new LinkedList<>()); + } + + /** + * Adds a list of operations to a class. + * + * @param className The name of the class to which the operations belong. + * @param operations A list of maps where each map represents an operation name and its associated return value (if any). + */ + public void addOperations(String className, List operations) { + List currentOperations = classToOperationMap.computeIfAbsent(className, k -> new LinkedList<>()); + + operations.forEach(operation -> { + String[] parts = operation.split("::"); + currentOperations.add(parts[1]); + }); + } + + /** + * Adds an attribute to an existing class with the specified name and type. + *

+ * This method associates an attribute with its name and type to an existing class identified by the provided class name. + * If the attribute type is "Integer" or "Real," it is treated as "Number" for consistency. The attribute is added to the + * class's attribute mapping, which categorizes attributes based on their types. + * + * @param className The name of the class to which the attribute will be added. + * @param attrName The name of the attribute to be added. + * @param attrType The type of the attribute to be added, which may be "Integer," "Real," or another type. + */ + public void addAttributeToExistingClass(String className, String attrName, String attrType) { + if ("Integer".equals(attrType) || "Real".equals(attrType)) { + attrType = "Number"; + } + + Map> classAttributes = classToAttributeMap.get(className); + List classAttributesForType = classAttributes.computeIfAbsent(attrType, k -> new LinkedList<>()); + classAttributesForType.add(attrName); + } + + /** + * Adds an object to a class or creates a new class for the object if it doesn't exist. + *

+ * This method associates an object with a class name. If the object already exists and is associated with one or more + * class names, the provided class name is added to the list of classes for that object. If the object doesn't exist, + * a new entry is created in the object-to-class mapping, associating the object with the provided class name. + * + * @param className The name of the class to which the object will be associated. + * @param objectName The name of the object to be added or associated with a class. + */ + public void addObject(String className, String objectName) { + objectsToClassMap.computeIfAbsent(objectName, key -> new LinkedList<>()).add(className); + classToObjectsMap.computeIfAbsent(className, key -> new LinkedList<>()).add(objectName); + } + + /** + * Adds the specified operation to the auto-completion map based on its OperationType. + * + * @param operationType The OperationType of the operation to be added. + */ + private void addOperationToMap(OperationType operationType) { + AutoCompletionOperation acop = new AutoCompletionOperation(); + String operation = operationType.operationName; + + if (operation.matches("\\b[_a-zA-Z][_a-zA-Z0-9]*\\b")) { + acop.name = operation; + acop.param = operationType.param; + } + + collectionToOperationsMap.computeIfAbsent(operationType.collectionType, k -> new ArrayList<>()) + .add(acop); + } + + /** + * Adds iteration operations to the auto-completion map. + * + * @param opName The name of the iteration operation to be added. + */ + private void addIterationOp(String opName) { + List params = getParamForIterationOp(opName); + for (String param : params) { + AutoCompletionOperation acop = new AutoCompletionOperation(); + acop.name = opName; + acop.param = param; + collectionToOperationsMap.get("collection").add(acop); + } + } + + /** + * Gets the parameters for the specified iteration operation. + * + * @param opName The name of the iteration operation. + * @return A list of parameters for the iteration operation. + */ + private List getParamForIterationOp(String opName) { + return switch (opName) { + case "select", "reject", "exists", "one" -> List.of("booleanExpression", "iterator | booleanExpression"); + case "forAll" -> + List.of("booleanExpression", "iterator | booleanExpression", "iterator, iterator | booleanExpression"); + case "collect", "closure", "collectNested", "isUnique", "sortedBy", "any" -> + List.of("iterator | OCL-Expression", "OCL-Expression"); + + default -> List.of(""); + }; + } + + /** + * Gets a list of all associations in the auto-completion map. + * + * @return A list of association names. + */ + private List getAllAssociations() { + return new LinkedList<>(associationToClassNamesMap.keySet()); + } + + /** + * Gets a list of objects associated with the specified association. + * + * @param association The name of the association. + * @return A list of object names associated with the given association. + */ + private List getObjectsToAssociation(String association) { + List objects = new LinkedList<>(); + for (String className : associationToClassNamesMap.get(association)) { + objects.addAll(classToObjectsMap.get(className)); + } + return objects; + } + + /** + * Gets a list of all classes in the auto-completion map. + * + * @return A list of class names. + */ + private List getAllClasses() { + return new ArrayList<>(classToAttributeMap.keySet()); + } + + /** + * Gets a list of all objects in the auto-completion map. + * + * @return A list of object names. + */ + private List getAllObjects() { + return new ArrayList<>(objectsToClassMap.keySet()); + } + + + /** + * Retrieves a list of all objects with exceptions based on a comma-separated list of object names. + *

+ * This method returns a list of all objects obtained from getAllObjects() while excluding the objects + * specified in the comma-separated "objectNames" string. If "objectNames" is null or empty, it returns + * the complete list of objects. + * + * @param parserResult The result of parsing the USE-command destroy, containing object names to exclude from the result. + * @return A list of all objects with exceptions based on the specified "objectNames." + */ + private List getAllObjectsWithException(ResultTypeUseDestroy parserResult) { + List allObjects = getAllObjects(); + if (parserResult.objectNames != null) { + List currentObjects = List.of(parserResult.objectNames.split(",")); + allObjects.removeAll(currentObjects); + } + sortSuggestions(allObjects); + return allObjects; + } + + /** + * Retrieves a list of suggestions for attributes associated with an object and optionally a specific attribute type. + *

+ * This method retrieves a list of attribute suggestions for an object, based on the classes to which the object belongs. + * It considers the provided object name and optionally an operation type (attribute type). If an operation type is specified, + * it returns a list of attributes of that type from the object's associated classes. If no operation type is specified, + * it returns all attributes from the object's associated classes. + * + * @param parserResult The result of parsing the OCLObjects, containing object name and optional operation type information. + * @return A list of attribute suggestions for the object, or an empty list if no suggestions are available or if the object is not found. + */ + private List getSuggestionsObjects(ResultTypeOCLObjects parserResult) { + String objectName = parserResult.objectName; + String operationType = parserResult.operationType; + + List classnames = objectsToClassMap.get(objectName); + List attributes = new LinkedList<>(); + + if ("Real".equals(operationType) || "Integer".equals(operationType)) { + operationType = "Number"; + } + + try { + for (String classname : classnames) {//all classes the object is an instance of + Map> classAttributes = classToAttributeMap.get(classname); + if (operationType != null) {//if not null only a specific attribute type is needed + List classAttributesForType = classAttributes.get(operationType); + attributes.addAll(classAttributesForType); + } else {//otherwise add all attributes this class has + for (List attrsForType : classAttributes.values()) { + attributes.addAll(attrsForType); + } + } + } + sortSuggestions(attributes); + return attributes; + } catch (NullPointerException ne) { + return Collections.emptyList(); + } + } + + /** + * Retrieves a list of suggestions for operations on a collection based on its type. + * + * @param parserResult The result of parsing representing the needed type of autocompletion + * @return A list of autocompletion suggestions + */ + private List getSuggestionCollections(ResultTypeOCLCollections parserResult) { + if (parserResult instanceof ResultTypeOCLCollectionsEndWithPipe) { + ResultTypeOCLCollectionsEndWithPipe parsedResult = (ResultTypeOCLCollectionsEndWithPipe) parserResult; + String iterExprIdentifier = parsedResult.iterExprIdentifier; + + return List.of(iterExprIdentifier); + } else if (parserResult instanceof ResultTypeOCLCollectionsContainsColon) { + ResultTypeOCLCollectionsContainsColon parsedResult = (ResultTypeOCLCollectionsContainsColon) parserResult; + String className = parsedResult.className; + String elemType = parsedResult.elemType; + + if (className != null) { + className = className.trim(); + if (elemType.startsWith(className)) { + String suffix = elemType.substring(elemType.indexOf(className) + className.length()); + return List.of(suffix); + } else if ("Integer".startsWith(className)) { + return List.of("Integer".substring(className.length())); + } else if ("Real".startsWith(className)) { + return List.of("Real".substring(className.length())); + } else { + return List.of(); + } + } else { + return List.of(elemType); + } + } else if (parserResult instanceof ResultTypeOCLCollectionsEndsWithCommaAndIsForAll) { + return List.of("e2"); + } else if (parserResult instanceof ResultTypeOCLCollectionsEndsWithPipeAndContainsColon) { + ResultTypeOCLCollectionsEndsWithPipeAndContainsColon parsedResult = (ResultTypeOCLCollectionsEndsWithPipeAndContainsColon) parserResult; + String className = parsedResult.className; + + if (classToObjectsMap.get(className) != null) { + List objects = new LinkedList<>(classToObjectsMap.get(className)); + sortSuggestions(objects); + return objects; + } else { + return List.of(); + } + } else if (parserResult instanceof ResultTypeOCLCollectionsDefault) { + ResultTypeOCLCollectionsDefault parsedResult = (ResultTypeOCLCollectionsDefault) parserResult; + String prefix = parsedResult.prefix; + + List operations = new LinkedList<>(); + for (AutoCompletionOperation acop : collectionToOperationsMap.get("set")) { //TODO: type in default speichern + operations.add(acop.name + "(" + acop.param + ")"); + } + + for (AutoCompletionOperation acop : collectionToOperationsMap.get("collection")) { + operations.add(acop.name + "(" + acop.param + ")"); + } + + if (prefix != null) { + operations = operations.stream() + .filter(op -> op.startsWith(prefix)) + .map(objectName -> objectName.substring(prefix.length())) + .collect(Collectors.toList()); + } + + sortSuggestions(operations); + return operations; + } else { + return List.of(); + } + } + + /** + * Retrieves a list of suggestions for the USE-command create. + *

+ * Depending on the subtype, either a List of all classes and associations is retrieved, + * or a List containing all object names except the one given when the subtype is an association type. + * + * @param parserResult The result of parsing the USE-command create, containing subtype, object name, and association name information due to inheritance. + * @return A list of suggested classes and associations or object names based on the specified subtype and object name. + */ + private List getSuggestionCreate(ResultTypeUseCreate parserResult) { + List result = new LinkedList<>(); + if (parserResult instanceof ResultTypeUseCreateAssociation) { + ResultTypeUseCreateAssociation parsedResult = (ResultTypeUseCreateAssociation) parserResult; + String objectName = parsedResult.objectName; + String associationName = parsedResult.associationName; + + result.addAll(getObjectsToAssociation(associationName)); + result.remove(objectName); + } else if (parserResult instanceof ResultTypeUseCreateDefault) { + result.addAll(getAllClasses()); + result.addAll(getAllAssociations()); + } + sortSuggestions(result); + return result; + } + + /** + * Retrieves a list of suggestions for the USE-command insert based on the specified subtype and excluding given object names. + * + * @param parserResult The result of parsing the USE-command insert, containing subtype and object names information due to inheritance. + * @return A list of suggestions based on the specified subtype and object names. + */ + private List getSuggestionInsert(ResultTypeUseInsert parserResult) { + List result; + + if (parserResult instanceof ResultTypeUseInsertObjects) { + ResultTypeUseInsertObjects parsedResult = (ResultTypeUseInsertObjects) parserResult; + String objectNames = parsedResult.objectNames; + result = getAllObjectsWithException(new ResultTypeUseDestroy(objectNames)); + } else if (parserResult instanceof ResultTypeUseInsertAssociation) { + result = getAllAssociations(); + } else { + result = List.of(); + } + + sortSuggestions(result); + return result; + } + + /** + * Retrieves a list of suggestions for the USE-command delete based on the specified subtype and object names. + * + * @param parserResult The result of parsing the USE-command delete, containing subtype and object names information due to inheritance. + * @return A list of suggestions for deletion based on the specified subtype and object names. + */ + private List getSuggestionsDelete(ResultTypeUseDelete parserResult) { + List result; + if (parserResult instanceof ResultTypeUseDeleteObjects) { + String objectNames = ((ResultTypeUseDeleteObjects) parserResult).objectNames; + result = getAllObjectsWithException(new ResultTypeUseDestroy(objectNames)); + } else if (parserResult instanceof ResultTypeUseDeleteAssociation) { + result = getAllAssociations(); + } else { + result = List.of(); + } + + sortSuggestions(result); + return result; + } + + /** + * Retrieves a list of suggestions for the USE-command set based on the specified subtype and object name. + * + * @param parserResult The result of parsing the USE-command set, containing subtype and object name information due to inheritance. + * @return A list of suggestions for setting values based on the specified subtype and object name. + */ + private List getSuggestionSet(ResultTypeUseSet parserResult) { + List result; + if (parserResult instanceof ResultTypeUseSetObject) { + result = getAllObjects(); + } else if (parserResult instanceof ResultTypeUseSetAttr) { + String objectName = ((ResultTypeUseSetAttr) parserResult).objectName; + result = getSuggestionsObjects(new ResultTypeOCLObjects(objectName, null)); + } else { + result = List.of(); + } + + sortSuggestions(result); + return result; + } + + /** + * Retrieves a list of suggestions based on the specified subtype and object name for opening operations or objects. + *

+ * This method provides suggestions for different subtypes related to opening operations or objects. + * For "openterPartObject," it returns a list of all objects from getAllObjects(). + * For "openterPartOperation," it returns a list of operations associated with the provided object name. + * + * @param parserResult The result of parsing the USE-command openter, containing subtype and object name information due to inheritance. + * @return A list of suggestions for opening operations or objects based on the specified subtype and object name. + */ + private List getSuggestionOpenter(ResultTypeUseOpenter parserResult) { + if (parserResult instanceof ResultTypeUseOpenterObject) { + String objectName = ((ResultTypeUseOpenterObject) parserResult).objectName; + if (objectName != null) { + return getSuggestionsWithObjectPrefix(new ResultTypeOCLObjectPrefix(objectName)); + } else { + return getAllObjects(); + } + } else if (parserResult instanceof ResultTypeUseOpenterOperation) { + ResultTypeUseOpenterOperation parsedResult = (ResultTypeUseOpenterOperation) parserResult; + String objectName = parsedResult.objectName; + String operationPrefix = parsedResult.operationPrefix; + + List classes = objectsToClassMap.get(objectName); + List operations = classes.stream() + .flatMap(c -> classToOperationMap.getOrDefault(c, Collections.emptyList()).stream()) + .filter(operation -> operationPrefix == null || operation.startsWith(operationPrefix)) + .map(operation -> operationPrefix == null ? operation : operation.substring(operationPrefix.length())) + .collect(Collectors.toList()); + sortSuggestions(operations); + return operations; + } else { + return List.of(); + } + } + + /** + * Retrieves a list of parameters for the USE-command check + * + * @return A list of parameters for the USE-command check + */ + private List getSuggestionCheck() { + return List.of("-a", "-d", "-v"); + } + + /** + * Retrieves a list of parameters for the USE-command step + * + * @return A list of parameters for the USE-command step + */ + private List getSuggestionStep() { + return List.of("on"); + } + + /** + * Retrieves a list of folders and .use files in the specified path as suggestions for the USE-command open. + *

+ * This method takes a path as input and returns a list containing the names of folders and + * .use files found within the directory at the given path. + * + * @param parserResult The result of parsing the USE-command open, containing the path information. + * @return A list of folder and .use file names in the specified directory path, + * or an empty list if the path is invalid or no such files are found. + */ + private List getSuggestionOpen(ResultTypeUseOpen parserResult) { + String path = parserResult.path; + File directory = new File(path); + if (directory.exists() && directory.isDirectory()) { + File[] files = directory.listFiles(); + if (files != null) { + List folderAndUseFiles = new ArrayList<>(); + for (File file : files) { + if (file.isDirectory() || (file.isFile() && file.getName().endsWith(".use"))) { + folderAndUseFiles.add(file.getName()); + } + } + sortSuggestionsOpen(folderAndUseFiles); + return folderAndUseFiles; + } + } + + return Collections.emptyList(); + } + + /** + * Retrieves a list of suggestions based on the specified subtype for the USE-command info. + *

+ * This method provides suggestions for different subtypes related to obtaining information. + * For "infoClass," it returns a list of all classes. + * For all other subtypes, it returns a predefined list of suggestions, including "class," "model," "state," + * "opstack," "prog," and "vars." + * + * @param parserResult The result of parsing the USE-command info, containing subtype information due to inheritance. + * @return A list of suggestions for obtaining information based on the specified subtype. + */ + private List getSuggestionInfo(ResultTypeUseInfo parserResult) { + if (parserResult instanceof ResultTypeUseInfoClass) { + List result = getAllClasses(); + sortSuggestions(result); + return result; + } else if (parserResult instanceof ResultTypeUseInfoDefault) { + return Arrays.asList("class", "model", "opstack", "prog", "state", "vars"); + } else { + return List.of(); + } + } + + /** + * Retrieves a list of suggestions containing each object name that starts with the given letters. + * + * @param parserResult The result of parsing the OCL object prefix, containing the object prefix. + * @return A list of suggestions of object names. + */ + private List getSuggestionsWithObjectPrefix(ResultTypeOCLObjectPrefix parserResult) { + String objectPrefix = parserResult.objectPrefix; + + Set objectsAndCollectionTypes = new HashSet<>(objectsToClassMap.keySet()); + objectsAndCollectionTypes.addAll(collectionToOperationsMap.keySet()); + List result = objectsAndCollectionTypes.stream() + .filter(objectName -> objectName != null && objectName.startsWith(objectPrefix)) + .map(objectName -> objectName.equals(objectPrefix) ? objectName : objectName.substring(objectPrefix.length())) + .collect(Collectors.toList()); + sortSuggestions(result); + return result; + } + + + private List getSuggestionsWithAttributePrefix(ResultTypeOCLAttributePrefix parserResult) { + String objectName = parserResult.objectName; + String attributePrefix = parserResult.attributePrefix; + String operationType = parserResult.operationType; + + if(operationType != null && parserResult.operationType.isEmpty()){ + operationType = null; + } + + + List allAttributes = getSuggestionsObjects(new ResultTypeOCLObjects(objectName, operationType));//TODO attributeprefix + + List result = allAttributes.stream() + .filter(attributeName -> attributeName != null && attributeName.startsWith(attributePrefix)) + .map(attributeName -> attributeName.substring(attributePrefix.length())) + .collect(Collectors.toList()); + sortSuggestions(result); + return result; + } + + /** + * Sorts the suggestion list for the USE-Command open. + *

+ * THis method sorts the suggestions for the USE-Command open. + * USE-Files are at the beginning of the list and folders follow. + * + * @param folderAndUseFiles the list of USE-Files and folders + */ + private void sortSuggestionsOpen(List folderAndUseFiles) { + folderAndUseFiles.sort((file1, file2) -> { + if (file1.endsWith(".use") && !file2.endsWith(".use")) { + return -1; + } else if (!file1.endsWith(".use") && file2.endsWith(".use")) { + return 1; + } + return file1.compareTo(file2); + }); + } + + /** + * Sorts a suggestion list alphabetically. + * + *

If any result type needs custom sorting, a new sorting method can be created + * and called instead of this one. + * + * @param suggestions The list of suggestions to be sorted. + */ + private void sortSuggestions(List suggestions) { + Collections.sort(suggestions); + } + + /** + * =================================Test members================================= + */ + + /** + * Constructs an AutoCompletion object for testing purposes. + *

+ * WARNING: Should not be used in production as it may not work as intended. + * + * @param model The MModel used to initialize the autocomplete dictionaries. + */ + public AutoCompletion(MModel model) { + classToAttributeMap = new HashMap<>(); + objectsToClassMap = new HashMap<>(); + collectionToOperationsMap = new HashMap<>(); + classToOperationMap = new HashMap<>(); + associationToClassNamesMap = new HashMap<>(); + classToObjectsMap = new HashMap<>(); + this.model = model; + + initializeAutocompletion(); + } + + /** + * Initializes the autocompletion dictionaries by populating them with data from the provided MModel. + *

+ * WARNING: Should not be used in production as it may not work as intended. + *

+ * This method adds classes, attributes, operations, associations, and standard operations + * to the autocompletion dictionaries based on the provided MModel. + */ + private void initializeAutocompletion() { + Collection mClasses = this.model.classes(); + //Add all classes, attributes and operations to autocompletion + for (MClass mClass : mClasses) { + addClass(mClass.name()); + + List operations = mClass.operations().stream() + .map(MOperation::toString) + .collect(Collectors.toList()); + addOperations(mClass.name(), operations); + List attributes = mClass.allAttributes(); + for (MAttribute mAttribute : attributes) { + addAttributeToExistingClass(mClass.name(), mAttribute.name(), mAttribute.type().toString()); + } + } + + //add associations + Collection associations = this.model.associations(); + for (MAssociation mAssociation : associations) { + List classNames = new LinkedList<>(); + for (MClass mClass : mAssociation.associatedClasses()) { + classNames.add(mClass.name()); + } + addAssociation(mAssociation.name(), classNames); + } + + //operations + for (OpGeneric op : ExpStdOp.opmap.values()) { + OperationType operationType = op.getOperationType(); + + addOperationToMap(operationType); + } + for (String opName : ParserHelper.queryIdentMap.keySet()) { + addIterationOp(opName); + } + + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletionParser.java b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletionParser.java new file mode 100644 index 000000000..c2babc32a --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletionParser.java @@ -0,0 +1,724 @@ +package org.tzi.use.autocompletion; + +import org.tzi.use.autocompletion.parserResultTypes.ResultTypeRoot; +import org.tzi.use.autocompletion.parserResultTypes.ocl.*; +import org.tzi.use.autocompletion.parserResultTypes.useCommands.*; +import org.tzi.use.parser.ocl.OCLCompiler; +import org.tzi.use.uml.mm.MModel; +import org.tzi.use.uml.ocl.expr.Expression; +import org.tzi.use.uml.ocl.value.VarBindings; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * The AutoCompletionParser class provides functionality for parsing and processing user input + * related to the USE modeling environment. It uses the existing ANTLR-Parser and USE-specific functionalities + * for handling OCL expressions and USE commands. + *

+ * The input string is parsed instantly when an object is instantiated. To retrieve the result {@link #getResult()} must be called + */ +public class AutoCompletionParser { + private final ParserResult parserResult; + + /** + * Constructor for AutoCompletionParser. + * + * @param model The MModel from the USE environment. + * @param input The user input string to be parsed and processed. + */ + public AutoCompletionParser(MModel model, String input) { + PrintWriter err = new PrintWriter(new StringWriter()); + parserResult = new ParserResult(); + + parse(model, input, err); + + processResult(); + } + + /** + * Retrieves the parsing result. + *

+ * Needs to be called after instantiation + * + * @return The parsing result encapsulated in a ParserResult object. + */ + public ParserResult getResult() { + return parserResult; + } + + + /** + * Processes the parsing result to set the final results in a structured manner. + *

+ * Necessary since the result only consists of a collection of types until now and needs to be processed, so it can be used by {@link AutoCompletion}. + */ + private void processResult() { + for (String type : parserResult.foundTypes) { + switch (type) { + case "objectName" -> { + if (parserResult.result == null) { + parserResult.result = new ResultTypeOCLObjectPrefix(parserResult.foundValues.get("objectPrefix")); + } else { + if (parserResult.result instanceof ResultTypeOCLObjects) { + parserResult.result = new ResultTypeOCLAttributePrefix(((ResultTypeOCLObjects) parserResult.result).objectName, ((ResultTypeOCLObjects) parserResult.result).operationType, parserResult.foundValues.get("objectPrefix")); + } + } + } + case "object" -> { + if (parserResult.result == null) {//TODO: evtl. if von oben einfügen, wirkt aber nicht notwendig + parserResult.result = new ResultTypeOCLObjects(parserResult.foundValues.get("objectName"), parserResult.foundValues.get("operationType")); + } + } + case "set", "bag", "orderedSet", "sequence" -> { //might be orderedset + if (!parserResult.foundValues.containsKey("operationName")) { + parserResult.result = new ResultTypeOCLCollectionsDefault(parserResult.foundValues.get("objectPrefix"), type); + } + + if (parserResult.foundValues.containsKey("attributePrefix")) { + parserResult.result = new ResultTypeOCLAttributePrefix(parserResult.foundValues.get("objectName"), "", parserResult.foundValues.get("attributePrefix")); + } + } + case "iterationExpression" -> { + Set keySet = parserResult.foundValues.keySet(); + if ("endsWithPipeAndContainsColon".equals(parserResult.foundValues.get("subtype"))) { + parserResult.result = new ResultTypeOCLCollectionsEndsWithPipeAndContainsColon(parserResult.foundValues.get("className")); + } else if ("containsColon".equals(parserResult.foundValues.get("subtype"))) { + parserResult.result = new ResultTypeOCLCollectionsContainsColon(parserResult.foundValues.get("className"), parserResult.foundValues.get("elemType")); + } else if ("endsWithPipe".equals(parserResult.foundValues.get("subtype"))) { + parserResult.result = new ResultTypeOCLCollectionsEndWithPipe(parserResult.foundValues.get("identifier")); + } else if (keySet.contains("operationPrefix")) { + if (parserResult.result == null) { + parserResult.result = new ResultTypeOCLCollectionsDefault(parserResult.foundValues.get("operationPrefix"), null); + } else if (parserResult.result instanceof ResultTypeOCLCollectionsDefault) { + parserResult.result = new ResultTypeOCLCollectionsDefault(parserResult.foundValues.get("operationPrefix"), ((ResultTypeOCLCollectionsDefault) parserResult.result).collectionType); + } else { + System.out.println("Hier sollten wir nicht sein"); //TODO: maybe bei "endsWithCommaAndIsForAll" + } + } + } + + default -> { + if (parserResult.foundTypes.get(0).startsWith("USE")) { //ignore boolean, Integer, String + createUseResultTypes(parserResult.foundTypes.get(0)); + } + } + } + } + } + + /** + * Creates a specific instance of {@link ResultTypeRoot} based on the provided 'type'. + * + * @param type The type of USE command to specify the needed tesult type. + */ + private void createUseResultTypes(String type) { + switch (type) { + case "USE_openter" -> createUseOpenterResult(); + case "USE_set" -> createUseSetResult(); + case "USE_info" -> createUseInfoResult(); + case "USE_open" -> createUseOpenResult(); + case "USE_step" -> createUseStepResult(); + case "USE_insert" -> createUseInsertResult(); + case "USE_delete" -> createUseDeleteResult(); + case "USE_create" -> createUseCreateResult(); + case "USE_destroy" -> createUseDestroyResult(); + case "USE_check" -> createUseCheckResult(); + } + } + + /** + * Creates a ResultType for the USE-command "openter". + * The ResultType can be either {@link ResultTypeUseOpenterOperation} or {@link ResultTypeUseOpenterObject}. + */ + private void createUseOpenterResult() { + if (parserResult.foundValues.containsKey("objectName") && parserResult.foundValues.containsKey("operationPrefix")) { + parserResult.result = new ResultTypeUseOpenterOperation(parserResult.foundValues.get("objectName"), parserResult.foundValues.get("operationPrefix")); + } else { + parserResult.result = new ResultTypeUseOpenterObject(parserResult.foundValues.get("objectName")); + } + } + + /** + * Creates a ResultType for the USE command "set". + * The ResultType can be either {@link ResultTypeUseSetAttr} or {@link ResultTypeUseSetObject}. + */ + private void createUseSetResult() { + if (parserResult.foundValues.containsKey("objectName")) { + parserResult.result = new ResultTypeUseSetAttr(parserResult.foundValues.get("objectName")); + } else { + parserResult.result = new ResultTypeUseSetObject(); + } + } + + /** + * Creates a ResultType for the USE command "info". + * The ResultType can be either {@link ResultTypeUseInfoClass} or {@link ResultTypeUseInfoDefault}. + */ + private void createUseInfoResult() { + if ("infoClass".equals(parserResult.foundValues.get("subtype"))) { + parserResult.result = new ResultTypeUseInfoClass(); + } else { + parserResult.result = new ResultTypeUseInfoDefault(); + } + } + + /** + * Creates a ResultType for the USE command "open". + * The ResultType is {@link ResultTypeUseOpen}. + */ + private void createUseOpenResult() { + parserResult.result = new ResultTypeUseOpen(parserResult.foundValues.get("path")); + } + + /** + * Creates a ResultType for the USE command "step". + * The ResultType is {@link ResultTypeUseStep}. + */ + private void createUseStepResult() { + parserResult.result = new ResultTypeUseStep(); + } + + /** + * Creates a ResultType for the USE command "insert". + * The ResultType can be either {@link ResultTypeUseInsertObjects} or {@link ResultTypeUseInsertAssociation}. + */ + private void createUseInsertResult() { + if (parserResult.foundValues.containsKey("objectNames")) { + parserResult.result = new ResultTypeUseInsertObjects(parserResult.foundValues.get("objectNames")); + } else { + parserResult.result = new ResultTypeUseInsertAssociation(); + } + } + + /** + * Creates a ResultType for the USE command "delete". + * The ResultType can be either {@link ResultTypeUseDeleteObjects} or {@link ResultTypeUseDeleteAssociation}. + */ + private void createUseDeleteResult() { + if ("deletePartObjects".equals(parserResult.foundValues.get("subtype"))) { + parserResult.result = new ResultTypeUseDeleteObjects(parserResult.foundValues.get("objectNames")); + } else { + parserResult.result = new ResultTypeUseDeleteAssociation(); + } + } + + /** + * Creates a ResultType for the USE command "create". + * The ResultType can be either {@link ResultTypeUseCreateAssociation} or {@link ResultTypeUseCreateDefault}. + */ + private void createUseCreateResult() { + if ("association".equals(parserResult.foundValues.get("subtype"))) { + parserResult.result = new ResultTypeUseCreateAssociation(parserResult.foundValues.get("objectName"), parserResult.foundValues.get("associationName")); + } else { + parserResult.result = new ResultTypeUseCreateDefault(); + } + } + + /** + * Creates a ResultType for the USE command "destroy". + * The ResultType is {@link ResultTypeUseDestroy}. + */ + private void createUseDestroyResult() { + parserResult.result = new ResultTypeUseDestroy(parserResult.foundValues.get("objectNames")); + } + + /** + * Creates a ResultType for the USE command "check". + * The ResultType is {@link ResultTypeUseCheck}. + */ + private void createUseCheckResult() { + parserResult.result = new ResultTypeUseCheck(); + } + + + /** + * Parses the user input using the provided MModel and updates the parserResult object. + * + * @param model The MModel from the USE environment. + * @param input The user input string to be parsed. + * @param err AutoCompletionPrintWriter for error handling. + */ + public void parse(MModel model, String input, PrintWriter err) { + Map typeOfInput = getUSECommandFromInput(input); + + //only continue if input was not a USE-command + if (!typeOfInput.get("type").isEmpty()) { + parserResult.foundTypes.add(typeOfInput.get("type")); + typeOfInput.remove("type"); + for (String key : typeOfInput.keySet()) { + parserResult.foundValues.put(key, typeOfInput.get(key)); + } + } else { + parseOCLOnly(model, input, err); + } + } + + /** + * Parses the input using OCL only (this means not considering USE-Commands), handling various cases, + * including collection types and iteration expressions. + * + * @param model The MModel from the USE environment. + * @param input The user input string to be parsed. + * @param err AutoCompletionPrintWriter for error handling. + */ + private void parseOCLOnly(MModel model, String input, PrintWriter err) { + //remove leading ? and possible spaces following these + input = input.trim().replaceFirst("^(\\?\\?|\\?)\\s*", ""); + + + String trailingDelimiter = getTrailingDelimiter(input); + String type = typeOfInput(model, input, err, trailingDelimiter); + + if (type == null) {//input doesnt compile as type + List tokens = splitInput(input); + + if (tokens.size() == 1) { + handle(input, trailingDelimiter); + } else { + for (String token : tokens) { + token = handleParenthesis(token.trim()); + + parseOCLOnly(model, token.trim(), err); //could remove something -> recursion + } + } + } else { //input compiles as type + handleAddingType(type, trailingDelimiter); + } + + } + + /** + * Handles a token containing parentheses, specifically in the context of iteration expressions. + * Parses the token, extracts relevant information, and updates the parserResult accordingly. + * + * @param token The token to be processed. + * @return The processed token or a modified version based on the handling logic. + */ + private String handleParenthesis(String token) { + String res = token; + + if (!token.startsWith("(") && token.contains("(") && iterationExpressionAllowed(token)) {//TODO: might be wrong to check if iteration expression is allowed + if (!token.contains(",") || (token.contains(",") && token.startsWith("forAll"))) { //illegal character + if (token.contains("(")) { + res = token.substring(token.indexOf("(") + 1); + if (token.endsWith(")")) { + res = res.substring(0, res.length() - 1).trim(); + } + + if (res.endsWith(",") && token.startsWith("forAll")) { + parserResult.foundTypes.add("iterationExpression"); + parserResult.foundValues.put("subtype", "endsWithCommaAndIsForAll"); + } else if (res.endsWith("|")) { + if (res.contains(":")) { + parserResult.foundTypes.add("iterationExpression"); + parserResult.foundValues.put("identifier", "soos"); + parserResult.foundValues.put("className", "soos"); + parserResult.foundValues.put("subtype", "endsWithPipeAndContainsColon"); + } else { + parserResult.foundTypes.add("iterationExpression"); + parserResult.foundValues.put("identifier", "soos"); + parserResult.foundValues.put("subtype", "endsWithPipe"); + } + } else if (res.contains(":")) { + String[] strings = res.split(":"); + if (strings.length > 1) { + parserResult.foundValues.put("className", strings[1]); + } + parserResult.foundTypes.add("iterationExpression"); + parserResult.foundValues.put("identifier", "soos"); + parserResult.foundValues.put("subtype", "containsColon"); + + } + //} + parserResult.foundValues.put("operationName", token.substring(0, token.indexOf("("))); + } + } else { + parserResult.clearAll(); + } + } else if (token.endsWith(")") && token.length() > 1) { + parserResult.foundValues.put("attributePrefix", token.substring(0, token.length() - 1)); + } + return res; + } + + /** + * Checks whether the given token (an operation name) is an operation allowing an iteration expression as a parameter. + * + * @param token The token to be checked. + * @return True if the token is allowed for iteration expression; otherwise, false. + */ + private boolean iterationExpressionAllowed(String token) { + String operation = token.substring(0, token.indexOf("(")); + return List.of("forAll", "any", "collect", "collectNested", "exists", "isUnique", "one", "reject", "select", "sortedBy").contains(operation); + } + + /** + * Handles the addition of a type to the parsing result. Modifies the parserResult based on the type and trailing delimiter. + * + * @param type The type to be added. + * @param trailingDelimiter The trailing delimiter associated with the type. + */ + private void handleAddingType(String type, String trailingDelimiter) { + if (type.equals("OclVoid")) {//don't add anything if token was null + return; + } + + type = handleCollectionTypes(type, trailingDelimiter); + + + if ("String".equals(type) || "Integer".equals(type) || "Real".equals(type) || "Boolean".equals(type)) {//set operation type + parserResult.foundValues.put("operationType", type); + } + + if (type != null) { + parserResult.foundTypes.add(type); + } + } + + /** + * Handles collection types, extracting element types and modifying the parsing result accordingly. + * + * @param type The collection type to be handled. + * @param trailingDelimiter The trailing delimiter associated with the type. + * @return The modified type or null in certain cases. + */ + private String handleCollectionTypes(String type, String trailingDelimiter) { + if (type.startsWith("Set") || type.startsWith("OrderedSet") || type.startsWith("Bag") || type.startsWith("Sequence")) { + + if (!trailingDelimiter.equals("->")) { + return null; + } + + String regex = "(\\w+)\\((\\w+)\\)"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(type); + + if (matcher.find()) { + String collectionType = matcher.group(1); + String elemType = matcher.group(2); + + parserResult.foundValues.put("elemType", elemType); + return Character.toLowerCase(collectionType.charAt(0)) + collectionType.substring(1); + } else { + return type; + } + } else { + return type; + } + } + + /** + * Determines the type of the given OCL expression by compiling it using the provided model. + * + * @param model The MModel used for compilation. + * @param input The OCL expression to be compiled and analyzed. + * @param err The PrintWriter to capture compilation errors. + * @param trailingDelimiter The trailing delimiter associated with the input. + * @return The type of the OCL expression, or null if compilation fails. + */ + private String typeOfInput(MModel model, String input, PrintWriter err, String trailingDelimiter) { + input = input.replace(trailingDelimiter, ""); + Expression expr = OCLCompiler.compileExpression(model, input, "AutoCompletion", err, new VarBindings()); + + if (expr != null) { + return expr.type().toString(); + } else { + return null; + } + } + + /** + * Extracts the trailing delimiter, if any, from the given input string based on a predefined set of delimiters. + * + * @param input The input string to be analyzed for a trailing delimiter. + * @return The trailing delimiter found in the input, or an empty string if none is present. + */ + private String getTrailingDelimiter(String input) { + Pattern pattern = Pattern.compile("([.=><]|<=|>=|->)$"); + Matcher matcher = pattern.matcher(input); + + if (matcher.find()) { + return matcher.group(1); // Return the trailing delimiter + } else { + return ""; // No trailing delimiter found + } + } + + /** + * Handles the given input based on the provided trailing delimiter. It updates the parserResult accordingly. + * + * @param input The input string to be handled. + * @param trailingDelimiter The trailing delimiter associated with the input. + */ + private void handle(String input, String trailingDelimiter) { + switch (trailingDelimiter) { + case "." -> { + if (isValidJavaIdentifier(input.substring(0, input.length() - 1))) { + parserResult.foundTypes.add("object"); + input = input.replace(trailingDelimiter, ""); + parserResult.foundValues.put("objectName", input.trim()); + } + } + case "" -> { + if (isValidJavaIdentifier(input)) {//actual object or operation prefix + parserResult.foundTypes.add("objectName");//TODO: change to prefix + parserResult.foundValues.put("objectPrefix", input.trim()); + } else {//iterationexpression + handleIterationExpression(input); + } + } + } + } + + /** + * Handles the given input as an iteration expression, extracting the identifier and optional class name. + * + * @param input The input string representing an iteration expression. + */ + private void handleIterationExpression(String input) { + String[] parts = input.split("[:|]"); + + if (parts.length > 0) { + String identifier = parts[0].trim(); + String className = (parts.length > 1) ? parts[1].trim() : null; + if (isValidJavaIdentifier(identifier)) { + parserResult.foundValues.put("identifier", identifier); + + if (className != null) { + parserResult.foundValues.put("className", className); + } + } + } + } + + /** + * Splits the given input string into tokens based on a predefined set of delimiters and returns the result. + * + * @param input The input string to be split into tokens. + * @return A list of tokens obtained from the input string after splitting. + */ + private List splitInput(String input) { + String pattern = "(?<=<=|>=|=|>|<|\\.)"; + String[] tokens = input.split(pattern); + + return formatReals(tokens); + } + + /** + * Formats the given array of tokens, identifying and combining consecutive tokens representing real numbers. + * + * @param tokens The array of tokens to be formatted, possibly containing consecutive parts of real numbers. + * @return A list of formatted tokens, where consecutive parts of real numbers are combined into complete real numbers. + */ + private List formatReals(String[] tokens) { + List res = new LinkedList<>(); + for (int i = 0; i < tokens.length - 1; i++) { + String otherToken = tokens[i + 1]; + otherToken = formatOtherToken(otherToken).trim(); + + String potentialRealNumber = tokens[i] + otherToken; + String regex = "^\\d+\\.\\d+$"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(potentialRealNumber); + + // Check if the input string matches the pattern + if (matcher.matches()) { + res.add(potentialRealNumber); + i++; //skip next token since it's already added + } else { + res.add(tokens[i]); + } + } + + res.add(tokens[tokens.length - 1]);//add last token since it is skipped in loop + + return res; + } + + /** + * Formats the given token by extracting a portion of it up to the next OCL operator or the end of the token. + * + * @param token The token to be formatted, possibly containing parts up to the next OCL operator. + * @return The formatted portion of the token, up to the next OCL operator or the end of the token. + */ + private String formatOtherToken(String token) { + String otherToken = ""; + + Pattern pattern = Pattern.compile(".*?(?=(<=|>=|=|<|>|\\.|$))"); + Matcher matcher = pattern.matcher(token); + + if (matcher.find()) { + otherToken = matcher.group(); + } + return otherToken; + } + + /** + * Parses the input string to determine the USE command type and its parameters. + * + * @param input The input string containing the USE command. + * @return A {@code Map} representing the USE command and its parameters. + */ + private Map getUSECommandFromInput(String input) { + Map result = new HashMap<>(); + result.put("type", ""); + + if (input.startsWith("!")) { + input = input.replaceAll("^!\\s+", "!"); + } + input = input.replaceAll("\\s*\\(\\s*", "("); + + if (input.startsWith("!create")) { + if (input.trim().endsWith(":")) { + result.put("type", "USE_create"); + } else if (input.trim().endsWith(",")) { + result.put("objectName", getObjectName(input)); + result.put("associationName", getAssociationName(input)); + result.put("subtype", "association"); + result.put("type", "USE_create"); + } else if (input.matches(".*between\\s*\\(.*")) { + result.put("associationName", getAssociationName(input)); + result.put("subtype", "association"); + result.put("type", "USE_create"); + } + } else if (input.startsWith("!destroy ")) { + result.put("type", "USE_destroy"); + //track all objects in the input so these are not recommended again + String objectNames = input.substring("!destroy ".length()).trim(); + if (!objectNames.isEmpty()) { + result.put("objectNames", objectNames); + } + } else if (input.matches("!insert\\s*\\(.*")) { + result.put("type", "USE_insert"); + if (input.endsWith("into ")) { + result.put("subtype", "insertPartAssociation"); + } else { + result.put("subtype", "insertPartObjects"); + int indexOfOpeningParenthesis = input.indexOf("("); + String objectNames = input.substring(indexOfOpeningParenthesis + 1); + if (!objectNames.isEmpty()) { + result.put("objectNames", objectNames); + } else { + result.put("objectNames", null); + } + } + } else if (input.startsWith("!delete (") || input.startsWith("!delete(")) { + result.put("type", "USE_delete"); + if (input.endsWith("from ")) { + result.put("subtype", "deletePartAssociation"); + } else { + int indexOfOpeningParenthesis = input.indexOf("("); + String objectNames = input.substring(indexOfOpeningParenthesis + 1); + if (!objectNames.isEmpty()) { + result.put("objectNames", objectNames); + } + result.put("subtype", "deletePartObjects"); + } + } else if (input.startsWith("!set ")) { + result.put("type", "USE_set"); + if (input.endsWith(".")) { + result.put("subtype", "setPartAttr"); + input = input.substring(0, input.length() - 1); + result.put("objectName", getLastWord(input)); + } else { + result.put("subtype", "setPartObject"); + } + } else if (input.startsWith("!openter ")) { + result.put("type", "USE_openter"); + if (input.endsWith("!openter ")) { + result.put("subtype", "openterPartObject"); + } else { + String[] words = input.split("\\s"); + if ((words.length == 2 && input.endsWith(" ")) || words.length == 3) { + result.put("objectName", words[1]); + if (words.length == 3) { + result.put("operationPrefix", words[2]); + } else { + result.put("operationPrefix", null); + } + result.put("subtype", "openterPartOperation"); + } else { + result.put("objectName", words[1]); + result.put("subtype", "openterPartObject"); + } + } + } else if (input.startsWith("!opexit ")) { + result.put("type", "USE_opexit"); + } else if (input.startsWith("check")) { + result.put("type", "USE_check"); + } else if (input.startsWith("step")) { + result.put("type", "USE_step"); + } else if (input.startsWith("!open")) { + result.put("type", "USE_open"); + result.put("path", getLastWord(input)); + } else if (input.startsWith("info")) { + result.put("type", "USE_info"); + if (input.endsWith("class ")) { + result.put("subtype", "infoClass"); + } + } + return result; + } + + /** + * Retrieves the last word from the input string. + * + * @param input The input string. + * @return The last word in the input string, or an empty string if the input is null or empty. + */ + private String getLastWord(String input) { + String[] words = input.split("\\s+"); + if (words.length > 0) { + return words[words.length - 1]; + } else { + return ""; + } + } + + /** + * Retrieves the association name from the input string. + * + * @param input The input string. + * @return The association name, or null if not found. + */ + private String getAssociationName(String input) { + //retrieve associationName + Pattern pattern = Pattern.compile("\\s*:\\s*(.*?)\\s*between"); + Matcher matcher = pattern.matcher(input); + if (matcher.find()) { + return matcher.group(1); + } else { + return null; + } + } + + /** + * Retrieves the object name from the input string. + * + * @param input The input string. + * @return The object name, or null if not found. + */ + private String getObjectName(String input) { + Pattern pattern = Pattern.compile("\\(([^,]+),"); + Matcher matcher = pattern.matcher(input); + if (matcher.find()) { + return matcher.group(1); + } else { + return null; + } + } + + /** + * Checks if the given string is a valid Java identifier. + * + * @param str The string to be checked. + * @return {@code true} if the string is a valid Java identifier, {@code false} otherwise. + */ + private boolean isValidJavaIdentifier(String str) { + return str.matches("[a-zA-Z_$][a-zA-Z_$0-9]*"); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/ParserResult.java b/use-gui/src/main/java/org/tzi/use/autocompletion/ParserResult.java new file mode 100644 index 000000000..04008b5b4 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/ParserResult.java @@ -0,0 +1,53 @@ +package org.tzi.use.autocompletion; + +import org.tzi.use.autocompletion.parserResultTypes.ResultTypeRoot; + +import java.util.*; + +/** + * Represents the result of a parsed input String by {@link AutoCompletionParser} + * It contains information about found types, found values, and the root result type. + */ +public class ParserResult { + public List foundTypes; + + public Map foundValues; + + public ResultTypeRoot result; + + public ParserResult(){ + foundTypes = new LinkedList<>(); + foundValues = new HashMap<>(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ParserResult that = (ParserResult) o; + return Objects.equals(foundTypes, that.foundTypes) && + Objects.equals(foundValues, that.foundValues) && + Objects.equals(result, that.result); + } + + @Override + public int hashCode() { + return Objects.hash(foundTypes, foundValues, result); + } + + @Override + public String toString() { + return "ParserResult{" + + "foundTypes=" + foundTypes + + ", foundValues=" + foundValues + + ", result=" + result + + '}'; + } + + + + public void clearAll() { + foundTypes.clear(); + foundValues.clear(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionForClassName.java b/use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionForClassName.java new file mode 100644 index 000000000..94d3ef08e --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionForClassName.java @@ -0,0 +1,78 @@ +package org.tzi.use.autocompletion; + +import java.util.*; + +/** + * Is a specialized map that associates types with their + * respective attributes. Is used in {@link AutoCompletion} to map class names to types to attributes. + * Each Type is mapped to a list of attribute names, where each attribute + * is represented as a string. + */ +public class SuggestionForClassName implements Map> { + //contains the attributes of a class seperated by Type, e.g.: "Number", "String", "Boolean" + private final Map> typeToAttributeMap; + + public SuggestionForClassName() { + typeToAttributeMap = new HashMap<>(); + } + + @Override + public int size() { + return typeToAttributeMap.size(); + } + + @Override + public boolean isEmpty() { + return typeToAttributeMap.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return typeToAttributeMap.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return typeToAttributeMap.containsValue(value); + } + + @Override + public List get(Object key) { + return typeToAttributeMap.get(key); + } + + @Override + public List put(String key, List value) { + return typeToAttributeMap.put(key, value); + } + + @Override + public List remove(Object key) { + return typeToAttributeMap.remove(key); + } + + @Override + public void putAll(Map> m) { + typeToAttributeMap.putAll(m); + } + + @Override + public void clear() { + typeToAttributeMap.clear(); + } + + @Override + public Set keySet() { + return typeToAttributeMap.keySet(); + } + + @Override + public Collection> values() { + return typeToAttributeMap.values(); + } + + @Override + public Set>> entrySet() { + return typeToAttributeMap.entrySet(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionResult.java b/use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionResult.java new file mode 100644 index 000000000..1372ee1f5 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionResult.java @@ -0,0 +1,12 @@ +package org.tzi.use.autocompletion; + +import java.util.List; + +/** + * Represents the result of an autocompletion suggestion. + * It contains a list of suggested strings and the prefix (can be null) used for generating these suggestions. + */ +public class SuggestionResult { + public List suggestions; + public String prefix; +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ResultTypeRoot.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ResultTypeRoot.java new file mode 100644 index 000000000..7ddf62dd7 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ResultTypeRoot.java @@ -0,0 +1,8 @@ +package org.tzi.use.autocompletion.parserResultTypes; + +/** + * This abstract class serves as the root type for all ResultType classes in the autocompletion parser. + * Subclasses of this class represent different types of results obtained during parsing. + */ +public abstract class ResultTypeRoot { +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCL.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCL.java new file mode 100644 index 000000000..2cec38383 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCL.java @@ -0,0 +1,10 @@ +package org.tzi.use.autocompletion.parserResultTypes.ocl; + +import org.tzi.use.autocompletion.parserResultTypes.ResultTypeRoot; + +/** + * This abstract class serves as the parent for all ResultType classes associated with OCL + * in the autocompletion parser. It extends {@link ResultTypeRoot}. + */ +public abstract class ResultTypeOCL extends ResultTypeRoot { +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLAttributePrefix.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLAttributePrefix.java new file mode 100644 index 000000000..92935ebb0 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLAttributePrefix.java @@ -0,0 +1,39 @@ +package org.tzi.use.autocompletion.parserResultTypes.ocl; + +import java.util.Objects; + +/** + * Represents the autocompletion parser's result type for OCL expressions with an attribute prefix. + * It extends {@link ResultTypeOCL}. + */ +public class ResultTypeOCLAttributePrefix extends ResultTypeOCL { + + public String objectName; + public String operationType; + public String attributePrefix; + + public ResultTypeOCLAttributePrefix(String objectName, String operationType, String attributePrefix) { + this.objectName = objectName; + this.operationType = operationType; + this.attributePrefix = attributePrefix; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeOCLAttributePrefix that = (ResultTypeOCLAttributePrefix) o; + return Objects.equals(objectName, that.objectName) && + Objects.equals(operationType, that.operationType) && + Objects.equals(attributePrefix, that.attributePrefix); + } + + @Override + public String toString() { + return "ResultTypeOCLAttributePrefix{" + + "objectName='" + objectName + '\'' + + ", operationType='" + operationType + '\'' + + ", attributePrefix='" + attributePrefix + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollections.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollections.java new file mode 100644 index 000000000..768af31bd --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollections.java @@ -0,0 +1,8 @@ +package org.tzi.use.autocompletion.parserResultTypes.ocl; + +/** + * Represents the abstract base class for autocompletion parser result types associated with OCL collections. + * It extends {@link ResultTypeOCL}. + */ +public abstract class ResultTypeOCLCollections extends ResultTypeOCL{ +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsContainsColon.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsContainsColon.java new file mode 100644 index 000000000..483da150c --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsContainsColon.java @@ -0,0 +1,35 @@ +package org.tzi.use.autocompletion.parserResultTypes.ocl; + +import java.util.Objects; + +/** + * Represents a specific autocompletion parser result type associated with OCL collections. + * It is used for iteration expressions containing a colon but not ending on "," or "|" + * It extends {@link ResultTypeOCLCollections}. + */ +public class ResultTypeOCLCollectionsContainsColon extends ResultTypeOCLCollections { + public String className; + public String elemType; + + public ResultTypeOCLCollectionsContainsColon(String className, String elemType) { + this.className = className; + this.elemType = elemType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeOCLCollectionsContainsColon that = (ResultTypeOCLCollectionsContainsColon) o; + return Objects.equals(className, that.className) && + Objects.equals(elemType, that.elemType); + } + + @Override + public String toString() { + return "ResultTypeOCLCollectionsContainsColon{" + + "className='" + className + '\'' + + ", elemType='" + elemType + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsDefault.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsDefault.java new file mode 100644 index 000000000..62eae1fa4 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsDefault.java @@ -0,0 +1,30 @@ +package org.tzi.use.autocompletion.parserResultTypes.ocl; + +import java.util.Objects; + +public class ResultTypeOCLCollectionsDefault extends ResultTypeOCLCollections { + public String prefix; + + public String collectionType; + + public ResultTypeOCLCollectionsDefault(String prefix, String collectionType) { + this.prefix = prefix; + this.collectionType = collectionType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeOCLCollectionsDefault that = (ResultTypeOCLCollectionsDefault) o; + return Objects.equals(prefix, that.prefix); + } + + @Override + public String toString() { + return "ResultTypeOCLCollectionsDefault{" + + "prefix='" + prefix + '\'' + + "collectionType='" + collectionType + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndWithPipe.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndWithPipe.java new file mode 100644 index 000000000..418dc5a81 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndWithPipe.java @@ -0,0 +1,30 @@ +package org.tzi.use.autocompletion.parserResultTypes.ocl; + +import java.util.Objects; + +/** + * Represents a specific autocompletion parser result type associated with OCL collections. + * It is used for expressions ending with a pipe (|) and containing an iteration expression identifier. + * It extends {@link ResultTypeOCLCollections}. + */ +public class ResultTypeOCLCollectionsEndWithPipe extends ResultTypeOCLCollections{ + public String iterExprIdentifier; + + public ResultTypeOCLCollectionsEndWithPipe(String iterExprIdentifier) { + this.iterExprIdentifier = iterExprIdentifier; + } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeOCLCollectionsEndWithPipe that = (ResultTypeOCLCollectionsEndWithPipe) o; + return Objects.equals(iterExprIdentifier, that.iterExprIdentifier); + } + + @Override + public String toString() { + return "ResultTypeOCLCollectionsEndWithPipe{" + + "iterExprIdentifier='" + iterExprIdentifier + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithCommaAndIsForAll.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithCommaAndIsForAll.java new file mode 100644 index 000000000..9b007f80b --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithCommaAndIsForAll.java @@ -0,0 +1,14 @@ +package org.tzi.use.autocompletion.parserResultTypes.ocl; + +/** + * Represents a specific autocompletion parser result type associated with OCL collections. + * It is used for expressions ending with a comma and having "forAll" as operation. + * It extends {@link ResultTypeOCLCollections}. + */ +public class ResultTypeOCLCollectionsEndsWithCommaAndIsForAll extends ResultTypeOCLCollections{ + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o != null && getClass() == o.getClass(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithPipeAndContainsColon.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithPipeAndContainsColon.java new file mode 100644 index 000000000..91b9309b1 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithPipeAndContainsColon.java @@ -0,0 +1,31 @@ +package org.tzi.use.autocompletion.parserResultTypes.ocl; + +import java.util.Objects; + +/** + * Represents a specific autocompletion parser result type associated with OCL collections. + * It is used for expressions ending with a pipe (|) and containing a colon (:). + * It extends {@link ResultTypeOCLCollections}. + */ +public class ResultTypeOCLCollectionsEndsWithPipeAndContainsColon extends ResultTypeOCLCollections{ + public String className; + + public ResultTypeOCLCollectionsEndsWithPipeAndContainsColon(String className) { + this.className = className; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeOCLCollectionsEndsWithPipeAndContainsColon that = (ResultTypeOCLCollectionsEndsWithPipeAndContainsColon) o; + return Objects.equals(className, that.className); + } + + @Override + public String toString() { + return "ResultTypeOCLCollectionsEndsWithPipeAndContainsColon{" + + "className='" + className + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjectPrefix.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjectPrefix.java new file mode 100644 index 000000000..dcc6bbed8 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjectPrefix.java @@ -0,0 +1,30 @@ +package org.tzi.use.autocompletion.parserResultTypes.ocl; + +import java.util.Objects; + +/** + * Represents a specific autocompletion parser result type associated with OCL. + * It is used for expressions containing an object prefix. + * It extends {@link ResultTypeOCL}. + */ +public class ResultTypeOCLObjectPrefix extends ResultTypeOCL{ + public String objectPrefix; + + public ResultTypeOCLObjectPrefix(String objectPrefix) { + this.objectPrefix = objectPrefix; + } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeOCLObjectPrefix that = (ResultTypeOCLObjectPrefix) o; + return Objects.equals(objectPrefix, that.objectPrefix); + } + + @Override + public String toString() { + return "ResultTypeOCLObjectPrefix{" + + "objectPrefix='" + objectPrefix + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjects.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjects.java new file mode 100644 index 000000000..71f0f2fee --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjects.java @@ -0,0 +1,36 @@ +package org.tzi.use.autocompletion.parserResultTypes.ocl; + +import java.util.Objects; + +/** + * Represents a specific autocompletion parser result type associated with OCL. + * It is used for expressions containing a completed object name. + * It extends {@link ResultTypeOCL}. + */ +public class ResultTypeOCLObjects extends ResultTypeOCL { + + public String objectName; + + public String operationType; + + public ResultTypeOCLObjects(String objectName, String operationType) { + this.objectName = objectName; + this.operationType = operationType; + }@Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeOCLObjects that = (ResultTypeOCLObjects) o; + return Objects.equals(objectName, that.objectName) && + Objects.equals(operationType, that.operationType); + } + + @Override + public String toString() { + return "ResultTypeOCLObjects{" + + "objectName='" + objectName + '\'' + + ", operationType='" + operationType + '\'' + + '}'; + } + +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCheck.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCheck.java new file mode 100644 index 000000000..f03699cdf --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCheck.java @@ -0,0 +1,13 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents a specific autocompletion parser result type associated with the USE command "check". + * It is a subtype of {@link ResultTypeUseCommands}. + */ +public class ResultTypeUseCheck extends ResultTypeUseCommands{ + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o != null && getClass() == o.getClass(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCommands.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCommands.java new file mode 100644 index 000000000..95f6317c3 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCommands.java @@ -0,0 +1,10 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +import org.tzi.use.autocompletion.parserResultTypes.ResultTypeRoot; + +/** + * This abstract class serves as the parent for all ResultType classes associated with USE commands + * in the autocompletion parser. It extends {@link ResultTypeRoot}. + */ +public abstract class ResultTypeUseCommands extends ResultTypeRoot { +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreate.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreate.java new file mode 100644 index 000000000..6c2366c59 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreate.java @@ -0,0 +1,9 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an abstract autocompletion parser result type associated with USE commands for creation. + * It is a subtype of {@link ResultTypeUseCommands}. + */ + +public abstract class ResultTypeUseCreate extends ResultTypeUseCommands{ +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateAssociation.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateAssociation.java new file mode 100644 index 000000000..9f78aa876 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateAssociation.java @@ -0,0 +1,33 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +import java.util.Objects; + +/** + * Represents an autocompletion parser result type associated with the USE command "create" when an association name was already given. + * It is a subtype of {@link ResultTypeUseCreate}. + */ +public class ResultTypeUseCreateAssociation extends ResultTypeUseCreate{ + public String objectName; + public String associationName; + + public ResultTypeUseCreateAssociation(String objectName, String associationName) { + this.objectName = objectName; + this.associationName = associationName; + } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeUseCreateAssociation that = (ResultTypeUseCreateAssociation) o; + return Objects.equals(objectName, that.objectName) && + Objects.equals(associationName, that.associationName); + } + + @Override + public String toString() { + return "ResultTypeUseCreateAssociation{" + + "objectName='" + objectName + '\'' + + ", associationName='" + associationName + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateDefault.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateDefault.java new file mode 100644 index 000000000..2f2defc87 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateDefault.java @@ -0,0 +1,13 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an autocompletion parser result type associated with the USE command "create" when there was no association name already given. + * It is a subtype of {@link ResultTypeUseCreate}. + */ +public class ResultTypeUseCreateDefault extends ResultTypeUseCreate{ + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o != null && getClass() == o.getClass(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDelete.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDelete.java new file mode 100644 index 000000000..f7abf9f8b --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDelete.java @@ -0,0 +1,8 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an abstract autocompletion parser result type associated with the USE command "delete". + * It is a subtype of {@link ResultTypeUseCommands}. + */ +public abstract class ResultTypeUseDelete extends ResultTypeUseCommands{ +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteAssociation.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteAssociation.java new file mode 100644 index 000000000..c6b3554f1 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteAssociation.java @@ -0,0 +1,13 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an autocompletion parser result type associated with the USE command "delete" when object names were already given. + * It is a subtype of {@link ResultTypeUseDelete}. + */ +public class ResultTypeUseDeleteAssociation extends ResultTypeUseDelete { + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o != null && getClass() == o.getClass(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteObjects.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteObjects.java new file mode 100644 index 000000000..b480bb125 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteObjects.java @@ -0,0 +1,30 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +import java.util.Objects; + +/** + * Represents an autocompletion parser result type associated with the USE command "delete" when no object names were already given. + * It is a subtype of {@link ResultTypeUseDelete}. + */ +public class ResultTypeUseDeleteObjects extends ResultTypeUseDelete{ + public String objectNames; //comma seperated list + + public ResultTypeUseDeleteObjects(String objectNames) { + this.objectNames = objectNames; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeUseDeleteObjects that = (ResultTypeUseDeleteObjects) o; + return Objects.equals(objectNames, that.objectNames); + } + + @Override + public String toString() { + return "ResultTypeUseDeleteObjects{" + + "objectNames='" + objectNames + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDestroy.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDestroy.java new file mode 100644 index 000000000..af3632cce --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDestroy.java @@ -0,0 +1,31 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +import java.util.Objects; + +/** + * Represents a specific autocompletion parser result type associated with the USE command "destroy". + * It extends {@link ResultTypeUseCommands}. + */ + +public class ResultTypeUseDestroy extends ResultTypeUseCommands{ + public String objectNames; //comma seperated list + + public ResultTypeUseDestroy(String objectNames) { + this.objectNames = objectNames; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeUseDestroy that = (ResultTypeUseDestroy) o; + return Objects.equals(objectNames, that.objectNames); + } + + @Override + public String toString() { + return "ResultTypeUseDestroy{" + + "objectNames='" + objectNames + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfo.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfo.java new file mode 100644 index 000000000..db1bc79e5 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfo.java @@ -0,0 +1,9 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an abstract autocompletion parser result type associated with USE commands that provide information. + * It serves as a parent class for various specific result types related to USE information commands. + * It extends {@link ResultTypeUseCommands}. + */ +public abstract class ResultTypeUseInfo extends ResultTypeUseCommands{ +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoClass.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoClass.java new file mode 100644 index 000000000..994f7823c --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoClass.java @@ -0,0 +1,13 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an autocompletion parser result type associated with the USE command "info" when the class parameter was already given. + * It is a subtype of {@link ResultTypeUseInfo}. + */ +public class ResultTypeUseInfoClass extends ResultTypeUseInfo{ + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o != null && getClass() == o.getClass(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoDefault.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoDefault.java new file mode 100644 index 000000000..29442c3a2 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoDefault.java @@ -0,0 +1,13 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an autocompletion parser result type associated with the USE command "info" when no parameter was already given. + * It is a subtype of {@link ResultTypeUseInfo}. + */ +public class ResultTypeUseInfoDefault extends ResultTypeUseInfo{ + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o != null && getClass() == o.getClass(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsert.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsert.java new file mode 100644 index 000000000..f580f7987 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsert.java @@ -0,0 +1,9 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an abstract autocompletion parser result type associated with the USE command "insert". + * It serves as a parent class for various specific result types related to USE insert commands. + * It extends {@link ResultTypeUseCommands}. + */ +public abstract class ResultTypeUseInsert extends ResultTypeUseCommands{ +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertAssociation.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertAssociation.java new file mode 100644 index 000000000..6b4848100 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertAssociation.java @@ -0,0 +1,13 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an autocompletion parser result type associated with the USE command "insert" when objects names were already given. + * It is a subtype of {@link ResultTypeUseInsert}. + */ +public class ResultTypeUseInsertAssociation extends ResultTypeUseInsert{ + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o != null && getClass() == o.getClass(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertObjects.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertObjects.java new file mode 100644 index 000000000..b32a5953e --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertObjects.java @@ -0,0 +1,30 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +import java.util.Objects; + +/** + * Represents an autocompletion parser result type associated with the USE command "insert" when no objects names were already given. + * It is a subtype of {@link ResultTypeUseInsert}. + */ +public class ResultTypeUseInsertObjects extends ResultTypeUseInsert{ + public String objectNames; //comma seperated list + + public ResultTypeUseInsertObjects(String objectNames) { + this.objectNames = objectNames; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeUseInsertObjects that = (ResultTypeUseInsertObjects) o; + return Objects.equals(objectNames, that.objectNames); + } + + @Override + public String toString() { + return "ResultTypeUseInsertObjects{" + + "objectNames='" + objectNames + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpen.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpen.java new file mode 100644 index 000000000..32bbc3bdc --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpen.java @@ -0,0 +1,30 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +import java.util.Objects; + +/** + * Represents a specific autocompletion parser result type associated with the USE command "open". + * It extends {@link ResultTypeUseCommands}. + */ +public class ResultTypeUseOpen extends ResultTypeUseCommands{ + public String path; + + public ResultTypeUseOpen(String path) { + this.path = path; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeUseOpen that = (ResultTypeUseOpen) o; + return Objects.equals(path, that.path); + } + + @Override + public String toString() { + return "ResultTypeUseOpen{" + + "path='" + path + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenter.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenter.java new file mode 100644 index 000000000..656fc0bc3 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenter.java @@ -0,0 +1,9 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an abstract autocompletion parser result type associated with the USE command "openter". + * It serves as the parent class for more specific result types related to "openter". + * It extends {@link ResultTypeUseCommands}. + */ +public abstract class ResultTypeUseOpenter extends ResultTypeUseCommands{ +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterObject.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterObject.java new file mode 100644 index 000000000..5df833525 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterObject.java @@ -0,0 +1,30 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +import java.util.Objects; + +/** + * Represents an autocompletion parser result type associated with the USE command "openter" when no object name was already given. + * It is a subtype of {@link ResultTypeUseOpenter}. + */ +public class ResultTypeUseOpenterObject extends ResultTypeUseOpenter{ + public String objectName; + + public ResultTypeUseOpenterObject(String objectName) { + this.objectName = objectName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeUseOpenterObject that = (ResultTypeUseOpenterObject) o; + return Objects.equals(objectName, that.objectName); + } + + @Override + public String toString() { + return "ResultTypeUseOpenterObject{" + + "objectName='" + objectName + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterOperation.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterOperation.java new file mode 100644 index 000000000..c7eaeda4e --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterOperation.java @@ -0,0 +1,34 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +import java.util.Objects; + +/** + * Represents an autocompletion parser result type associated with the USE command "openter" when the object name was already given. + * It is a subtype of {@link ResultTypeUseOpenter}. + */ +public class ResultTypeUseOpenterOperation extends ResultTypeUseOpenter{ + public String objectName; + public String operationPrefix; + + public ResultTypeUseOpenterOperation(String objectName, String operationPrefix) { + this.objectName = objectName; + this.operationPrefix = operationPrefix; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeUseOpenterOperation that = (ResultTypeUseOpenterOperation) o; + return Objects.equals(objectName, that.objectName) && + Objects.equals(operationPrefix, that.operationPrefix); + } + + @Override + public String toString() { + return "ResultTypeUseOpenterOperation{" + + "objectName='" + objectName + '\'' + + ", operationPrefix='" + operationPrefix + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSet.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSet.java new file mode 100644 index 000000000..8909faccf --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSet.java @@ -0,0 +1,9 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an abstract autocompletion parser result type associated with the USE command "set". + * It serves as the parent class for more specific result types related to "set". + * It extends {@link ResultTypeUseCommands}. + */ +public abstract class ResultTypeUseSet extends ResultTypeUseCommands{ +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetAttr.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetAttr.java new file mode 100644 index 000000000..76485c0c0 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetAttr.java @@ -0,0 +1,30 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +import java.util.Objects; + +/** + * Represents an autocompletion parser result type associated with the USE command "set" when the object name was already given. + * It is a subtype of {@link ResultTypeUseSet}. + */ +public class ResultTypeUseSetAttr extends ResultTypeUseSet { + public String objectName; + + public ResultTypeUseSetAttr(String objectName) { + this.objectName = objectName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ResultTypeUseSetAttr that = (ResultTypeUseSetAttr) o; + return Objects.equals(objectName, that.objectName); + } + + @Override + public String toString() { + return "ResultTypeUseSetAttr{" + + "objectName='" + objectName + '\'' + + '}'; + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetObject.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetObject.java new file mode 100644 index 000000000..486b7e5b4 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetObject.java @@ -0,0 +1,13 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents an autocompletion parser result type associated with the USE command "set" when the object name was already given. + * It is a subtype of {@link ResultTypeUseSet}. + */ +public class ResultTypeUseSetObject extends ResultTypeUseSet{ + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o != null && getClass() == o.getClass(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseStep.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseStep.java new file mode 100644 index 000000000..477521052 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseStep.java @@ -0,0 +1,13 @@ +package org.tzi.use.autocompletion.parserResultTypes.useCommands; + +/** + * Represents a specific autocompletion parser result type associated with the USE command "step". + * It extends {@link ResultTypeUseCommands}. + */ +public class ResultTypeUseStep extends ResultTypeUseCommands{ + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o != null && getClass() == o.getClass(); + } +} diff --git a/use-gui/src/main/java/org/tzi/use/gui/main/CreateObjectDialog.java b/use-gui/src/main/java/org/tzi/use/gui/main/CreateObjectDialog.java index 603d893a5..522bc6832 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/main/CreateObjectDialog.java +++ b/use-gui/src/main/java/org/tzi/use/gui/main/CreateObjectDialog.java @@ -176,6 +176,7 @@ private void createObject() { JOptionPane.ERROR_MESSAGE); return; } + fParent.getAutocompletionInstance().addObject(cls.name(), name); fParent.createObject(cls, name); } diff --git a/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java b/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java index a9726f66e..3ccb1093f 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java +++ b/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java @@ -34,8 +34,10 @@ import org.tzi.use.uml.ocl.expr.MultiplicityViolationException; import org.tzi.use.uml.ocl.value.Value; import org.tzi.use.uml.sys.MSystem; +import org.tzi.use.autocompletion.AutoCompletion; import org.tzi.use.util.StringUtil; import org.tzi.use.util.TeeWriter; +import org.tzi.use.autocompletion.SuggestionResult; import javax.swing.*; import java.awt.*; @@ -43,8 +45,11 @@ import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.awt.event.KeyEvent; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.LinkedList; +import java.util.List; /** * A dialog for entering and evaluating OCL expressions. @@ -55,6 +60,8 @@ class EvalOCLDialog extends JDialog { private MSystem fSystem; + private AutoCompletion autocompletion; + private final JTextArea fTextIn; private final JTextArea fTextOut; @@ -215,6 +222,39 @@ public void actionPerformed(ActionEvent e) { contentPane.add(btnPane, BorderLayout.EAST); getRootPane().setDefaultButton(btnEval); + Action ctrlSpaceAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + SwingWorker worker = new SwingWorker() { + @Override + protected SuggestionResult doInBackground() throws Exception { + return autocompletion.getSuggestions(fTextIn.getText(), true); + } + + @Override + protected void done() { + try { + SuggestionResult suggestion = get(); + // Open a new window to display the results (if not empty) + if (suggestion != null && !suggestion.suggestions.isEmpty()) { + displayResultsWindow(suggestion, textPane); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }; + + worker.execute(); + } + }; + + // Map the Ctrl+Space key combination to the action + KeyStroke ctrlSpaceKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_DOWN_MASK); + fTextIn.getInputMap(JComponent.WHEN_FOCUSED).put(ctrlSpaceKeyStroke, "ctrlSpaceAction"); + fTextIn.getActionMap().put("ctrlSpaceAction", ctrlSpaceAction); + + pack(); setSize(new Dimension(500, 200)); setLocationRelativeTo(parent); @@ -315,4 +355,107 @@ private boolean evaluate(String in, boolean evalTree) { } return false; } + + /** + * Displays a window with autocomplete suggestions. + *

+ * This method takes a {@code SuggestionResult} object and a {@code JPanel} where the suggestions + * will be displayed. The suggestions are formatted and displayed in a {@code JList} within a scrollable + * {@code JScrollPane}. The user can confirm a selection by pressing the Enter key. + *

+ * The formatting includes highlighting the prefix in blue and, if the suggestion contains parentheses, + * highlighting the content within parentheses in orange. + * + * @param suggestion The {@code SuggestionResult} object containing the list of suggestions and the prefix. + * @param textPane The {@code JPanel} where the autocomplete suggestions will be displayed. + */ + private void displayResultsWindow(SuggestionResult suggestion, JPanel textPane) { + List suggestionList = suggestion.suggestions; + String prefix = suggestion.prefix; + + List displayedList = new LinkedList<>(); + + for (int i = 0; i < suggestionList.size(); i++) { + String sugString = suggestionList.get(i); + + if (sugString.contains("(")) { + sugString = sugString.substring(0, sugString.indexOf("(") + 1) + "" + sugString.substring(sugString.indexOf("(") + 1, sugString.indexOf(")")) + "" + ")"; + } + + if (prefix != null) { + displayedList.add(i, "" + prefix + "" + sugString); + } else { + displayedList.add(i, suggestionList.get(i)); + } + } + + + JList resultList = new JList<>(displayedList.toArray(new String[0])); + resultList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + resultList.setCellRenderer(new TwoColorListCellRenderer()); + + resultList.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "confirmSelection"); + resultList.getActionMap().put("confirmSelection", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + String selectedValue = suggestionList.get(resultList.getSelectedIndex()); + if (selectedValue != null) { + String currentText = fTextIn.getText(); + + String newText; + boolean hasParenthesis = false; + if (selectedValue.endsWith(")")) {//TODO: more elegant + if (selectedValue.contains("(")) { + int firstIndex = selectedValue.indexOf("("); + selectedValue = selectedValue.substring(0, firstIndex + 1); + } + newText = currentText + selectedValue + ")"; + hasParenthesis = true; + } else if(currentText.endsWith(")")){ + newText = currentText.substring(0, currentText.length()-1) + selectedValue + ")"; + hasParenthesis = true; + }else{ + newText = currentText + selectedValue; + } + + fTextIn.setText(newText); + if(hasParenthesis){ + fTextIn.setCaretPosition(newText.length()-1); + }else{ + fTextIn.setCaretPosition(newText.length()); + } + } + textPane.remove(1); + fTextIn.requestFocus(); + pack(); + } + }); + + JScrollPane scrollPane = new JScrollPane(resultList); + textPane.add(scrollPane, 1); + + resultList.requestFocus(); + pack(); + } + + public void setAutoCompletion(AutoCompletion autocompletion) { + this.autocompletion = autocompletion; + } +} + +/** + * Custom list cell renderer for displaying two-colored text in a {@code JList}. + *

+ * This renderer extends {@code DefaultListCellRenderer} and overrides the + * {@code getListCellRendererComponent} method to display HTML-formatted text in a JLabel. + * The HTML formatting is used to apply two colors to the text, providing visual distinction. + * The text is enclosed in <html> tags to enable rendering of HTML-formatted content. + */ +class TwoColorListCellRenderer extends DefaultListCellRenderer { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + label.setText("" + value + ""); + return label; + } } diff --git a/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java b/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java index 3215ff688..e675a0b12 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java +++ b/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java @@ -64,6 +64,7 @@ import org.tzi.use.uml.sys.soil.MExitOperationStatement; import org.tzi.use.uml.sys.soil.MNewObjectStatement; import org.tzi.use.uml.sys.soil.MStatement; +import org.tzi.use.autocompletion.AutoCompletion; import org.tzi.use.util.Log; import org.tzi.use.util.StringUtil; import org.tzi.use.util.USEWriter; @@ -99,6 +100,8 @@ public class MainWindow extends JFrame { private final StatusBar fStatusBar; + private AutoCompletion autocompletion; + private final LogPanel fLogPanel; private final PrintWriter fLogWriter; @@ -1157,6 +1160,8 @@ public void run() { fSession.setSystem(system); } }); + + autocompletion = new AutoCompletion(model,fSession); if (system != null) { Options.getRecentFiles().push(f.toString()); @@ -1589,6 +1594,7 @@ private class ActionStateEvalOCL extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { EvalOCLDialog dlg = new EvalOCLDialog(fSession, MainWindow.this); + dlg.setAutoCompletion(autocompletion); dlg.setVisible(true); } } @@ -2239,4 +2245,8 @@ public void internalFrameDeactivated(InternalFrameEvent ev) { private Icon getIcon(String name) { return new ImageIcon(getClass().getResource(Options.getIconPath(name))); } + + public AutoCompletion getAutocompletionInstance(){ + return autocompletion; + } } diff --git a/use-gui/src/main/java/org/tzi/use/util/input/AutoCompletionOperation.java b/use-gui/src/main/java/org/tzi/use/util/input/AutoCompletionOperation.java new file mode 100644 index 000000000..726597414 --- /dev/null +++ b/use-gui/src/main/java/org/tzi/use/util/input/AutoCompletionOperation.java @@ -0,0 +1,20 @@ +package org.tzi.use.util.input; + +/** + * Represents an Operation within the context of the autocompletion with a name and a parameter. + *

+ * This class is specifically used to describe overloaded operations to the user and is not used to define + * operations within USE. It is necessary because {@link org.tzi.use.util.OperationType} saves parameters in a way + * that is not gui-friendly and does not help the user to understand every possible argument an operation can receive. + */ +public class AutoCompletionOperation { + /** + * The name of the operation. + */ + public String name; + + /** + * The parameters of the operation describing which type of expression is allowed (e.g. iterator, boolean expressions, ...) + */ + public String param; +} diff --git a/use-gui/src/test/java/org/tzi/use/TestSystem.java b/use-gui/src/test/java/org/tzi/use/TestSystem.java new file mode 100644 index 000000000..d26f98bdf --- /dev/null +++ b/use-gui/src/test/java/org/tzi/use/TestSystem.java @@ -0,0 +1,173 @@ +/* + * USE - UML based specification environment + * Copyright (C) 1999-2010 Mark Richters, University of Bremen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package org.tzi.use; + +import org.tzi.use.uml.mm.*; +import org.tzi.use.uml.ocl.expr.*; +import org.tzi.use.uml.ocl.type.TypeFactory; +import org.tzi.use.uml.ocl.value.IntegerValue; +import org.tzi.use.uml.sys.MSystem; +import org.tzi.use.uml.sys.MSystemException; +import org.tzi.use.uml.sys.MSystemState; +import org.tzi.use.uml.sys.soil.MVariableAssignmentStatement; +import org.tzi.use.autocompletion.AutoCompletion; +import org.tzi.use.util.soil.VariableEnvironment; + +import java.util.Collections; +import java.util.List; + +/** + * Helper to setup a test system. + * + * @author Daniel Gent + */ +public class TestSystem { + + private MSystem fSystem; + + private AutoCompletion autoCompletion; + + public TestSystem() throws MSystemException, MInvalidModelException, ExpInvalidException { + init(); + } + + public AutoCompletion getAutoCompletion() { + return autoCompletion; + } + + public MSystem getSystem() { + return fSystem; + } + + public MModel getModel() { + return fSystem.model(); + } + + public MSystemState getState() { + return fSystem.state(); + } + + public VariableEnvironment getVarEnv() { + return fSystem.getVariableEnvironment(); + } + + + private void init() throws MSystemException, MInvalidModelException, ExpInvalidException { + ModelFactory factory = new ModelFactory(); + MModel model = factory.createModel("testModel"); + + MClass c1 = factory.createClass("class1", false); + model.addClass(c1); + c1.addAttribute(factory.createAttribute("attribute1_1", TypeFactory.mkBoolean())); + c1.addAttribute(factory.createAttribute("attribute1_2", TypeFactory.mkInteger())); + c1.addAttribute(factory.createAttribute("attribute1_3", TypeFactory.mkReal())); + c1.addAttribute(factory.createAttribute("attribute1_4", TypeFactory.mkString())); + MOperation op1 = new MOperation("op1", new VarDeclList(new VarDecl("p1", TypeFactory.mkInteger())), TypeFactory.mkInteger()); + op1.setStatement(new MVariableAssignmentStatement("result", IntegerValue.valueOf(42))); + c1.addOperation(op1); + MOperation op2 = new MOperation("op2", new VarDeclList(new VarDecl("p1", TypeFactory.mkReal())), TypeFactory.mkInteger()); + op2.setStatement(new MVariableAssignmentStatement("result", IntegerValue.valueOf(40))); + c1.addOperation(op2); + MOperation op3 = new MOperation("operation3", new VarDeclList(new VarDecl("p1", TypeFactory.mkBoolean())), TypeFactory.mkBoolean()); + op3.setStatement(new MVariableAssignmentStatement("result", IntegerValue.valueOf(42))); + c1.addOperation(op3); + + + MClass c2 = factory.createClass("class2", false); + model.addClass(c2); + c2.addAttribute(factory.createAttribute("attribute2_1", TypeFactory.mkBoolean())); + c2.addAttribute(factory.createAttribute("attribute2_2", TypeFactory.mkBoolean())); + c2.addAttribute(factory.createAttribute("attribute2_3", TypeFactory.mkReal())); + + List emptyQualifiers = Collections.emptyList(); + MAssociation a1 = factory.createAssociation("A1"); + a1.addAssociationEnd( + factory.createAssociationEnd( + c1, + "E1", + new MMultiplicity(0, 1), + MAggregationKind.NONE, + false, emptyQualifiers)); + + a1.addAssociationEnd( + factory.createAssociationEnd( + c2, + "E2", + new MMultiplicity(0, 1), + MAggregationKind.NONE, + false, emptyQualifiers)); + + model.addAssociation(a1); + + MAssociationClass ac1 = factory.createAssociationClass("AC1", false); + ac1.addAssociationEnd( + factory.createAssociationEnd( + c1, + "role1", + new MMultiplicity(0, 1), + MAggregationKind.NONE, + false, emptyQualifiers)); + + ac1.addAssociationEnd( + factory.createAssociationEnd( + c2, + "role2", + new MMultiplicity(0, 1), + MAggregationKind.NONE, + false, emptyQualifiers)); + + model.addClass(ac1); + model.addAssociation(ac1); + + fSystem = new MSystem(model); + + autoCompletion = new AutoCompletion(model); + initObjectsAndLinks(autoCompletion, c1, c2); + } + + private void initObjectsAndLinks(AutoCompletion autoCompletion, MClass c1, MClass c2) throws MSystemException { + MSystemState state = fSystem.state(); + VariableEnvironment varEnv = fSystem.getVariableEnvironment(); + + MClass C1 = getModel().getClass("class1"); + MClass C2 = getModel().getClass("class2"); + + varEnv.assign("v", IntegerValue.valueOf(42)); + + autoCompletion.addObject(c1.name(), "obj1"); + autoCompletion.addObject(c1.name(), "obj2"); + autoCompletion.addObject(c1.name(), "obj3"); + autoCompletion.addObject(c1.name(), "obj4"); + + autoCompletion.addObject(c2.name(), "obj5"); + autoCompletion.addObject(c2.name(), "obj6"); + autoCompletion.addObject(c2.name(), "obj7"); + autoCompletion.addObject(c2.name(), "obj8"); + + state.createObject(c1, "obj1"); + state.createObject(c1, "obj2"); + state.createObject(c1, "obj3"); + state.createObject(c1, "obj4"); + state.createObject(c2, "obj5"); + state.createObject(c2, "obj6"); + state.createObject(c2, "obj7"); + state.createObject(c2, "obj8"); + } +} diff --git a/use-gui/src/test/java/org/tzi/use/autocompletion/AllTests.java b/use-gui/src/test/java/org/tzi/use/autocompletion/AllTests.java new file mode 100644 index 000000000..76fa6701e --- /dev/null +++ b/use-gui/src/test/java/org/tzi/use/autocompletion/AllTests.java @@ -0,0 +1,41 @@ +/* + * USE - UML based specification environment + * Copyright (C) 1999-2004 Mark Richters, University of Bremen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package org.tzi.use.autocompletion; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Runs all test in package org.tzi.use.graph. + * + * @author Hanna Bauerdick + * @author Fabian Gutsche + */ +public class AllTests { + + private AllTests(){} + + public static Test suite() { + final TestSuite test = new TestSuite( "All auto completion tests" ); + test.addTestSuite( AutoCompletionSuggestionTest.class ); + test.addTestSuite(AutoCompletionParserTest.class); + return test; + } +} diff --git a/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionParserTest.java b/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionParserTest.java new file mode 100644 index 000000000..a16cd6668 --- /dev/null +++ b/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionParserTest.java @@ -0,0 +1,805 @@ +/* + * USE - UML based specification environment + * Copyright (C) 1999-2004 Mark Richters, University of Bremen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package org.tzi.use.autocompletion; + +import junit.framework.TestCase; +import org.tzi.use.TestSystem; +import org.tzi.use.autocompletion.parserResultTypes.ocl.*; +import org.tzi.use.autocompletion.parserResultTypes.useCommands.*; +import org.tzi.use.uml.mm.MInvalidModelException; +import org.tzi.use.uml.ocl.expr.ExpInvalidException; +import org.tzi.use.uml.sys.MSystemException; + +/** + * Test auto completion parser. + * + * @author Till Aul + * @see AutoCompletionParser + */ + +public class AutoCompletionParserTest extends TestCase { + public void testParserCaseObjectsIntegers() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("object"); + expectedResult.foundValues.put("objectName", "obj1"); + expectedResult.result = new ResultTypeOCLObjects("obj1", null); + + String input = "obj1."; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + input = "obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + input = "null = obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.foundTypes.add(0, "Integer"); + expectedResult.foundValues.put("operationType", "Integer"); + expectedResult.result = new ResultTypeOCLObjects("obj1", "Integer"); + //whitespaces + input = "5 = obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "5=obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "5= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "5 =obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + //operations + //< and > + input = "3 > obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 >= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 < obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 <= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseObjectsBooleans() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("Boolean"); + expectedResult.foundTypes.add("object"); + expectedResult.foundValues.put("objectName", "obj1"); + expectedResult.foundValues.put("operationType", "Boolean"); + expectedResult.result = new ResultTypeOCLObjects("obj1", "Boolean"); + + //operations + //< and > + String input = "true > obj1."; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "false >= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "true < obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "false <= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseObjectsReals() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("Real"); + expectedResult.foundTypes.add("object"); + expectedResult.foundValues.put("objectName", "obj1"); + expectedResult.foundValues.put("operationType", "Real"); + expectedResult.result = new ResultTypeOCLObjects("obj1", "Real"); + + + //whitespaces + String input = "5.2 = obj1."; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "5.2=obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "5.2= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "5.2 =obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + //operations + //< and > + input = "5.2 > obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "5.2 >= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "5.2 < obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "5.2 <= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseObjectsStrings() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("String"); + expectedResult.foundTypes.add("object"); + expectedResult.foundValues.put("objectName", "obj1"); + expectedResult.foundValues.put("operationType", "String"); + expectedResult.result = new ResultTypeOCLObjects("obj1", "String"); + + //whi + String input = "'5' = obj1."; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5'=obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5'= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5' =obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + //operations + //< and > + input = "'3' > obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'3' >= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'3' < obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'3' <= obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseObjectPrefix() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + ParserResult expectedResult = new ParserResult(); + + expectedResult.foundTypes.add("objectName"); + expectedResult.foundValues.put("objectPrefix", "obj"); + expectedResult.foundTypes.add(0, "String"); + expectedResult.foundValues.put("operationType", "String"); + expectedResult.result = new ResultTypeOCLObjectPrefix("obj"); + + String input = "'5' = obj"; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.foundValues.remove("operationType"); + expectedResult.foundTypes.remove(0); + expectedResult.result = new ResultTypeOCLObjectPrefix("obj"); + + input = "obj"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundTypes.add(0, "Boolean"); + expectedResult.foundValues.put("operationType", "Boolean"); + expectedResult.result = new ResultTypeOCLObjectPrefix("obj"); + input = "false = obj"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseAttributePrefix() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + ParserResult expectedResult = new ParserResult(); + + expectedResult.foundTypes.add("object"); + expectedResult.foundTypes.add("objectName"); + expectedResult.foundValues.put("objectPrefix", "attr"); + expectedResult.foundValues.put("objectName", "obj"); + expectedResult.result = new ResultTypeOCLAttributePrefix("obj", null, "attr"); + + String input = "obj.attr"; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.foundTypes.add(0, "String"); + expectedResult.foundValues.put("operationType", "String"); + expectedResult.result = new ResultTypeOCLAttributePrefix("obj", "String", "attr"); + + input = "'5' = obj.attr"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.foundTypes.set(0, "Boolean"); + expectedResult.foundValues.put("operationType", "Boolean"); + expectedResult.result = new ResultTypeOCLAttributePrefix("obj", "Boolean", "attr"); + + input = "false = obj.attr"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseCollectionSet() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + ParserResult expectedResult = new ParserResult(); + + expectedResult.foundTypes.add("set"); + expectedResult.foundValues.put("elemType", "Integer"); + expectedResult.result = new ResultTypeOCLCollectionsDefault(null, "set"); + + String input = "Set{1,2,3}->"; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.foundTypes.add(0, "String"); + expectedResult.foundValues.put("operationType", "String"); + //whitespaces + input = "'5' = Set{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5'=Set{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5'= Set{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5' =Set{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.foundTypes.set(0, "Integer"); + expectedResult.foundValues.put("operationType", "Integer"); + //operations + //< and > + input = "3 > Set{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 >= Set{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 < Set{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 <= Set{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseCollectionBag() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("bag"); + expectedResult.foundValues.put("elemType", "Integer"); + expectedResult.result = new ResultTypeOCLCollectionsDefault(null, "bag"); + + String input = "Bag{1,2,3}->"; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundTypes.add(0, "String"); + expectedResult.foundValues.put("operationType", "String"); + //whitespaces + input = "'5' = Bag{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5'=Bag{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5'= Bag{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5' =Bag{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundTypes.set(0, "Integer"); + expectedResult.foundValues.put("operationType", "Integer"); + //operations + //< and > + input = "3 > Bag{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 >= Bag{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 < Bag{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 <= Bag{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseCollectionSequence() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("sequence"); + expectedResult.foundValues.put("elemType", "Integer"); + expectedResult.result = new ResultTypeOCLCollectionsDefault(null, "sequence"); + + String input = "Sequence{1,2,3}->"; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundTypes.add(0, "String"); + expectedResult.foundValues.put("operationType", "String"); + //whitespaces + input = "'5' = Sequence{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5'=Sequence{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5'= Sequence{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5' =Sequence{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundTypes.set(0, "Integer"); + expectedResult.foundValues.put("operationType", "Integer"); + //operations + //< and > + input = "3 > Sequence{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 >= Sequence{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 < Sequence{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 <= Sequence{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseCollectionOrderedSet() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("orderedSet"); + expectedResult.foundValues.put("elemType", "Integer"); + expectedResult.result = new ResultTypeOCLCollectionsDefault(null, "orderedSet"); + + String input = "OrderedSet{1,2,3}->"; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundTypes.add(0, "String"); + expectedResult.foundValues.put("operationType", "String"); + //whitespaces + input = "'5' = OrderedSet{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5'=OrderedSet{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5'= OrderedSet{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "'5' =OrderedSet{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundTypes.set(0, "Integer"); + expectedResult.foundValues.put("operationType", "Integer"); + //operations + //< and > + input = "3 > OrderedSet{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 >= OrderedSet{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 < OrderedSet{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "3 <= OrderedSet{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseCollectionNegativeTest() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + String input = "OrderedSet{1,2,3}."; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "Bag{1,2,3}."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "Set{1,2,3}."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "Sequence{1,2,3}."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseObjectNegativeTest() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + ParserResult expectedResult = new ParserResult(); + + String input = "obj1->"; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseUseCommandsOCLExpression() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("orderedSet"); + expectedResult.foundValues.put("elemType", "Integer"); + expectedResult.result = new ResultTypeOCLCollectionsDefault(null, "orderedSet"); + + String input = "? OrderedSet{1,2,3} ->"; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "?OrderedSet{1,2,3} ->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "?? OrderedSet{1,2,3} ->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "??OrderedSet{1,2,3} ->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + expectedResult.foundTypes.add(0, "Integer"); + expectedResult.foundValues.put("operationType", "Integer"); + input = "3 > OrderedSet{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.clearAll(); + expectedResult.foundTypes.add("bag"); + expectedResult.foundValues.put("elemType", "Integer"); + input = "? Bag{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundTypes.set(0, "set"); + input = "? Set{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundTypes.set(0, "sequence"); + input = "? Sequence{1,2,3}->"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundValues.clear(); + expectedResult.foundTypes.add("object"); + expectedResult.foundValues.put("objectName", "obj1"); + expectedResult.foundTypes.set(0, "Integer"); + expectedResult.foundValues.put("operationType", "Integer"); + expectedResult.result = new ResultTypeOCLObjects("obj1", "Integer"); + + input = "5 = obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundTypes.set(0, "String"); + expectedResult.foundValues.put("operationType", "String"); + expectedResult.result = new ResultTypeOCLObjects("obj1", "String"); + input = "'5' = obj1."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseUseCommandsCreateDestroy() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("USE_create"); + expectedResult.result = new ResultTypeUseCreateDefault(); + + String input = "!create greenApple : "; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "!create greenApple, redApple: "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "!create greenApple, redApple, yellowApple : "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + //whitespaces + input = "! create greenApple,redApple,yellowApple : "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "!create greenApple,redApple,yellowApple : "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "!create greenApple,redApple,yellowApple: "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "!create greenApple,redApple,yellowApple :"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "!create greenApple,redApple,yellowApple:"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundValues.put("subtype", "association"); + expectedResult.foundValues.put("associationName", "FruitSalad"); + expectedResult.result = new ResultTypeUseCreateAssociation(null, "FruitSalad"); + input = "!create smallSalad : FruitSalad between ("; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + //whitespaces + input = "! create smallSalad: FruitSalad between ("; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "!create smallSalad: FruitSalad between ("; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "!create smallSalad :FruitSalad between ("; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + input = "!create smallSalad:FruitSalad between ("; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.foundTypes.set(0, "USE_destroy"); + expectedResult.foundValues.remove("subtype"); + expectedResult.foundValues.remove("associationName"); + expectedResult.result = new ResultTypeUseDestroy(null); + + input = "!destroy "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseUseCommandsInsertDelete() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("USE_insert"); + expectedResult.foundValues.put("subtype", "insertPartObjects"); + expectedResult.foundValues.put("objectNames", null); + expectedResult.result = new ResultTypeUseInsertObjects(null); + + String input = "!insert ("; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.foundValues.put("subtype", "insertPartAssociation"); + expectedResult.foundValues.remove("objectNames"); + expectedResult.result = new ResultTypeUseInsertAssociation(); + + input = "!insert (redApple, yellowApple, bigLemon) into "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.foundTypes.set(0, "USE_delete"); + expectedResult.foundValues.put("subtype", "deletePartAssociation"); + expectedResult.result = new ResultTypeUseDeleteAssociation(); + + input = "!delete (redApple, yellowApple, bigLemon) from "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseUseCommandsSet() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("USE_set"); + expectedResult.foundValues.put("subtype", "setPartObject"); + expectedResult.result = new ResultTypeUseSetObject(); + + String input = "!set "; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.foundValues.put("subtype", "setPartAttr"); + expectedResult.foundValues.put("objectName", "redApple"); + expectedResult.result = new ResultTypeUseSetAttr("redApple"); + + input = "!set redApple."; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseUseCommandsOpEnter() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("USE_openter"); + expectedResult.foundValues.put("subtype", "openterPartObject"); + expectedResult.result = new ResultTypeUseOpenterObject(null); + + String input = "!openter "; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + ParserResult r = parser.getResult(); + + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundValues.put("subtype", "openterPartOperation"); + expectedResult.foundValues.put("objectName", "banana"); + expectedResult.foundValues.put("operationPrefix", null); + expectedResult.result = new ResultTypeUseOpenterOperation("banana", null); + + input = "!openter banana "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseUseCommandsCheck() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("USE_check"); + expectedResult.result = new ResultTypeUseCheck(); + String input = "check "; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseUseCommandsStep() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("USE_step"); + expectedResult.result = new ResultTypeUseStep(); + String input = "step "; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseUseCommandsOpen() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("USE_open"); + expectedResult.foundValues.put("path", "../"); + expectedResult.result = new ResultTypeUseOpen("../"); + String input = "!open ../"; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundValues.put("path", "../examples/Documentation/Demo/"); + expectedResult.result = new ResultTypeUseOpen("../examples/Documentation/Demo/"); + input = "!open ../examples/Documentation/Demo/"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseUseCommandsInfo() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + expectedResult.foundTypes.add("USE_info"); + expectedResult.result = new ResultTypeUseInfoDefault(); + String input = "info "; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + expectedResult.foundValues.put("subtype", "infoClass"); + expectedResult.result = new ResultTypeUseInfoClass(); + input = "info class "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseIterationExpressions() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + + expectedResult.foundTypes.add("set"); + expectedResult.foundTypes.add("iterationExpression"); + expectedResult.foundValues.put("elemType", "Integer"); + expectedResult.foundValues.put("subtype", "endsWithPipeAndContainsColon"); + expectedResult.foundValues.put("identifier", "e1"); + expectedResult.foundValues.put("className", "class1"); + expectedResult.foundValues.put("operationName", "forAll"); + expectedResult.result = new ResultTypeOCLCollectionsEndsWithPipeAndContainsColon("class1"); + + String input = "Set{1,2,3}->forAll(e1: class1 | "; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.clearAll(); + expectedResult.foundTypes.add("set"); + expectedResult.foundTypes.add("objectName"); + expectedResult.foundValues.put("elemType", "Integer"); + expectedResult.foundValues.put("objectPrefix", "obj"); + expectedResult.foundValues.put("operationName", "forAll"); + expectedResult.result = new ResultTypeOCLObjectPrefix("obj"); + + input = "Set{1,2,3}->forAll(obj)"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.clearAll(); + expectedResult.foundTypes.add("set"); + expectedResult.foundTypes.add("object"); + expectedResult.foundValues.put("elemType", "Integer"); + expectedResult.foundValues.put("objectName", "obj1"); + expectedResult.foundValues.put("operationName", "forAll"); + expectedResult.result = new ResultTypeOCLObjects("obj1", null); + + input = "Set{1,2,3}->forAll(obj1.)"; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + + + expectedResult.clearAll(); + expectedResult.foundTypes.add("set"); + expectedResult.foundTypes.add("iterationExpression"); + expectedResult.foundValues.put("elemType", "Integer"); + expectedResult.foundValues.put("identifier", "e1"); + expectedResult.foundValues.put("subtype", "containsColon"); + expectedResult.foundValues.put("operationName", "forAll"); + expectedResult.result = new ResultTypeOCLCollectionsContainsColon(null, "Integer"); + + input = "Set{1,2,3}->forAll(e1: "; + parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } + + public void testParserCaseIterationExpressionsNegativeTests() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + + ParserResult expectedResult = new ParserResult(); + String input = "Set{1,2,3}->select(e1, )"; + AutoCompletionParser parser = new AutoCompletionParser(testSystem.getModel(), input); + assertEquals(expectedResult, parser.getResult()); + } +} \ No newline at end of file diff --git a/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java b/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java new file mode 100644 index 000000000..4febbae1d --- /dev/null +++ b/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java @@ -0,0 +1,258 @@ +/* + * USE - UML based specification environment + * Copyright (C) 1999-2004 Mark Richters, University of Bremen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package org.tzi.use.autocompletion; + +import junit.framework.TestCase; +import org.tzi.use.TestSystem; +import org.tzi.use.uml.mm.MInvalidModelException; +import org.tzi.use.uml.ocl.expr.ExpInvalidException; +import org.tzi.use.uml.sys.MSystemException; + +import java.util.*; + + +/** + * Test auto completion suggestions. + * + * @author Till Aul + * @see AutoCompletion + */ + +public class AutoCompletionSuggestionTest extends TestCase { + + public void testSuggestionsCaseObjectNameStarted() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("j1", "j2", "j3", "j4", "j5", "j6", "j7", "j8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("ob", true).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("true = ob", true).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("'5' = ob", true).suggestions)); + + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("ob", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("true = ob", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("'5' = ob", false).suggestions)); + } + + public void testSuggestionsCaseAttributeNameStarted() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("tribute1_1", "tribute1_2", "tribute1_3", "tribute1_4"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("obj1.at", false).suggestions)); + + expectedResult = Set.of("tribute1_2", "tribute1_3"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("5 = obj1.at", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("5 = obj2.at", false).suggestions)); + + expectedResult = Set.of("tribute2_1", "tribute2_2", "tribute2_3"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("obj5.at", false).suggestions)); + + expectedResult = Set.of("tribute2_1", "tribute2_2"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("false = obj5.at", false).suggestions)); + } + + public void testSuggestionsCaseOperationNameStarted() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("cludes(T2 element)", "cludesAll(Collection(T2) other)", "cluding(T2 element)", "tersection(Set(T2) other)", "tersection(Bag(T2) other)"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->in", true).suggestions)); + } + + public void testSuggestionsCaseObjects() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + + Set expectedResult = Set.of("attribute1_1", "attribute1_2", "attribute1_3", "attribute1_4"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("obj1.", true).suggestions)); + + expectedResult = Set.of("attribute1_2", "attribute1_3"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("5 = obj1.", true).suggestions)); + + expectedResult = Set.of("attribute1_4"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("'5' = obj1.", true).suggestions)); + + expectedResult = Set.of("attribute2_1", "attribute2_2", "attribute2_3"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("obj5.", true).suggestions)); + + expectedResult = Set.of("attribute1_2", "attribute1_3"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("6.2 = obj1.", true).suggestions)); + + expectedResult = Set.of("attribute1_1"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("false = obj1.", true).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("true = obj1.", true).suggestions)); + } + + public void testSuggestionsCaseCreateAndDestroy() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("class1", "class2", "A1", "AC1"); + + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 :", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3:", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : ", false).suggestions)); + + expectedResult = Set.of("obj1", "obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : AC1 between (", false).suggestions)); + expectedResult = Set.of("obj1", "obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : AC1 between(", false).suggestions)); + + expectedResult = Set.of("obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : AC1 between (obj1,", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : AC1 between (obj1, ", false).suggestions)); + + expectedResult = Set.of("obj1", "obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!destroy ", false).suggestions)); + + expectedResult = Set.of("obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!destroy obj1,", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!destroy obj1, ", false).suggestions)); + } + + public void testSuggestionsCaseInsertAndDelete() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("obj1", "obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert(", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert (", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert( ", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert ( ", false).suggestions)); + + expectedResult = Set.of("obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert(obj1,", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert(obj1, ", false).suggestions)); + + expectedResult = Set.of("A1", "AC1"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert(obj1, obj2) into ", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert(obj1,obj2) into ", false).suggestions)); + + expectedResult = Set.of("obj1", "obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete(", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete (", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete( ", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete ( ", false).suggestions)); + + expectedResult = Set.of("obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete(obj1,", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete(obj1, ", false).suggestions)); + + expectedResult = Set.of("A1", "AC1"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete(obj1, obj2) from ", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete(obj1,obj2) from ", false).suggestions)); + } + + public void testSuggestionsCaseSet() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("obj1", "obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!set ", false).suggestions)); + + + expectedResult = Set.of("attribute1_1", "attribute1_2", "attribute1_3", "attribute1_4"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!set obj1.", false).suggestions)); + + expectedResult = Set.of("attribute2_1", "attribute2_2", "attribute2_3"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!set obj5.", false).suggestions)); + } + + public void testSuggestionsCaseOpenterAndOpexit() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("obj1", "obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter ", false).suggestions)); + + expectedResult = Set.of("1", "2", "3", "4", "5", "6", "7", "8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter obj", false).suggestions)); + + expectedResult = Set.of("op1", "op2", "operation3"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter obj1 ", false).suggestions)); + + expectedResult = Set.of("1", "2", "eration3"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter obj1 op", false).suggestions)); + + expectedResult = Set.of("ration3"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter obj1 ope", false).suggestions)); + } + + public void testSuggestionsCaseCheck() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("-v", "-d", "-a"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("check ", false).suggestions)); + } + + public void testSuggestionsCaseStep() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("on"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("step ", false).suggestions)); + } + + public void testSuggestionsCaseOpen() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + String path = "../use-core/src/main/resources/examples/Documentation/Demo"; + + Set expectedResult = Set.of("Classdiagram", "SplitCommands", "Demo.use"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!open " + path, false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!open " + path, false).suggestions)); + } + + public void testSuggestionsCaseInfo() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("class", "model", "state", "opstack", "prog", "vars"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("info ", false).suggestions)); + + expectedResult = Set.of("class1", "class2", "AC1"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("info class ", false).suggestions)); + } + + public void testSuggestionCaseIterationExpressions() throws MSystemException, ExpInvalidException, MInvalidModelException { + TestSystem testSystem = new TestSystem(); + AutoCompletion testee = testSystem.getAutoCompletion(); + + Set expectedResult = Set.of("1", "2", "3", "4", "5", "6", "7", "8"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(obj)", true).suggestions)); + + expectedResult = Set.of("attribute1_1", "attribute1_2", "attribute1_3", "attribute1_4"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(obj1.)", true).suggestions)); + + expectedResult = Set.of("_1", "_2", "_3", "_4"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(obj2.attribute1)", true).suggestions)); + + expectedResult = Set.of("Integer"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(e1: )", true).suggestions)); + + expectedResult = Set.of("eger"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(obj: Int)", true).suggestions)); + } +} \ No newline at end of file From 84a8fc80c9e27d11d4685c76bfde9f87e5381b6d Mon Sep 17 00:00:00 2001 From: till Date: Tue, 28 Nov 2023 10:22:36 +0100 Subject: [PATCH 02/10] Fixed Nullpointer in autocompletion initialization --- use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java b/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java index e675a0b12..74f7bd042 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java +++ b/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java @@ -1149,6 +1149,7 @@ protected boolean compile(final Path f) { fLogWriter.println(model.getStats()); // create system system = new MSystem(model); + autocompletion = new AutoCompletion(model,fSession); } else { system = null; } @@ -1160,8 +1161,6 @@ public void run() { fSession.setSystem(system); } }); - - autocompletion = new AutoCompletion(model,fSession); if (system != null) { Options.getRecentFiles().push(f.toString()); From 60b1ab7137d9eaf1bef7c5cdb435d7280783486a Mon Sep 17 00:00:00 2001 From: till Date: Tue, 28 Nov 2023 10:23:25 +0100 Subject: [PATCH 03/10] Fixed occurrence of multiple autocompletion result lists in the EvalOCLDialog --- .../org/tzi/use/gui/main/EvalOCLDialog.java | 112 ++++++++++-------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java b/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java index 3ccb1093f..a918f74a4 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java +++ b/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java @@ -74,6 +74,10 @@ class EvalOCLDialog extends JDialog { private final JButton btnEval; + private JList autocompletionResultList; + + private JScrollPane autocompletionScrollPane; + private final ChangeListener sessionChangeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { @@ -225,7 +229,7 @@ public void actionPerformed(ActionEvent e) { Action ctrlSpaceAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { - SwingWorker worker = new SwingWorker() { + SwingWorker worker = new SwingWorker<>() { @Override protected SuggestionResult doInBackground() throws Exception { return autocompletion.getSuggestions(fTextIn.getText(), true); @@ -371,6 +375,63 @@ private boolean evaluate(String in, boolean evalTree) { */ private void displayResultsWindow(SuggestionResult suggestion, JPanel textPane) { List suggestionList = suggestion.suggestions; + List displayedList = formatStrings(suggestion, suggestionList); + + if (autocompletionScrollPane == null) { + autocompletionResultList = new JList<>(displayedList.toArray(new String[0])); + autocompletionResultList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + autocompletionResultList.setCellRenderer(new TwoColorListCellRenderer()); + + autocompletionResultList.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "confirmSelection"); + autocompletionResultList.getActionMap().put("confirmSelection", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + String selectedValue = suggestionList.get(autocompletionResultList.getSelectedIndex()); + if (selectedValue != null) { + String currentText = fTextIn.getText(); + + String newText; + boolean hasParenthesis = false; + if (selectedValue.endsWith(")")) { + if (selectedValue.contains("(")) { + int firstIndex = selectedValue.indexOf("("); + selectedValue = selectedValue.substring(0, firstIndex + 1); + } + newText = currentText + selectedValue + ")"; + hasParenthesis = true; + } else if(currentText.endsWith(")")){ + newText = currentText.substring(0, currentText.length()-1) + selectedValue + ")"; + hasParenthesis = true; + } else { + newText = currentText + selectedValue; + } + + fTextIn.setText(newText); + if(hasParenthesis){ + fTextIn.setCaretPosition(newText.length()-1); + } else { + fTextIn.setCaretPosition(newText.length()); + } + } + autocompletionScrollPane = null; + autocompletionResultList = null; + textPane.remove(1); + fTextIn.requestFocus(); + pack(); + } + }); + + autocompletionScrollPane = new JScrollPane(autocompletionResultList); + textPane.add(autocompletionScrollPane, 1); + } else { + autocompletionResultList.setListData(displayedList.toArray(new String[0])); + } + + autocompletionResultList.requestFocus(); + pack(); + } + + private List formatStrings(SuggestionResult suggestion, List suggestionList) { String prefix = suggestion.prefix; List displayedList = new LinkedList<>(); @@ -388,54 +449,7 @@ private void displayResultsWindow(SuggestionResult suggestion, JPanel textPane) displayedList.add(i, suggestionList.get(i)); } } - - - JList resultList = new JList<>(displayedList.toArray(new String[0])); - resultList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - resultList.setCellRenderer(new TwoColorListCellRenderer()); - - resultList.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "confirmSelection"); - resultList.getActionMap().put("confirmSelection", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - String selectedValue = suggestionList.get(resultList.getSelectedIndex()); - if (selectedValue != null) { - String currentText = fTextIn.getText(); - - String newText; - boolean hasParenthesis = false; - if (selectedValue.endsWith(")")) {//TODO: more elegant - if (selectedValue.contains("(")) { - int firstIndex = selectedValue.indexOf("("); - selectedValue = selectedValue.substring(0, firstIndex + 1); - } - newText = currentText + selectedValue + ")"; - hasParenthesis = true; - } else if(currentText.endsWith(")")){ - newText = currentText.substring(0, currentText.length()-1) + selectedValue + ")"; - hasParenthesis = true; - }else{ - newText = currentText + selectedValue; - } - - fTextIn.setText(newText); - if(hasParenthesis){ - fTextIn.setCaretPosition(newText.length()-1); - }else{ - fTextIn.setCaretPosition(newText.length()); - } - } - textPane.remove(1); - fTextIn.requestFocus(); - pack(); - } - }); - - JScrollPane scrollPane = new JScrollPane(resultList); - textPane.add(scrollPane, 1); - - resultList.requestFocus(); - pack(); + return displayedList; } public void setAutoCompletion(AutoCompletion autocompletion) { From e157e9ae379e8e8ca5fa1ff11e4bbd33389f0ee8 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 8 Dec 2023 11:02:05 +0100 Subject: [PATCH 04/10] Removed data structures in autocompletion class. Now uses MSystemState to get information about the loaded model --- .../use/autocompletion/AutoCompletion.java | 348 ++++++++---------- .../tzi/use/gui/main/CreateObjectDialog.java | 2 - .../java/org/tzi/use/gui/main/MainWindow.java | 2 +- .../src/test/java/org/tzi/use/TestSystem.java | 12 +- .../AutoCompletionSuggestionTest.java | 10 +- 5 files changed, 168 insertions(+), 206 deletions(-) diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java index 51296825b..7aa6e5b04 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java @@ -9,6 +9,9 @@ import org.tzi.use.uml.mm.*; import org.tzi.use.uml.ocl.expr.ExpStdOp; import org.tzi.use.uml.ocl.expr.operations.OpGeneric; +import org.tzi.use.uml.ocl.type.Type; +import org.tzi.use.uml.sys.MObject; +import org.tzi.use.uml.sys.MSystemState; import org.tzi.use.util.OperationType; import org.tzi.use.util.input.AutoCompletionOperation; @@ -20,46 +23,31 @@ * The AutoCompletion class provides support for auto-completion for USE-Commands, collection operations and class members. * It maps class names to a list of their attributes, object names to their (inherited) classes, * collections to associated operations, classes to available operations, associations to related class names, - * and classes to their instantiated objects. The auto-completion is based on the provided MModel. + * and classes to their instantiated objects. The auto-completion is based on the provided MModel and an MSystemState. *

- * This class is initialized with an MModel and a Session, and it monitors the session for changes + * This class is initialized with an MModel, an MSystemState and a Session, and it monitors the session for changes * to update the auto-completion accordingly. *

* - * * @author [Till Aul] */ public class AutoCompletion { - //Maps classnames to a list their attributes (seperated according to their type) - private final Map classToAttributeMap; - - //Maps object names to their (inherited) classes - private final Map> objectsToClassMap; - private final Map> collectionToOperationsMap; - private final Map> classToOperationMap; - - private final Map> associationToClassNamesMap; - - private final Map> classToObjectsMap; - private final MModel model; + private final MSystemState state; + /** * Initializes the AutoCompletion instance with the given MModel and Session. * * @param model The MModel to be used for AutoCompletion. * @param session The Session to monitor for changes and update AutoCompletion. */ - public AutoCompletion(MModel model, Session session) { - classToAttributeMap = new HashMap<>(); - objectsToClassMap = new HashMap<>(); + public AutoCompletion(MModel model, MSystemState state, Session session) { collectionToOperationsMap = new HashMap<>(); - classToOperationMap = new HashMap<>(); - associationToClassNamesMap = new HashMap<>(); - classToObjectsMap = new HashMap<>(); this.model = model; + this.state = state; //initialize autoCompletion after session is changed ChangeListener sessionChangeListener = e -> initializeAutocompletion(session); @@ -73,32 +61,6 @@ public AutoCompletion(MModel model, Session session) { * @param session The Session from which to gather information for auto-completion. */ private void initializeAutocompletion(Session session) { - Collection mClasses = session.system().model().classes(); - //Add all classes, attributes and operations to autocompletion - //addOperations(String className, List operations); - for (MClass mClass : mClasses) { - addClass(mClass.name()); - - List operations = mClass.operations().stream() - .map(MOperation::toString) - .collect(Collectors.toList()); - addOperations(mClass.name(), operations); - List attributes = mClass.allAttributes(); - for (MAttribute mAttribute : attributes) { - addAttributeToExistingClass(mClass.name(), mAttribute.name(), mAttribute.type().toString()); - } - } - - //add associations - Collection associations = session.system().model().associations(); - for (MAssociation mAssociation : associations) { - List classNames = new LinkedList<>(); - for (MClass mClass : mAssociation.associatedClasses()) { - classNames.add(mClass.name()); - } - addAssociation(mAssociation.name(), classNames); - } - //operations for (OpGeneric op : ExpStdOp.opmap.values()) { OperationType operationType = op.getOperationType(); @@ -141,8 +103,8 @@ public SuggestionResult getSuggestions(String input, boolean OCLOnly) { /** * Processes and provides suggestions based on the specified 'type' for OCL expressions. * - * @param result The result of parsing the OCL expression. - * @param suggestions The existing SuggestionResult object to update with suggestions. + * @param result The result of parsing the OCL expression. + * @param suggestions The existing SuggestionResult object to update with suggestions. * @return A SuggestionResult object containing suggestions based on the provided 'type'. */ private SuggestionResult handleOCLTypes(ResultTypeRoot result, SuggestionResult suggestions) { @@ -197,77 +159,6 @@ private SuggestionResult handleUseCommands(ResultTypeRoot result, SuggestionResu return suggestions; } - /** - * Adds the specified association and associated class names to the auto-completion map. - * - * @param association The name of the association. - * @param classNames The list of class names associated with the given association. - */ - public void addAssociation(String association, List classNames) { - associationToClassNamesMap.put(association, classNames); - } - - /** - * Adds a new class with an empty attribute map to the class-to-attribute mapping. - * - * @param className The name of the class to be added. - */ - public void addClass(String className) { - classToAttributeMap.put(className, new SuggestionForClassName()); - classToObjectsMap.put(className, new LinkedList<>()); - } - - /** - * Adds a list of operations to a class. - * - * @param className The name of the class to which the operations belong. - * @param operations A list of maps where each map represents an operation name and its associated return value (if any). - */ - public void addOperations(String className, List operations) { - List currentOperations = classToOperationMap.computeIfAbsent(className, k -> new LinkedList<>()); - - operations.forEach(operation -> { - String[] parts = operation.split("::"); - currentOperations.add(parts[1]); - }); - } - - /** - * Adds an attribute to an existing class with the specified name and type. - *

- * This method associates an attribute with its name and type to an existing class identified by the provided class name. - * If the attribute type is "Integer" or "Real," it is treated as "Number" for consistency. The attribute is added to the - * class's attribute mapping, which categorizes attributes based on their types. - * - * @param className The name of the class to which the attribute will be added. - * @param attrName The name of the attribute to be added. - * @param attrType The type of the attribute to be added, which may be "Integer," "Real," or another type. - */ - public void addAttributeToExistingClass(String className, String attrName, String attrType) { - if ("Integer".equals(attrType) || "Real".equals(attrType)) { - attrType = "Number"; - } - - Map> classAttributes = classToAttributeMap.get(className); - List classAttributesForType = classAttributes.computeIfAbsent(attrType, k -> new LinkedList<>()); - classAttributesForType.add(attrName); - } - - /** - * Adds an object to a class or creates a new class for the object if it doesn't exist. - *

- * This method associates an object with a class name. If the object already exists and is associated with one or more - * class names, the provided class name is added to the list of classes for that object. If the object doesn't exist, - * a new entry is created in the object-to-class mapping, associating the object with the provided class name. - * - * @param className The name of the class to which the object will be associated. - * @param objectName The name of the object to be added or associated with a class. - */ - public void addObject(String className, String objectName) { - objectsToClassMap.computeIfAbsent(objectName, key -> new LinkedList<>()).add(className); - classToObjectsMap.computeIfAbsent(className, key -> new LinkedList<>()).add(objectName); - } - /** * Adds the specified operation to the auto-completion map based on its OperationType. * @@ -325,7 +216,7 @@ private List getParamForIterationOp(String opName) { * @return A list of association names. */ private List getAllAssociations() { - return new LinkedList<>(associationToClassNamesMap.keySet()); + return model.associations().stream().map(MNamedElement::name).collect(Collectors.toList()); } /** @@ -336,19 +227,58 @@ private List getAllAssociations() { */ private List getObjectsToAssociation(String association) { List objects = new LinkedList<>(); - for (String className : associationToClassNamesMap.get(association)) { - objects.addAll(classToObjectsMap.get(className)); + MAssociation mAssoc = stringToMAssociation(association); + Set classes = mAssoc.associatedClasses(); + List classNames = classes.stream().map(MNamedElement::name).collect(Collectors.toList()); + + for (String className : classNames) { + MClass c = classNameToMClass(className); + if (c != null) { + objects.addAll(state.objectsOfClass(c).stream().map(MObject::name).collect(Collectors.toList())); + } } return objects; } + private MAssociation stringToMAssociation(String name) { + Optional matchingAssociation = model.associations().stream() + .filter(assoc -> Objects.equals(assoc.name(), name)) + .findFirst(); + + return matchingAssociation.orElse(null); + } + + /** * Gets a list of all classes in the auto-completion map. * * @return A list of class names. */ private List getAllClasses() { - return new ArrayList<>(classToAttributeMap.keySet()); + List result = new LinkedList<>(); + for (MClass c : model.classes()) { + result.add(c.name()); + } + return result; + } + + /** + * Retrieves an MClass object based on its name from the model. + *

+ * This method searches for an MClass object within the model that has the specified class name. + * If a matching class is found, it is returned; otherwise, null is returned. + * + * @param className The name of the class to retrieve. + * @return An MClass object with the specified class name, or null if no matching class is found. + */ + private MClass classNameToMClass(String className) { + MClass res = null; + for (MClass c : model.classes()) { + if (c.name().equals(className)) { + res = c; + } + } + return res; } /** @@ -357,7 +287,7 @@ private List getAllClasses() { * @return A list of object names. */ private List getAllObjects() { - return new ArrayList<>(objectsToClassMap.keySet()); + return state.allObjects().stream().map(MObject::name).collect(Collectors.toList()); } @@ -396,32 +326,101 @@ private List getSuggestionsObjects(ResultTypeOCLObjects parserResult) { String objectName = parserResult.objectName; String operationType = parserResult.operationType; - List classnames = objectsToClassMap.get(objectName); - List attributes = new LinkedList<>(); - if ("Real".equals(operationType) || "Integer".equals(operationType)) { operationType = "Number"; } - try { - for (String classname : classnames) {//all classes the object is an instance of - Map> classAttributes = classToAttributeMap.get(classname); - if (operationType != null) {//if not null only a specific attribute type is needed - List classAttributesForType = classAttributes.get(operationType); - attributes.addAll(classAttributesForType); - } else {//otherwise add all attributes this class has - for (List attrsForType : classAttributes.values()) { - attributes.addAll(attrsForType); - } - } + MObject obj = state.objectByName(objectName); + List attrs = obj.cls().allAttributes(); + List ops = obj.cls().allOperations(); + + List attributes = new LinkedList<>(filterAttributes(attrs, operationType)); + attributes.addAll(filterOperations(ops, operationType)); + + sortSuggestions(attributes); + return attributes; + } + + /** + * Filters a list of MAttribute objects based on the specified operation type. + *

+ * This method takes a list of MAttribute objects and filters them based on the provided + * operationType. If operationType is null or empty, all attribute names are included in the result. + * If operationType is specified, only attributes matching the specified type are included. + * + * @param attributes The list of MAttribute objects to be filtered. + * @param operationType The type of operation to filter by (e.g., "Boolean", "Number", "String"). + * @return A list of attribute names filtered based on the specified operationType. + */ + private List filterAttributes(List attributes, String operationType) { + return attributes.stream() + .filter(attr -> operationType == null || + (operationType.equals("Boolean") && attr.type().isKindOfBoolean(Type.VoidHandling.EXCLUDE_VOID)) || + (operationType.equals("Number") && attr.type().isKindOfNumber(Type.VoidHandling.EXCLUDE_VOID)) || + (operationType.equals("String") && attr.type().isKindOfString(Type.VoidHandling.EXCLUDE_VOID))) + .map(MAttribute::name) + .collect(Collectors.toList()); + } + + /** + * Filters a list of MOperation objects based on the specified operation type. + *

+ * This method takes a list of MOperation objects and filters them based on the provided + * operationType. If operationType is null or empty, all operation names are included in the result. + * If operationType is specified, only operations returning the specified type are included. + * Additionally, the parameters of each operation are formatted using the {@link #formatOperationWithParameters(MOperation)} + * method before being added to the result list. + * + * @param operations The list of MOperation objects to be filtered. + * @param operationType The type of operation to filter by (e.g., "Boolean", "Number", "String"). + * @return A list of formatted operation names with parameters filtered based on the specified operationType. + * @see #formatOperationWithParameters(MOperation) + */ + private List filterOperations(List operations, String operationType) { + return operations.stream() + .filter(op -> operationType == null || + ("Boolean".equals(operationType) && op.resultType().isKindOfBoolean(Type.VoidHandling.EXCLUDE_VOID)) || + ("Number".equals(operationType) && op.resultType().isKindOfNumber(Type.VoidHandling.EXCLUDE_VOID)) || + ("String".equals(operationType) && op.resultType().isKindOfString(Type.VoidHandling.EXCLUDE_VOID))) + .map(this::formatOperationWithParameters) + .collect(Collectors.toList()); + } + + /** + * Formats the operation name and its parameters.. + *

+ * The method takes an MOperation and extracts its name and parameter list. The parameter list is then + * formatted in the following way: "type identifier, type identifier, ...". The formatted parameters + * are appended to the operation name in the format "operationName(type identifier, type identifier, ...)". + * + * @param op The MOperation object to be formatted. + * @return The formatted string representing the operation name and its parameters. + */ + private String formatOperationWithParameters(MOperation op) { + String operationName = op.name(); + String parameterList = op.paramList().toString(); + + // Format the parameter list + StringBuilder formattedParameters = new StringBuilder(); + String[] lines = parameterList.split("\n"); + for (String line : lines) { + String[] parts = line.split(":"); + if (parts.length == 2) { + String identifier = parts[0].trim(); + String type = parts[1].trim(); + formattedParameters.append(type).append(" ").append(identifier).append(", "); } - sortSuggestions(attributes); - return attributes; - } catch (NullPointerException ne) { - return Collections.emptyList(); } + + // Remove the trailing comma and space + if (formattedParameters.length() > 0) { + formattedParameters.setLength(formattedParameters.length() - 2); + } + + return operationName + "(" + formattedParameters + ")"; } + /** * Retrieves a list of suggestions for operations on a collection based on its type. * @@ -459,11 +458,12 @@ private List getSuggestionCollections(ResultTypeOCLCollections parserRes } else if (parserResult instanceof ResultTypeOCLCollectionsEndsWithPipeAndContainsColon) { ResultTypeOCLCollectionsEndsWithPipeAndContainsColon parsedResult = (ResultTypeOCLCollectionsEndsWithPipeAndContainsColon) parserResult; String className = parsedResult.className; + Collection objects = state.objectsOfClass(classNameToMClass(className)); - if (classToObjectsMap.get(className) != null) { - List objects = new LinkedList<>(classToObjectsMap.get(className)); - sortSuggestions(objects); - return objects; + if (!objects.isEmpty()) { + List objs = objects.stream().map(MObject::name).collect(Collectors.toList()); + sortSuggestions(objs); + return objs; } else { return List.of(); } @@ -576,7 +576,12 @@ private List getSuggestionSet(ResultTypeUseSet parserResult) { result = getAllObjects(); } else if (parserResult instanceof ResultTypeUseSetAttr) { String objectName = ((ResultTypeUseSetAttr) parserResult).objectName; - result = getSuggestionsObjects(new ResultTypeOCLObjects(objectName, null)); + List attrs = state.objectByName(objectName).cls().allAttributes(); + result = new LinkedList<>(); + for (MAttribute attr : attrs) { + result.add(attr.name()); + } + } else { result = List.of(); } @@ -592,7 +597,7 @@ private List getSuggestionSet(ResultTypeUseSet parserResult) { * For "openterPartObject," it returns a list of all objects from getAllObjects(). * For "openterPartOperation," it returns a list of operations associated with the provided object name. * - * @param parserResult The result of parsing the USE-command openter, containing subtype and object name information due to inheritance. + * @param parserResult The result of parsing the USE-command openter, containing subtype and object name information due to inheritance. * @return A list of suggestions for opening operations or objects based on the specified subtype and object name. */ private List getSuggestionOpenter(ResultTypeUseOpenter parserResult) { @@ -608,11 +613,9 @@ private List getSuggestionOpenter(ResultTypeUseOpenter parserResult) { String objectName = parsedResult.objectName; String operationPrefix = parsedResult.operationPrefix; - List classes = objectsToClassMap.get(objectName); - List operations = classes.stream() - .flatMap(c -> classToOperationMap.getOrDefault(c, Collections.emptyList()).stream()) - .filter(operation -> operationPrefix == null || operation.startsWith(operationPrefix)) - .map(operation -> operationPrefix == null ? operation : operation.substring(operationPrefix.length())) + List operations = state.objectByName(objectName).cls().allOperations().stream() + .filter(operation -> operationPrefix == null || operation.name().startsWith(operationPrefix)) + .map(operation -> operationPrefix == null ? operation.name() : operation.name().substring(operationPrefix.length())) .collect(Collectors.toList()); sortSuggestions(operations); return operations; @@ -701,7 +704,8 @@ private List getSuggestionInfo(ResultTypeUseInfo parserResult) { private List getSuggestionsWithObjectPrefix(ResultTypeOCLObjectPrefix parserResult) { String objectPrefix = parserResult.objectPrefix; - Set objectsAndCollectionTypes = new HashSet<>(objectsToClassMap.keySet()); + Set objectsAndCollectionTypes = new HashSet<>(state.allObjectNames()); + objectsAndCollectionTypes.addAll(List.of("Set", "OrderedSet", "Sequence", "Bag")); objectsAndCollectionTypes.addAll(collectionToOperationsMap.keySet()); List result = objectsAndCollectionTypes.stream() .filter(objectName -> objectName != null && objectName.startsWith(objectPrefix)) @@ -774,14 +778,10 @@ private void sortSuggestions(List suggestions) { * * @param model The MModel used to initialize the autocomplete dictionaries. */ - public AutoCompletion(MModel model) { - classToAttributeMap = new HashMap<>(); - objectsToClassMap = new HashMap<>(); + public AutoCompletion(MModel model, MSystemState state) { collectionToOperationsMap = new HashMap<>(); - classToOperationMap = new HashMap<>(); - associationToClassNamesMap = new HashMap<>(); - classToObjectsMap = new HashMap<>(); this.model = model; + this.state = state; initializeAutocompletion(); } @@ -795,31 +795,6 @@ public AutoCompletion(MModel model) { * to the autocompletion dictionaries based on the provided MModel. */ private void initializeAutocompletion() { - Collection mClasses = this.model.classes(); - //Add all classes, attributes and operations to autocompletion - for (MClass mClass : mClasses) { - addClass(mClass.name()); - - List operations = mClass.operations().stream() - .map(MOperation::toString) - .collect(Collectors.toList()); - addOperations(mClass.name(), operations); - List attributes = mClass.allAttributes(); - for (MAttribute mAttribute : attributes) { - addAttributeToExistingClass(mClass.name(), mAttribute.name(), mAttribute.type().toString()); - } - } - - //add associations - Collection associations = this.model.associations(); - for (MAssociation mAssociation : associations) { - List classNames = new LinkedList<>(); - for (MClass mClass : mAssociation.associatedClasses()) { - classNames.add(mClass.name()); - } - addAssociation(mAssociation.name(), classNames); - } - //operations for (OpGeneric op : ExpStdOp.opmap.values()) { OperationType operationType = op.getOperationType(); @@ -829,6 +804,5 @@ private void initializeAutocompletion() { for (String opName : ParserHelper.queryIdentMap.keySet()) { addIterationOp(opName); } - } } diff --git a/use-gui/src/main/java/org/tzi/use/gui/main/CreateObjectDialog.java b/use-gui/src/main/java/org/tzi/use/gui/main/CreateObjectDialog.java index 522bc6832..378eb7b32 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/main/CreateObjectDialog.java +++ b/use-gui/src/main/java/org/tzi/use/gui/main/CreateObjectDialog.java @@ -176,8 +176,6 @@ private void createObject() { JOptionPane.ERROR_MESSAGE); return; } - fParent.getAutocompletionInstance().addObject(cls.name(), name); - fParent.createObject(cls, name); } } diff --git a/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java b/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java index 74f7bd042..825627122 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java +++ b/use-gui/src/main/java/org/tzi/use/gui/main/MainWindow.java @@ -1149,7 +1149,7 @@ protected boolean compile(final Path f) { fLogWriter.println(model.getStats()); // create system system = new MSystem(model); - autocompletion = new AutoCompletion(model,fSession); + autocompletion = new AutoCompletion(model, system.state(), fSession); } else { system = null; } diff --git a/use-gui/src/test/java/org/tzi/use/TestSystem.java b/use-gui/src/test/java/org/tzi/use/TestSystem.java index d26f98bdf..ce29d50f1 100644 --- a/use-gui/src/test/java/org/tzi/use/TestSystem.java +++ b/use-gui/src/test/java/org/tzi/use/TestSystem.java @@ -138,7 +138,7 @@ private void init() throws MSystemException, MInvalidModelException, ExpInvalidE fSystem = new MSystem(model); - autoCompletion = new AutoCompletion(model); + autoCompletion = new AutoCompletion(model, fSystem.state()); initObjectsAndLinks(autoCompletion, c1, c2); } @@ -151,16 +151,6 @@ private void initObjectsAndLinks(AutoCompletion autoCompletion, MClass c1, MClas varEnv.assign("v", IntegerValue.valueOf(42)); - autoCompletion.addObject(c1.name(), "obj1"); - autoCompletion.addObject(c1.name(), "obj2"); - autoCompletion.addObject(c1.name(), "obj3"); - autoCompletion.addObject(c1.name(), "obj4"); - - autoCompletion.addObject(c2.name(), "obj5"); - autoCompletion.addObject(c2.name(), "obj6"); - autoCompletion.addObject(c2.name(), "obj7"); - autoCompletion.addObject(c2.name(), "obj8"); - state.createObject(c1, "obj1"); state.createObject(c1, "obj2"); state.createObject(c1, "obj3"); diff --git a/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java b/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java index 4febbae1d..24206a36f 100644 --- a/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java +++ b/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java @@ -82,10 +82,10 @@ public void testSuggestionsCaseObjects() throws MSystemException, ExpInvalidExce AutoCompletion testee = testSystem.getAutoCompletion(); - Set expectedResult = Set.of("attribute1_1", "attribute1_2", "attribute1_3", "attribute1_4"); + Set expectedResult = Set.of("attribute1_1", "attribute1_2", "attribute1_3", "attribute1_4", "op1(Integer p1)", "op2(Real p1)", "operation3(Boolean p1)"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("obj1.", true).suggestions)); - expectedResult = Set.of("attribute1_2", "attribute1_3"); + expectedResult = Set.of("attribute1_2", "attribute1_3", "op1(Integer p1)", "op2(Real p1)"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("5 = obj1.", true).suggestions)); expectedResult = Set.of("attribute1_4"); @@ -94,10 +94,10 @@ public void testSuggestionsCaseObjects() throws MSystemException, ExpInvalidExce expectedResult = Set.of("attribute2_1", "attribute2_2", "attribute2_3"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("obj5.", true).suggestions)); - expectedResult = Set.of("attribute1_2", "attribute1_3"); + expectedResult = Set.of("attribute1_2", "attribute1_3", "op1(Integer p1)", "op2(Real p1)"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("6.2 = obj1.", true).suggestions)); - expectedResult = Set.of("attribute1_1"); + expectedResult = Set.of("attribute1_1", "operation3(Boolean p1)"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("false = obj1.", true).suggestions)); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("true = obj1.", true).suggestions)); } @@ -243,7 +243,7 @@ public void testSuggestionCaseIterationExpressions() throws MSystemException, Ex Set expectedResult = Set.of("1", "2", "3", "4", "5", "6", "7", "8"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(obj)", true).suggestions)); - expectedResult = Set.of("attribute1_1", "attribute1_2", "attribute1_3", "attribute1_4"); + expectedResult = Set.of("attribute1_1", "attribute1_2", "attribute1_3", "attribute1_4", "op1(Integer p1)", "op2(Real p1)", "operation3(Boolean p1)"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(obj1.)", true).suggestions)); expectedResult = Set.of("_1", "_2", "_3", "_4"); From b6587abb3cab19af5692439621eab3fd385159c5 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 8 Dec 2023 12:47:44 +0100 Subject: [PATCH 05/10] Added documentation in documentation/documentation.md --- documentation/documentation.md | 86 ++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/documentation/documentation.md b/documentation/documentation.md index 4317822cb..c0f562f5e 100644 --- a/documentation/documentation.md +++ b/documentation/documentation.md @@ -99,3 +99,89 @@ All plugin-actions are by default only enabled, i.e. the menuitem and toolbar-bu a model is loaded. A plugin-action can change this behaviour by overriding the default method ```boolean shouldBeEnabled(IPluginAction pluginAction)```. This is especially useful, if an action does not require a model to be loaded. + +## Autocompletion for OCL statements + +### Design + +```mermaid +classDiagram + class Autocompletion { + - model: MModel + - state: MSystemState + + getSuggestions(input: String, OCLOnly boolean): SuggestionResult + } + + class AutocompletionParser { + - resultType ResultTypeRoot + + AutoCompletionParser(model: MModel, input: String): AutoCompletionparser + + getResult(): ResultTypeRoot + + parse(model: MModel, input: String, err: PrintWriter): void + - processResult(): void + } + + class ResultTypeRoot { + // Base class for result types + } + + class SuggestionResult { + + suggestions: List + + prefix: String + } + + Autocompletion -- AutocompletionParser: uses + AutocompletionParser -- ResultTypeRoot: creates + AutocompletionParser --|> Suggestion: creates + +``` + +The autocompletion process involves a lexer, a parser and a suggester. + +The 'Autocompletion' serves as the suggester, while the 'AutoCompletionParser' acts as both lexer and parser. + +An instance of 'Autocompletion' is held by the Main Window, ensuring only one instance exists when the GUI is active. Upon loading a new model, the existing instance is replaced with a new one. + +```mermaid +sequenceDiagram + participant Autocompletion + participant AutocompletionParser + participant ResultTypeRoot + participant Suggestion + + EvalOCLDialog->>Autocompletion: getSuggestions(input, OCLOnly) + Autocompletion->>AutocompletionParser: create instance + AutocompletionParser->>AutocompletionParser: parse() + AutocompletionParser->>AutocompletionParser: processResult() + AutocompletionParser-->>ResultTypeRoot: create instance + Autocompletion->>AutocompletionParser: getResult() + AutocompletionParser-->>SuggestionResult: create instance +``` + +In the 'getSuggestions' method of 'Autocompletion,' a new 'AutoCompletionParser' instance is created. The constructor of 'AutoCompletionParser' involves parsing the input string and processing the found types. + +Processing is essential as the 'parse' method only collects types found in the input string. In 'processResult,' an instance of a subtype of 'ResultTypeRoot' is created. + +The 'getSuggestions' method then calls 'getResult' of the 'AutoCompletionParser' object, returning a 'SuggestionResult' object containing suggestion strings and a possible prefix for colored input. + +### Usage in GUI + +The hotkey 'CTRL+SPACE' triggers autocompletion in the 'EvalOClDialog,' and 'ENTER' selects a value. Currently, these hotkeys are not customizable. + +### Usage in Code + +To obtain suggestions for an OCL statement: + +- Call the 'getSuggestions' method of the 'AutocompleteManager' instance. + +For obtaining only a subtype of 'ResultTypeRoot' without suggestions: +- Instantiate a new 'AutocompleteParser' object. +- Call the 'getResult' method of the new object. + +### Adding autocompletion support for new operation + +When adding a new operation for a collection type in USE these steps have to be completed: + +- Create a new subclass of 'OpGeneric,' e.g., 'MyOperation.' +- Register it through the given 'registerOperation' in 'StandardOperationsMyCollection.' + +To add autocompletion support for 'MyOperation' no additional steps are required, as both autocompletion and the OCL compiler rely on the same data structure ('opmap' in 'ExpStdOp'). The new operation seamlessly becomes part of the suggestion list, e.g., when typing 'MyCollection{MyValues}->MyOp.' \ No newline at end of file From 4c661184e4b8ba67700081cf923224ca9c9dd507 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 8 Dec 2023 12:58:55 +0100 Subject: [PATCH 06/10] Removed unused class SuggestionForClassNames --- .../SuggestionForClassName.java | 78 ------------------- 1 file changed, 78 deletions(-) delete mode 100644 use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionForClassName.java diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionForClassName.java b/use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionForClassName.java deleted file mode 100644 index 94d3ef08e..000000000 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/SuggestionForClassName.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.tzi.use.autocompletion; - -import java.util.*; - -/** - * Is a specialized map that associates types with their - * respective attributes. Is used in {@link AutoCompletion} to map class names to types to attributes. - * Each Type is mapped to a list of attribute names, where each attribute - * is represented as a string. - */ -public class SuggestionForClassName implements Map> { - //contains the attributes of a class seperated by Type, e.g.: "Number", "String", "Boolean" - private final Map> typeToAttributeMap; - - public SuggestionForClassName() { - typeToAttributeMap = new HashMap<>(); - } - - @Override - public int size() { - return typeToAttributeMap.size(); - } - - @Override - public boolean isEmpty() { - return typeToAttributeMap.isEmpty(); - } - - @Override - public boolean containsKey(Object key) { - return typeToAttributeMap.containsKey(key); - } - - @Override - public boolean containsValue(Object value) { - return typeToAttributeMap.containsValue(value); - } - - @Override - public List get(Object key) { - return typeToAttributeMap.get(key); - } - - @Override - public List put(String key, List value) { - return typeToAttributeMap.put(key, value); - } - - @Override - public List remove(Object key) { - return typeToAttributeMap.remove(key); - } - - @Override - public void putAll(Map> m) { - typeToAttributeMap.putAll(m); - } - - @Override - public void clear() { - typeToAttributeMap.clear(); - } - - @Override - public Set keySet() { - return typeToAttributeMap.keySet(); - } - - @Override - public Collection> values() { - return typeToAttributeMap.values(); - } - - @Override - public Set>> entrySet() { - return typeToAttributeMap.entrySet(); - } -} From 982b4aac73c4284292848c45c2b9c6a3cecba997 Mon Sep 17 00:00:00 2001 From: till Date: Fri, 8 Dec 2023 13:18:08 +0100 Subject: [PATCH 07/10] Updated AutocompletionResultTypes: Changed empty abstract classes to interfaces --- .../use/autocompletion/parserResultTypes/ResultTypeRoot.java | 2 +- .../autocompletion/parserResultTypes/ocl/ResultTypeOCL.java | 4 ++-- .../parserResultTypes/ocl/ResultTypeOCLAttributePrefix.java | 2 +- .../parserResultTypes/ocl/ResultTypeOCLCollections.java | 2 +- .../ocl/ResultTypeOCLCollectionsContainsColon.java | 2 +- .../ocl/ResultTypeOCLCollectionsDefault.java | 2 +- .../ocl/ResultTypeOCLCollectionsEndWithPipe.java | 2 +- .../ocl/ResultTypeOCLCollectionsEndsWithCommaAndIsForAll.java | 2 +- .../ResultTypeOCLCollectionsEndsWithPipeAndContainsColon.java | 2 +- .../parserResultTypes/ocl/ResultTypeOCLObjectPrefix.java | 2 +- .../parserResultTypes/ocl/ResultTypeOCLObjects.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseCheck.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseCommands.java | 4 ++-- .../parserResultTypes/useCommands/ResultTypeUseCreate.java | 2 +- .../useCommands/ResultTypeUseCreateAssociation.java | 2 +- .../useCommands/ResultTypeUseCreateDefault.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseDelete.java | 2 +- .../useCommands/ResultTypeUseDeleteAssociation.java | 2 +- .../useCommands/ResultTypeUseDeleteObjects.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseDestroy.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseInfo.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseInfoClass.java | 2 +- .../useCommands/ResultTypeUseInfoDefault.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseInsert.java | 2 +- .../useCommands/ResultTypeUseInsertAssociation.java | 2 +- .../useCommands/ResultTypeUseInsertObjects.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseOpen.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseOpenter.java | 2 +- .../useCommands/ResultTypeUseOpenterObject.java | 2 +- .../useCommands/ResultTypeUseOpenterOperation.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseSet.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseSetAttr.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseSetObject.java | 2 +- .../parserResultTypes/useCommands/ResultTypeUseStep.java | 2 +- 34 files changed, 36 insertions(+), 36 deletions(-) diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ResultTypeRoot.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ResultTypeRoot.java index 7ddf62dd7..b1bdbf6c9 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ResultTypeRoot.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ResultTypeRoot.java @@ -4,5 +4,5 @@ * This abstract class serves as the root type for all ResultType classes in the autocompletion parser. * Subclasses of this class represent different types of results obtained during parsing. */ -public abstract class ResultTypeRoot { +public interface ResultTypeRoot { } diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCL.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCL.java index 2cec38383..c6b3f89de 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCL.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCL.java @@ -3,8 +3,8 @@ import org.tzi.use.autocompletion.parserResultTypes.ResultTypeRoot; /** - * This abstract class serves as the parent for all ResultType classes associated with OCL + * This interface serves as the parent for all ResultType classes associated with OCL * in the autocompletion parser. It extends {@link ResultTypeRoot}. */ -public abstract class ResultTypeOCL extends ResultTypeRoot { +public interface ResultTypeOCL extends ResultTypeRoot { } diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLAttributePrefix.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLAttributePrefix.java index 92935ebb0..f15ac879d 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLAttributePrefix.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLAttributePrefix.java @@ -6,7 +6,7 @@ * Represents the autocompletion parser's result type for OCL expressions with an attribute prefix. * It extends {@link ResultTypeOCL}. */ -public class ResultTypeOCLAttributePrefix extends ResultTypeOCL { +public class ResultTypeOCLAttributePrefix implements ResultTypeOCL { public String objectName; public String operationType; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollections.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollections.java index 768af31bd..18de09d28 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollections.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollections.java @@ -4,5 +4,5 @@ * Represents the abstract base class for autocompletion parser result types associated with OCL collections. * It extends {@link ResultTypeOCL}. */ -public abstract class ResultTypeOCLCollections extends ResultTypeOCL{ +public interface ResultTypeOCLCollections extends ResultTypeOCL{ } diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsContainsColon.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsContainsColon.java index 483da150c..8b836c313 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsContainsColon.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsContainsColon.java @@ -7,7 +7,7 @@ * It is used for iteration expressions containing a colon but not ending on "," or "|" * It extends {@link ResultTypeOCLCollections}. */ -public class ResultTypeOCLCollectionsContainsColon extends ResultTypeOCLCollections { +public class ResultTypeOCLCollectionsContainsColon implements ResultTypeOCLCollections { public String className; public String elemType; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsDefault.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsDefault.java index 62eae1fa4..7ebf90d07 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsDefault.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsDefault.java @@ -2,7 +2,7 @@ import java.util.Objects; -public class ResultTypeOCLCollectionsDefault extends ResultTypeOCLCollections { +public class ResultTypeOCLCollectionsDefault implements ResultTypeOCLCollections { public String prefix; public String collectionType; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndWithPipe.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndWithPipe.java index 418dc5a81..d092cf8f4 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndWithPipe.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndWithPipe.java @@ -7,7 +7,7 @@ * It is used for expressions ending with a pipe (|) and containing an iteration expression identifier. * It extends {@link ResultTypeOCLCollections}. */ -public class ResultTypeOCLCollectionsEndWithPipe extends ResultTypeOCLCollections{ +public class ResultTypeOCLCollectionsEndWithPipe implements ResultTypeOCLCollections{ public String iterExprIdentifier; public ResultTypeOCLCollectionsEndWithPipe(String iterExprIdentifier) { diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithCommaAndIsForAll.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithCommaAndIsForAll.java index 9b007f80b..e4dbd64dd 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithCommaAndIsForAll.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithCommaAndIsForAll.java @@ -5,7 +5,7 @@ * It is used for expressions ending with a comma and having "forAll" as operation. * It extends {@link ResultTypeOCLCollections}. */ -public class ResultTypeOCLCollectionsEndsWithCommaAndIsForAll extends ResultTypeOCLCollections{ +public class ResultTypeOCLCollectionsEndsWithCommaAndIsForAll implements ResultTypeOCLCollections{ @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithPipeAndContainsColon.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithPipeAndContainsColon.java index 91b9309b1..00f5ca9b3 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithPipeAndContainsColon.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLCollectionsEndsWithPipeAndContainsColon.java @@ -7,7 +7,7 @@ * It is used for expressions ending with a pipe (|) and containing a colon (:). * It extends {@link ResultTypeOCLCollections}. */ -public class ResultTypeOCLCollectionsEndsWithPipeAndContainsColon extends ResultTypeOCLCollections{ +public class ResultTypeOCLCollectionsEndsWithPipeAndContainsColon implements ResultTypeOCLCollections{ public String className; public ResultTypeOCLCollectionsEndsWithPipeAndContainsColon(String className) { diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjectPrefix.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjectPrefix.java index dcc6bbed8..f1e8efb67 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjectPrefix.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjectPrefix.java @@ -7,7 +7,7 @@ * It is used for expressions containing an object prefix. * It extends {@link ResultTypeOCL}. */ -public class ResultTypeOCLObjectPrefix extends ResultTypeOCL{ +public class ResultTypeOCLObjectPrefix implements ResultTypeOCL{ public String objectPrefix; public ResultTypeOCLObjectPrefix(String objectPrefix) { diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjects.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjects.java index 71f0f2fee..558f2bec6 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjects.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/ocl/ResultTypeOCLObjects.java @@ -7,7 +7,7 @@ * It is used for expressions containing a completed object name. * It extends {@link ResultTypeOCL}. */ -public class ResultTypeOCLObjects extends ResultTypeOCL { +public class ResultTypeOCLObjects implements ResultTypeOCL { public String objectName; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCheck.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCheck.java index f03699cdf..b8f9d0704 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCheck.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCheck.java @@ -4,7 +4,7 @@ * Represents a specific autocompletion parser result type associated with the USE command "check". * It is a subtype of {@link ResultTypeUseCommands}. */ -public class ResultTypeUseCheck extends ResultTypeUseCommands{ +public class ResultTypeUseCheck implements ResultTypeUseCommands{ @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCommands.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCommands.java index 95f6317c3..0729ee723 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCommands.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCommands.java @@ -3,8 +3,8 @@ import org.tzi.use.autocompletion.parserResultTypes.ResultTypeRoot; /** - * This abstract class serves as the parent for all ResultType classes associated with USE commands + * This interface serves as the parent for all ResultType classes associated with USE commands * in the autocompletion parser. It extends {@link ResultTypeRoot}. */ -public abstract class ResultTypeUseCommands extends ResultTypeRoot { +public interface ResultTypeUseCommands extends ResultTypeRoot { } diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreate.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreate.java index 6c2366c59..ac461ecdb 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreate.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreate.java @@ -5,5 +5,5 @@ * It is a subtype of {@link ResultTypeUseCommands}. */ -public abstract class ResultTypeUseCreate extends ResultTypeUseCommands{ +public interface ResultTypeUseCreate extends ResultTypeUseCommands{ } diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateAssociation.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateAssociation.java index 9f78aa876..a0be3ef50 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateAssociation.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateAssociation.java @@ -6,7 +6,7 @@ * Represents an autocompletion parser result type associated with the USE command "create" when an association name was already given. * It is a subtype of {@link ResultTypeUseCreate}. */ -public class ResultTypeUseCreateAssociation extends ResultTypeUseCreate{ +public class ResultTypeUseCreateAssociation implements ResultTypeUseCreate{ public String objectName; public String associationName; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateDefault.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateDefault.java index 2f2defc87..7ad9e1ec9 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateDefault.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseCreateDefault.java @@ -4,7 +4,7 @@ * Represents an autocompletion parser result type associated with the USE command "create" when there was no association name already given. * It is a subtype of {@link ResultTypeUseCreate}. */ -public class ResultTypeUseCreateDefault extends ResultTypeUseCreate{ +public class ResultTypeUseCreateDefault implements ResultTypeUseCreate{ @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDelete.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDelete.java index f7abf9f8b..3151bb39b 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDelete.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDelete.java @@ -4,5 +4,5 @@ * Represents an abstract autocompletion parser result type associated with the USE command "delete". * It is a subtype of {@link ResultTypeUseCommands}. */ -public abstract class ResultTypeUseDelete extends ResultTypeUseCommands{ +public interface ResultTypeUseDelete extends ResultTypeUseCommands{ } diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteAssociation.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteAssociation.java index c6b3554f1..206930730 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteAssociation.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteAssociation.java @@ -4,7 +4,7 @@ * Represents an autocompletion parser result type associated with the USE command "delete" when object names were already given. * It is a subtype of {@link ResultTypeUseDelete}. */ -public class ResultTypeUseDeleteAssociation extends ResultTypeUseDelete { +public class ResultTypeUseDeleteAssociation implements ResultTypeUseDelete { @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteObjects.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteObjects.java index b480bb125..9b05bb9c6 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteObjects.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDeleteObjects.java @@ -6,7 +6,7 @@ * Represents an autocompletion parser result type associated with the USE command "delete" when no object names were already given. * It is a subtype of {@link ResultTypeUseDelete}. */ -public class ResultTypeUseDeleteObjects extends ResultTypeUseDelete{ +public class ResultTypeUseDeleteObjects implements ResultTypeUseDelete{ public String objectNames; //comma seperated list public ResultTypeUseDeleteObjects(String objectNames) { diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDestroy.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDestroy.java index af3632cce..32ae10c3b 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDestroy.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseDestroy.java @@ -7,7 +7,7 @@ * It extends {@link ResultTypeUseCommands}. */ -public class ResultTypeUseDestroy extends ResultTypeUseCommands{ +public class ResultTypeUseDestroy implements ResultTypeUseCommands{ public String objectNames; //comma seperated list public ResultTypeUseDestroy(String objectNames) { diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfo.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfo.java index db1bc79e5..75cb8662e 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfo.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfo.java @@ -5,5 +5,5 @@ * It serves as a parent class for various specific result types related to USE information commands. * It extends {@link ResultTypeUseCommands}. */ -public abstract class ResultTypeUseInfo extends ResultTypeUseCommands{ +public interface ResultTypeUseInfo extends ResultTypeUseCommands{ } diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoClass.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoClass.java index 994f7823c..196fb9b9a 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoClass.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoClass.java @@ -4,7 +4,7 @@ * Represents an autocompletion parser result type associated with the USE command "info" when the class parameter was already given. * It is a subtype of {@link ResultTypeUseInfo}. */ -public class ResultTypeUseInfoClass extends ResultTypeUseInfo{ +public class ResultTypeUseInfoClass implements ResultTypeUseInfo{ @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoDefault.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoDefault.java index 29442c3a2..7b6def0b4 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoDefault.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInfoDefault.java @@ -4,7 +4,7 @@ * Represents an autocompletion parser result type associated with the USE command "info" when no parameter was already given. * It is a subtype of {@link ResultTypeUseInfo}. */ -public class ResultTypeUseInfoDefault extends ResultTypeUseInfo{ +public class ResultTypeUseInfoDefault implements ResultTypeUseInfo{ @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsert.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsert.java index f580f7987..6b688b19a 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsert.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsert.java @@ -5,5 +5,5 @@ * It serves as a parent class for various specific result types related to USE insert commands. * It extends {@link ResultTypeUseCommands}. */ -public abstract class ResultTypeUseInsert extends ResultTypeUseCommands{ +public interface ResultTypeUseInsert extends ResultTypeUseCommands{ } diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertAssociation.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertAssociation.java index 6b4848100..7ef417f3f 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertAssociation.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertAssociation.java @@ -4,7 +4,7 @@ * Represents an autocompletion parser result type associated with the USE command "insert" when objects names were already given. * It is a subtype of {@link ResultTypeUseInsert}. */ -public class ResultTypeUseInsertAssociation extends ResultTypeUseInsert{ +public class ResultTypeUseInsertAssociation implements ResultTypeUseInsert{ @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertObjects.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertObjects.java index b32a5953e..4a82922f3 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertObjects.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseInsertObjects.java @@ -6,7 +6,7 @@ * Represents an autocompletion parser result type associated with the USE command "insert" when no objects names were already given. * It is a subtype of {@link ResultTypeUseInsert}. */ -public class ResultTypeUseInsertObjects extends ResultTypeUseInsert{ +public class ResultTypeUseInsertObjects implements ResultTypeUseInsert{ public String objectNames; //comma seperated list public ResultTypeUseInsertObjects(String objectNames) { diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpen.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpen.java index 32bbc3bdc..e99c5aabc 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpen.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpen.java @@ -6,7 +6,7 @@ * Represents a specific autocompletion parser result type associated with the USE command "open". * It extends {@link ResultTypeUseCommands}. */ -public class ResultTypeUseOpen extends ResultTypeUseCommands{ +public class ResultTypeUseOpen implements ResultTypeUseCommands{ public String path; public ResultTypeUseOpen(String path) { diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenter.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenter.java index 656fc0bc3..b8ea02663 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenter.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenter.java @@ -5,5 +5,5 @@ * It serves as the parent class for more specific result types related to "openter". * It extends {@link ResultTypeUseCommands}. */ -public abstract class ResultTypeUseOpenter extends ResultTypeUseCommands{ +public interface ResultTypeUseOpenter extends ResultTypeUseCommands{ } diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterObject.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterObject.java index 5df833525..f64ba3b40 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterObject.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterObject.java @@ -6,7 +6,7 @@ * Represents an autocompletion parser result type associated with the USE command "openter" when no object name was already given. * It is a subtype of {@link ResultTypeUseOpenter}. */ -public class ResultTypeUseOpenterObject extends ResultTypeUseOpenter{ +public class ResultTypeUseOpenterObject implements ResultTypeUseOpenter{ public String objectName; public ResultTypeUseOpenterObject(String objectName) { diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterOperation.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterOperation.java index c7eaeda4e..55a697080 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterOperation.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseOpenterOperation.java @@ -6,7 +6,7 @@ * Represents an autocompletion parser result type associated with the USE command "openter" when the object name was already given. * It is a subtype of {@link ResultTypeUseOpenter}. */ -public class ResultTypeUseOpenterOperation extends ResultTypeUseOpenter{ +public class ResultTypeUseOpenterOperation implements ResultTypeUseOpenter{ public String objectName; public String operationPrefix; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSet.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSet.java index 8909faccf..e7cd94aca 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSet.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSet.java @@ -5,5 +5,5 @@ * It serves as the parent class for more specific result types related to "set". * It extends {@link ResultTypeUseCommands}. */ -public abstract class ResultTypeUseSet extends ResultTypeUseCommands{ +public interface ResultTypeUseSet extends ResultTypeUseCommands{ } diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetAttr.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetAttr.java index 76485c0c0..15d3bf05f 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetAttr.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetAttr.java @@ -6,7 +6,7 @@ * Represents an autocompletion parser result type associated with the USE command "set" when the object name was already given. * It is a subtype of {@link ResultTypeUseSet}. */ -public class ResultTypeUseSetAttr extends ResultTypeUseSet { +public class ResultTypeUseSetAttr implements ResultTypeUseSet { public String objectName; public ResultTypeUseSetAttr(String objectName) { diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetObject.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetObject.java index 486b7e5b4..becdd3939 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetObject.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseSetObject.java @@ -4,7 +4,7 @@ * Represents an autocompletion parser result type associated with the USE command "set" when the object name was already given. * It is a subtype of {@link ResultTypeUseSet}. */ -public class ResultTypeUseSetObject extends ResultTypeUseSet{ +public class ResultTypeUseSetObject implements ResultTypeUseSet{ @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseStep.java b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseStep.java index 477521052..76578f9c7 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseStep.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/parserResultTypes/useCommands/ResultTypeUseStep.java @@ -4,7 +4,7 @@ * Represents a specific autocompletion parser result type associated with the USE command "step". * It extends {@link ResultTypeUseCommands}. */ -public class ResultTypeUseStep extends ResultTypeUseCommands{ +public class ResultTypeUseStep implements ResultTypeUseCommands{ @Override public boolean equals(Object o) { if (this == o) return true; From 06c5b06713b64fa6c4f0cb5259c74164d636a6ca Mon Sep 17 00:00:00 2001 From: till Date: Sat, 9 Dec 2023 10:42:23 +0100 Subject: [PATCH 08/10] Updated display of autocompletion results: Now depending on caret position and prefix. Also fixed possible null pointer in autocompletion class. --- .../use/autocompletion/AutoCompletion.java | 4 + .../org/tzi/use/gui/main/EvalOCLDialog.java | 137 +++++++++++------- 2 files changed, 90 insertions(+), 51 deletions(-) diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java index 7aa6e5b04..ecb52fc54 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java @@ -331,6 +331,10 @@ private List getSuggestionsObjects(ResultTypeOCLObjects parserResult) { } MObject obj = state.objectByName(objectName); + if (obj == null) { + return List.of(); + } + List attrs = obj.cls().allAttributes(); List ops = obj.cls().allOperations(); diff --git a/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java b/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java index a918f74a4..2dacbdcf6 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java +++ b/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java @@ -40,12 +40,14 @@ import org.tzi.use.autocompletion.SuggestionResult; import javax.swing.*; +import javax.swing.text.BadLocationException; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.KeyEvent; +import java.awt.geom.Rectangle2D; import java.io.PrintWriter; import java.io.StringWriter; import java.util.LinkedList; @@ -76,8 +78,10 @@ class EvalOCLDialog extends JDialog { private JList autocompletionResultList; - private JScrollPane autocompletionScrollPane; - + private JPopupMenu autocompletionPopupMenu; + + private DefaultListModel autocompletionListModel; + private final ChangeListener sessionChangeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { @@ -137,7 +141,7 @@ public void windowClosing(WindowEvent e) { // create panel on the right containing buttons JPanel btnPane = new JPanel(); btnEvalBrowser = new JButton("Browser"); - + btnEval = new JButton("Evaluate"); btnEval.setMnemonic('E'); @@ -365,88 +369,119 @@ private boolean evaluate(String in, boolean evalTree) { *

* This method takes a {@code SuggestionResult} object and a {@code JPanel} where the suggestions * will be displayed. The suggestions are formatted and displayed in a {@code JList} within a scrollable - * {@code JScrollPane}. The user can confirm a selection by pressing the Enter key. + * {@code JScrollPane} within a popupmenu. The user can confirm a selection by pressing the Enter key. *

* The formatting includes highlighting the prefix in blue and, if the suggestion contains parentheses, * highlighting the content within parentheses in orange. * * @param suggestion The {@code SuggestionResult} object containing the list of suggestions and the prefix. - * @param textPane The {@code JPanel} where the autocomplete suggestions will be displayed. + * @param textPane The {@code JPanel} where the autocomplete suggestions will be displayed. */ private void displayResultsWindow(SuggestionResult suggestion, JPanel textPane) { List suggestionList = suggestion.suggestions; - List displayedList = formatStrings(suggestion, suggestionList); + List displayedList = formatStrings(suggestion); - if (autocompletionScrollPane == null) { - autocompletionResultList = new JList<>(displayedList.toArray(new String[0])); + if (autocompletionPopupMenu == null) { + autocompletionListModel = new DefaultListModel<>(); + autocompletionResultList = new JList<>(autocompletionListModel); autocompletionResultList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); autocompletionResultList.setCellRenderer(new TwoColorListCellRenderer()); - autocompletionResultList.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "confirmSelection"); - autocompletionResultList.getActionMap().put("confirmSelection", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - String selectedValue = suggestionList.get(autocompletionResultList.getSelectedIndex()); - if (selectedValue != null) { - String currentText = fTextIn.getText(); - - String newText; - boolean hasParenthesis = false; - if (selectedValue.endsWith(")")) { - if (selectedValue.contains("(")) { - int firstIndex = selectedValue.indexOf("("); - selectedValue = selectedValue.substring(0, firstIndex + 1); - } - newText = currentText + selectedValue + ")"; - hasParenthesis = true; - } else if(currentText.endsWith(")")){ - newText = currentText.substring(0, currentText.length()-1) + selectedValue + ")"; - hasParenthesis = true; - } else { - newText = currentText + selectedValue; - } + autocompletionPopupMenu = new JPopupMenu(); + autocompletionPopupMenu.add(new JScrollPane(autocompletionResultList)); + textPane.add(autocompletionPopupMenu); + } - fTextIn.setText(newText); - if(hasParenthesis){ - fTextIn.setCaretPosition(newText.length()-1); - } else { - fTextIn.setCaretPosition(newText.length()); + autocompletionResultList.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "confirmSelection"); + Action confirmSelectionAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + int selectedIndex = autocompletionResultList.getSelectedIndex(); + + if (selectedIndex >= 0 && selectedIndex < autocompletionListModel.size()) { + String selectedValue = suggestionList.get(selectedIndex); + String currentText = fTextIn.getText(); + + String newText; + boolean hasParenthesis = false; + if (selectedValue.endsWith(")")) { + if (selectedValue.contains("(")) { + int firstIndex = selectedValue.indexOf("("); + selectedValue = selectedValue.substring(0, firstIndex + 1); } + newText = currentText + selectedValue + ")"; + hasParenthesis = true; + } else if (currentText.endsWith(")")) { + newText = currentText.substring(0, currentText.length() - 1) + selectedValue + ")"; + hasParenthesis = true; + } else { + newText = currentText + selectedValue; + } + + fTextIn.setText(newText); + if (hasParenthesis) { + fTextIn.setCaretPosition(newText.length() - 1); + } else { + fTextIn.setCaretPosition(newText.length()); } - autocompletionScrollPane = null; - autocompletionResultList = null; - textPane.remove(1); - fTextIn.requestFocus(); - pack(); } - }); - autocompletionScrollPane = new JScrollPane(autocompletionResultList); - textPane.add(autocompletionScrollPane, 1); - } else { - autocompletionResultList.setListData(displayedList.toArray(new String[0])); + autocompletionPopupMenu.setVisible(false); + } + }; + + autocompletionResultList.getActionMap().put("confirmSelection", confirmSelectionAction); + + autocompletionListModel.clear(); + autocompletionListModel.addAll(displayedList); + + int caretPosition = fTextIn.getCaretPosition(); + Rectangle2D caretRectangle; + try { + caretRectangle = fTextIn.modelToView2D(caretPosition); + } catch (BadLocationException e) { + e.printStackTrace(); + return; } + int xOffset = -2; //due to the boarder of the popupmenu + int yOffset = 12; + + int prefixPixelOffset = SwingUtilities.computeStringWidth(fTextIn.getFontMetrics(fTextIn.getFont()), suggestion.prefix); + + autocompletionPopupMenu.show(fTextIn, (int) caretRectangle.getX() + xOffset - prefixPixelOffset, (int) caretRectangle.getY() + yOffset); autocompletionResultList.requestFocus(); pack(); } - private List formatStrings(SuggestionResult suggestion, List suggestionList) { + /** + * Formats a list of suggestion strings for display, highlighting the prefix in blue + * and content within parentheses in orange. + * + * @param suggestion The SuggestionResult object containing the prefix and the list of suggestions. + * @return The formatted list of suggestions. + */ + private List formatStrings(SuggestionResult suggestion) { String prefix = suggestion.prefix; List displayedList = new LinkedList<>(); - for (int i = 0; i < suggestionList.size(); i++) { - String sugString = suggestionList.get(i); + for (int i = 0; i < suggestion.suggestions.size(); i++) { + String sugString = suggestion.suggestions.get(i); + // Highlight content within parentheses in orange if (sugString.contains("(")) { - sugString = sugString.substring(0, sugString.indexOf("(") + 1) + "" + sugString.substring(sugString.indexOf("(") + 1, sugString.indexOf(")")) + "" + ")"; + sugString = sugString.substring(0, sugString.indexOf("(") + 1) + + "" + + sugString.substring(sugString.indexOf("(") + 1, sugString.indexOf(")")) + + ")"; } + // Highlight the prefix in blue if (prefix != null) { displayedList.add(i, "" + prefix + "" + sugString); } else { - displayedList.add(i, suggestionList.get(i)); + displayedList.add(i, suggestion.suggestions.get(i)); } } return displayedList; From 9739f1d11b57d80259d2a1a62cbf5c692b78b6c8 Mon Sep 17 00:00:00 2001 From: till Date: Sat, 9 Dec 2023 15:39:15 +0100 Subject: [PATCH 09/10] Added dynamically refreshed suggestions Updated documentation: CTRL+SPACE is no longer needed Removed some unnecessary comments --- documentation/documentation.md | 8 +- .../autocompletion/AutoCompletionParser.java | 10 +- .../org/tzi/use/gui/main/EvalOCLDialog.java | 220 ++++++++++++++---- 3 files changed, 191 insertions(+), 47 deletions(-) diff --git a/documentation/documentation.md b/documentation/documentation.md index c0f562f5e..9e2c439f5 100644 --- a/documentation/documentation.md +++ b/documentation/documentation.md @@ -165,7 +165,13 @@ The 'getSuggestions' method then calls 'getResult' of the 'AutoCompletionParser' ### Usage in GUI -The hotkey 'CTRL+SPACE' triggers autocompletion in the 'EvalOClDialog,' and 'ENTER' selects a value. Currently, these hotkeys are not customizable. +1. **Autocompletion suggestions:** Suggestions are updated in real-time as characters are typed in the text area. + +2. **Navigation:** Use the 'ArrowDown' and 'ArrowUp' keys to move through the suggested list. + +3. **Selection:** Press 'Enter' to choose an item from the suggestion list. + +4. **Focus:** The text area retains focus for all other keyboard inputs, even when the suggestion list is active. ### Usage in Code diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletionParser.java b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletionParser.java index c2babc32a..5d287f894 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletionParser.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletionParser.java @@ -69,11 +69,11 @@ private void processResult() { } } case "object" -> { - if (parserResult.result == null) {//TODO: evtl. if von oben einfügen, wirkt aber nicht notwendig + if (parserResult.result == null) { parserResult.result = new ResultTypeOCLObjects(parserResult.foundValues.get("objectName"), parserResult.foundValues.get("operationType")); } } - case "set", "bag", "orderedSet", "sequence" -> { //might be orderedset + case "set", "bag", "orderedSet", "sequence" -> { if (!parserResult.foundValues.containsKey("operationName")) { parserResult.result = new ResultTypeOCLCollectionsDefault(parserResult.foundValues.get("objectPrefix"), type); } @@ -95,8 +95,6 @@ private void processResult() { parserResult.result = new ResultTypeOCLCollectionsDefault(parserResult.foundValues.get("operationPrefix"), null); } else if (parserResult.result instanceof ResultTypeOCLCollectionsDefault) { parserResult.result = new ResultTypeOCLCollectionsDefault(parserResult.foundValues.get("operationPrefix"), ((ResultTypeOCLCollectionsDefault) parserResult.result).collectionType); - } else { - System.out.println("Hier sollten wir nicht sein"); //TODO: maybe bei "endsWithCommaAndIsForAll" } } } @@ -301,7 +299,7 @@ private void parseOCLOnly(MModel model, String input, PrintWriter err) { private String handleParenthesis(String token) { String res = token; - if (!token.startsWith("(") && token.contains("(") && iterationExpressionAllowed(token)) {//TODO: might be wrong to check if iteration expression is allowed + if (!token.startsWith("(") && token.contains("(") && iterationExpressionAllowed(token)) { if (!token.contains(",") || (token.contains(",") && token.startsWith("forAll"))) { //illegal character if (token.contains("(")) { res = token.substring(token.indexOf("(") + 1); @@ -465,7 +463,7 @@ private void handle(String input, String trailingDelimiter) { } case "" -> { if (isValidJavaIdentifier(input)) {//actual object or operation prefix - parserResult.foundTypes.add("objectName");//TODO: change to prefix + parserResult.foundTypes.add("objectName"); parserResult.foundValues.put("objectPrefix", input.trim()); } else {//iterationexpression handleIterationExpression(input); diff --git a/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java b/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java index 2dacbdcf6..9902b3139 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java +++ b/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java @@ -40,18 +40,17 @@ import org.tzi.use.autocompletion.SuggestionResult; import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import javax.swing.text.BadLocationException; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.awt.event.KeyEvent; +import java.awt.event.*; import java.awt.geom.Rectangle2D; import java.io.PrintWriter; import java.io.StringWriter; import java.util.LinkedList; import java.util.List; +import java.util.Set; /** * A dialog for entering and evaluating OCL expressions. @@ -126,6 +125,85 @@ public void windowClosing(WindowEvent e) { JPanel textPane = new JPanel(); textPane.setLayout(new BoxLayout(textPane, BoxLayout.Y_AXIS)); + fTextIn.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + handleDocumentUpdate(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + handleDocumentUpdate(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + handleDocumentUpdate(); + } + + private void handleDocumentUpdate() { + // Check if arrow up, arrow down, or enter keys are pressed + boolean arrowOrEnterPressed = isArrowOrEnterPressed(); + + // Call updateAutocompletionResults only if the specific conditions are met + if (!arrowOrEnterPressed) { + updateAutocompletionResults(textPane); + } + } + + private boolean isArrowOrEnterPressed() { + KeyStroke upKeyStroke = KeyStroke.getKeyStroke("pressed UP"); + KeyStroke downKeyStroke = KeyStroke.getKeyStroke("pressed DOWN"); + KeyStroke enterKeyStroke = KeyStroke.getKeyStroke("pressed ENTER"); + + return fTextIn.getInputMap().get(upKeyStroke) == null + || fTextIn.getInputMap().get(downKeyStroke) == null + || fTextIn.getInputMap().get(enterKeyStroke) == null; + } + }); + + + InputMap inputMap = fTextIn.getInputMap(JComponent.WHEN_FOCUSED); + ActionMap actionMap = fTextIn.getActionMap(); + + KeyStroke enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); + KeyStroke upKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0); + KeyStroke downKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0); + + inputMap.put(enterKeyStroke, "enterPressed"); + inputMap.put(upKeyStroke, "upPressed"); + inputMap.put(downKeyStroke, "downPressed"); + + actionMap.put("enterPressed", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + autocompletionResultList.setFocusable(true); + autocompletionResultList.requestFocus(); + } + }); + + actionMap.put("upPressed", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + autocompletionResultList.setFocusable(true); + autocompletionResultList.requestFocus(); + int lastIndex = autocompletionResultList.getModel().getSize() - 1; + int selectedIndex = autocompletionResultList.getSelectedIndex(); + autocompletionResultList.setSelectedIndex(selectedIndex < 0 ? lastIndex : selectedIndex - 1); + } + }); + + actionMap.put("downPressed", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + autocompletionResultList.setFocusable(true); + autocompletionResultList.requestFocus(); + int lastIndex = autocompletionResultList.getModel().getSize() - 1; + int selectedIndex = autocompletionResultList.getSelectedIndex(); + autocompletionResultList.setSelectedIndex(selectedIndex == lastIndex ? 0 : selectedIndex + 1); + } + }); + JPanel p = new JPanel(new BorderLayout()); p.add(textInLabel, BorderLayout.NORTH); p.add(new JScrollPane(fTextIn), BorderLayout.CENTER); @@ -230,39 +308,6 @@ public void actionPerformed(ActionEvent e) { contentPane.add(btnPane, BorderLayout.EAST); getRootPane().setDefaultButton(btnEval); - Action ctrlSpaceAction = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - SwingWorker worker = new SwingWorker<>() { - @Override - protected SuggestionResult doInBackground() throws Exception { - return autocompletion.getSuggestions(fTextIn.getText(), true); - } - - @Override - protected void done() { - try { - SuggestionResult suggestion = get(); - // Open a new window to display the results (if not empty) - if (suggestion != null && !suggestion.suggestions.isEmpty()) { - displayResultsWindow(suggestion, textPane); - } - } catch (Exception ex) { - ex.printStackTrace(); - } - } - }; - - worker.execute(); - } - }; - - // Map the Ctrl+Space key combination to the action - KeyStroke ctrlSpaceKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_DOWN_MASK); - fTextIn.getInputMap(JComponent.WHEN_FOCUSED).put(ctrlSpaceKeyStroke, "ctrlSpaceAction"); - fTextIn.getActionMap().put("ctrlSpaceAction", ctrlSpaceAction); - - pack(); setSize(new Dimension(500, 200)); setLocationRelativeTo(parent); @@ -275,6 +320,31 @@ protected void done() { fTextOut.addKeyListener(ekl); } + private void updateAutocompletionResults(JPanel textPane) { + SwingWorker worker = new SwingWorker<>() { + @Override + protected SuggestionResult doInBackground() { + return autocompletion.getSuggestions(fTextIn.getText(), true); + } + + @Override + protected void done() { + try { + SuggestionResult suggestion = get(); + if (suggestion != null && !suggestion.suggestions.isEmpty()) { + displayResultsWindow(suggestion, textPane); + } else { + autocompletionPopupMenu.setVisible(false); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }; + + worker.execute(); + } + /** * Returns the session's system if available or an empty model. * @@ -387,6 +457,69 @@ private void displayResultsWindow(SuggestionResult suggestion, JPanel textPane) autocompletionResultList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); autocompletionResultList.setCellRenderer(new TwoColorListCellRenderer()); + autocompletionResultList.addKeyListener(new KeyAdapter() { + //Keys needing special treatment + final Set specialKeySet = Set.of( + KeyEvent.VK_BACK_SPACE, + KeyEvent.VK_LEFT, + KeyEvent.VK_RIGHT + // Add more special keys as needed + ); + + @Override + public void keyPressed(KeyEvent e) { + int keyCode = e.getKeyCode(); + if (specialKeySet.contains(keyCode)) { + handleSpecialKey(e, keyCode);// Handle special keys (backspace, arrow keys, etc.) separately + } else { + // If a normal key is pressed, give focus back to textarea + fTextIn.requestFocus(); + + // Simulate the keypress in the textarea + char keyChar = e.getKeyChar(); + + if(keyCode != KeyEvent.VK_UP && keyCode != KeyEvent.VK_DOWN && keyCode != KeyEvent.VK_ENTER && keyCode != KeyEvent.VK_ESCAPE){ + fTextIn.append(String.valueOf(keyChar)); + } + } + } + + private void handleSpecialKey(KeyEvent e, int keyCode) { + fTextIn.dispatchEvent(new KeyEvent(fTextIn, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), e.getModifiersEx(), keyCode, KeyEvent.CHAR_UNDEFINED)); + fTextIn.requestFocus(); + } + }); + + + InputMap inputMap = autocompletionResultList.getInputMap(JComponent.WHEN_FOCUSED); + ActionMap actionMap = autocompletionResultList.getActionMap(); + + KeyStroke enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); + KeyStroke upKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0); + KeyStroke downKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0); + + inputMap.put(enterKeyStroke, "enterPressed"); + inputMap.put(upKeyStroke, "upPressed"); + inputMap.put(downKeyStroke, "downPressed"); + + actionMap.put("upPressed", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + int lastIndex = autocompletionResultList.getModel().getSize() - 1; + int selectedIndex = autocompletionResultList.getSelectedIndex(); + autocompletionResultList.setSelectedIndex(selectedIndex <= 0 ? lastIndex : selectedIndex - 1); + } + }); + + actionMap.put("downPressed", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + int lastIndex = autocompletionResultList.getModel().getSize() - 1; + int selectedIndex = autocompletionResultList.getSelectedIndex(); + autocompletionResultList.setSelectedIndex(selectedIndex == lastIndex ? 0 : selectedIndex + 1); + } + }); + autocompletionPopupMenu = new JPopupMenu(); autocompletionPopupMenu.add(new JScrollPane(autocompletionResultList)); textPane.add(autocompletionPopupMenu); @@ -427,6 +560,7 @@ public void actionPerformed(ActionEvent e) { } autocompletionPopupMenu.setVisible(false); + fTextIn.requestFocusInWindow(); } }; @@ -449,8 +583,11 @@ public void actionPerformed(ActionEvent e) { int prefixPixelOffset = SwingUtilities.computeStringWidth(fTextIn.getFontMetrics(fTextIn.getFont()), suggestion.prefix); + fTextIn.requestFocusInWindow(); + autocompletionPopupMenu.show(fTextIn, (int) caretRectangle.getX() + xOffset - prefixPixelOffset, (int) caretRectangle.getY() + yOffset); - autocompletionResultList.requestFocus(); + autocompletionResultList.setFocusable(false); + fTextIn.requestFocusInWindow(); pack(); } @@ -479,7 +616,10 @@ private List formatStrings(SuggestionResult suggestion) { // Highlight the prefix in blue if (prefix != null) { - displayedList.add(i, "" + prefix + "" + sugString); + displayedList.add(i, "" + prefix + ""); + if(!sugString.equals(prefix)){ + displayedList.set(i, displayedList.get(i) + sugString); + } } else { displayedList.add(i, suggestion.suggestions.get(i)); } From 58befff6d6e34b0327ba7b175fab9f386e3775a8 Mon Sep 17 00:00:00 2001 From: till Date: Mon, 22 Apr 2024 12:10:15 +0200 Subject: [PATCH 10/10] Refactored autocompetion testsystem Fixxed nullpointer when there were no suggestions Changed suggestion behaviour for = statements --- .../use/autocompletion/AutoCompletion.java | 31 +++++++-- .../org/tzi/use/gui/main/EvalOCLDialog.java | 4 +- .../src/test/java/org/tzi/use/TestSystem.java | 41 ++++++------ .../AutoCompletionSuggestionTest.java | 63 ++++++++++--------- 4 files changed, 80 insertions(+), 59 deletions(-) diff --git a/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java index ecb52fc54..e0d6e90b5 100644 --- a/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java +++ b/use-gui/src/main/java/org/tzi/use/autocompletion/AutoCompletion.java @@ -338,11 +338,27 @@ private List getSuggestionsObjects(ResultTypeOCLObjects parserResult) { List attrs = obj.cls().allAttributes(); List ops = obj.cls().allOperations(); - List attributes = new LinkedList<>(filterAttributes(attrs, operationType)); - attributes.addAll(filterOperations(ops, operationType)); + //only add type matching operations and attrs to matching + List matching = new LinkedList<>(filterAttributes(attrs, operationType)); + matching.addAll(filterOperations(ops, operationType)); - sortSuggestions(attributes); - return attributes; + //add every operation and every attribute to temp + Set temp = new HashSet<>(); + temp.addAll(attrs.stream().map(MAttribute::name).collect(Collectors.toList())); + temp.addAll(ops.stream().map(this::formatOperationWithParameters).collect(Collectors.toList())); + + //remove matching operations and attributes from temp + matching.forEach(temp::remove); + List tempList = new LinkedList<>(temp); + + //sort both lists + sortSuggestions(matching); + sortSuggestions(tempList); + + //append temp at the end of matching + matching.addAll(tempList); + + return matching; } /** @@ -730,13 +746,16 @@ private List getSuggestionsWithAttributePrefix(ResultTypeOCLAttributePre } - List allAttributes = getSuggestionsObjects(new ResultTypeOCLObjects(objectName, operationType));//TODO attributeprefix + List allAttributes = getSuggestionsObjects(new ResultTypeOCLObjects(objectName, operationType)); List result = allAttributes.stream() .filter(attributeName -> attributeName != null && attributeName.startsWith(attributePrefix)) .map(attributeName -> attributeName.substring(attributePrefix.length())) .collect(Collectors.toList()); - sortSuggestions(result); + + //Don't sort since getSuggestionsObjects already created a list containing a sorted list appended to another sorted list + //sortSuggestions(result); + return result; } diff --git a/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java b/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java index 9902b3139..26bf54e05 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java +++ b/use-gui/src/main/java/org/tzi/use/gui/main/EvalOCLDialog.java @@ -334,7 +334,9 @@ protected void done() { if (suggestion != null && !suggestion.suggestions.isEmpty()) { displayResultsWindow(suggestion, textPane); } else { - autocompletionPopupMenu.setVisible(false); + if(autocompletionPopupMenu != null) { + autocompletionPopupMenu.setVisible(false); + } } } catch (Exception ex) { ex.printStackTrace(); diff --git a/use-gui/src/test/java/org/tzi/use/TestSystem.java b/use-gui/src/test/java/org/tzi/use/TestSystem.java index ce29d50f1..9df6d5850 100644 --- a/use-gui/src/test/java/org/tzi/use/TestSystem.java +++ b/use-gui/src/test/java/org/tzi/use/TestSystem.java @@ -36,7 +36,6 @@ /** * Helper to setup a test system. * - * @author Daniel Gent */ public class TestSystem { @@ -73,35 +72,35 @@ private void init() throws MSystemException, MInvalidModelException, ExpInvalidE ModelFactory factory = new ModelFactory(); MModel model = factory.createModel("testModel"); - MClass c1 = factory.createClass("class1", false); + MClass c1 = factory.createClass("Human", false); model.addClass(c1); - c1.addAttribute(factory.createAttribute("attribute1_1", TypeFactory.mkBoolean())); - c1.addAttribute(factory.createAttribute("attribute1_2", TypeFactory.mkInteger())); - c1.addAttribute(factory.createAttribute("attribute1_3", TypeFactory.mkReal())); - c1.addAttribute(factory.createAttribute("attribute1_4", TypeFactory.mkString())); - MOperation op1 = new MOperation("op1", new VarDeclList(new VarDecl("p1", TypeFactory.mkInteger())), TypeFactory.mkInteger()); + c1.addAttribute(factory.createAttribute("attr_isMarried", TypeFactory.mkBoolean())); + c1.addAttribute(factory.createAttribute("attr_age", TypeFactory.mkInteger())); + c1.addAttribute(factory.createAttribute("attr_size", TypeFactory.mkReal())); + c1.addAttribute(factory.createAttribute("attr_name", TypeFactory.mkString())); + MOperation op1 = new MOperation("addToAge", new VarDeclList(new VarDecl("num", TypeFactory.mkInteger())), TypeFactory.mkInteger()); op1.setStatement(new MVariableAssignmentStatement("result", IntegerValue.valueOf(42))); c1.addOperation(op1); - MOperation op2 = new MOperation("op2", new VarDeclList(new VarDecl("p1", TypeFactory.mkReal())), TypeFactory.mkInteger()); + MOperation op2 = new MOperation("addToSize", new VarDeclList(new VarDecl("num", TypeFactory.mkReal())), TypeFactory.mkInteger()); op2.setStatement(new MVariableAssignmentStatement("result", IntegerValue.valueOf(40))); c1.addOperation(op2); - MOperation op3 = new MOperation("operation3", new VarDeclList(new VarDecl("p1", TypeFactory.mkBoolean())), TypeFactory.mkBoolean()); + MOperation op3 = new MOperation("compareToIsMarried", new VarDeclList(new VarDecl("bool", TypeFactory.mkBoolean())), TypeFactory.mkBoolean()); op3.setStatement(new MVariableAssignmentStatement("result", IntegerValue.valueOf(42))); c1.addOperation(op3); - MClass c2 = factory.createClass("class2", false); + MClass c2 = factory.createClass("Dog", false); model.addClass(c2); - c2.addAttribute(factory.createAttribute("attribute2_1", TypeFactory.mkBoolean())); - c2.addAttribute(factory.createAttribute("attribute2_2", TypeFactory.mkBoolean())); - c2.addAttribute(factory.createAttribute("attribute2_3", TypeFactory.mkReal())); + c2.addAttribute(factory.createAttribute("attr_hasOwner", TypeFactory.mkBoolean())); + c2.addAttribute(factory.createAttribute("attr_isHappy", TypeFactory.mkBoolean())); + c2.addAttribute(factory.createAttribute("attr_size", TypeFactory.mkReal())); List emptyQualifiers = Collections.emptyList(); - MAssociation a1 = factory.createAssociation("A1"); + MAssociation a1 = factory.createAssociation("ownership_assoc"); a1.addAssociationEnd( factory.createAssociationEnd( c1, - "E1", + "owner_assoc", new MMultiplicity(0, 1), MAggregationKind.NONE, false, emptyQualifiers)); @@ -109,18 +108,18 @@ private void init() throws MSystemException, MInvalidModelException, ExpInvalidE a1.addAssociationEnd( factory.createAssociationEnd( c2, - "E2", + "owned_assoc", new MMultiplicity(0, 1), MAggregationKind.NONE, false, emptyQualifiers)); model.addAssociation(a1); - MAssociationClass ac1 = factory.createAssociationClass("AC1", false); + MAssociationClass ac1 = factory.createAssociationClass("ownership_assocclass", false); ac1.addAssociationEnd( factory.createAssociationEnd( c1, - "role1", + "owner_assocclass", new MMultiplicity(0, 1), MAggregationKind.NONE, false, emptyQualifiers)); @@ -128,7 +127,7 @@ private void init() throws MSystemException, MInvalidModelException, ExpInvalidE ac1.addAssociationEnd( factory.createAssociationEnd( c2, - "role2", + "owned_assocclass", new MMultiplicity(0, 1), MAggregationKind.NONE, false, emptyQualifiers)); @@ -146,8 +145,8 @@ private void initObjectsAndLinks(AutoCompletion autoCompletion, MClass c1, MClas MSystemState state = fSystem.state(); VariableEnvironment varEnv = fSystem.getVariableEnvironment(); - MClass C1 = getModel().getClass("class1"); - MClass C2 = getModel().getClass("class2"); + MClass C1 = getModel().getClass("Human"); + MClass C2 = getModel().getClass("Dog"); varEnv.assign("v", IntegerValue.valueOf(42)); diff --git a/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java b/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java index 24206a36f..1b71c8f87 100644 --- a/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java +++ b/use-gui/src/test/java/org/tzi/use/autocompletion/AutoCompletionSuggestionTest.java @@ -55,18 +55,18 @@ public void testSuggestionsCaseAttributeNameStarted() throws MSystemException, E TestSystem testSystem = new TestSystem(); AutoCompletion testee = testSystem.getAutoCompletion(); - Set expectedResult = Set.of("tribute1_1", "tribute1_2", "tribute1_3", "tribute1_4"); + Set expectedResult = Set.of("tr_isMarried", "tr_age", "tr_size", "tr_name"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("obj1.at", false).suggestions)); - expectedResult = Set.of("tribute1_2", "tribute1_3"); - assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("5 = obj1.at", false).suggestions)); - assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("5 = obj2.at", false).suggestions)); + List expectedResultList = List.of("tr_age", "tr_size", "tr_isMarried", "tr_name"); + assertEquals(expectedResultList, new LinkedList<>(testee.getSuggestions("5 = obj1.at", false).suggestions)); + assertEquals(expectedResultList, new LinkedList<>(testee.getSuggestions("5 = obj2.at", false).suggestions)); - expectedResult = Set.of("tribute2_1", "tribute2_2", "tribute2_3"); + expectedResult = Set.of("tr_hasOwner", "tr_isHappy", "tr_size"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("obj5.at", false).suggestions)); - expectedResult = Set.of("tribute2_1", "tribute2_2"); - assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("false = obj5.at", false).suggestions)); + expectedResultList = List.of("tr_size", "tr_hasOwner", "tr_isHappy"); + assertEquals(expectedResultList, new LinkedList<>(testee.getSuggestions("3.5 = obj5.at", false).suggestions)); } public void testSuggestionsCaseOperationNameStarted() throws MSystemException, ExpInvalidException, MInvalidModelException { @@ -82,22 +82,23 @@ public void testSuggestionsCaseObjects() throws MSystemException, ExpInvalidExce AutoCompletion testee = testSystem.getAutoCompletion(); - Set expectedResult = Set.of("attribute1_1", "attribute1_2", "attribute1_3", "attribute1_4", "op1(Integer p1)", "op2(Real p1)", "operation3(Boolean p1)"); + Set expectedResult = Set.of("attr_isMarried", "attr_age", "attr_size", "attr_name", "addToAge(Integer num)", "addToSize(Real num)", "compareToIsMarried(Boolean bool)"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("obj1.", true).suggestions)); - expectedResult = Set.of("attribute1_2", "attribute1_3", "op1(Integer p1)", "op2(Real p1)"); + //expectedResult = Set.of("attr_age", "attr_size", "op1(Integer p1)", "op2(Real p1)"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("5 = obj1.", true).suggestions)); - expectedResult = Set.of("attribute1_4"); + //expectedResult = Set.of("attr_name"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("'5' = obj1.", true).suggestions)); - expectedResult = Set.of("attribute2_1", "attribute2_2", "attribute2_3"); + expectedResult = Set.of("attr_hasOwner", "attr_isHappy", "attr_size"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("obj5.", true).suggestions)); - expectedResult = Set.of("attribute1_2", "attribute1_3", "op1(Integer p1)", "op2(Real p1)"); + //expectedResult = Set.of("attr_age", "attr_size", "op1(Integer p1)", "op2(Real p1)"); + expectedResult = Set.of("attr_isMarried", "attr_age", "attr_size", "attr_name", "addToAge(Integer num)", "addToSize(Real num)", "compareToIsMarried(Boolean bool)"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("6.2 = obj1.", true).suggestions)); - expectedResult = Set.of("attribute1_1", "operation3(Boolean p1)"); + //expectedResult = Set.of("attr_isMarried", "operation3(Boolean p1)"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("false = obj1.", true).suggestions)); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("true = obj1.", true).suggestions)); } @@ -106,20 +107,20 @@ public void testSuggestionsCaseCreateAndDestroy() throws MSystemException, ExpIn TestSystem testSystem = new TestSystem(); AutoCompletion testee = testSystem.getAutoCompletion(); - Set expectedResult = Set.of("class1", "class2", "A1", "AC1"); + Set expectedResult = Set.of("Human", "Dog", "ownership_assoc", "ownership_assocclass"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 :", false).suggestions)); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3:", false).suggestions)); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : ", false).suggestions)); expectedResult = Set.of("obj1", "obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); - assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : AC1 between (", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : ownership_assoc between (", false).suggestions)); expectedResult = Set.of("obj1", "obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); - assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : AC1 between(", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : ownership_assoc between(", false).suggestions)); expectedResult = Set.of("obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); - assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : AC1 between (obj1,", false).suggestions)); - assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : AC1 between (obj1, ", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : ownership_assoc between (obj1,", false).suggestions)); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!create obj3 : ownership_assoc between (obj1, ", false).suggestions)); expectedResult = Set.of("obj1", "obj2", "obj3", "obj4", "obj5", "obj6", "obj7", "obj8"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!destroy ", false).suggestions)); @@ -144,7 +145,7 @@ public void testSuggestionsCaseInsertAndDelete() throws MSystemException, ExpInv assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert(obj1,", false).suggestions)); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert(obj1, ", false).suggestions)); - expectedResult = Set.of("A1", "AC1"); + expectedResult = Set.of("ownership_assoc", "ownership_assocclass"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert(obj1, obj2) into ", false).suggestions)); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!insert(obj1,obj2) into ", false).suggestions)); @@ -158,7 +159,7 @@ public void testSuggestionsCaseInsertAndDelete() throws MSystemException, ExpInv assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete(obj1,", false).suggestions)); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete(obj1, ", false).suggestions)); - expectedResult = Set.of("A1", "AC1"); + expectedResult = Set.of("ownership_assoc", "ownership_assocclass"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete(obj1, obj2) from ", false).suggestions)); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!delete(obj1,obj2) from ", false).suggestions)); } @@ -171,10 +172,10 @@ public void testSuggestionsCaseSet() throws MSystemException, ExpInvalidExceptio assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!set ", false).suggestions)); - expectedResult = Set.of("attribute1_1", "attribute1_2", "attribute1_3", "attribute1_4"); + expectedResult = Set.of("attr_isMarried", "attr_age", "attr_size", "attr_name"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!set obj1.", false).suggestions)); - expectedResult = Set.of("attribute2_1", "attribute2_2", "attribute2_3"); + expectedResult = Set.of("attr_hasOwner", "attr_isHappy", "attr_size"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!set obj5.", false).suggestions)); } @@ -188,14 +189,14 @@ public void testSuggestionsCaseOpenterAndOpexit() throws MSystemException, ExpIn expectedResult = Set.of("1", "2", "3", "4", "5", "6", "7", "8"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter obj", false).suggestions)); - expectedResult = Set.of("op1", "op2", "operation3"); + expectedResult = Set.of("addToAge", "addToSize", "compareToIsMarried"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter obj1 ", false).suggestions)); - expectedResult = Set.of("1", "2", "eration3"); - assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter obj1 op", false).suggestions)); + expectedResult = Set.of("ToAge", "ToSize"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter obj1 add", false).suggestions)); - expectedResult = Set.of("ration3"); - assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter obj1 ope", false).suggestions)); + expectedResult = Set.of("ize"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("!openter obj1 addToS", false).suggestions)); } public void testSuggestionsCaseCheck() throws MSystemException, ExpInvalidException, MInvalidModelException { @@ -232,7 +233,7 @@ public void testSuggestionsCaseInfo() throws MSystemException, ExpInvalidExcepti Set expectedResult = Set.of("class", "model", "state", "opstack", "prog", "vars"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("info ", false).suggestions)); - expectedResult = Set.of("class1", "class2", "AC1"); + expectedResult = Set.of("Human", "Dog", "ownership_assocclass"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("info class ", false).suggestions)); } @@ -243,11 +244,11 @@ public void testSuggestionCaseIterationExpressions() throws MSystemException, Ex Set expectedResult = Set.of("1", "2", "3", "4", "5", "6", "7", "8"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(obj)", true).suggestions)); - expectedResult = Set.of("attribute1_1", "attribute1_2", "attribute1_3", "attribute1_4", "op1(Integer p1)", "op2(Real p1)", "operation3(Boolean p1)"); + expectedResult = Set.of("attr_isMarried", "attr_age", "attr_size", "attr_name", "addToAge(Integer num)", "addToSize(Real num)", "compareToIsMarried(Boolean bool)"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(obj1.)", true).suggestions)); - expectedResult = Set.of("_1", "_2", "_3", "_4"); - assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(obj2.attribute1)", true).suggestions)); + expectedResult = Set.of("isMarried", "age", "size", "name"); + assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(obj2.attr_)", true).suggestions)); expectedResult = Set.of("Integer"); assertEquals(expectedResult, new HashSet<>(testee.getSuggestions("Set{1,2,3}->forAll(e1: )", true).suggestions));