diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag index d4e166a34a2b62150d76dfa0decf3c0a827bb649..4baff8e5763af0b3a1c8870fa1d2b0a935bb9a24 100644 --- a/ragconnect.base/src/main/jastadd/Analysis.jrag +++ b/ragconnect.base/src/main/jastadd/Analysis.jrag @@ -15,6 +15,19 @@ aspect Analysis { } return numberOfSameDefs > 1; } + eq RelationEndpointTarget.isAlreadyDefined() { + // define lookup here, as not used elsewhere + int numberOfSameDefs = 0; + for (EndpointTarget target : ragconnect().givenEndpointTargetList()) { + if (target.isRelationEndpointTarget()) { + RelationEndpointTarget other = target.asRelationEndpointTarget(); + if (other.getRole().equals(this.getRole())) { + numberOfSameDefs += 1; + } + } + } + return numberOfSameDefs > 1; + } eq TokenEndpointTarget.isAlreadyDefined() { return lookupTokenEndpointDefinitions(getToken()).stream() .filter(containingEndpointDefinition()::matchesType) @@ -49,7 +62,8 @@ aspect Analysis { return target.primitivePrettyPrint().equals(this.primitivePrettyPrint()); } syn String JavaTypeUse.primitivePrettyPrint() { - switch (getName()) { + String name = getName(); + switch (name) { case "boolean": case "Boolean": return "boolean"; @@ -78,6 +92,7 @@ aspect Analysis { syn boolean EndpointTarget.hasAttributeResetMethod(); eq AttributeEndpointTarget.hasAttributeResetMethod() = false; + eq RelationEndpointTarget.hasAttributeResetMethod() = false; eq TokenEndpointTarget.hasAttributeResetMethod() = getToken().getNTA(); eq TypeEndpointTarget.hasAttributeResetMethod() = getType().getNTA(); eq ContextFreeTypeEndpointTarget.hasAttributeResetMethod() = false; diff --git a/ragconnect.base/src/main/jastadd/Intermediate.jadd b/ragconnect.base/src/main/jastadd/Intermediate.jadd index 5ca17cdc4ca04db353dbf073fc0cff3d8dd7df93..c3c513d18e2d8f1de1b29492621c3bffc3796ab2 100644 --- a/ragconnect.base/src/main/jastadd/Intermediate.jadd +++ b/ragconnect.base/src/main/jastadd/Intermediate.jadd @@ -59,6 +59,7 @@ aspect SharedMustache { case "warn": case "error": case "exception": + //noinspection DuplicateBranchesInSwitch return "ASTNode." + logConsoleErr(); default: return "unknownLoggingLevelForConsole_" + level + "_"; @@ -74,9 +75,8 @@ aspect SharedMustache { } } syn boolean EndpointTarget.typeIsList() = false; - eq TypeEndpointTarget.typeIsList() { - return getType().isListComponent(); - } + eq TypeEndpointTarget.typeIsList() = getType().isListComponent(); + eq RelationEndpointTarget.typeIsList() = getRole().isListRole(); syn boolean EndpointTarget.typeIsOpt() = false; eq TypeEndpointTarget.typeIsOpt() { @@ -215,6 +215,10 @@ aspect MustacheMappingApplicationAndDefinition { eq MAttributeSendDefinition.preemptiveExpectedValue() = lastValueGetterCall(); eq MAttributeSendDefinition.preemptiveReturn() = "return false;"; + eq MRelationSendDefinition.firstInputVarName() = getterMethodCall(); + eq MRelationSendDefinition.preemptiveExpectedValue() = lastValueGetterCall(); + eq MRelationSendDefinition.preemptiveReturn() = "return false;"; + eq MTokenReceiveDefinition.firstInputVarName() = "message"; eq MTokenReceiveDefinition.preemptiveExpectedValue() = getterMethodCall(); eq MTokenReceiveDefinition.preemptiveReturn() = "return;"; @@ -425,6 +429,13 @@ aspect MustacheReceiveAndSendAndHandleUri { eq AttributeEndpointTarget.parentTypeName() = getParentTypeDecl().getName(); eq AttributeEndpointTarget.entityName() = getName(); + eq RelationEndpointTarget.getterMethodName() = "get" + getRole().getterMethodName(); + eq RelationEndpointTarget.parentTypeName() = getRole().getType().getName(); + eq RelationEndpointTarget.entityName() = getRole().getName(); + + syn String NavigableRole.getterMethodName() = getName(); + eq ListRole.getterMethodName() = getName() + "List"; + eq TokenEndpointTarget.getterMethodName() = "get" + getToken().getName(); eq TokenEndpointTarget.parentTypeName() = getToken().containingTypeDecl().getName(); eq TokenEndpointTarget.entityName() = getToken().getName(); @@ -519,6 +530,9 @@ aspect MustacheSendDefinition { eq MAttributeSendDefinition.updateMethodName() = ragconnect().internalRagConnectPrefix() + "_update_attr_" + getEndpointDefinition().entityName(); eq MAttributeSendDefinition.writeMethodName() = ragconnect().internalRagConnectPrefix() + "_writeLastValue_attr_" + getEndpointDefinition().entityName(); + eq MRelationSendDefinition.updateMethodName() = ragconnect().internalRagConnectPrefix() + "_update_" + getEndpointDefinition().entityName(); + eq MRelationSendDefinition.writeMethodName() = ragconnect().internalRagConnectPrefix() + "_writeLastValue_" + getEndpointDefinition().entityName(); + eq MTokenReceiveDefinition.updateMethodName() = null; eq MTokenReceiveDefinition.writeMethodName() = null; @@ -641,6 +655,12 @@ aspect AttributesForMustache { } return new MAttributeSendDefinition(); } + MEndpointDefinition RelationEndpointTarget.createMEndpointDefinition(boolean isSend) { + if (!isSend) { + throw new IllegalArgumentException("RelationEndpointTarget can only be sent!"); + } + return new MRelationSendDefinition(); + } MEndpointDefinition TokenEndpointTarget.createMEndpointDefinition(boolean isSend) { return isSend ? new MTokenSendDefinition() : new MTokenReceiveDefinition(); } diff --git a/ragconnect.base/src/main/jastadd/Intermediate.relast b/ragconnect.base/src/main/jastadd/Intermediate.relast index 486158a4a4094291a756cf8c7715ed6104f5e252..8fa32f92b7bc1fdc0b6fad196b82523a660dc6c2 100644 --- a/ragconnect.base/src/main/jastadd/Intermediate.relast +++ b/ragconnect.base/src/main/jastadd/Intermediate.relast @@ -2,6 +2,7 @@ abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition* rel MEndpointDefinition.EndpointDefinition -> EndpointDefinition; MAttributeSendDefinition : MEndpointDefinition; +MRelationSendDefinition : MEndpointDefinition; abstract MTokenEndpointDefinition : MEndpointDefinition; MTokenReceiveDefinition : MTokenEndpointDefinition; MTokenSendDefinition : MTokenEndpointDefinition; diff --git a/ragconnect.base/src/main/jastadd/Mappings.jrag b/ragconnect.base/src/main/jastadd/Mappings.jrag index aa070695a83e31f819d62308b094679b27125a8e..b5569995f579ac097cc78bac9a2980775affa39a 100644 --- a/ragconnect.base/src/main/jastadd/Mappings.jrag +++ b/ragconnect.base/src/main/jastadd/Mappings.jrag @@ -1,10 +1,18 @@ aspect DefaultMappings { private String RagConnect.baseDefaultMappingTypeNamePart(String typeName) { - return capitalize(typeName).replace("[]", "s").replace("<", "").replace(">", "List"); + if (typeName.contains(".")) { + StringBuilder sb = new StringBuilder(); + for (String part : typeName.split("\\.")) { + sb.append(baseDefaultMappingTypeNamePart(part)); + } + return sb.toString(); + } else { + return capitalize(typeName).replace("[]", "s").replace("<", "").replace(">", "List"); + } } - private MappingDefinitionType RagConnect.baseDefaultMappingTypeFromName(String typeName) { + private MappingDefinitionType RagConnect.baseDefaultMappingTypeName(String typeName) { return typeName.endsWith("[]") ? new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse(typeName.replace("[]", ""))) : new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName)); @@ -13,9 +21,9 @@ aspect DefaultMappings { private DefaultMappingDefinition RagConnect.createDefaultMappingDefinition(String prefix, String fromTypeName, String toTypeName, String content) { DefaultMappingDefinition result = new DefaultMappingDefinition(); result.setID(prefix + baseDefaultMappingTypeNamePart(fromTypeName) + "To" + baseDefaultMappingTypeNamePart(toTypeName) + "Mapping"); - result.setFromType(baseDefaultMappingTypeFromName(fromTypeName)); + result.setFromType(baseDefaultMappingTypeName(fromTypeName)); result.setFromVariableName("input"); - result.setToType(baseDefaultMappingTypeFromName(toTypeName)); + result.setToType(baseDefaultMappingTypeName(toTypeName)); result.setContent(content); return result; } @@ -67,7 +75,7 @@ aspect DefaultMappings { ); } - syn nta DefaultMappingDefinition RagConnect.defaultBytesToListTreeMapping(String typeName) { + syn nta DefaultMappingDefinition RagConnect.defaultBytesToListMapping(String typeName) { return treeDefaultMappingDefinition("byte[]", configJastAddList() + "<" + typeName + ">", "String content = new String(input);\n" + "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" + @@ -78,7 +86,7 @@ aspect DefaultMappings { "return result;" ); } - syn nta DefaultMappingDefinition RagConnect.defaultListTreeToBytesMapping() { + syn nta DefaultMappingDefinition RagConnect.defaultListToBytesMapping() { return treeDefaultMappingDefinition(configJastAddList(), "byte[]", "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + @@ -88,6 +96,16 @@ aspect DefaultMappings { "return outputStream.toString().getBytes();" ); } + syn nta DefaultMappingDefinition RagConnect.defaultJavaUtilListToBytesMapping() { + return treeDefaultMappingDefinition("java.util.List", "byte[]", + "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" + + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + + "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n" + + "serializeJavaUtilList(input, generator);\n" + + "generator.flush();\n" + + "return outputStream.toString().getBytes();" + ); + } syn nta DefaultMappingDefinition RagConnect.defaultBooleanToBytesMapping() = baseDefaultMappingDefinition( "boolean", "byte[]", "return java.nio.ByteBuffer.allocate(1).put((byte) (input ? 1 : 0)).array();"); @@ -218,7 +236,7 @@ aspect Mappings { default: try { TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName()); - return getEndpointTarget().isTypeEndpointTarget() && typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultBytesToListTreeMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName()); + return getEndpointTarget().isTypeEndpointTarget() && typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultBytesToListMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName()); } catch (Exception ignore) { } System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this); @@ -254,7 +272,10 @@ aspect Mappings { return ragconnect().defaultStringToBytesMapping(); default: if (getEndpointTarget().isTypeEndpointTarget() && typeIsList() && !getIndexBasedListAccess()) { - return ragconnect().defaultListTreeToBytesMapping(); + return ragconnect().defaultListToBytesMapping(); + } + if (getEndpointTarget().isRelationEndpointTarget() && typeIsList() && !getIndexBasedListAccess()) { + return ragconnect().defaultJavaUtilListToBytesMapping(); } try { TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName()); @@ -281,10 +302,14 @@ aspect Mappings { } syn String EndpointTarget.targetTypeName(); eq AttributeEndpointTarget.targetTypeName() = getTypeName(); + eq RelationEndpointTarget.targetTypeName() = getRole().oppositeRole().targetTypeName(); eq TokenEndpointTarget.targetTypeName() = getToken().effectiveJavaTypeUse().getName(); eq TypeEndpointTarget.targetTypeName() = getType().getTypeDecl().getName(); eq ContextFreeTypeEndpointTarget.targetTypeName() = getTypeDecl().getName(); + syn String Role.targetTypeName() = getType().getName(); + eq ListRole.targetTypeName() = "java.util.List<" + getType().getName() + ">"; + // eq ReceiveFromRestDefinition.suitableDefaultMapping() { // String typeName = getMappingList().isEmpty() ? // getToken().getJavaTypeUse().getName() : @@ -360,9 +385,10 @@ aspect Mappings { for (TypeDecl typeDecl : getProgram().typeDecls()) { result.add(defaultBytesToTreeMapping(typeDecl.getName())); result.add(defaultTreeToBytesMapping(typeDecl.getName())); - result.add(defaultBytesToListTreeMapping(typeDecl.getName())); + result.add(defaultBytesToListMapping(typeDecl.getName())); } - result.add(defaultListTreeToBytesMapping()); + result.add(defaultListToBytesMapping()); + result.add(defaultJavaUtilListToBytesMapping()); // // string conversion // result.add(defaultStringToBooleanMapping()); // result.add(defaultStringToIntMapping()); diff --git a/ragconnect.base/src/main/jastadd/NameResolution.jrag b/ragconnect.base/src/main/jastadd/NameResolution.jrag index 6d9b1a78abb66f3628d66c259d92f1e90dce94b4..0690eca4a260b5f7396dfe2ca6905b3aa98e0aa5 100644 --- a/ragconnect.base/src/main/jastadd/NameResolution.jrag +++ b/ragconnect.base/src/main/jastadd/NameResolution.jrag @@ -146,4 +146,39 @@ aspect RagConnectNameResolution { return null; } + // rel ___ -> Navigable + refine RefResolverStubs eq ASTNode.globallyResolveNavigableRoleByToken(String id) { + NavigableRole result = tryGloballyResolveNavigableRoleByToken(id); + if (result == null) { + System.err.println("Could not resolve role '" + id + "'."); + } + return result; + } + syn NavigableRole ASTNode.tryGloballyResolveNavigableRoleByToken(String id) { + // id is of the form 'type_name + "." + role_name' + int dotIndex = id.indexOf("."); + String typeName = id.substring(0, dotIndex); + String roleName = id.substring(dotIndex + 1); + for (Relation relation : program().relations()) { + if (relation.isDirectedRelation()) { + if (relation.asDirectedRelation().getSource().matches(typeName, roleName)) { + return relation.asDirectedRelation().getSource(); + } + } else { + if (relation.asBidirectionalRelation().getLeft().matches(typeName, roleName)) { + return relation.asBidirectionalRelation().getLeft(); + } + if (relation.asBidirectionalRelation().getRight().matches(typeName, roleName)) { + return relation.asBidirectionalRelation().getRight(); + } + } + } + return null; + } + + syn boolean Role.matches(String typeName, String roleName) = false; + eq NavigableRole.matches(String typeName, String roleName) { + return getType().getName().equals(typeName) && getName().equals(roleName); + } + } diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag index dac193142b1382f01b61e696a0f9d5b02dd8b038..5288a9f14f2cc8e3298d3d20fa53076fd72be09e 100644 --- a/ragconnect.base/src/main/jastadd/Navigation.jrag +++ b/ragconnect.base/src/main/jastadd/Navigation.jrag @@ -1,4 +1,4 @@ -aspect NewStuff { +aspect GeneratedNavigation { /** Tests if EndpointTarget is a TokenEndpointTarget. * @return 'true' if this is a TokenEndpointTarget, otherwise 'false' @@ -30,6 +30,12 @@ aspect NewStuff { syn boolean EndpointTarget.isAttributeEndpointTarget() = false; eq AttributeEndpointTarget.isAttributeEndpointTarget() = true; + /** Tests if EndpointTarget is a RelationEndpointTarget. + * @return 'true' if this is a RelationEndpointTarget, otherwise 'false' + */ + syn boolean EndpointTarget.isRelationEndpointTarget() = false; + eq RelationEndpointTarget.isRelationEndpointTarget() = true; + /** casts a EndpointTarget into a TokenEndpointTarget if possible. * @return 'this' cast to a TokenEndpointTarget or 'null' */ @@ -64,6 +70,13 @@ aspect NewStuff { syn AttributeEndpointTarget EndpointTarget.asAttributeEndpointTarget(); eq EndpointTarget.asAttributeEndpointTarget() = null; eq AttributeEndpointTarget.asAttributeEndpointTarget() = this; + + /** casts a EndpointTarget into a RelationEndpointTarget if possible. + * @return 'this' cast to a RelationEndpointTarget or 'null' + */ + syn RelationEndpointTarget EndpointTarget.asRelationEndpointTarget(); + eq EndpointTarget.asRelationEndpointTarget() = null; + eq RelationEndpointTarget.asRelationEndpointTarget() = this; } aspect RagConnectNavigation { @@ -121,6 +134,13 @@ aspect RagConnectNavigation { // --- effectiveJavaTypeUse (should be in preprocessor) --- syn lazy JavaTypeUse TokenComponent.effectiveJavaTypeUse() = hasJavaTypeUse() ? getJavaTypeUse() : new SimpleJavaTypeUse("String"); + // --- oppositeRole --- + inh Role Role.oppositeRole(); + eq DirectedRelation.getSource().oppositeRole() = getTarget(); + eq DirectedRelation.getTarget().oppositeRole() = getSource(); + eq BidirectionalRelation.getLeft().oppositeRole() = getRight(); + eq BidirectionalRelation.getRight().oppositeRole() = getLeft(); + // --- isDefaultMappingDefinition --- syn boolean MappingDefinition.isDefaultMappingDefinition() = false; eq DefaultMappingDefinition.isDefaultMappingDefinition() = true; diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast index 5605324e99fd6206f4dc4c7eb3a92da3342e224d..5037cff1ffa0a7c803ce0d74207637417b1afbda 100644 --- a/ragconnect.base/src/main/jastadd/RagConnect.relast +++ b/ragconnect.base/src/main/jastadd/RagConnect.relast @@ -13,12 +13,11 @@ TypeEndpointTarget : EndpointTarget; rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget*; ContextFreeTypeEndpointTarget : EndpointTarget; rel ContextFreeTypeEndpointTarget.TypeDecl <-> TypeDecl.ContextFreeTypeEndpointTarget*; -UntypedEndpointTarget : EndpointTarget ::= <TypeName> <ChildName> <IsAttribute:boolean>; // only used by parser -// to be integrated: AttributeEndpointTarget : EndpointTarget ::= <Name> <TypeName> ; rel AttributeEndpointTarget.ParentTypeDecl <-> TypeDecl.AttributeEndpointTarget*; -//RelationEndpointTarget : EndpointTarget ; -//rel RelationEndpointTarget.Role <-> Role.RelationEndpointTarget* ; +RelationEndpointTarget : EndpointTarget ; +rel RelationEndpointTarget.Role <-> NavigableRole.RelationEndpointTarget* ; +UntypedEndpointTarget : EndpointTarget ::= <TypeName> <ChildName> <IsAttribute:boolean>; // only used by parser DependencyDefinition ::= <ID>; rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*; diff --git a/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag b/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag index 04e8cadc459653830e5f29e2d189b1aa2c6c0ad5..70f778c7b8f63b86d2d60bf0d71d1beb6cdeb41e 100644 --- a/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag +++ b/ragconnect.base/src/main/jastadd/parser/ParserRewrites.jrag @@ -16,6 +16,14 @@ aspect ParserRewrites { return result; } + when (getChildName() != null && tryGloballyResolveNavigableRoleByToken(combinedName()) != null) + to RelationEndpointTarget { + RelationEndpointTarget result = new RelationEndpointTarget(); + result.copyOtherValuesFrom(this); + result.setRole(NavigableRole.createRef(this.combinedName())); + return result; + } + when (getChildName() == "") to ContextFreeTypeEndpointTarget { ContextFreeTypeEndpointTarget result = new ContextFreeTypeEndpointTarget(); diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java index dfe3f9789681b3dee17c7fec7b2c671ef400808c..f671349843dddf5e81099660365668fbf5ae4c66 100644 --- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java +++ b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java @@ -124,9 +124,7 @@ public class Compiler extends AbstractCompiler { compiler.run(args); } catch (CompilerException e) { System.err.println(e.getMessage()); - if (compiler.isVerbose()) { - e.printStackTrace(); - } + e.printStackTrace(); System.exit(1); } } diff --git a/ragconnect.base/src/main/resources/ListAspect.mustache b/ragconnect.base/src/main/resources/ListAspect.mustache index 31eaf5ac82a5854956077403e92124c9b6160353..e7ec88e5e579e7a7b50bbeb83d0779bc64203991 100644 --- a/ragconnect.base/src/main/resources/ListAspect.mustache +++ b/ragconnect.base/src/main/resources/ListAspect.mustache @@ -11,6 +11,19 @@ public void {{configJastAddList}}.serialize(com.fasterxml.jackson.core.JsonGener } } +protected static <T extends ASTNode> void ASTNode.serializeJavaUtilList( + java.util.List<T> input, com.fasterxml.jackson.core.JsonGenerator g) throws SerializationException { + try { + g.writeStartArray(); + for (T child : input) { + child.serialize(g); + } + g.writeEndArray(); + } catch (java.io.IOException e) { + throw new SerializationException("unable to serialize list", e); + } +} + {{#typesForReceivingListEndpoints}} public static {{configJastAddList}}<{{Name}}> {{Name}}.deserializeList(com.fasterxml.jackson.databind.node.ArrayNode node) throws DeserializationException { {{configJastAddList}}<{{Name}}> result = new {{configJastAddList}}<>(); diff --git a/ragconnect.base/src/main/resources/mappingApplication.mustache b/ragconnect.base/src/main/resources/mappingApplication.mustache index 836e8d438397db9fc8e862c828e5b57eeb34d2f1..31cd12365e1158425f45c2666d86faa0bee14c0e 100644 --- a/ragconnect.base/src/main/resources/mappingApplication.mustache +++ b/ragconnect.base/src/main/resources/mappingApplication.mustache @@ -1,3 +1,6 @@ +if ({{firstInputVarName}} == null) { + {{preemptiveReturn}} +} {{{lastDefinitionToType}}} {{lastResult}}; try { {{#innerMappingDefinitions}} diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle index e0250d81e554a31b5aa5abe2f7036200f921d59b..1c80e4eb08857cd472fec4ef64cfd8b029cbfcb0 100644 --- a/ragconnect.tests/build.gradle +++ b/ragconnect.tests/build.gradle @@ -151,6 +151,8 @@ def JASTADD_INCREMENTAL_OPTIONS_TRACING_FULL = JASTADD_INCREMENTAL_OPTIONS.clone JASTADD_INCREMENTAL_OPTIONS_TRACING_FULL.set(0, '--tracing=all') JASTADD_INCREMENTAL_OPTIONS_TRACING_FULL.set(1, '--incremental=param,debug') +classes.dependsOn(':ragconnect.base:jar') + // --- Test: Example --- task compileExampleTest(type: RagConnectTest) { ragconnect { diff --git a/ragconnect.tests/src/test/01-input/relation/Test.connect b/ragconnect.tests/src/test/01-input/relation/Test.connect index f63801b9fb48d7daae3ea7415c2f89449b7d6625..5fdc1f08be19d29600f4c1a314114cfae7cdf99c 100644 --- a/ragconnect.tests/src/test/01-input/relation/Test.connect +++ b/ragconnect.tests/src/test/01-input/relation/Test.connect @@ -1,27 +1,27 @@ -//send SenderRoot.MyA; -//send SenderRoot.OptionalA; -//send SenderRoot.ListA; -// -//send SenderRoot.BiMyA; -//send SenderRoot.BiOptionalA; -//send SenderRoot.BiListA; -// -//send SenderRoot.MyB using ConcatValues; -//send SenderRoot.OptionalB using ConcatValues; -//send SenderRoot.ListB using ConcatValueList; -// -//send SenderRoot.BiMyB using ConcatValues; -//send SenderRoot.BiOptionalB using ConcatValues; -//send SenderRoot.BiListB using ConcatValueList; +send SenderRoot.MyA; +send SenderRoot.OptionalA; +send SenderRoot.ListA; + +send SenderRoot.BiMyA; +send SenderRoot.BiOptionalA; +send SenderRoot.BiListA; + +send SenderRoot.MyB using ConcatValues; +send SenderRoot.OptionalB using ConcatValues; +send SenderRoot.ListB using ConcatValueList; + +send SenderRoot.BiMyB using ConcatValues; +send SenderRoot.BiOptionalB using ConcatValues; +send SenderRoot.BiListB using ConcatValueList; ConcatValues maps B b to String {: - return b,getValue() + b.getInner().getInnerValue(); + return b.getValue() + b.getInner().getInnerValue(); :} -ConcatValueList maps JastAddList<B> list to String {: +ConcatValueList maps java.util.List<B> list to String {: StringBuilder sb = new StringBuilder(); for (B b : list) { - sb.append(b,getValue() + b.getInner().getInnerValue()).append(";"); + sb.append(b.getValue() + b.getInner().getInnerValue()).append(";"); } return sb.toString(); :} diff --git a/ragconnect.tests/src/test/01-input/relation/Test.jadd b/ragconnect.tests/src/test/01-input/relation/Test.jadd index 2f54e7f851452f24d623b8d424744ecc8544af79..9ed81e10ad62435704b667aabc5624e2dd959a61 100644 --- a/ragconnect.tests/src/test/01-input/relation/Test.jadd +++ b/ragconnect.tests/src/test/01-input/relation/Test.jadd @@ -4,21 +4,18 @@ aspect MakeCodeCompile { } aspect MakeCodeWork { - public boolean SenderRoot.connectMyA(String uriString, boolean sendCurrentValue) { return true; } - public boolean SenderRoot.connectOptionalA(String uriString, boolean sendCurrentValue) { return true; } - public boolean SenderRoot.connectListA(String uriString, boolean sendCurrentValue) { return true; } - // - public boolean SenderRoot.connectBiMyA(String uriString, boolean sendCurrentValue) { return true; } - public boolean SenderRoot.connectBiOptionalA(String uriString, boolean sendCurrentValue) { return true; } - public boolean SenderRoot.connectBiListA(String uriString, boolean sendCurrentValue) { return true; } - // - public boolean SenderRoot.connectMyB(String uriString, boolean sendCurrentValue) { return true; } - public boolean SenderRoot.connectOptionalB(String uriString, boolean sendCurrentValue) { return true; } - public boolean SenderRoot.connectListB(String uriString, boolean sendCurrentValue) { return true; } - // - public boolean SenderRoot.connectBiMyB(String uriString, boolean sendCurrentValue) { return true; } - public boolean SenderRoot.connectBiOptionalB(String uriString, boolean sendCurrentValue) { return true; } - public boolean SenderRoot.connectBiListB(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectMyA(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectOptionalA(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectListA(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectBiMyA(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectBiOptionalA(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectBiListA(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectMyB(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectOptionalB(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectListB(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectBiMyB(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectBiOptionalB(String uriString, boolean sendCurrentValue) { return true; } + //public boolean SenderRoot.connectBiListB(String uriString, boolean sendCurrentValue) { return true; } } aspect NameResolution { // overriding customID guarantees to produce the same JSON representation for equal lists diff --git a/ragconnect.tests/src/test/01-input/relation/Test.relast b/ragconnect.tests/src/test/01-input/relation/Test.relast index 5c56aefbedb08269b0a57cfd5e3d5fe259fd7760..d0cc7790d80eeeb27de65b68a52cd615bfc5761f 100644 --- a/ragconnect.tests/src/test/01-input/relation/Test.relast +++ b/ragconnect.tests/src/test/01-input/relation/Test.relast @@ -4,21 +4,21 @@ rel SenderRoot.MyA -> A; rel SenderRoot.OptionalA? -> A; rel SenderRoot.ListA* -> A; -rel SenderRoot.BiMyA <-> A.ToMyA; -rel SenderRoot.BiOptionalA? <-> A.ToOptionalA; -rel SenderRoot.BiListA* <-> A.ToListA; +rel SenderRoot.BiMyA <-> A.ToMyA?; +rel SenderRoot.BiOptionalA? <-> A.ToOptionalA?; +rel SenderRoot.BiListA* <-> A.ToListA?; rel SenderRoot.MyB -> B; rel SenderRoot.OptionalB? -> B; rel SenderRoot.ListB* -> B; -rel SenderRoot.BiMyB <-> B.ToMyB; -rel SenderRoot.BiOptionalB? <-> B.ToOptionalB; -rel SenderRoot.BiListB* <-> B.ToListB; +rel SenderRoot.BiMyB <-> B.ToMyB?; +rel SenderRoot.BiOptionalB? <-> B.ToOptionalB?; +rel SenderRoot.BiListB* <-> B.ToListB?; ReceiverRoot ::= -FromMyA:A FromOptionalA:A FromListA:A -FromBiMyA:A FromBiOptionalA:A FromBiListA:A +FromMyA:A FromOptionalA:A FromListA:A* +FromBiMyA:A FromBiOptionalA:A FromBiListA:A* <FromMyB:String> <FromOptionalB:String> <FromListB:String> <FromBiMyB:String> <FromBiOptionalB:String> <FromBiListB:String> ; diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RelationTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RelationTest.java index 1c857f6d8aafabd3304dc40fd7bab6c76878b0cd..bf56da42b08a404357f2bd8ef8951a9b53ae3836 100644 --- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RelationTest.java +++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/RelationTest.java @@ -1,22 +1,23 @@ package org.jastadd.ragconnect.tests; import org.assertj.core.groups.Tuple; -import org.hamcrest.Matchers; import relationInc.ast.*; import org.junit.jupiter.api.Tag; import java.io.IOException; +import java.util.List; +import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.function.Supplier; import static java.util.function.Predicate.isEqual; -import static org.hamcrest.Matchers.equalTo; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.groups.Tuple.tuple; import static org.hamcrest.Matchers.hasProperty; import static org.jastadd.ragconnect.tests.TestUtils.*; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; /** * Test case "relation". @@ -24,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.fail; * @author rschoene - Initial contribution */ @Tag("Incremental") +@Tag("New") public class RelationTest extends AbstractMqttTest { private static final String TOPIC_WILDCARD = "rel/#"; @@ -96,10 +98,10 @@ public class RelationTest extends AbstractMqttTest { // connect receive assertTrue(receiverRoot.connectFromMyA(mqttUri(TOPIC_MY_A))); assertTrue(receiverRoot.connectFromOptionalA(mqttUri(TOPIC_OPTIONAL_A))); - assertTrue(receiverRoot.connectFromListA(mqttUri(TOPIC_LIST_A))); + assertTrue(receiverRoot.connectFromListAList(mqttUri(TOPIC_LIST_A))); assertTrue(receiverRoot.connectFromBiMyA(mqttUri(TOPIC_BI_MY_A))); assertTrue(receiverRoot.connectFromBiOptionalA(mqttUri(TOPIC_BI_OPTIONAL_A))); - assertTrue(receiverRoot.connectFromBiListA(mqttUri(TOPIC_BI_LIST_A))); + assertTrue(receiverRoot.connectFromBiListAList(mqttUri(TOPIC_BI_LIST_A))); assertTrue(receiverRoot.connectFromMyB(mqttUri(TOPIC_MY_B))); assertTrue(receiverRoot.connectFromOptionalB(mqttUri(TOPIC_OPTIONAL_B))); assertTrue(receiverRoot.connectFromListB(mqttUri(TOPIC_LIST_B))); @@ -124,10 +126,10 @@ public class RelationTest extends AbstractMqttTest { assertTrue(senderBi.connectBiOptionalB(mqttUri(TOPIC_BI_OPTIONAL_B), isWriteCurrentValue())); assertTrue(senderBi.connectBiListB(mqttUri(TOPIC_BI_LIST_B), isWriteCurrentValue())); - waitForNonNull(receiverRoot::getFromMyA); - waitForNonNull(receiverRoot::getFromBiMyA); - waitForNonNull(receiverRoot::getFromMyB); - waitForNonNull(receiverRoot::getFromBiMyB); +// waitForNonNull(receiverRoot::getFromMyA); +// waitForNonNull(receiverRoot::getFromBiMyA); +// waitForNonNull(receiverRoot::getFromMyB); +// waitForNonNull(receiverRoot::getFromBiMyB); } private <T> void waitForValue(T expectedValue, Callable<T> callable) { @@ -146,6 +148,8 @@ public class RelationTest extends AbstractMqttTest { protected void communicateSendInitialValue() throws IOException, InterruptedException { // TODO implement test // TODO also check disconnect + check(6, "a1", null, tuple(), null, null, tuple(), + "b1", "", tuple(), "", "", tuple()); } @Override @@ -155,52 +159,69 @@ public class RelationTest extends AbstractMqttTest { // TODO also check disconnect } - private void checkAs(int numberOfValues, String myA, String optionalA, Tuple listA, - String biMyA, String biOptionalA, Tuple biListA) { + private void check(int numberOfValues, + String myA, String optionalA, Tuple listA, + String biMyA, String biOptionalA, Tuple biListA, + String myB, String optionalB, Tuple listB, + String biMyB, String biOptionalB, Tuple biListB) { awaitEquals(numberOfValues, () -> data.numberOfValues, "numberOfValues"); // awaitEquals(Objects.requireNonNullElse(basic, ""), // receiverRoot::getFromBasic, "basic"); - awaitAorNull(myA, receiverRoot::getFromMyA, "myA"); - awaitAorNull(optionalA, receiverRoot::getFromOptionalA, "myA"); - // TODO compare list + // A values + assertNullOrA(myA, receiverRoot.getFromMyA(), "myA"); + assertNullOrA(optionalA, receiverRoot.getFromOptionalA(), "optionalA"); + assertListEqualsForA(listA.toList(), receiverRoot.getFromListAList(), "listA"); - awaitAorNull(biMyA, receiverRoot::getFromBiMyA, "biMyA"); - awaitAorNull(biOptionalA, receiverRoot::getFromBiOptionalA, "biMyA"); - // TODO compare bi-list - } + assertNullOrA(biMyA, receiverRoot.getFromBiMyA(), "biMyA"); + assertNullOrA(biOptionalA, receiverRoot.getFromBiOptionalA(), "biOptionalA"); + assertListEqualsForA(biListA.toList(), receiverRoot.getFromBiListAList(), "biListA"); - // TODO checkBs + // B values + assertNullOrB(myB, receiverRoot.getFromMyB(), "myB"); + assertNullOrB(optionalB, receiverRoot.getFromOptionalB(), "optionalB"); + assertListEqualsForB(listB.toList(), receiverRoot.getFromListB(), "listB"); - private void awaitNull(Supplier<A> actual, String alias) { - awaitMqtt().alias(alias).until(() -> actual.get() == null); + assertNullOrB(biMyB, receiverRoot.getFromBiMyB(), "biMyB"); + assertNullOrB(biOptionalB, receiverRoot.getFromBiOptionalB(), "biOptionalB"); + assertListEqualsForB(biListB.toList(), receiverRoot.getFromBiListB(), "biListB"); } private <T> void awaitEquals(T expected, Callable<T> actual, String alias) { awaitMqtt().alias(alias).until(actual, isEqual(expected)); } - private void awaitAorNull(String expectedValue, Callable<A> actual, String alias) { + private void assertNullOrA(String expectedValue, A actual, String alias) { if (expectedValue == null) { - awaitNull(convertToSupplier(actual), alias + " null"); + assertNull(actual, alias); return; } String expectedInner = "inner" + expectedValue; - awaitMqtt().alias(alias).until(actual, Matchers.allOf( - hasProperty("Value", equalTo(expectedValue)), - hasProperty("Inner", hasProperty("InnerValue", equalTo(expectedInner))))); - } - - private Supplier<A> convertToSupplier(Callable<A> actual) { - return () -> { - try { - return actual.call(); - } catch (Exception e) { - fail(e); - return null; - } - }; + assertThat(actual.getValue()).describedAs(alias + ".Value").isEqualTo(expectedValue); + assertThat(actual.getInner()).describedAs(alias + ".inner != null").isNotNull(); + assertThat(actual.getInner().getInnerValue()).describedAs(alias + ".inner.Value").isEqualTo(expectedInner); + } + + private void assertListEqualsForA(List<Object> expected, JastAddList<A> actual, String alias) { + assertEquals(expected.size(), actual.getNumChild(), alias + ".size"); + for (int i = 0, expectedSize = expected.size(); i < expectedSize; i++) { + String s = (String) expected.get(i); + assertNullOrA(s, actual.getChild(i), alias + "[" + i + "]"); + } + } + + private void assertNullOrB(String expected, String actual, String alias) { + assertEquals(Objects.requireNonNullElse(expected, ""), actual, alias); + } + + private void assertListEqualsForB(List<Object> expected, String actual, String alias) { + String[] actualTokens = actual.split(";"); + assertEquals(expected.size(), actualTokens.length, alias + ".size"); + for (int i = 0, expectedSize = expected.size(); i < expectedSize; i++) { + String s = (String) expected.get(i); + assertNullOrB(s, actualTokens[i], alias + "[" + i + "]"); + } } @Override