diff --git a/build.gradle b/build.gradle index 7bfebcfe84f3d6f53a6ceb997eb0108610135e31..c986f2a61adf610a9c2e5fdc7fd20eb193a9a46a 100644 --- a/build.gradle +++ b/build.gradle @@ -69,19 +69,12 @@ task relast(type: JavaExec) { group = 'Build' main = "-jar" - doFirst { - delete "src/gen/jastadd/*.ast" - delete "src/gen/jastadd/RagConnect.jadd" - delete "src/gen/jastadd/RagConnectRefResolver.jadd" - delete "src/gen/jastadd/RagConnectResolverStubs.jrag" - mkdir "src/gen/jastadd/" - } - args = [ "../libs/relast.jar", "../relast.preprocessor/src/main/jastadd/RelAst.relast", "./src/main/jastadd/RagConnect.relast", "./src/main/jastadd/MustacheNodes.relast", + "./src/main/jastadd/YAML.relast", "--listClass=java.util.ArrayList", "--jastAddList=JastAddList", "--useJastAddNames", @@ -90,14 +83,15 @@ task relast(type: JavaExec) { "--grammarName=./src/gen/jastadd/RagConnect" ] - inputs.files file("../libs/relast.jar"), - file("../relast.preprocessor/src/main/jastadd/RelAST.relast"), - file("./src/main/jastadd/RagConnect.relast") - file("./src/main/jastadd/MustacheNodes.relast") - outputs.files file("./src/gen/jastadd/RagConnect.ast"), - file("./src/gen/jastadd/RagConnect.jadd"), - file("./src/gen/jastadd/RagConnectRefResolver.jadd"), - file('./src/gen/jastadd/RagConnectResolverStubs.jrag') + inputs.files("../libs/relast.jar", + "../relast.preprocessor/src/main/jastadd/RelAst.relast", + "./src/main/jastadd/RagConnect.relast", + "./src/main/jastadd/MustacheNodes.relast", + "./src/main/jastadd/YAML.relast") + outputs.files("./src/gen/jastadd/RagConnect.ast", + "./src/gen/jastadd/RagConnect.jadd", + "./src/gen/jastadd/RagConnectRefResolver.jadd", + './src/gen/jastadd/RagConnectResolverStubs.jrag') } jastadd { diff --git a/src/main/jastadd/Analysis.jrag b/src/main/jastadd/Analysis.jrag index 623a3f0ad8c0457d8d0606bf70dea0adfea89ea4..b62399960f34a4b8c23a8d23762ec195491ce4e3 100644 --- a/src/main/jastadd/Analysis.jrag +++ b/src/main/jastadd/Analysis.jrag @@ -1,13 +1,15 @@ aspect Analysis { // --- lookupTokenEndpointDefinition --- - inh TokenEndpointDefinition TokenEndpointDefinition.lookupTokenEndpointDefinition(TokenComponent token); - eq RagConnect.getEndpointDefinition().lookupTokenEndpointDefinition(TokenComponent token) { + inh java.util.List<TokenEndpointDefinition> TokenEndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token); + eq RagConnect.getEndpointDefinition().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token); + syn java.util.List<TokenEndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) { + java.util.List<TokenEndpointDefinition> result = new java.util.ArrayList<>(); for (EndpointDefinition def : getEndpointDefinitionList()) { if (def.isTokenEndpointDefinition() && def.asTokenEndpointDefinition().getToken().equals(token)) { - return def.asTokenEndpointDefinition(); + result.add(def.asTokenEndpointDefinition()); } } - return null; + return result; } // --- lookupDependencyDefinition --- @@ -22,6 +24,34 @@ aspect Analysis { } // --- isAlreadyDefined --- - syn boolean TokenEndpointDefinition.isAlreadyDefined() = lookupTokenEndpointDefinition(getToken()) != this; + syn boolean TokenEndpointDefinition.isAlreadyDefined() = lookupTokenEndpointDefinitions(getToken()).size() > 1; syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this; + + syn boolean MappingDefinitionType.assignableTo(JavaTypeUse target); + eq JavaMappingDefinitionType.assignableTo(JavaTypeUse target) = getType().assignableTo(target); + eq JavaArrayMappingDefinitionType.assignableTo(JavaTypeUse target) { + if (!target.getName().endsWith("[]")) { return false; } + return getType().assignableTo(new SimpleJavaTypeUse(target.getName().replace("[]", ""))); + } + syn boolean JavaTypeUse.assignableTo(JavaTypeUse target) { + // target var = this; + return target.primitivePrettyPrint().equals(this.primitivePrettyPrint()); + } + syn String JavaTypeUse.primitivePrettyPrint() { + switch(getName()) { + case "int": + case "Integer": return "int"; + case "short": + case "Short": return "short"; + case "long": + case "Long": return "long"; + case "float": + case "Float": return "float"; + case "double": + case "Double": return "double"; + case "char": + case "Character": return "char"; + default: return getName(); + } + } } diff --git a/src/main/jastadd/Errors.jrag b/src/main/jastadd/Errors.jrag index bfee2545d4bee28223b04ce931ccd6a6429cf13f..fe2fcd59efd365174ca2803affebc34819cf3d7e 100644 --- a/src/main/jastadd/Errors.jrag +++ b/src/main/jastadd/Errors.jrag @@ -7,32 +7,32 @@ aspect Errors { [new TreeSet<ErrorMessage>()] root RagConnect; - ReceiveFromMqttDefinition contributes error("Receive definition already defined for " + getToken().getName()) + ReceiveTokenEndpointDefinition contributes error("Receive definition already defined for " + getToken().getName()) when isAlreadyDefined() to RagConnect.errors(); - ReceiveFromMqttDefinition contributes error("Receiving target token must not be an NTA token!") + ReceiveTokenEndpointDefinition contributes error("Receiving target token must not be an NTA token!") when getToken().getNTA() to RagConnect.errors(); // if first mapping is null, then suitableDefaultMapping() == null - ReceiveFromMqttDefinition contributes error("No suitable default mapping found for type " + + ReceiveTokenEndpointDefinition contributes error("No suitable default mapping found for type " + ((getMappingList().isEmpty()) - ? getToken().getJavaTypeUse().prettyPrint() + ? getToken().effectiveJavaTypeUse().prettyPrint() : getMappingList().get(0).getFromType().prettyPrint())) when effectiveMappings().get(0) == null to RagConnect.errors(); - ReceiveFromMqttDefinition contributes error("to-type of last mapping must be type of the Token!") - when getToken().getJavaTypeUse().prettyPrint().equals( - effectiveMappings().get(effectiveMappings().size() - 1)) + ReceiveTokenEndpointDefinition contributes error("to-type of last mapping (" + effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() + ") not assignable to type of the Token (" + getToken().effectiveJavaTypeUse().prettyPrint() + ")!") + when !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo( + getToken().effectiveJavaTypeUse()) to RagConnect.errors(); - SendToMqttDefinition contributes error("Sending target token must be an NTA token!") + SendTokenEndpointDefinition contributes error("Sending target token must be an NTA token!") when !getToken().getNTA() to RagConnect.errors(); - SendToMqttDefinition contributes error("Send definition already defined for " + getToken().getName()) + SendTokenEndpointDefinition contributes error("Send definition already defined for " + getToken().getName()) when isAlreadyDefined() to RagConnect.errors(); @@ -44,7 +44,7 @@ aspect Errors { when isAlreadyDefinedAsList() to RagConnect.errors(); - DependencyDefinition contributes error("There must be a send update definition targeting " + getSource().parentTypeypeAndName() + " for dependency definition " + getID()) + DependencyDefinition contributes error("There must be a send endpoint definition targeting " + getSource().parentTypeypeAndName() + " for dependency definition " + getID()) when targetEndpointDefinition() == null to RagConnect.errors(); } diff --git a/src/main/jastadd/MustacheNodes.relast b/src/main/jastadd/MustacheNodes.relast index ce85caba554d1f582079b299f457d7675ab12dfa..62ecdb6166a783e125cefef2826d550b73ab9e1b 100644 --- a/src/main/jastadd/MustacheNodes.relast +++ b/src/main/jastadd/MustacheNodes.relast @@ -1,6 +1,3 @@ -//TypeComponentMustache ; -//rel TypeComponentMustache.TypeComponent -> TypeComponent ; - MRagConnect ::= ReceiveDefinition:MReceiveDefinition* SendDefinition:MSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent*; abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*; MReceiveDefinition : MEndpointDefinition; @@ -12,9 +9,9 @@ MTypeComponent; MTokenComponent; rel MRagConnect.RagConnect -> RagConnect; -rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition; -rel MReceiveDefinition.ReceiveFromMqttDefinition -> ReceiveFromMqttDefinition; -rel MSendDefinition.SendToMqttDefinition -> SendToMqttDefinition; +rel MInnerMappingDefinition.MMappingDefinition -> MMappingDefinition; +rel MReceiveDefinition.ReceiveTokenEndpointDefinition -> ReceiveTokenEndpointDefinition; +rel MSendDefinition.SendTokenEndpointDefinition -> SendTokenEndpointDefinition; rel MMappingDefinition.MappingDefinition -> MappingDefinition; rel MDependencyDefinition.DependencyDefinition -> DependencyDefinition; rel MTypeComponent.TypeComponent -> TypeComponent; diff --git a/src/main/jastadd/Navigation.jrag b/src/main/jastadd/Navigation.jrag index 2314386eb9f74ecb5699d6fba91956e80f75fa63..752fc36aea4c59348874c1a08134696a9543b6cb 100644 --- a/src/main/jastadd/Navigation.jrag +++ b/src/main/jastadd/Navigation.jrag @@ -10,9 +10,12 @@ aspect Navigation { eq MRagConnect.getChild().ragconnect() = getRagConnect(); // --- containedFile + eq Grammar.getChild().containedFile() = null; eq RagConnect.getChild().containedFile() = null; + eq MRagConnect.getChild().containedFile() = null; // --- containedFileName --- + eq Grammar.getChild().containedFileName() = null; // should be in PP eq RagConnect.getChild().containedFileName() = getFileName(); eq MRagConnect.getChild().containedFileName() = null; @@ -24,30 +27,33 @@ aspect Navigation { syn TokenEndpointDefinition EndpointDefinition.asTokenEndpointDefinition() = null; eq TokenEndpointDefinition.asTokenEndpointDefinition() = this; - // --- isSendToMqttDefinition --- - syn boolean EndpointDefinition.isSendToMqttDefinition() = false; - eq SendToMqttDefinition.isSendToMqttDefinition() = true; + // --- isSendTokenEndpointDefinition --- + syn boolean EndpointDefinition.isSendTokenEndpointDefinition() = false; + eq SendTokenEndpointDefinition.isSendTokenEndpointDefinition() = true; - // --- asSendToMqttDefinition --- - syn SendToMqttDefinition EndpointDefinition.asSendToMqttDefinition() = null; - eq SendToMqttDefinition.asSendToMqttDefinition() = this; + // --- asSendTokenEndpointDefinition --- + syn SendTokenEndpointDefinition EndpointDefinition.asSendTokenEndpointDefinition() = null; + eq SendTokenEndpointDefinition.asSendTokenEndpointDefinition() = this; - // --- asReceiveFromMqttDefinition --- - syn ReceiveFromMqttDefinition EndpointDefinition.asReceiveFromMqttDefinition() = null; - eq ReceiveFromMqttDefinition.asReceiveFromMqttDefinition() = this; + // --- asReceiveTokenEndpointDefinition --- + syn ReceiveTokenEndpointDefinition EndpointDefinition.asReceiveTokenEndpointDefinition() = null; + eq ReceiveTokenEndpointDefinition.asReceiveTokenEndpointDefinition() = this; // --- targetEndpointDefinition --- - syn SendToMqttDefinition DependencyDefinition.targetEndpointDefinition() { + syn SendTokenEndpointDefinition DependencyDefinition.targetEndpointDefinition() { // resolve definition in here, as we do not need resolveMethod in any other place (yet) for (EndpointDefinition endpointDefinition : ragconnect().getEndpointDefinitionList()) { - if (endpointDefinition.isSendToMqttDefinition() && - endpointDefinition.asSendToMqttDefinition().getToken().equals(this.getTarget())) { - return endpointDefinition.asSendToMqttDefinition(); + if (endpointDefinition.isSendTokenEndpointDefinition() && + endpointDefinition.asSendTokenEndpointDefinition().getToken().equals(this.getTarget())) { + return endpointDefinition.asSendTokenEndpointDefinition(); } } return null; } + // --- effectiveJavaTypeUse (should be in preprocessor) --- + syn lazy JavaTypeUse TokenComponent.effectiveJavaTypeUse() = hasJavaTypeUse() ? getJavaTypeUse() : new SimpleJavaTypeUse("String"); + // --- isDefaultMappingDefinition --- syn boolean MappingDefinition.isDefaultMappingDefinition() = false; eq DefaultMappingDefinition.isDefaultMappingDefinition() = true; diff --git a/src/main/jastadd/Printing.jrag b/src/main/jastadd/Printing.jrag new file mode 100644 index 0000000000000000000000000000000000000000..74607aeb7737e1e43c9565b799693889a648a9df --- /dev/null +++ b/src/main/jastadd/Printing.jrag @@ -0,0 +1,75 @@ +aspect Printing { + String ASTNode.PRINT_INDENT = " "; + + syn String MappingDefinitionType.prettyPrint(); + eq JavaMappingDefinitionType.prettyPrint() = getType().getName(); + eq JavaArrayMappingDefinitionType.prettyPrint() = getType().getName() + "[]"; + + syn String JavaTypeUse.prettyPrint() { + StringBuilder sb = new StringBuilder(); + generateAbstractGrammar(sb); + return sb.toString(); + } + + syn String Document.prettyPrint() { + StringBuilder sb = new StringBuilder(); + sb.append("# RagConnect created at ").append(java.time.Instant.now()).append("\n"); + for (ComplexElement element : getComplexElementList()) { + element.prettyPrint(sb, false, ""); + } + if (sb.charAt(sb.length() - 1) != '\n') { + sb.append("\n"); + } + return sb.toString(); + } + syn StringBuilder Element.prettyPrint(StringBuilder sb, boolean printIndent, String indent); + eq ValueElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) { + sb.append(getValue()); + return sb; + } + eq StringElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) { + sb.append("\"").append(getValue()).append("\""); + return sb; + } + eq ListElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) { + if (isEmpty()) { + sb.append("[]"); + } else { + for (Element element : getElementList()) { + sb.append(indent).append("- "); + element.prettyPrint(sb, false, indent + PRINT_INDENT); + sb.append("\n"); + } + // delete last newline + sb.deleteCharAt(sb.length() - 1); + } + return sb; + } + eq KeyValuePair.prettyPrint(StringBuilder sb, boolean printIndent, String indent) { + if (printIndent) sb.append(indent); + sb.append(getKey()).append(": "); + if (getValue().isComplex() && !getValue().isEmpty()) { + sb.append("\n"); + getValue().prettyPrint(sb, true, indent + PRINT_INDENT); //); + } else { + getValue().prettyPrint(sb, false, indent); + } + return sb; + } + eq MappingElement.prettyPrint(StringBuilder sb, boolean printIndent, String indent) { + if (isEmpty()) { + sb.append("{}"); + } else { + boolean first = true; + for (KeyValuePair pair : getKeyValuePairList()) { + if (!first || printIndent) sb.append(indent); + first = false; + pair.prettyPrint(sb, false, indent); // + PRINT_INDENT + sb.append("\n"); + } + // delete last newline + sb.deleteCharAt(sb.length() - 1); + } + return sb; + } +} diff --git a/src/main/jastadd/RagConnect.relast b/src/main/jastadd/RagConnect.relast index d30116efae1a6735e88922e31f42f99911c6532a..accc617714ff6bdd24a22831ea84e5d93e6a15b8 100644 --- a/src/main/jastadd/RagConnect.relast +++ b/src/main/jastadd/RagConnect.relast @@ -2,13 +2,13 @@ RagConnect ::= EndpointDefinition* DependencyDefinition* MappingDefinition* Prog abstract EndpointDefinition ::= <AlwaysApply:boolean> ; -rel EndpointDefinition.Mapping* -> MappingDefinition; +rel EndpointDefinition.Mapping* <-> MappingDefinition.UsedAt*; abstract TokenEndpointDefinition : EndpointDefinition; rel TokenEndpointDefinition.Token -> TokenComponent; -ReceiveFromMqttDefinition : TokenEndpointDefinition; -SendToMqttDefinition : TokenEndpointDefinition; +ReceiveTokenEndpointDefinition : TokenEndpointDefinition; +SendTokenEndpointDefinition : TokenEndpointDefinition; DependencyDefinition ::= <ID>; rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*; diff --git a/src/main/jastadd/Util.jadd b/src/main/jastadd/Util.jadd new file mode 100644 index 0000000000000000000000000000000000000000..3b59b13c53a8d0e1ca2a05feba43294797a4577e --- /dev/null +++ b/src/main/jastadd/Util.jadd @@ -0,0 +1,5 @@ +aspect Util { + static String ASTNode.capitalize(String s) { + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } +} diff --git a/src/main/jastadd/YAML.jrag b/src/main/jastadd/YAML.jrag new file mode 100644 index 0000000000000000000000000000000000000000..93dd0f34b7b7b0505cf0970b6541fa3ae5bee7fa --- /dev/null +++ b/src/main/jastadd/YAML.jrag @@ -0,0 +1,31 @@ +aspect Navigation { + eq Document.getChild().program() = null; + eq Document.getChild().ragconnect() = null; + eq Document.getChild().containedFile() = null; + eq Document.getChild().containedFileName() = getFileName(); + + syn boolean Element.isComplex() = false; + eq ComplexElement.isComplex() = true; + + syn boolean Element.isEmpty() = false; + eq MappingElement.isEmpty() = getNumKeyValuePair() == 0; + eq ListElement.isEmpty() = getNumElement() == 0; +} + +aspect Helper { + public static ValueElement ValueElement.of(int value) { + return new ValueElement(String.valueOf(value)); + } + public static ValueElement ValueElement.of(boolean value) { + return new ValueElement(String.valueOf(value)); + } + public static ValueElement ValueElement.of(String value) { + return new ValueElement(value); + } + public static StringElement StringElement.of(String value) { + return new StringElement(value); + } + public void MappingElement.addKeyValuePair(String key, Element value) { + addKeyValuePair(new KeyValuePair(key, value)); + } +} diff --git a/src/main/jastadd/YAML.relast b/src/main/jastadd/YAML.relast new file mode 100644 index 0000000000000000000000000000000000000000..94f4ab20fdc3838348bd53b812b3d1477cae3558 --- /dev/null +++ b/src/main/jastadd/YAML.relast @@ -0,0 +1,9 @@ +Document ::= <FileName> ComplexElement* ; +abstract Element ; +abstract ComplexElement : Element ; +MappingElement : ComplexElement ::= KeyValuePair* ; +KeyValuePair : ComplexElement ::= <Key> Value:Element ; +ListElement : ComplexElement ::= Element* ; +abstract SimpleElement : Element ; +ValueElement : SimpleElement ::= <Value> ; +StringElement : SimpleElement ::= <Value> ; diff --git a/src/main/jastadd/backend/Configuration.jadd b/src/main/jastadd/backend/Configuration.jadd index d4f5b43f27cb329f1ff82318c6f383f0f69ebbd6..8d3b16c3f99014408c6aefa5655201d222c3d892 100644 --- a/src/main/jastadd/backend/Configuration.jadd +++ b/src/main/jastadd/backend/Configuration.jadd @@ -2,4 +2,6 @@ aspect Configuration { public static boolean ASTNode.loggingEnabledForReads = false; public static boolean ASTNode.loggingEnabledForWrites = false; public static TypeDecl ASTNode.rootNode; + public static boolean ASTNode.usesMqtt; + public static boolean ASTNode.usesRest; } diff --git a/src/main/jastadd/backend/Generation.jadd b/src/main/jastadd/backend/Generation.jadd index 067626ed42f12add896ca4eea7c65917309c0e2e..4b8d357e82e82eb8454451978589434f1ef44505 100644 --- a/src/main/jastadd/backend/Generation.jadd +++ b/src/main/jastadd/backend/Generation.jadd @@ -1,41 +1,19 @@ -aspect GenerationUtils { - public static final String ASTNode.aspectIndent = " "; - - public String ASTNode.ind(int n) { - StringBuilder s = new StringBuilder(); - for (int i = 0; i < n; i++) { - s.append(aspectIndent); - } - return s.toString(); - } - - // --- prettyPrint --- - syn String MappingDefinitionType.prettyPrint(); - eq JavaMappingDefinitionType.prettyPrint() = getType().getName(); - eq JavaArrayMappingDefinitionType.prettyPrint() = getType().getName() + "[]"; - - syn String JavaTypeUse.prettyPrint() { - StringBuilder sb = new StringBuilder(); - generateAbstractGrammar(sb); - return sb.toString(); - } -} - -/* Open questions -- Should all string constants be defined on the normal AST, or on the special mustache AST? +/* +Design considerations +- InnerMappingDefinition needed for iteration attribute (first, last) - not easily possible with list-relation */ aspect AttributesForMustache { // --- MRagConnect --- - eq MRagConnect.getChild().mqttHandlerAttribute() = mqttHandlerAttribute(); - eq MRagConnect.getChild().mqttHandlerField() = mqttHandlerField(); eq MRagConnect.getRootTypeComponent(int i).isFirst() = i == 0; - syn String MRagConnect.mqttHandlerAttribute() = getRagConnect().mqttHandlerAttribute(); - syn String MRagConnect.mqttHandlerField() = getRagConnect().mqttHandlerField(); - syn String MRagConnect.mqttSetHostMethod() = getRagConnect().mqttSetHostMethod(); - syn String MRagConnect.mqttWaitUntilReadyMethod() = getRagConnect().mqttWaitUntilReadyMethod(); - syn String MRagConnect.mqttCloseMethod() = getRagConnect().mqttCloseMethod(); + syn String MRagConnect.closeMethod() = "ragconnectCloseConnections"; + syn String MRagConnect.mqttHandlerAttribute() = "_mqttHandler"; + syn String MRagConnect.mqttHandlerField() = "_mqttHandler"; + syn String MRagConnect.mqttSetupWaitUntilReadyMethod() = "ragconnectSetupMqttWaitUntilReady"; + + syn String MRagConnect.restHandlerAttribute() = "_restHandler"; + syn String MRagConnect.restHandlerField() = "_restHandler"; // --- MEndpointDefinition --- syn String MEndpointDefinition.preemptiveExpectedValue(); @@ -44,29 +22,25 @@ aspect AttributesForMustache { syn String MEndpointDefinition.firstInputVarName(); eq MEndpointDefinition.getInnerMappingDefinition(int i).isLast() = i == getNumInnerMappingDefinition() - 1; - eq MEndpointDefinition.getInnerMappingDefinition().resultVarPrefix() = resultVarPrefix(); - eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : resultVarPrefix() + getInnerMappingDefinition(i - 1).getMappingDefinition().methodName(); - - inh String MEndpointDefinition.mqttHandlerAttribute(); + eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : getInnerMappingDefinition(i - 1).outputVarName(); - syn String MEndpointDefinition.connectMethod() = endpointDef().connectMethod(); + syn String MEndpointDefinition.connectParameterName() = "uriString"; + syn String MEndpointDefinition.connectMethod() = "connect" + tokenName(); syn TokenComponent MEndpointDefinition.token() = endpointDef().getToken(); syn boolean MEndpointDefinition.alwaysApply() = endpointDef().getAlwaysApply(); - syn String MEndpointDefinition.resultVarPrefix() = "result"; // we do not need "_" here, because methodName begins with one syn String MEndpointDefinition.parentTypeName() = token().containingTypeDecl().getName(); syn String MEndpointDefinition.tokenName() = token().getName(); syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1); - syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().ToType(); - syn String MEndpointDefinition.lastDefinitionName() = lastDefinition().methodName(); - syn String MEndpointDefinition.lastResult() = resultVarPrefix() + lastDefinitionName(); + syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().toType(); + syn String MEndpointDefinition.lastResult() = lastDefinition().outputVarName(); syn String MEndpointDefinition.condition() { - if (lastDefinition().getMappingDefinition().getToType().isArray()) { + if (lastDefinition().mappingDef().getToType().isArray()) { return "java.util.Arrays.equals(" + preemptiveExpectedValue() + ", " + lastResult() + ")"; } - if (token().isPrimitiveType() && lastDefinition().getMappingDefinition().getToType().isPrimitiveType()) { + if (token().isPrimitiveType() && lastDefinition().mappingDef().getToType().isPrimitiveType()) { return preemptiveExpectedValue() + " == " + lastResult(); } - if (lastDefinition().getMappingDefinition().isDefaultMappingDefinition()) { + if (lastDefinition().mappingDef().isDefaultMappingDefinition()) { return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")"; } return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null"; @@ -74,66 +48,67 @@ aspect AttributesForMustache { // --- MInnerMappingDefinition --- inh boolean MInnerMappingDefinition.isLast(); - inh String MInnerMappingDefinition.resultVarPrefix(); - syn String MInnerMappingDefinition.ToType() = getMappingDefinition().getToType().prettyPrint(); - syn String MInnerMappingDefinition.methodName() = getMappingDefinition().methodName(); inh String MInnerMappingDefinition.inputVarName(); + syn String MInnerMappingDefinition.toType() = mappingDef().getToType().prettyPrint(); + syn String MInnerMappingDefinition.methodName() = getMMappingDefinition().methodName(); + syn MappingDefinition MInnerMappingDefinition.mappingDef() = getMMappingDefinition().getMappingDefinition(); + syn String MInnerMappingDefinition.outputVarName() = "result" + methodName(); // we do not need "_" in between here, because methodName begins with one // --- MReceiveDefinition --- eq MReceiveDefinition.preemptiveExpectedValue() = "get" + tokenName() + "()"; eq MReceiveDefinition.preemptiveReturn() = "return;"; - eq MReceiveDefinition.endpointDef() = getReceiveFromMqttDefinition(); + eq MReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition(); eq MReceiveDefinition.firstInputVarName() = "message"; // --- MSendDefinition --- eq MSendDefinition.preemptiveExpectedValue() = lastValue(); eq MSendDefinition.preemptiveReturn() = "return false;"; - eq MSendDefinition.endpointDef() = getSendToMqttDefinition(); + eq MSendDefinition.endpointDef() = getSendTokenEndpointDefinition(); eq MSendDefinition.firstInputVarName() = "get" + tokenName() + "()"; - syn String MSendDefinition.sendTopic() = getSendToMqttDefinition().sendTopic(); - syn String MSendDefinition.lastValue() = getSendToMqttDefinition().lastValue(); - syn String MSendDefinition.updateMethod() = getSendToMqttDefinition().updateMethod(); - syn String MSendDefinition.writeMethod() = getSendToMqttDefinition().writeMethod(); - syn String MSendDefinition.tokenResetMethod() = getSendToMqttDefinition().tokenResetMethod(); + syn String MSendDefinition.sender() = "_sender_" + tokenName(); + syn String MSendDefinition.lastValue() = "_lastValue" + tokenName(); + syn String MSendDefinition.updateMethod() = "_update_" + tokenName(); + syn String MSendDefinition.writeMethod() = "_writeLastValue_" + tokenName(); + syn String MSendDefinition.tokenResetMethod() = "get" + tokenName() + "_reset"; // --- MMappingDefinition --- syn String MMappingDefinition.toType() = getMappingDefinition().getToType().prettyPrint(); - syn String MMappingDefinition.methodName() = getMappingDefinition().methodName(); + syn String MMappingDefinition.methodName() = "_apply_" + getMappingDefinition().getID(); syn String MMappingDefinition.fromType() = getMappingDefinition().getFromType().prettyPrint(); syn String MMappingDefinition.fromVariableName() = getMappingDefinition().getFromVariableName(); syn String MMappingDefinition.content() = getMappingDefinition().getContent(); // --- MDependencyDefinition --- syn String MDependencyDefinition.targetParentTypeName() = getDependencyDefinition().getTarget().containingTypeDecl().getName(); - syn String MDependencyDefinition.dependencyMethod() = getDependencyDefinition().dependencyMethod(); + syn String MDependencyDefinition.dependencyMethod() = "add" + capitalize(getDependencyDefinition().getID()); syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName(); - syn String MDependencyDefinition.internalRelationPrefix() = getDependencyDefinition().internalRelationPrefix(); - syn nta MEndpointDefinition MDependencyDefinition.targetEndpointDefinition() { + syn String MDependencyDefinition.internalRelationPrefix() = "_internal_" + getDependencyDefinition().getID(); + syn nta MSendDefinition MDependencyDefinition.targetEndpointDefinition() { return getDependencyDefinition().targetEndpointDefinition().toMustache(); } // --- MTypeComponent --- syn String MTypeComponent.name() = getTypeComponent().getName(); - inh String MTypeComponent.mqttHandlerAttribute(); - inh String MTypeComponent.mqttHandlerField(); inh boolean MTypeComponent.isFirst(); // --- MTokenComponent --- syn String MTokenComponent.parentTypeName() = getTokenComponent().containingTypeDecl().getName(); syn String MTokenComponent.name() = getTokenComponent().getName(); - syn String MTokenComponent.javaType() = getTokenComponent().getJavaTypeUse().prettyPrint(); - syn String MTokenComponent.internalName() = getTokenComponent().internalName(); + syn String MTokenComponent.javaType() = getTokenComponent().effectiveJavaTypeUse().prettyPrint(); + syn String MTokenComponent.internalName() = getTokenComponent().getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + name(); + syn String MTokenComponent.externalName() = name(); // --- toMustache --- syn lazy MRagConnect RagConnect.toMustache() { MRagConnect result = new MRagConnect(); result.setRagConnect(this); for (EndpointDefinition def : getEndpointDefinitionList()) { - if (def.isSendToMqttDefinition()) { - result.addSendDefinition(def.asSendToMqttDefinition().toMustache()); + if (def.isSendTokenEndpointDefinition()) { + SendTokenEndpointDefinition sendDef = def.asSendTokenEndpointDefinition(); + result.addSendDefinition(sendDef.toMustache()); } else { - result.addReceiveDefinition(def.asReceiveFromMqttDefinition().toMustache()); + result.addReceiveDefinition(def.asReceiveTokenEndpointDefinition().toMustache()); } } for (MappingDefinition def : allMappingDefinitions()) { @@ -155,41 +130,46 @@ aspect AttributesForMustache { return result; } -//MInnerMappingDefinition.MappingDefinition -> MappingDefinition; protected void MEndpointDefinition.addInnerMappings() { for (MappingDefinition def : endpointDef().effectiveMappings()) { MInnerMappingDefinition inner = new MInnerMappingDefinition(); - inner.setMappingDefinition(def); + inner.setMMappingDefinition(def.toMustache()); addInnerMappingDefinition(inner); } } - syn lazy MReceiveDefinition ReceiveFromMqttDefinition.toMustache() { + + syn lazy MReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() { MReceiveDefinition result = new MReceiveDefinition(); - result.setReceiveFromMqttDefinition(this); + result.setReceiveTokenEndpointDefinition(this); result.addInnerMappings(); return result; } - syn lazy MSendDefinition SendToMqttDefinition.toMustache() { + + syn lazy MSendDefinition SendTokenEndpointDefinition.toMustache() { MSendDefinition result = new MSendDefinition(); - result.setSendToMqttDefinition(this); + result.setSendTokenEndpointDefinition(this); result.addInnerMappings(); return result; } + syn lazy MMappingDefinition MappingDefinition.toMustache() { MMappingDefinition result = new MMappingDefinition(); result.setMappingDefinition(this); return result; } + syn lazy MDependencyDefinition DependencyDefinition.toMustache() { MDependencyDefinition result = new MDependencyDefinition(); result.setDependencyDefinition(this); return result; } + syn lazy MTypeComponent TypeComponent.toMustache() { MTypeComponent result = new MTypeComponent(); result.setTypeComponent(this); return result; } + syn lazy MTokenComponent TokenComponent.toMustache() { MTokenComponent result = new MTokenComponent(); result.setTokenComponent(this); @@ -201,37 +181,6 @@ aspect AttributesForMustache { } aspect AspectGeneration { - // naming convention attributes - syn String TokenComponent.internalName() = getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + getName(); - syn String TokenComponent.externalName() = getName(); - - syn String TokenEndpointDefinition.connectMethod() = "connect" + getToken().getName(); - syn String SendToMqttDefinition.sendTopic() = "_topic_" + getToken().getName(); - syn String SendToMqttDefinition.lastValue() = "_lastValue" + getToken().getName(); - syn String SendToMqttDefinition.updateMethod() = "_update_" + getToken().getName(); - syn String SendToMqttDefinition.writeMethod() = "_writeLastValue_" + getToken().getName(); - syn String SendToMqttDefinition.tokenResetMethod() = "get" + getToken().getName() + "_reset"; - syn String MappingDefinition.methodName() = "_apply_" + getID(); - syn String DependencyDefinition.dependencyMethod() = "add" + - Character.toUpperCase(getID().charAt(0)) + - getID().substring(1); - syn String DependencyDefinition.internalRelationPrefix() = "_internal_" + getID(); - syn String DependencyDefinition.internalTokenName() = getSource().internalName(); - - syn String RagConnect.mqttHandlerAttribute() = "_mqttHandler"; - syn String RagConnect.mqttHandlerField() = "_mqttHandler"; - - syn String RagConnect.mqttSetHostMethod() = "MqttSetHost"; - syn String RagConnect.mqttWaitUntilReadyMethod() = "MqttWaitUntilReady"; - syn String RagConnect.mqttCloseMethod() = "MqttCloseConnections"; - - // naming copy attributes - // --- mqttHandlerAttribute --- - inh String EndpointDefinition.mqttHandlerAttribute(); - inh String MappingDefinition.mqttHandlerAttribute(); - inh String DependencyDefinition.mqttHandlerAttribute(); - eq RagConnect.getChild().mqttHandlerAttribute() = mqttHandlerAttribute(); - // --- rootNodeName --- syn String ASTNode.rootNodeName() = rootNode.getName(); @@ -275,10 +224,11 @@ aspect RelationGeneration { } syn nta Relation DependencyDefinition.getRelationToCreate() { + String internalRelationPrefix = toMustache().internalRelationPrefix(); BidirectionalRelation result = new BidirectionalRelation(); - NavigableRole left = new ListRole(internalRelationPrefix() + "Source"); + NavigableRole left = new ListRole(internalRelationPrefix + "Source"); left.setType(getTarget().containingTypeDecl()); - NavigableRole right = new ListRole(internalRelationPrefix() + "Target"); + NavigableRole right = new ListRole(internalRelationPrefix + "Target"); right.setType(getSource().containingTypeDecl()); result.setLeft(left); result.setRight(right); @@ -294,9 +244,9 @@ aspect GrammarExtension { } b.append("<"); if (!getName().equals("")) { - b.append(internalName()).append(":"); + b.append(toMustache().internalName()).append(":"); } - getJavaTypeUse().generateAbstractGrammar(b); + effectiveJavaTypeUse().generateAbstractGrammar(b); b.append(">"); if (getNTA()) { b.append("/"); diff --git a/src/main/jastadd/backend/Mappings.jrag b/src/main/jastadd/backend/Mappings.jrag index 07cbf5b81a531638ac092635f915ed856a65df4c..7aa82d304079592d605997484d1348a3d3681875 100644 --- a/src/main/jastadd/backend/Mappings.jrag +++ b/src/main/jastadd/backend/Mappings.jrag @@ -1,117 +1,91 @@ aspect DefaultMappings { - private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinitionFromBytes(String typeName) { - DefaultMappingDefinition result = new DefaultMappingDefinition(); - result.setID("_DefaultBytesTo" + Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1) + "Mapping"); - result.setFromType(new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse("byte"))); - result.setFromVariableName("bytes"); - result.setToType(new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName))); - return result; - } - - private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinitionToBytes(String typeName) { - DefaultMappingDefinition result = new DefaultMappingDefinition(); - result.setID("_Default" + Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1) + "ToBytesMapping"); - result.setFromType(new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName))); - result.setFromVariableName("input"); - result.setToType(new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse("byte"))); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultBytesToIntMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("int"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getInt();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultBytesToShortMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("short"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getShort();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultBytesToLongMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("long"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getLong();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultBytesToFloatMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("float"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getFloat();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultBytesToDoubleMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("double"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getDouble();"); - return result; + private String RagConnect.baseDefaultMappingTypeNamePart(String typeName) { + return capitalize(typeName).replace("[]", "s"); } - syn nta DefaultMappingDefinition RagConnect.defaultBytesToCharMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("char"); - result.setContent("return java.nio.ByteBuffer.wrap(bytes).getChar();"); - return result; + private MappingDefinitionType RagConnect.baseDefaultMappingTypeFromName(String typeName) { + return typeName.endsWith("[]") ? + new JavaArrayMappingDefinitionType(new SimpleJavaTypeUse(typeName.replace("[]", ""))) : + new JavaMappingDefinitionType(new SimpleJavaTypeUse(typeName)); } - syn nta DefaultMappingDefinition RagConnect.defaultBytesToStringMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionFromBytes("String"); - result.setContent("return new String(bytes);"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("int"); - result.setContent("return java.nio.ByteBuffer.allocate(4).putInt(input).array();"); + private DefaultMappingDefinition RagConnect.baseDefaultMappingDefinition(String fromTypeName, String toTypeName, String content) { + DefaultMappingDefinition result = new DefaultMappingDefinition(); + result.setID("_Default" + baseDefaultMappingTypeNamePart(fromTypeName) + "To" + baseDefaultMappingTypeNamePart(toTypeName) + "Mapping"); + result.setFromType(baseDefaultMappingTypeFromName(fromTypeName)); + result.setFromVariableName("input"); + result.setToType(baseDefaultMappingTypeFromName(toTypeName)); + result.setContent(content); return result; } - syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("short"); - result.setContent("return java.nio.ByteBuffer.allocate(2).putShort(input).array();"); - return result; - } + syn nta DefaultMappingDefinition RagConnect.defaultBytesToIntMapping() = baseDefaultMappingDefinition( + "byte[]", "int", "return java.nio.ByteBuffer.wrap(input).getInt();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToShortMapping() = baseDefaultMappingDefinition( + "byte[]", "short", "return java.nio.ByteBuffer.wrap(input).getShort();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToLongMapping() = baseDefaultMappingDefinition( + "byte[]", "long", "return java.nio.ByteBuffer.wrap(input).getLong();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToFloatMapping() = baseDefaultMappingDefinition( + "byte[]", "float", "return java.nio.ByteBuffer.wrap(input).getFloat();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToDoubleMapping() = baseDefaultMappingDefinition( + "byte[]", "double", "return java.nio.ByteBuffer.wrap(input).getDouble();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToCharMapping() = baseDefaultMappingDefinition( + "byte[]", "char", "return java.nio.ByteBuffer.wrap(input).getChar();"); + syn nta DefaultMappingDefinition RagConnect.defaultBytesToStringMapping() = baseDefaultMappingDefinition( + "byte[]", "String", "return new String(input);"); - syn nta DefaultMappingDefinition RagConnect.defaultLongToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("long"); - result.setContent("return java.nio.ByteBuffer.allocate(8).putLong(input).array();"); - return result; - } + syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() = baseDefaultMappingDefinition( + "int", "byte[]", "return java.nio.ByteBuffer.allocate(Integer.BYTES).putInt(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() = baseDefaultMappingDefinition( + "short", "byte[]", "return java.nio.ByteBuffer.allocate(Short.BYTES).putShort(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultLongToBytesMapping() = baseDefaultMappingDefinition( + "long", "byte[]", "return java.nio.ByteBuffer.allocate(Long.BYTES).putLong(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultFloatToBytesMapping() = baseDefaultMappingDefinition( + "float", "byte[]", "return java.nio.ByteBuffer.allocate(Float.BYTES).putFloat(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultDoubleToBytesMapping() = baseDefaultMappingDefinition( + "double", "byte[]", "return java.nio.ByteBuffer.allocate(Double.BYTES).putDouble(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultCharToBytesMapping() = baseDefaultMappingDefinition( + "char", "byte[]", "return java.nio.ByteBuffer.allocate(Character.BYTES).putChar(input).array();"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToBytesMapping() = baseDefaultMappingDefinition( + "String", "byte[]", "return input.getBytes();"); - syn nta DefaultMappingDefinition RagConnect.defaultFloatToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("float"); - result.setContent("return java.nio.ByteBuffer.allocate(4).putFloat(input).array();"); - return result; - } + syn nta DefaultMappingDefinition RagConnect.defaultStringToIntMapping() = baseDefaultMappingDefinition( + "String", "int", "return Integer.parseInt(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToShortMapping() = baseDefaultMappingDefinition( + "String", "short", "return Short.parseShort(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToLongMapping() = baseDefaultMappingDefinition( + "String", "long", "return Long.parseLong(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToFloatMapping() = baseDefaultMappingDefinition( + "String", "float", "return Float.parseFloat(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToDoubleMapping() = baseDefaultMappingDefinition( + "String", "double", "return Double.parseDouble(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultStringToCharMapping() = baseDefaultMappingDefinition( + "String", "char", "return input.charAt(0);"); - syn nta DefaultMappingDefinition RagConnect.defaultDoubleToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("double"); - result.setContent("return java.nio.ByteBuffer.allocate(8).putDouble(input).array();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultCharToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("char"); - result.setContent("return java.nio.ByteBuffer.allocate(2).putChar(input).array();"); - return result; - } - - syn nta DefaultMappingDefinition RagConnect.defaultStringToBytesMapping() { - DefaultMappingDefinition result = baseDefaultMappingDefinitionToBytes("String"); - result.setContent("return input.getBytes();"); - return result; - } + syn nta DefaultMappingDefinition RagConnect.defaultIntToStringMapping() = baseDefaultMappingDefinition( + "int", "String", "return String.valueOf(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultShortToStringMapping() = baseDefaultMappingDefinition( + "short", "String", "return String.valueOf(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultLongToStringMapping() = baseDefaultMappingDefinition( + "long", "String", "return String.valueOf(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultFloatToStringMapping() = baseDefaultMappingDefinition( + "float", "String", "return String.valueOf(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultDoubleToStringMapping() = baseDefaultMappingDefinition( + "double", "String", "return String.valueOf(input);"); + syn nta DefaultMappingDefinition RagConnect.defaultCharToStringMapping() = baseDefaultMappingDefinition( + "char", "String", "return String.valueOf(input);"); } aspect Mappings { // --- effectiveMappings --- syn java.util.List<MappingDefinition> EndpointDefinition.effectiveMappings(); - eq ReceiveFromMqttDefinition.effectiveMappings() { - // if there is a first mapping, check if its input type is byte[]. + eq ReceiveTokenEndpointDefinition.effectiveMappings() { + // if there is a first mapping, check if it is suitable. // or if no mappings are specified. // then prepend the suitable default mapping java.util.List<MappingDefinition> result; - if (getMappingList().isEmpty() || !getMappingList().get(0).getFromType().isByteArray()) { + if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) { result = new java.util.ArrayList(); result.add(suitableDefaultMapping()); result.addAll(getMappingList()); @@ -120,15 +94,13 @@ aspect Mappings { } return result; } - eq SendToMqttDefinition.effectiveMappings() { - // if there is a mapping, check if the output type of the last mapping is byte[]. + eq SendTokenEndpointDefinition.effectiveMappings() { + // if there is a mapping, check if it is suitable. // or if no mappings are specified. // then append the suitable default mapping java.util.List<MappingDefinition> result; - int numMappings = getMappingList().size(); - if (numMappings == 0 || !getMappingList().get(numMappings - 1).getToType().isByteArray()) { - result = new java.util.ArrayList(); - result.addAll(getMappingList()); + if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) { + result = new java.util.ArrayList(getMappingList()); result.add(suitableDefaultMapping()); } else { result = getMappingList(); @@ -136,8 +108,18 @@ aspect Mappings { return result; } + // --- hasSuitableEdgeMapping --- + syn boolean TokenEndpointDefinition.hasSuitableEdgeMapping(); + eq ReceiveTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(0)); + eq SendTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(getMappingList().size() - 1)); + + // --- isSuitableEdgeMapping(def) --- + syn boolean TokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def); + eq ReceiveTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getFromType().isByteArray(); + eq SendTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getToType().isByteArray(); + // --- isPrimitiveType --- - syn boolean TokenComponent.isPrimitiveType() = getJavaTypeUse().isPrimitiveType(); + syn boolean TokenComponent.isPrimitiveType() = effectiveJavaTypeUse().isPrimitiveType(); syn boolean JavaTypeUse.isPrimitiveType() = false; eq SimpleJavaTypeUse.isPrimitiveType() { switch(getName()) { @@ -160,9 +142,9 @@ aspect Mappings { // --- suitableDefaultMapping --- syn DefaultMappingDefinition EndpointDefinition.suitableDefaultMapping(); - eq ReceiveFromMqttDefinition.suitableDefaultMapping() { + eq ReceiveTokenEndpointDefinition.suitableDefaultMapping() { String typeName = getMappingList().isEmpty() ? - getToken().getJavaTypeUse().getName() : + getToken().effectiveJavaTypeUse().getName() : getMappingList().get(0).getFromType().prettyPrint(); switch(typeName) { case "int": @@ -181,9 +163,9 @@ aspect Mappings { default: return null; } } - eq SendToMqttDefinition.suitableDefaultMapping() { + eq SendTokenEndpointDefinition.suitableDefaultMapping() { String typeName = getMappingList().isEmpty() ? - getToken().getJavaTypeUse().getName() : + getToken().effectiveJavaTypeUse().getName() : getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint(); switch(typeName) { case "int": @@ -202,15 +184,60 @@ aspect Mappings { default: return null; } } +// eq ReceiveFromRestDefinition.suitableDefaultMapping() { +// String typeName = getMappingList().isEmpty() ? +// getToken().getJavaTypeUse().getName() : +// getMappingList().get(0).getFromType().prettyPrint(); +// switch(typeName) { +// case "int": +// case "Integer": return ragconnect().defaultStringToIntMapping(); +// case "short": +// case "Short": return ragconnect().defaultStringToShortMapping(); +// case "long": +// case "Long": return ragconnect().defaultStringToLongMapping(); +// case "float": +// case "Float": return ragconnect().defaultStringToFloatMapping(); +// case "double": +// case "Double": return ragconnect().defaultStringToDoubleMapping(); +// case "char": +// case "Character": return ragconnect().defaultStringToCharMapping(); +// default: return null; +// } +// } +// eq SendToRestDefinition.suitableDefaultMapping() { +// String typeName = getMappingList().isEmpty() ? +// getToken().getJavaTypeUse().getName() : +// getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint(); +// switch(typeName) { +// case "int": +// case "Integer": return ragconnect().defaultIntToStringMapping(); +// case "short": +// case "Short": return ragconnect().defaultShortToStringMapping(); +// case "long": +// case "Long": return ragconnect().defaultLongToStringMapping(); +// case "float": +// case "Float": return ragconnect().defaultFloatToStringMapping(); +// case "double": +// case "Double": return ragconnect().defaultDoubleToStringMapping(); +// case "char": +// case "Character": return ragconnect().defaultCharToStringMapping(); +// default: return null; +// } +// } // --- isByteArray --- syn boolean MappingDefinitionType.isByteArray() = false; eq JavaArrayMappingDefinitionType.isByteArray() = getType().getName().equals("byte"); +// // --- isString --- +// syn boolean MappingDefinitionType.isString() = false; +// eq JavaMappingDefinitionType.isString() = getType().getName().equals("String"); + // --- allMappingDefinitions --- syn java.util.List<MappingDefinition> RagConnect.allMappingDefinitions() { java.util.List<MappingDefinition> result = new java.util.ArrayList<>(); getMappingDefinitionList().iterator().forEachRemaining(result::add); + // byte[] conversion result.add(defaultBytesToIntMapping()); result.add(defaultBytesToShortMapping()); result.add(defaultBytesToLongMapping()); @@ -225,6 +252,19 @@ aspect Mappings { result.add(defaultDoubleToBytesMapping()); result.add(defaultCharToBytesMapping()); result.add(defaultStringToBytesMapping()); +// // string conversion +// result.add(defaultStringToIntMapping()); +// result.add(defaultStringToShortMapping()); +// result.add(defaultStringToLongMapping()); +// result.add(defaultStringToFloatMapping()); +// result.add(defaultStringToDoubleMapping()); +// result.add(defaultStringToCharMapping()); +// result.add(defaultIntToStringMapping()); +// result.add(defaultShortToStringMapping()); +// result.add(defaultLongToStringMapping()); +// result.add(defaultFloatToStringMapping()); +// result.add(defaultDoubleToStringMapping()); +// result.add(defaultCharToStringMapping()); return result; } } diff --git a/src/main/jastadd/backend/MustacheNodesToYAML.jrag b/src/main/jastadd/backend/MustacheNodesToYAML.jrag new file mode 100644 index 0000000000000000000000000000000000000000..2894d3a8a531a190e8146121dc8e7c97f2cecc91 --- /dev/null +++ b/src/main/jastadd/backend/MustacheNodesToYAML.jrag @@ -0,0 +1,155 @@ +aspect MustacheNodesToYAML { + syn Document MRagConnect.toYAML() { + Document doc = new Document(); + MappingElement root = new MappingElement(); + root.addKeyValuePair("rootNodeName", StringElement.of(rootNodeName())); + root.addKeyValuePair("closeMethod", StringElement.of(closeMethod())); + root.addKeyValuePair("usesMqtt", ValueElement.of(usesMqtt)); + root.addKeyValuePair("usesRest", ValueElement.of(usesRest)); + // mqtt + root.addKeyValuePair("mqttHandlerField", StringElement.of(mqttHandlerField())); + root.addKeyValuePair("mqttHandlerAttribute", StringElement.of(mqttHandlerAttribute())); + root.addKeyValuePair("mqttSetupWaitUntilReadyMethod", StringElement.of(mqttSetupWaitUntilReadyMethod())); + + // rootTypeComponents + ListElement rootTypeComponents = new ListElement(); + for (MTypeComponent comp : getRootTypeComponentList()) { + MappingElement inner = new MappingElement(); + inner.addKeyValuePair("first", ValueElement.of(comp.isFirst())); + inner.addKeyValuePair("name", StringElement.of(comp.name())); + rootTypeComponents.addElement(inner); + } + root.addKeyValuePair("RootTypeComponents", rootTypeComponents); + + // rest + root.addKeyValuePair("restHandlerField", StringElement.of(restHandlerField())); + root.addKeyValuePair("restHandlerAttribute", StringElement.of(restHandlerAttribute())); + + // ReceiveDefinitions + ListElement receiveDefinitions = new ListElement(); + for (MReceiveDefinition def : getReceiveDefinitionList()) { + receiveDefinitions.addElement(def.toYAML()); + } + root.addKeyValuePair("ReceiveDefinitions", receiveDefinitions); + + // SendDefinitions + ListElement sendDefinitions = new ListElement(); + for (MSendDefinition def : getSendDefinitionList()) { + sendDefinitions.addElement(def.toYAML()); + } + root.addKeyValuePair("SendDefinitions", sendDefinitions); + + // MappingDefinitions + ListElement mappingDefinitions = new ListElement(); + for (MMappingDefinition def : getMappingDefinitionList()) { + mappingDefinitions.addElement(def.toYAML()); + } + root.addKeyValuePair("MappingDefinitions", mappingDefinitions); + + // DependencyDefinitions + ListElement dependencyDefinitions = new ListElement(); + for (MDependencyDefinition def : getDependencyDefinitionList()) { + dependencyDefinitions.addElement(def.toYAML()); + } + root.addKeyValuePair("DependencyDefinitions", dependencyDefinitions); + + // TokenComponents + ListElement tokenComponents = new ListElement(); + for (MTokenComponent comp : getTokenComponentList()) { + tokenComponents.addElement(comp.toYAML()); + } + root.addKeyValuePair("TokenComponents", tokenComponents); + + doc.addComplexElement(root); + return doc; + } + + syn MappingElement MEndpointDefinition.toYAML() { + MappingElement result = new MappingElement(); + result.addKeyValuePair("parentTypeName", StringElement.of(parentTypeName())); + result.addKeyValuePair("connectMethod", StringElement.of(connectMethod())); + result.addKeyValuePair("connectParameterName", StringElement.of(connectParameterName())); + result.addKeyValuePair("lastDefinitionToType", StringElement.of(lastDefinitionToType())); + result.addKeyValuePair("preemptiveReturn", StringElement.of(preemptiveReturn())); + result.addKeyValuePair("alwaysApply", ValueElement.of(alwaysApply())); + result.addKeyValuePair("condition", StringElement.of( + condition().replace("\"", "\\\"").replace("\n", "\\n"))); + result.addKeyValuePair("lastResult", StringElement.of(lastResult())); + result.addKeyValuePair("tokenName", StringElement.of(tokenName())); + result.addKeyValuePair("InnerMappingDefinitions", innerMappingDefinitionsAsListElement()); + return result; + } + + syn MappingElement MReceiveDefinition.toYAML() { + MappingElement result = super.toYAML(); + result.addKeyValuePair("loggingEnabledForReads", ValueElement.of(loggingEnabledForReads)); + return result; + } + + syn MappingElement MSendDefinition.toYAML() { + MappingElement result = super.toYAML(); + result.addKeyValuePair("sender", StringElement.of(sender())); + result.addKeyValuePair("lastValue", StringElement.of(lastValue())); + result.addKeyValuePair("loggingEnabledForWrites", ValueElement.of(loggingEnabledForWrites)); + result.addKeyValuePair("updateMethod", StringElement.of(updateMethod())); + result.addKeyValuePair("writeMethod", StringElement.of(writeMethod())); + result.addKeyValuePair("tokenResetMethod", StringElement.of(tokenResetMethod())); + return result; + } + + syn Element MMappingDefinition.toYAML() { + MappingElement result = new MappingElement(); + result.addKeyValuePair("toType", StringElement.of(toType())); + result.addKeyValuePair("methodName", StringElement.of(methodName())); + result.addKeyValuePair("fromType", StringElement.of(fromType())); + result.addKeyValuePair("fromVariableName", StringElement.of(fromVariableName())); + result.addKeyValuePair("content", StringElement.of( + content().replace("\"", "\\\"").replace("\n", "\\n"))); + return result; + } + + syn Element MDependencyDefinition.toYAML() { + MappingElement result = new MappingElement(); + result.addKeyValuePair("targetParentTypeName", StringElement.of(targetParentTypeName())); + result.addKeyValuePair("dependencyMethod", StringElement.of(dependencyMethod())); + result.addKeyValuePair("sourceParentTypeName", StringElement.of(sourceParentTypeName())); + result.addKeyValuePair("internalRelationPrefix", StringElement.of(internalRelationPrefix())); + return result; + } + + syn Element MTokenComponent.toYAML() { + MappingElement result = new MappingElement(); + result.addKeyValuePair("parentTypeName", StringElement.of(parentTypeName())); + result.addKeyValuePair("name", StringElement.of(name())); + result.addKeyValuePair("javaType", StringElement.of(javaType())); + result.addKeyValuePair("internalName", StringElement.of(internalName())); + ListElement dependencyDefinitions = new ListElement(); + for (MDependencyDefinition def : getDependencyDefinitionList()) { + MappingElement inner = new MappingElement(); + inner.addKeyValuePair("targetParentTypeName", StringElement.of(def.targetParentTypeName())); + inner.addKeyValuePair("internalRelationPrefix", StringElement.of(def.internalRelationPrefix())); + MappingElement targetEndpointDefinition = new MappingElement(); + targetEndpointDefinition.addKeyValuePair("updateMethod", StringElement.of(def.targetEndpointDefinition().updateMethod())); + targetEndpointDefinition.addKeyValuePair("writeMethod", StringElement.of(def.targetEndpointDefinition().writeMethod())); + inner.addKeyValuePair("targetEndpointDefinition", targetEndpointDefinition); + dependencyDefinitions.addElement(inner); + } + result.addKeyValuePair("DependencyDefinitions", dependencyDefinitions); + return result; + } + + ListElement MEndpointDefinition.innerMappingDefinitionsAsListElement() { + ListElement innerMappingDefinitions = new ListElement(); + for (MInnerMappingDefinition def : getInnerMappingDefinitionList()) { + MappingElement inner = new MappingElement(); + inner.addKeyValuePair("toType", StringElement.of(def.toType())); + inner.addKeyValuePair("methodName", StringElement.of(def.methodName())); + inner.addKeyValuePair("inputVarName", StringElement.of(def.inputVarName())); + inner.addKeyValuePair("outputVarName", StringElement.of(def.outputVarName())); + inner.addKeyValuePair("last", ValueElement.of(def.isLast())); + innerMappingDefinitions.addElement(inner); + } + return innerMappingDefinitions; + } + +} diff --git a/src/main/jastadd/parser/RagConnect.parser b/src/main/jastadd/parser/RagConnect.parser index e2685c15555056c64d4f5e8bebdc458ecea981a3..0432b0df96c44060f6df1d6918a856f62810230d 100644 --- a/src/main/jastadd/parser/RagConnect.parser +++ b/src/main/jastadd/parser/RagConnect.parser @@ -10,41 +10,35 @@ RagConnect ragconnect private Iterable<String> makeMappingDefs(ArrayList<?> raw_mapping_defs) { return () -> raw_mapping_defs.stream().map(raw -> ((Symbol) raw).value.toString()).iterator(); } + private TokenEndpointDefinition enableAlwaysApply(TokenEndpointDefinition def) { + def.setAlwaysApply(true); + return def; + } :} ; EndpointDefinition endpoint_definition - = RECEIVE ID.type_name DOT ID.token_name SCOL + = endpoint_definition_type.endpointDef SCOL {: - ReceiveFromMqttDefinition result = new ReceiveFromMqttDefinition(); - result.setToken(TokenComponent.createRef(type_name + "." + token_name)); - return result; + return endpointDef; :} - | RECEIVE ID.type_name DOT ID.token_name USING string_list.mapping_defs SCOL + | endpoint_definition_type.endpointDef USING string_list.mapping_defs SCOL {: - ReceiveFromMqttDefinition result = new ReceiveFromMqttDefinition(); - result.setToken(TokenComponent.createRef(type_name + "." + token_name)); for (String mapping_def : makeMappingDefs(mapping_defs)) { - result.addMapping(MappingDefinition.createRef(mapping_def)); + endpointDef.addMapping(MappingDefinition.createRef(mapping_def)); } - return result; - :} - | SEND ID.type_name DOT ID.token_name SCOL - {: - SendToMqttDefinition result = new SendToMqttDefinition(); - result.setToken(TokenComponent.createRef(type_name + "." + token_name)); - return result; - :} - | SEND ID.type_name DOT ID.token_name USING string_list.mapping_defs SCOL - {: - SendToMqttDefinition result = new SendToMqttDefinition(); - result.setToken(TokenComponent.createRef(type_name + "." + token_name)); - for (String mapping_def : makeMappingDefs(mapping_defs)) { - result.addMapping(MappingDefinition.createRef(mapping_def)); - } - return result; + return endpointDef; :} ; +EndpointDefinition endpoint_definition_type + = RECEIVE token_ref {: return new ReceiveTokenEndpointDefinition().setToken(token_ref); :} + | SEND token_ref {: return new SendTokenEndpointDefinition().setToken(token_ref); :} +; + +TokenComponent token_ref + = ID.type_name DOT ID.token_name {: return TokenComponent.createRef(type_name + "." + token_name); :} +; + ArrayList string_list = ID | string_list COMMA ID diff --git a/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java b/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java index 8b33412c7994b5443088de6249273b9b83374414..711169ec2ea7d9c21b48dc52c5a7d2cf4cc8b427 100644 --- a/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java +++ b/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java @@ -14,18 +14,21 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.Collection; -import java.util.MissingResourceException; -import java.util.ResourceBundle; +import java.util.*; public class Compiler extends AbstractCompiler { // private ValueOption optionOutputDir; private ValueOption optionRootNode; + private ValueOption optionProtocols; + private BooleanOption optionPrintYaml; private BooleanOption optionVerbose; private BooleanOption optionLogReads; private BooleanOption optionLogWrites; + private static final String OPTION_PROTOCOL_MQTT = "mqtt"; + private static final String OPTION_PROTOCOL_REST = "rest"; + public Compiler() { super("ragconnect", true); } @@ -51,6 +54,10 @@ public class Compiler extends AbstractCompiler { } } + if (!optionRootNode.isMatched()) { + return error("Root node not specified"); + } + RagConnect ragConnect = parseProgram(getConfiguration().getFiles()); if (!ragConnect.errors().isEmpty()) { @@ -61,25 +68,41 @@ public class Compiler extends AbstractCompiler { System.exit(1); } + if (optionPrintYaml.value()) { + ASTNode.rootNode = ragConnect.getProgram().resolveTypeDecl(optionRootNode.value()); + String yamlContent = ragConnect.toMustache().toYAML().prettyPrint(); + System.out.println(yamlContent); + writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.yml"), yamlContent); + return 0; + } + printMessage("Writing output files"); - // copy MqttHandler into outputDir - final String mqttHandlerFileName = "MqttHandler.jadd"; - try { - InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(mqttHandlerFileName); - if (inputStream == null) { - throw new CompilerException("Could not open " + mqttHandlerFileName); + final List<String> handlers = new ArrayList<>(); + if (ASTNode.usesMqtt) { + handlers.add("MqttHandler.jadd"); + } + if (ASTNode.usesRest) { + handlers.add("RestHandler.jadd"); + } + // copy handlers into outputDir + for (String handlerFileName : handlers) { + try { + InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(handlerFileName); + if (inputStream == null) { + throw new CompilerException("Could not open " + handlerFileName); + } + Files.copy(inputStream, + getConfiguration().outputDir().toPath().resolve(handlerFileName), + StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new CompilerException("Could not copy " + handlerFileName, e); } - Files.copy(inputStream, - getConfiguration().outputDir().toPath().resolve(mqttHandlerFileName), - StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new CompilerException("Could not copy " + mqttHandlerFileName, e); } for (GrammarFile grammarFile : ragConnect.getProgram().getGrammarFileList()) { Path outputFile = getConfiguration().outputDir().toPath().resolve(grammarFile.getFileName()); - sendToFile(outputFile, grammarFile.generateAbstractGrammar()); + writeToFile(outputFile, grammarFile.generateAbstractGrammar()); } - sendToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), ragConnect.generateAspect(optionRootNode.value())); + writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), ragConnect.generateAspect(optionRootNode.value())); return 0; } @@ -89,8 +112,8 @@ public class Compiler extends AbstractCompiler { try { new Compiler().run(args); } catch (CompilerException e) { - System.err.println(e.getMessage()); - System.exit(-1); + e.printStackTrace(); + System.exit(1); } } @@ -118,7 +141,7 @@ public class Compiler extends AbstractCompiler { System.out.println(message); } - private void sendToFile(Path path, String str) throws CompilerException { + private void writeToFile(Path path, String str) throws CompilerException { try (BufferedWriter writer = Files.newBufferedWriter(path)) { writer.append(str); } catch (Exception e) { @@ -132,6 +155,15 @@ public class Compiler extends AbstractCompiler { new ValueOption("rootNode", "root node in the base grammar.") .acceptAnyValue() .needsValue(true)); + optionProtocols = addOption( + new ValueOption("protocols", "Protocols to enable") + .acceptMultipleValues(true) + .addDefaultValue(OPTION_PROTOCOL_MQTT, "Enable MQTT") + .addAcceptedValue(OPTION_PROTOCOL_REST, "Enable REST") + ); + optionPrintYaml = addOption( + new BooleanOption("printYaml", "Print out YAML instead of generating files") + .defaultValue(false)); optionVerbose = addOption( new BooleanOption("verbose", "Print more messages while compiling.") .defaultValue(false)); @@ -145,6 +177,8 @@ public class Compiler extends AbstractCompiler { private RagConnect parseProgram(Collection<String> files) throws CompilerException { Program program = new Program(); + boolean atLeastOneGrammar = false; + boolean atLeastOneRagConnect = false; RagConnect ragConnect = new RagConnect(); ragConnect.setProgram(program); @@ -160,20 +194,34 @@ public class Compiler extends AbstractCompiler { case "relast": // processGrammar parseGrammar(program, filename); + atLeastOneGrammar = true; break; case "connect": + case "ragconnect": // process ragConnect RagConnect parsedRagConnect = parseRagConnect(program, filename); mergeRagConnectDefinitions(ragConnect, parsedRagConnect); + atLeastOneRagConnect = true; break; default: throw new CompilerException("Unknown file extension in " + filename); } } + if (!atLeastOneGrammar) { + System.err.println("No grammar file specified! (*.ast, *.relast)"); + } + if (!atLeastOneRagConnect) { + System.err.println("No ragconnect file specified! (*.connect, *.ragconnect)"); + } + if (!atLeastOneGrammar && !atLeastOneRagConnect) { + System.exit(1); + } ragConnect.treeResolveAll(); ragConnect.additionalRelations().forEach(ragConnectGrammarPart::addDeclaration); ASTNode.loggingEnabledForReads = optionLogReads.value(); ASTNode.loggingEnabledForWrites = optionLogWrites.value(); + ASTNode.usesMqtt = optionProtocols.hasValue(OPTION_PROTOCOL_MQTT); + ASTNode.usesRest = optionProtocols.hasValue(OPTION_PROTOCOL_REST); return ragConnect; } diff --git a/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java b/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java index ac1109fef423ed58ebbbdfcaeacbdca42745e341..ee9ed046348efeaa4b12dbc816c55414d93289cf 100644 --- a/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java +++ b/src/main/java/org/jastadd/ragconnect/compiler/SimpleMain.java @@ -1,15 +1,11 @@ package org.jastadd.ragconnect.compiler; -import beaver.Parser; import org.jastadd.ragconnect.ast.*; import org.jastadd.ragconnect.parser.RagConnectParser; import org.jastadd.ragconnect.scanner.RagConnectScanner; -import java.io.BufferedReader; -import java.io.IOException; -import java.nio.ByteBuffer; +import java.io.Reader; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; /** @@ -19,141 +15,87 @@ import java.nio.file.Paths; */ public class SimpleMain { - // --- just testing byte[] conversion --- - public static void testing() { - byte[] bytes; - int i = 1; - double d = 2.3d; - float f = 4.2f; - short sh = 13; - long l = 7L; - String s = "Hello"; - char c = 'a'; - - Integer ii = Integer.valueOf(1); - if (!ii.equals(i)) throw new AssertionError("Ints not equal"); - - // int to byte - ByteBuffer i2b = ByteBuffer.allocate(4); - i2b.putInt(i); - bytes = i2b.array(); - - // byte to int - ByteBuffer b2i = ByteBuffer.wrap(bytes); - int actual_i = b2i.getInt(); - if (i != actual_i) throw new AssertionError("Ints not equal"); - - // double to byte - ByteBuffer d2b = ByteBuffer.allocate(8); - d2b.putDouble(d); - bytes = d2b.array(); - - // byte to double - ByteBuffer b2d = ByteBuffer.wrap(bytes); - double actual_d = b2d.getDouble(); - if (d != actual_d) throw new AssertionError("Doubles not equal"); - - // float to byte - ByteBuffer f2b = ByteBuffer.allocate(4); - f2b.putFloat(f); - bytes = f2b.array(); - - // byte to float - ByteBuffer b2f = ByteBuffer.wrap(bytes); - float actual_f = b2f.getFloat(); - if (f != actual_f) throw new AssertionError("Floats not equal"); - - // short to byte - ByteBuffer sh2b = ByteBuffer.allocate(2); - sh2b.putShort(sh); - bytes = sh2b.array(); - - // byte to short - ByteBuffer b2sh = ByteBuffer.wrap(bytes); - short actual_sh = b2sh.getShort(); - if (sh != actual_sh) throw new AssertionError("Shorts not equal"); - - // long to byte - ByteBuffer l2b = ByteBuffer.allocate(8); - l2b.putLong(l); - bytes = l2b.array(); - - // byte to long - ByteBuffer b2l = ByteBuffer.wrap(bytes); - long actual_l = b2l.getLong(); - if (l != actual_l) throw new AssertionError("Longs not equal"); - - // String to byte - bytes = s.getBytes(); - - // byte to String - String actual_s = new String(bytes); - if (!s.equals(actual_s)) throw new AssertionError("Strings not equal"); - - // char to byte - ByteBuffer c2b = ByteBuffer.allocate(2); - c2b.putChar(c); - bytes = c2b.array(); - - // byte to char - ByteBuffer b2c = ByteBuffer.wrap(bytes); - char actual_c = b2c.getChar(); - if (c != actual_c) throw new AssertionError("Floats not equal"); - } - - public static void main(String[] args) { -// testing(); - createManualAST(); - } - - private static void createManualAST() { - RagConnect model = new RagConnect(); - Program program = parseProgram(Paths.get("ros2rag.starter","src", "main", "jastadd", "RobotModel.relast")); - model.setProgram(program); - - MappingDefinition mappingDefinition = new MappingDefinition(); - mappingDefinition.setID("PoseToPosition"); - mappingDefinition.setFromType(makeMappingDefinitionType("int")); - mappingDefinition.setFromVariableName("x"); - mappingDefinition.setToType(makeMappingDefinitionType("Position")); - mappingDefinition.setContent(" pose.position.x += sqrt(.5 * size.x)\n" + - " MAP round(2)\n" + - " x = x / 100\n" + - " IGNORE_IF_SAME\n" + - " ;"); - model.addMappingDefinition(mappingDefinition); - - ReceiveFromMqttDefinition receiveFromMqttDefinition = new ReceiveFromMqttDefinition(); - receiveFromMqttDefinition.setAlwaysApply(false); - receiveFromMqttDefinition.setToken(TokenComponent.createRef("Link.CurrentPosition")); - receiveFromMqttDefinition.addMapping(mappingDefinition); - model.addEndpointDefinition(receiveFromMqttDefinition); - - model.treeResolveAll(); - for (ErrorMessage error : model.errors()) { - System.err.println(error); - } - - System.out.println(model.generateAspect("Model")); + private static void printManualYAML() { + Document doc = new Document(); + KeyValuePair root = new KeyValuePair(); + root.setKey("panda_mqtt_connector"); + MappingElement firstLevel = new MappingElement(); + firstLevel.addKeyValuePair(new KeyValuePair("server", new StringElement("tcp://localhost:1883"))); + firstLevel.addKeyValuePair(new KeyValuePair("robot_speed_factor", new ValueElement(".7"))); + + KeyValuePair topics = new KeyValuePair(); + topics.setKey("topics"); + MappingElement theTopics = new MappingElement(); + theTopics.addKeyValuePair(new KeyValuePair("robotConfig", new StringElement("robotconfig"))); + theTopics.addKeyValuePair(new KeyValuePair("trajectory", new StringElement("trajectory"))); + theTopics.addKeyValuePair(new KeyValuePair("nextStep", new StringElement("ros2rag/nextStep"))); + topics.setValue(theTopics); + firstLevel.addKeyValuePair(topics); + + firstLevel.addKeyValuePair(new KeyValuePair("zone_size", new ValueElement("0.5"))); + + KeyValuePair zones = new KeyValuePair(); + zones.setKey("zones"); + ListElement theZones = new ListElement(); + theZones.addElement(new StringElement("1 1")); + theZones.addElement(new StringElement("0 1")); + theZones.addElement(new StringElement("-1 1")); + zones.setValue(theZones); + firstLevel.addKeyValuePair(zones); + + KeyValuePair parts = new KeyValuePair(); + parts.setKey("end_effectors"); + KeyValuePair pandaParts = new KeyValuePair(); + pandaParts.setKey("panda"); + MappingElement thePanda = new MappingElement(); + thePanda.addKeyValuePair(new KeyValuePair("Link0", new StringElement("panda_link0"))); + thePanda.addKeyValuePair(new KeyValuePair("Link1", new StringElement("panda_link1"))); + thePanda.addKeyValuePair(new KeyValuePair("Link2", new StringElement("panda_link2"))); + thePanda.addKeyValuePair(new KeyValuePair("Link3", new StringElement("panda_link3"))); + thePanda.addKeyValuePair(new KeyValuePair("Link4", new StringElement("panda_link4"))); + thePanda.addKeyValuePair(new KeyValuePair("Link5", new StringElement("panda_link5"))); + thePanda.addKeyValuePair(new KeyValuePair("Link6", new StringElement("panda_link6"))); + thePanda.addKeyValuePair(new KeyValuePair("RightFinger", new StringElement("panda_rightfinger"))); + thePanda.addKeyValuePair(new KeyValuePair("LeftFinger", new StringElement("panda_leftfinger"))); + pandaParts.setValue(thePanda); + parts.setValue(pandaParts); + firstLevel.addKeyValuePair(parts); + + KeyValuePair end_effectors = new KeyValuePair(); + end_effectors.setKey("end_effectors"); + KeyValuePair endEffectorParts = new KeyValuePair(); + endEffectorParts.setKey("panda"); + endEffectorParts.setValue(new KeyValuePair("EndEffector", new StringElement("panda_hand"))); + end_effectors.setValue(endEffectorParts); + firstLevel.addKeyValuePair(end_effectors); + + KeyValuePair goalPoses = new KeyValuePair(); + goalPoses.setKey("goal_poses"); + ListElement theGoalPoses = new ListElement(); + addPose(theGoalPoses, "0.4 0.4 0.3"); + addPose(theGoalPoses, "-0.4 0.4 0.3"); + addPose(theGoalPoses, "-0.4 -0.4 0.3"); + addPose(theGoalPoses, "0.4 0.4 0.3"); + addPose(theGoalPoses, "-0.4 0.4 0.3"); + addPose(theGoalPoses, "0.4 0.4 0.3"); + goalPoses.setValue(theGoalPoses); + firstLevel.addKeyValuePair(goalPoses); + + root.setValue(firstLevel); + doc.addComplexElement(root); + + System.out.println(doc.prettyPrint()); } - private static MappingDefinitionType makeMappingDefinitionType(String type) { - JavaMappingDefinitionType result = new JavaMappingDefinitionType(); - result.setType(new SimpleJavaTypeUse(type)); - return result; + private static void addPose(ListElement theGoalPoses, String position) { + MappingElement goalPose1 = new MappingElement(); + goalPose1.addKeyValuePair(new KeyValuePair("position", new StringElement(position))); + goalPose1.addKeyValuePair(new KeyValuePair("orientation", new StringElement("1 1 0 0"))); + goalPose1.addKeyValuePair(new KeyValuePair("work", new StringElement("20000"))); + theGoalPoses.addElement(goalPose1); } - private static Program parseProgram(Path path) { - try (BufferedReader reader = Files.newBufferedReader(path)) { - RagConnectScanner scanner = new RagConnectScanner(reader); - RagConnectParser parser = new RagConnectParser(); - GrammarFile grammarFile = (GrammarFile) parser.parse(scanner); - Program program = new Program(); - program.addGrammarFile(grammarFile); - return program; - } catch (IOException | Parser.Exception e) { - e.printStackTrace(); - } - return null; + public static void main(String[] args) { + printManualYAML(); } } diff --git a/src/main/resources/MqttHandler.jadd b/src/main/resources/MqttHandler.jadd index 493d8e4452e81c5dbfa45a522a6366345908cdcc..6f40612520cfb97151600dff53c8e0d384ab441e 100644 --- a/src/main/resources/MqttHandler.jadd +++ b/src/main/resources/MqttHandler.jadd @@ -1,4 +1,73 @@ -aspect MqttHandler { +import java.io.IOException; +import java.util.concurrent.TimeUnit;aspect MqttHandler { +public class MqttServerHandler { + private final java.util.Map<String, MqttHandler> handlers = new java.util.HashMap<>(); + private long time; + private java.util.concurrent.TimeUnit unit; + private String name; + + public MqttServerHandler() { + this("RagConnect"); + } + + public MqttServerHandler(String name) { + this.name = name; + setupWaitUntilReady(1, TimeUnit.SECONDS); + } + + public void setupWaitUntilReady(long time, java.util.concurrent.TimeUnit unit) { + this.time = time; + this.unit = unit; + } + + public MqttHandler resolveHandler(java.net.URI uri) throws IOException { + MqttHandler handler = handlers.get(uri.getHost()); + if (handler == null) { + // first connect to that server + handler = new MqttHandler(); + if (uri.getPort() == -1) { + handler.setHost(uri.getHost()); + } else { + handler.setHost(uri.getHost(), uri.getPort()); + } + handlers.put(uri.getHost(), handler); + } + handler.waitUntilReady(this.time, this.unit); + return handler; + } + + public boolean newConnection(java.net.URI uri, java.util.function.Consumer<byte[]> callback) throws IOException { + return resolveHandler(uri).newConnection(extractTopic(uri), callback); + } + + public void publish(java.net.URI uri, byte[] bytes) throws IOException { + resolveHandler(uri).publish(extractTopic(uri), bytes); + } + + public void publish(java.net.URI uri, byte[] bytes, boolean retain) throws IOException { + resolveHandler(uri).publish(extractTopic(uri), bytes, retain); + } + + public void publish(java.net.URI uri, byte[] bytes, + org.fusesource.mqtt.client.QoS qos, boolean retain) throws IOException { + resolveHandler(uri).publish(extractTopic(uri), bytes, qos, retain); + } + + private String extractTopic(java.net.URI uri) { + String path = uri.getPath(); + if (path.charAt(0) == '/') { + path = path.substring(1); + } + return path; + } + + public void close() { + for (MqttHandler handler : handlers.values()) { + handler.close(); + } + } + +} /** * Helper class to receive updates via MQTT and use callbacks to handle those messages. * @@ -14,10 +83,8 @@ public class MqttHandler { private java.net.URI host; /** The connection to the MQTT broker. */ private org.fusesource.mqtt.client.CallbackConnection connection; - /** Whether we are subscribed to the topics yet */ - private final java.util.concurrent.locks.Condition readyCondition; - private final java.util.concurrent.locks.Lock readyLock; - private boolean ready; + /** Whether we are connected yet */ + private final java.util.concurrent.CountDownLatch readyLatch; private boolean sendWelcomeMessage = true; private org.fusesource.mqtt.client.QoS qos; /** Dispatch knowledge */ @@ -31,9 +98,7 @@ public class MqttHandler { this.name = java.util.Objects.requireNonNull(name, "Name must be set"); this.logger = org.apache.logging.log4j.LogManager.getLogger(MqttHandler.class); this.callbacks = new java.util.HashMap<>(); - this.readyLock = new java.util.concurrent.locks.ReentrantLock(); - this.readyCondition = readyLock.newCondition(); - this.ready = false; + this.readyLatch = new java.util.concurrent.CountDownLatch(1); this.qos = org.fusesource.mqtt.client.QoS.AT_LEAST_ONCE; } @@ -83,7 +148,7 @@ public class MqttHandler { String topicString = topic.toString(); java.util.List<java.util.function.Consumer<byte[]>> callbackList = callbacks.get(topicString); if (callbackList == null || callbackList.isEmpty()) { - logger.debug("Got a message, but no callback to call. Forgot to unsubscribe?"); + logger.debug("Got a message, but no callback to call. Forgot to subscribe?"); } else { byte[] message = body.toByteArray(); // System.out.println("message = " + Arrays.toString(message)); @@ -144,13 +209,7 @@ public class MqttHandler { } private void setReady() { - try { - readyLock.lock(); - ready = true; - readyCondition.signalAll(); - } finally { - readyLock.unlock(); - } + readyLatch.countDown(); } private void throwIf(java.util.concurrent.atomic.AtomicReference<Throwable> error) throws java.io.IOException { @@ -163,12 +222,15 @@ public class MqttHandler { this.qos = qos; } - public void newConnection(String topic, java.util.function.Consumer<byte[]> callback) { - if (!ready) { - // should maybe be something more kind than throwing an exception here - throw new IllegalStateException("Updater not ready"); + public boolean newConnection(String topic, java.util.function.Consumer<byte[]> callback) { + if (readyLatch.getCount() > 0) { + System.err.println("Handler not ready"); + return false; +// // should maybe be something more kind than throwing an exception here +// throw new IllegalStateException("Updater not ready"); } // register callback + logger.debug("new connection for {}", topic); if (callbacks.get(topic) == null) { callbacks.put(topic, new java.util.ArrayList<>()); @@ -189,6 +251,7 @@ public class MqttHandler { }); } callbacks.get(topic).add(callback); + return true; } /** @@ -202,15 +265,9 @@ public class MqttHandler { */ public boolean waitUntilReady(long time, java.util.concurrent.TimeUnit unit) { try { - readyLock.lock(); - if (ready) { - return true; - } - return readyCondition.await(time, unit); + return readyLatch.await(time, unit); } catch (InterruptedException e) { e.printStackTrace(); - } finally { - readyLock.unlock(); } return false; } @@ -253,7 +310,7 @@ public class MqttHandler { @Override public void onFailure(Throwable value) { - logger.warn("Could not publish on topic '{}'", topic); + logger.warn("Could not publish on topic '{}'", topic, value); } }); }); diff --git a/src/main/resources/RestHandler.jadd b/src/main/resources/RestHandler.jadd new file mode 100644 index 0000000000000000000000000000000000000000..f7b1a83304fe7aaebe97d105514c3f192d052aec --- /dev/null +++ b/src/main/resources/RestHandler.jadd @@ -0,0 +1,140 @@ +import java.util.concurrent.TimeUnit;aspect RestHandler { +public class RestServerHandler { + private static final int DEFAULT_PORT = 4567; + private final java.util.Map<Integer, RestHandler> handlers = new java.util.HashMap<>(); + private String name; + + public RestServerHandler() { + this("RagConnect"); + } + + public RestServerHandler(String name) { + this.name = name; + } + + private RestHandler resolveHandler(java.net.URI uri) { + int port = uri.getPort() != -1 ? uri.getPort() : DEFAULT_PORT; + RestHandler handler = handlers.get(port); + if (handler == null) { + // first connect to that server + handler = new RestHandler(); + handler.setPort(port); + handlers.put(port, handler); + } + return handler; + } + + public boolean newPUTConnection(java.net.URI uri, java.util.function.Consumer<String> callback) { + resolveHandler(uri).newPUTConnection(uri.getPath(), callback); + return true; + } + + public boolean newGETConnection(java.net.URI uri, SupplierWithException<String> supplier) { + resolveHandler(uri).newGETConnection(uri.getPath(), supplier); + return true; + } + + public void close() { + for (RestHandler handler : handlers.values()) { + handler.close(); + } + } +} +/** + * Helper class to receive updates and publishes information via REST. + * @author rschoene - Initial contribution + */ +public class RestHandler { + private static final int DEFAULT_PORT = 4567; + + private final org.apache.logging.log4j.Logger logger; + private final String name; + private int port; + private final java.util.concurrent.CountDownLatch exitCondition; + /** Dispatch knowledge */ + private final java.util.Map<String, java.util.List<java.util.function.Consumer<String>>> callbacks; + private final java.util.Map<String, SupplierWithException<String>> suppliers; + + public RestHandler() { + this("RagConnect"); + } + + public RestHandler(String name) { + this.logger = org.apache.logging.log4j.LogManager.getLogger(RestHandler.class); + this.name = name; + this.port = DEFAULT_PORT; + this.exitCondition = new java.util.concurrent.CountDownLatch(1); + this.callbacks = new java.util.HashMap<>(); + this.suppliers = new java.util.HashMap<>(); + } + + public RestHandler setPort(int port) { + this.port = port; + start(); + return this; + } + + public void newPUTConnection(String path, java.util.function.Consumer<String> callback) { + if (callbacks.containsKey(path)) { + callbacks.get(path).add(callback); + } else { + // setup path + java.util.List<java.util.function.Consumer<String>> callbackList = new java.util.ArrayList<>(); + callbackList.add(callback); + callbacks.put(path, callbackList); + spark.Spark.put(path, (request, response) -> { + String content = request.body(); + java.util.Set<String> errors = new java.util.HashSet<>(); + for (java.util.function.Consumer<String> f : callbackList) { + try { + f.accept(content); + } catch (Exception e) { + errors.add(e.getMessage()); + } + } + if (errors.isEmpty()) { + return "OK"; + } else { + return makeError(response, 500, errors.stream().collect(java.util.stream.Collectors.joining("\n", "The following error(s) happened: [", "]"))); + } + }); + } + } + + public void newGETConnection(String path, SupplierWithException<String> supplier) { + if (suppliers.get(path) != null) { + logger.warn("Overriding existing supplier for '{}'", path); + } + suppliers.put(path, supplier); + spark.Spark.get(path, (request, response) -> { + try { + return supplier.get(); + } catch (Exception e) { + return makeError(response, 500, e.getMessage()); + } + }); + } + + private String makeError(spark.Response response, int statusCode, String message) { + response.status(statusCode); + return message; + } + + private void start() { + logger.info("Starting REST server at {}", this.port); + spark.Spark.port(this.port); + spark.Spark.init(); + spark.Spark.awaitInitialization(); + } + + public void close() { + spark.Spark.stop(); + spark.Spark.awaitStop(); + } + +} +@FunctionalInterface +public interface SupplierWithException<T> { + public T get() throws Exception; +} +} diff --git a/src/main/resources/dependencyDefinition.mustache b/src/main/resources/dependencyDefinition.mustache index b8d74ffe8f8aa5aaa8979682e4442215af1974e8..0ab8d55242816a7d3ada0491c8c7db6dcd70ee42 100644 --- a/src/main/resources/dependencyDefinition.mustache +++ b/src/main/resources/dependencyDefinition.mustache @@ -1,3 +1,3 @@ - public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) { - add{{internalRelationPrefix}}Source(source); - } +public void {{targetParentTypeName}}.{{dependencyMethod}}({{sourceParentTypeName}} source) { + add{{internalRelationPrefix}}Source(source); +} diff --git a/src/main/resources/handleUri.mustache b/src/main/resources/handleUri.mustache new file mode 100644 index 0000000000000000000000000000000000000000..1a20e7c4df2a4c54ccba4c825fff55ca89a51dec --- /dev/null +++ b/src/main/resources/handleUri.mustache @@ -0,0 +1,11 @@ +String scheme,host, path; +java.net.URI uri; +try { + uri = new java.net.URI({{connectParameterName}}); + scheme = uri.getScheme(); + host = uri.getHost(); + path = uri.getPath(); +} catch (java.net.URISyntaxException e) { + System.err.println(e.getMessage()); // Maybe re-throw error? + return false; +} diff --git a/src/main/resources/handler.mustache b/src/main/resources/handler.mustache new file mode 100644 index 0000000000000000000000000000000000000000..e89c451007ff309a77297b9b66fe0e20dece73b8 --- /dev/null +++ b/src/main/resources/handler.mustache @@ -0,0 +1,7 @@ +aspect RagConnectHandler { +interface RagConnectHandler<T> { + boolean connectReceive(String path, java.util.function.Consumer<T> callback); + boolean sendPush(String path, T value); + boolean connectSendPull(String path, SupplierWithException<T> supplier); +} +} diff --git a/src/main/resources/mappingApplication.mustache b/src/main/resources/mappingApplication.mustache index 95ce2fe29329d87f880497b37697d50c8c0687be..064004b94886c155be883e7b186557c6fa87d38c 100644 --- a/src/main/resources/mappingApplication.mustache +++ b/src/main/resources/mappingApplication.mustache @@ -1,7 +1,7 @@ -{{lastDefinitionToType}} {{resultVarPrefix}}{{lastDefinitionName}}; +{{lastDefinitionToType}} {{lastResult}}; try { {{#InnerMappingDefinitions}} - {{^last}}{{ToType}} {{/last}}{{resultVarPrefix}}{{methodName}} = {{methodName}}({{inputVarName}});{{!inputVarName has to be computed beforehand}} + {{^last}}{{toType}} {{/last}}{{outputVarName}} = {{methodName}}({{inputVarName}}); {{/InnerMappingDefinitions}} } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/resources/mappingDefinition.mustache b/src/main/resources/mappingDefinition.mustache index 920c5a610b0ce0cff1687122c97b6e18ce63bf7f..fe26e949b7a8e446bf97947e1632624741a83726 100644 --- a/src/main/resources/mappingDefinition.mustache +++ b/src/main/resources/mappingDefinition.mustache @@ -1,3 +1,3 @@ - protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception { - {{{content}}}{{!maybe print line by line to get better indentation}} - } +protected static {{toType}} ASTNode.{{methodName}}({{fromType}} {{fromVariableName}}) throws Exception { + {{{content}}}{{!maybe print line by line to get better indentation}} +} diff --git a/src/main/resources/mqtt.mustache b/src/main/resources/mqtt.mustache index dbdb85e7954dbf50ae7e150ffc43dd4cbda0fd80..9391d1091c41eee257f9e6a1368297ef743e39ce 100644 --- a/src/main/resources/mqtt.mustache +++ b/src/main/resources/mqtt.mustache @@ -1,23 +1,16 @@ aspect MQTT { - private String {{rootNodeName}}.MqttName() { return "Ros2Rag"; } - private MqttHandler {{rootNodeName}}.{{mqttHandlerField}} = new MqttHandler(MqttName()); - public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host) throws java.io.IOException { - {{mqttHandlerField}}.setHost(host); - } - public void {{rootNodeName}}.{{mqttSetHostMethod}}(String host, int port) throws java.io.IOException { - {{mqttHandlerField}}.setHost(host, port); - } - - public boolean {{rootNodeName}}.{{mqttWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) { - return {{mqttHandlerField}}.waitUntilReady(time, unit); - } + private String {{rootNodeName}}.MqttName() { return "RagConnectMQTT"; } + private MqttServerHandler {{rootNodeName}}.{{mqttHandlerField}} = new MqttServerHandler(MqttName()); - public void {{rootNodeName}}.{{mqttCloseMethod}}() { - {{mqttHandlerField}}.close(); + public void {{rootNodeName}}.{{mqttSetupWaitUntilReadyMethod}}(long time, java.util.concurrent.TimeUnit unit) { + {{mqttHandlerField}}.setupWaitUntilReady(time, unit); } - {{#getRootTypeComponents}} - {{#first}}inh MqttHandler ASTNode.{{mqttHandlerAttribute}}();{{/first}} + {{#RootTypeComponents}} + {{#first}}inh MqttServerHandler ASTNode.{{mqttHandlerAttribute}}();{{/first}} eq {{rootNodeName}}.get{{name}}().{{mqttHandlerAttribute}}() = {{mqttHandlerField}}; - {{/getRootTypeComponents}} + {{/RootTypeComponents}} + {{^RootTypeComponents}} + syn MqttServerHandler {{rootNodeName}}.{{mqttHandlerAttribute}}() = {{mqttHandlerField}}; + {{/RootTypeComponents}} } diff --git a/src/main/resources/ragconnect.mustache b/src/main/resources/ragconnect.mustache index 0b455283977cf812a47b5266ee593c6597e97cc9..a39510d73854301efb1f05fe7f9c1fd476916ef7 100644 --- a/src/main/resources/ragconnect.mustache +++ b/src/main/resources/ragconnect.mustache @@ -1,22 +1,27 @@ -{{> mqtt}} +{{#usesMqtt}}{{> mqtt}}{{/usesMqtt}} +{{#usesRest}}{{> rest}}{{/usesRest}} aspect ROS2RAG { + public void {{rootNodeName}}.{{closeMethod}}() { + {{#usesMqtt}}{{mqttHandlerField}}.close();{{/usesMqtt}} + {{#usesRest}}{{restHandlerField}}.close();{{/usesRest}} + } {{#ReceiveDefinitions}} - {{> receiveDefinition}} + {{> receiveDefinition}} {{/ReceiveDefinitions}} {{#SendDefinitions}} - {{> sendDefinition}} + {{> sendDefinition}} {{/SendDefinitions}} {{#MappingDefinitions}} - {{> mappingDefinition}} + {{> mappingDefinition}} {{/MappingDefinitions}} {{#DependencyDefinitions}} - {{> dependencyDefinition}} + {{> dependencyDefinition}} {{/DependencyDefinitions}} {{#TokenComponents}} - {{> tokenComponent}} + {{> tokenComponent}} {{/TokenComponents}} } diff --git a/src/main/resources/receiveDefinition.mustache b/src/main/resources/receiveDefinition.mustache index e2df0433fe82b37df2c016887da52a11cfd2692b..1dd0e0ddbfbfd2acf03536ff41a2d740043eb34b 100644 --- a/src/main/resources/receiveDefinition.mustache +++ b/src/main/resources/receiveDefinition.mustache @@ -1,9 +1,23 @@ - public void {{parentTypeName}}.{{connectMethod}}(String topic) { - {{mqttHandlerAttribute}}().newConnection(topic, message -> { - {{> mappingApplication}} - {{#loggingEnabledForReads}} - System.out.println("[Receive] " + topic + " -> {{tokenName}} = " + {{lastResult}});{{!lastResult has to be a new attribute}} - {{/loggingEnabledForReads}} - set{{tokenName}}({{lastResult}}); +public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}) throws java.io.IOException { + {{>handleUri}} + java.util.function.Consumer<byte[]> consumer = message -> { + {{> mappingApplication}} + {{#loggingEnabledForReads}} + System.out.println("[Receive] " + {{connectParameterName}} + " -> {{tokenName}} = " + {{lastResult}}); + {{/loggingEnabledForReads}} + set{{tokenName}}({{lastResult}}); + }; + switch (scheme) { + {{#usesMqtt}} + case "mqtt": return {{mqttHandlerAttribute}}().newConnection(uri, consumer); + {{/usesMqtt}} + {{#usesRest}} + case "rest": return {{restHandlerAttribute}}().newPUTConnection(uri, input -> { + consumer.accept(input.getBytes()); }); + {{/usesRest}} + default: + System.err.println("Unknown protocol '" + scheme + "'."); + return false; } +} diff --git a/src/main/resources/rest.mustache b/src/main/resources/rest.mustache new file mode 100644 index 0000000000000000000000000000000000000000..6bbde2be4d26e519635c85a6f39f9a18514bfb19 --- /dev/null +++ b/src/main/resources/rest.mustache @@ -0,0 +1,12 @@ +aspect REST { + private String {{rootNodeName}}.RestName() { return "RagConnectREST"; } + private RestServerHandler {{rootNodeName}}.{{restHandlerField}} = new RestServerHandler(RestName()); + + {{#getRootTypeComponents}} + {{#first}}inh RestServerHandler ASTNode.{{restHandlerAttribute}}();{{/first}} + eq {{rootNodeName}}.get{{name}}().{{restHandlerAttribute}}() = {{restHandlerField}}; + {{/getRootTypeComponents}} + {{^getRootTypeComponents}} + syn RestServerHandler {{rootNodeName}}.{{restHandlerAttribute}}() = {{restHandlerField}}; + {{/getRootTypeComponents}} +} diff --git a/src/main/resources/sendDefinition.mustache b/src/main/resources/sendDefinition.mustache index 477dbc550132af842e53e8fed2cbbee0c8996296..fac4ecd37967b8047c9101b58fab273527979ab2 100644 --- a/src/main/resources/sendDefinition.mustache +++ b/src/main/resources/sendDefinition.mustache @@ -1,24 +1,51 @@ - private String {{parentTypeName}}.{{sendTopic}} = null; - private byte[] {{parentTypeName}}.{{lastValue}} = null; +private Runnable {{parentTypeName}}.{{sender}} = null; +private byte[] {{parentTypeName}}.{{lastValue}} = null; - public void {{parentTypeName}}.{{connectMethod}}(String topic, boolean writeCurrentValue) { - {{sendTopic}} = topic; - {{updateMethod}}(); - if (writeCurrentValue) { - {{writeMethod}}(); - } +public boolean {{parentTypeName}}.{{connectMethod}}(String {{connectParameterName}}, boolean writeCurrentValue) { + {{>handleUri}} + switch (scheme) { + {{#usesMqtt}} + case "mqtt": + // MqttHandler handler = {{mqttHandlerAttribute}}().resolveHandler(uri);{{!optimize later}} + {{sender}} = () -> { + {{#loggingEnabledForWrites}} + System.out.println("[Send] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{connectParameterName}}); + {{/loggingEnabledForWrites}} + try { + {{mqttHandlerAttribute}}().publish(uri, {{lastValue}}); + } catch (java.io.IOException e) { + e.printStackTrace(); + } + }; + {{updateMethod}}(); + if (writeCurrentValue) { + {{writeMethod}}(); + } + break; + {{/usesMqtt}} + {{#usesRest}} + case "rest": + {{restHandlerAttribute}}().newGETConnection(uri, () -> { + {{updateMethod}}(); + return new String({{lastValue}}); + }); + break; + {{/usesRest}} + default: + System.err.println("Unknown protocol '" + scheme + "'."); + return false; } + return true; +} - protected boolean {{parentTypeName}}.{{updateMethod}}() { - {{tokenResetMethod}}(); - {{> mappingApplication}} - {{lastValue}} = {{lastResult}}; - return true; - } +protected boolean {{parentTypeName}}.{{updateMethod}}() { + {{tokenResetMethod}}(); + {{> mappingApplication}} + {{lastValue}} = {{lastResult}}; + // normally we would return true here. unless no connect method was called so far to initialize {{sender}} yet + return {{sender}} != null; +} - protected void {{parentTypeName}}.{{writeMethod}}() { - {{#loggingEnabledForWrites}} - System.out.println("[Send] {{tokenName}} = " + get{{tokenName}}() + " -> " + {{sendTopic}}); - {{/loggingEnabledForWrites}} - {{mqttHandlerAttribute}}().publish({{sendTopic}}, {{lastValue}}); - } +protected void {{parentTypeName}}.{{writeMethod}}() { + {{sender}}.run(); +} diff --git a/src/main/resources/tokenComponent.mustache b/src/main/resources/tokenComponent.mustache index e6a136d1c8de08b6dce9104fd46fafa5d91b67b6..e78efb96b846bd7aa814723472032299a70c7de3 100644 --- a/src/main/resources/tokenComponent.mustache +++ b/src/main/resources/tokenComponent.mustache @@ -1,17 +1,19 @@ - public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) { - set{{internalName}}(value); - {{#DependencyDefinitions}} - for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) { - {{#targetEndpointDefinition}} - if (target.{{updateMethod}}()) { - target.{{writeMethod}}(); - } - {{/targetEndpointDefinition}} +public {{parentTypeName}} {{parentTypeName}}.set{{name}}({{javaType}} value) { + set{{internalName}}(value); + {{#DependencyDefinitions}} + for ({{targetParentTypeName}} target : get{{internalRelationPrefix}}TargetList()) { + {{#targetEndpointDefinition}} + {{!#isPush}} + if (target.{{updateMethod}}()) { + target.{{writeMethod}}(); } - {{/DependencyDefinitions}} - return this; + {{!/isPush}} + {{/targetEndpointDefinition}} } + {{/DependencyDefinitions}} + return this; +} - public {{javaType}} {{parentTypeName}}.get{{name}}() { - return get{{internalName}}(); - } +public {{javaType}} {{parentTypeName}}.get{{name}}() { + return get{{internalName}}(); +}