From a14db74f22d8e8e5eaa765928a3c01251c5324a3 Mon Sep 17 00:00:00 2001 From: rschoene <rene.schoene@tu-dresden.de> Date: Tue, 30 Nov 2021 15:45:11 +0100 Subject: [PATCH] working on concise grammar - auto-format aspects - some refactorings for #39 - unified handler setup - cleanup - update extending.md for new process (fewer steps now) --- pages/docs/extending.md | 30 +-- .../src/main/jastadd/Analysis.jrag | 77 ++----- .../src/main/jastadd/Configuration.jadd | 18 +- ragconnect.base/src/main/jastadd/Errors.jrag | 60 ++--- .../src/main/jastadd/Handlers.jrag | 14 ++ .../src/main/jastadd/NameResolution.jrag | 40 +++- .../src/main/jastadd/Navigation.jrag | 4 +- .../src/main/jastadd/RagConnect.relast | 39 ++-- .../src/main/jastadd/RagConnect_old.relast | 35 --- .../main/jastadd/intermediate/Generation.jadd | 25 +-- .../main/jastadd/intermediate/Mappings.jrag | 212 ++++++++++-------- .../src/main/jastadd/scanner/Keywords.flex | 2 - .../jastadd/ragconnect/compiler/Compiler.java | 97 ++++---- .../src/main/resources/MqttHandler.jadd | 17 +- .../src/main/resources/handler.mustache | 11 +- .../src/main/resources/mqtt.mustache | 5 - .../src/main/resources/ragconnect.mustache | 1 - .../main/resources/receiveDefinition.mustache | 32 ++- .../main/resources/sendDefinition.mustache | 32 ++- 19 files changed, 388 insertions(+), 363 deletions(-) create mode 100644 ragconnect.base/src/main/jastadd/Handlers.jrag delete mode 100644 ragconnect.base/src/main/jastadd/RagConnect_old.relast delete mode 100644 ragconnect.base/src/main/resources/mqtt.mustache diff --git a/pages/docs/extending.md b/pages/docs/extending.md index 61cabca..14fa8c8 100644 --- a/pages/docs/extending.md +++ b/pages/docs/extending.md @@ -1,32 +1,24 @@ # Extending `RagConnect` -To add a new communication protocol, the following locations have to be changed (replace `ABC` and `abc` with the name of the protocol): +To add a new communication protocol, the following locations have to be changed (replace `ABC` and `abc` with the name of the protocol). -Within `ragconnect.base/src/main/resources`: +### Within `ragconnect.base/src/main/resources` {% raw %} -- Add a new handler `ABCHandler`, if appropriate, similar to the existing handlers - - If further methods are needed for handler initialization, add a new template `abc.mustache` containing those procedures. Add `{{#usesABC}}{{> abc}}{{/usesABC}}` at the top of `ragconnect.mustache` to use this template -- In `receiveDefinition.mustache` and `sendDefinition.mustache`: add a new case in the switch statement defining the logic to happen for both definitions. If the new protocol is close to a PUSH semantic, follow `mqtt`. If it is closer to PULL semantic, follow `rest`. +- Add a new handler `ABCHandler.jadd`, similar to the existing handlers. +- In `handler.mustache`, add further methods if needed for handler usage in the application code (similar to `{{rootNodeName}}.{{SetupWaitUntilReadyMethodName}}` for `mqtt`) +- In `receiveDefinition.mustache` and `sendDefinition.mustache`: add a new case in the switch statements defining the logic to happen upon connect and disconnect for both definitions. If the new protocol is close to a PUSH semantic, follow `mqtt`. If it is closer to PULL semantic, follow `rest`. {% endraw %} -Within `ragconnect.base/src/main/jastadd`: +### Within `ragconnect.base/src/main/jastadd` -- In `backend/Configuration`: - - Add a new static boolean flag `usesABC` to indicate whether the protocol is used -- In `backend/Generation`: - - Add new attributes for type `MRagConnect` for handler attribute and handler field, if needed - - Add attributes for newly introduced references in changed mustache templates, if any - - Add a newly constructed handler within the definition of `RagConnect.toMustache` with the needed fields (class name, construction snippet, handler attribute, handler field, the boolean flag you just added to Configuration) -- In `backend/MustacheNodesToYAML`: - - Add key-value-pair for `usesABC` (and handler, if any) - - Add key-value-pairs for newly introduced referemces in changed mustache templates, if any +In `Handlers.jrag`: Add a new attribute `RagConnect.abcHandler()` returning the resolved handler -In `ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java`: +### Within `ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler` +In `Compiler.java`: - Add a new choice for `--protocols` similar to the existing ones -- Set the flag `usesABC` if the choice is given. -- Add code to add the handler to the list `handlers` if the choice is given, i.e., if `ASTNode.usesABC` +- Add a newly constructed handler in `setConfiguration` with the needed fields (definition file name within `resources` directory, commonly `ABCHandler.jadd`; class name of the handler; unique name for the protocol; whether the handler is used, i.e., if it was given in `--protocols`) Furthermore, new test cases are appreciated, see [below](#writing-tests). @@ -58,7 +50,7 @@ All tests are required to run both locally, and within the CI. ### build.gradle -Use the [PreprocessorPlugin][preprocessor-plugin], the build process can be written concisely in three parts per task: +Using the [PreprocessorPlugin][preprocessor-plugin], the build process can be written concisely in three parts per task: ```groovy task compileTreeAllowedTokens(type: RagConnectTest) { diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag index 88d2ffa..c968752 100644 --- a/ragconnect.base/src/main/jastadd/Analysis.jrag +++ b/ragconnect.base/src/main/jastadd/Analysis.jrag @@ -1,55 +1,16 @@ aspect Analysis { - // --- lookupTokenEndpointDefinition --- - inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token); - inh java.util.List<EndpointDefinition> EndpointTarget.lookupTokenEndpointDefinitions(TokenComponent token); - eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token); - syn java.util.List<EndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) { - java.util.List<EndpointDefinition> result = new java.util.ArrayList<>(); - for (EndpointTarget target : allEndpointTargetList()) { - if (target.isTokenEndpointTarget() && target.asTokenEndpointTarget().getToken().equals(token)) { - result.add(target.containingEndpointDefinition()); - } - } - return result; - } - - // --- lookupTypeEndpointDefinition --- - inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTypeEndpointDefinitions(TypeComponent type); - inh java.util.List<EndpointDefinition> EndpointTarget.lookupTypeEndpointDefinitions(TypeComponent type); - eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type); - syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) { - java.util.List<EndpointDefinition> result = new java.util.ArrayList<>(); - for (EndpointTarget target : allEndpointTargetList()) { - if (target.isTypeEndpointTarget() && target.asTypeEndpointTarget().getType().equals(type)) { - result.add(target.containingEndpointDefinition()); - } - } - return result; - } - - // --- lookupDependencyDefinition --- - inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id); - eq RagConnect.getConnectSpecificationFile().lookupDependencyDefinition(TypeDecl source, String id) { - for (DependencyDefinition def : allDependencyDefinitionList()) { - if (def.getID().equals(id) && def.getSource().containingTypeDecl().equals(source)) { - return def; - } - } - return null; - } - // --- isAlreadyDefined --- syn boolean EndpointDefinition.isAlreadyDefined() = getEndpointTarget().isAlreadyDefined(); syn boolean EndpointTarget.isAlreadyDefined(); eq TokenEndpointTarget.isAlreadyDefined() { return lookupTokenEndpointDefinitions(getToken()).stream() - .filter(containingEndpointDefinition()::matchesType) - .count() > 1; + .filter(containingEndpointDefinition()::matchesType) + .count() > 1; } eq TypeEndpointTarget.isAlreadyDefined() { return lookupTypeEndpointDefinitions(getType()).stream() - .filter(containingEndpointDefinition()::matchesType) - .count() > 1; + .filter(containingEndpointDefinition()::matchesType) + .count() > 1; } syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this; @@ -60,7 +21,9 @@ aspect Analysis { 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; } + if (!target.getName().endsWith("[]")) { + return false; + } return getType().assignableTo(new SimpleJavaTypeUse(target.getName().replace("[]", ""))); } syn boolean JavaTypeUse.assignableTo(JavaTypeUse target) { @@ -68,22 +31,30 @@ aspect Analysis { return target.primitivePrettyPrint().equals(this.primitivePrettyPrint()); } syn String JavaTypeUse.primitivePrettyPrint() { - switch(getName()) { + switch (getName()) { case "boolean": - case "Boolean": return "boolean"; + case "Boolean": + return "boolean"; case "int": - case "Integer": return "int"; + case "Integer": + return "int"; case "short": - case "Short": return "short"; + case "Short": + return "short"; case "long": - case "Long": return "long"; + case "Long": + return "long"; case "float": - case "Float": return "float"; + case "Float": + return "float"; case "double": - case "Double": return "double"; + case "Double": + return "double"; case "char": - case "Character": return "char"; - default: return getName(); + case "Character": + return "char"; + default: + return getName(); } } diff --git a/ragconnect.base/src/main/jastadd/Configuration.jadd b/ragconnect.base/src/main/jastadd/Configuration.jadd index 8fd0e64..72a3e9d 100644 --- a/ragconnect.base/src/main/jastadd/Configuration.jadd +++ b/ragconnect.base/src/main/jastadd/Configuration.jadd @@ -1,11 +1,9 @@ -aspect Configuration { - public static boolean ASTNode.loggingEnabledForReads = false; - public static boolean ASTNode.loggingEnabledForWrites = false; - public static boolean ASTNode.loggingEnabledForIncremental = false; - public static TypeDecl ASTNode.rootNode; - public static String ASTNode.JastAddList = "List"; - public static boolean ASTNode.usesMqtt; - public static boolean ASTNode.usesRest; - public static boolean ASTNode.incrementalOptionActive; - public static boolean ASTNode.experimentalJastAdd329; +aspect ConfigurationShortcuts { + syn boolean RagConnect.loggingEnabledForReads() = getConfiguration().getLoggingEnabledForReads(); + syn boolean RagConnect.loggingEnabledForWrites() = getConfiguration().getLoggingEnabledForWrites(); + syn boolean RagConnect.loggingEnabledForIncremental() = getConfiguration().getLoggingEnabledForIncremental(); + syn TypeDecl RagConnect.rootNode() = getConfiguration().getRootNode(); + syn String RagConnect.JastAddList() = getConfiguration().getJastAddList(); + syn boolean RagConnect.incrementalOptionActive() = getConfiguration().getIncrementalOptionActive(); + syn boolean RagConnect.experimentalJastAdd329() = getConfiguration().getExperimentalJastAdd329(); } diff --git a/ragconnect.base/src/main/jastadd/Errors.jrag b/ragconnect.base/src/main/jastadd/Errors.jrag index 610f994..61f9665 100644 --- a/ragconnect.base/src/main/jastadd/Errors.jrag +++ b/ragconnect.base/src/main/jastadd/Errors.jrag @@ -3,36 +3,37 @@ aspect Errors { [new TreeSet<ErrorMessage>()] root RagConnect; - EndpointDefinition contributes error("Endpoint definition already defined for " + parentTypeName() + "." + entityName()) - when isAlreadyDefined() - to RagConnect.errors(); + EndpointDefinition contributes error("Endpoint definition already defined for " + parentTypeName() + "." + entityName()) + when isAlreadyDefined() + to RagConnect.errors(); - EndpointDefinition contributes error("Receiving target token must not be an NTA token!") - when !getSend() && getEndpointTarget().isTokenEndpointTarget() && token().getNTA() - to RagConnect.errors(); + EndpointDefinition contributes error("Receiving target token must not be an NTA token!") + when !getSend() && getEndpointTarget().isTokenEndpointTarget() && token().getNTA() + to RagConnect.errors(); - // if first mapping is null, then suitableDefaultMapping() == null - EndpointDefinition contributes error("No suitable default mapping found for type " + + // if first mapping is null, then suitableDefaultMapping() == null + EndpointDefinition contributes error("No suitable default mapping found for type " + ((getMappingList().isEmpty()) - ? token().effectiveJavaTypeUse().prettyPrint() - : getMappingList().get(0).getFromType().prettyPrint())) - when !getSend() && getEndpointTarget().isTokenEndpointTarget() && effectiveMappings().get(0) == null - to RagConnect.errors(); - - // TODO copy for type endpoint target - EndpointDefinition contributes error("to-type of last mapping (" + effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() + ") not assignable to type of the token (" + token().effectiveJavaTypeUse().prettyPrint() + ")!") - when !getSend() && getEndpointTarget().isTokenEndpointTarget() && - !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo( - token().effectiveJavaTypeUse()) - to RagConnect.errors(); - - DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID()) - when isAlreadyDefined() - to RagConnect.errors(); - - DependencyDefinition contributes error("The name of a dependency definition must not be equal to a list-node on the source") - when isAlreadyDefinedAsList() - to RagConnect.errors(); + ? token().effectiveJavaTypeUse().prettyPrint() + : getMappingList().get(0).getFromType().prettyPrint())) + when !getSend() && getEndpointTarget().isTokenEndpointTarget() && effectiveMappings().get(0) == null + to RagConnect.errors(); + + EndpointDefinition contributes error("to-type of last mapping (" + + effectiveMappings().get(effectiveMappings().size() - 1).getToType().prettyPrint() + + ") not assignable to type of the token (" + token().effectiveJavaTypeUse().prettyPrint() + ")!") + when !getSend() && getEndpointTarget().isTokenEndpointTarget() && + !effectiveMappings().get(effectiveMappings().size() - 1).getToType().assignableTo( + token().effectiveJavaTypeUse()) + to RagConnect.errors(); + + DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID()) + when isAlreadyDefined() + to RagConnect.errors(); + + DependencyDefinition contributes error("The name of a dependency definition must not be equal to a list-node on the source") + when isAlreadyDefinedAsList() + to RagConnect.errors(); } aspect ErrorHelpers { @@ -65,12 +66,15 @@ aspect ErrorMessage { public ASTNode getNode() { return node; } + public int getLine() { return line; } + public int getCol() { return col; } + public String getMessage() { return message; } @@ -91,7 +95,7 @@ aspect ErrorMessage { return n; } - n = col-err.col; + n = col - err.col; if (n != 0) { return n; } diff --git a/ragconnect.base/src/main/jastadd/Handlers.jrag b/ragconnect.base/src/main/jastadd/Handlers.jrag new file mode 100644 index 0000000..57c7ccd --- /dev/null +++ b/ragconnect.base/src/main/jastadd/Handlers.jrag @@ -0,0 +1,14 @@ +aspect RagConnectHandlers { + syn Handler RagConnect.mqttHandler() = resolveHandlerByName("mqtt"); + syn Handler RagConnect.restHandler() = resolveHandlerByName("rest"); + + private Handler RagConnect.resolveHandlerByName(String uniqueName) { + for (Handler handler : getHandlerList()) { + if (uniqueName.equals(handler.getUniqueName())) { + return handler; + } + } + System.err.println("Could not find handler with name '" + uniqueName + "'"); + return null; + } +} diff --git a/ragconnect.base/src/main/jastadd/NameResolution.jrag b/ragconnect.base/src/main/jastadd/NameResolution.jrag index 47ae57a..9b37ee7 100644 --- a/ragconnect.base/src/main/jastadd/NameResolution.jrag +++ b/ragconnect.base/src/main/jastadd/NameResolution.jrag @@ -1,4 +1,42 @@ aspect RagConnectNameResolution { + // --- lookupTokenEndpointDefinition --- + inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTokenEndpointDefinitions(TokenComponent token); + inh java.util.List<EndpointDefinition> EndpointTarget.lookupTokenEndpointDefinitions(TokenComponent token); + eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token); + syn java.util.List<EndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) { + java.util.List<EndpointDefinition> result = new java.util.ArrayList<>(); + for (EndpointTarget target : allEndpointTargetList()) { + if (target.isTokenEndpointTarget() && target.asTokenEndpointTarget().getToken().equals(token)) { + result.add(target.containingEndpointDefinition()); + } + } + return result; + } + + // --- lookupTypeEndpointDefinition --- + inh java.util.List<EndpointDefinition> EndpointDefinition.lookupTypeEndpointDefinitions(TypeComponent type); + inh java.util.List<EndpointDefinition> EndpointTarget.lookupTypeEndpointDefinitions(TypeComponent type); + eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type); + syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) { + java.util.List<EndpointDefinition> result = new java.util.ArrayList<>(); + for (EndpointTarget target : allEndpointTargetList()) { + if (target.isTypeEndpointTarget() && target.asTypeEndpointTarget().getType().equals(type)) { + result.add(target.containingEndpointDefinition()); + } + } + return result; + } + + // --- lookupDependencyDefinition --- + inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id); + eq RagConnect.getConnectSpecificationFile().lookupDependencyDefinition(TypeDecl source, String id) { + for (DependencyDefinition def : allDependencyDefinitionList()) { + if (def.getID().equals(id) && def.getSource().containingTypeDecl().equals(source)) { + return def; + } + } + return null; + } // rel EndpointDefinition.Mapping* -> MappingDefinition refine RefResolverStubs eq EndpointDefinition.resolveMappingByToken(String id, int position) { @@ -64,7 +102,7 @@ aspect RagConnectNameResolution { } // rel ___ -> TokenComponent (from relast-preprocessor) - // refine here to have an attribute without writing on stderr if not found + // refine from relast PP here to have an attribute that does not write on stderr if no TokenComponent was found refine NameResolution eq ASTNode.globallyResolveTokenComponentByToken(String id) { TokenComponent result = tryGloballyResolveTokenComponentByToken(id); if (result == null) { diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag index d09444c..369ba4f 100644 --- a/ragconnect.base/src/main/jastadd/Navigation.jrag +++ b/ragconnect.base/src/main/jastadd/Navigation.jrag @@ -112,7 +112,9 @@ aspect RagConnectNavigation { syn EndpointDefinition DependencyDefinition.targetEndpointDefinition() { // resolve definition in here, as we do not need resolveMethod in any other place (yet) for (EndpointDefinition endpointDefinition : ragconnect().allEndpointDefinitionList()) { - if (!endpointDefinition.getSend()) { continue; } + if (!endpointDefinition.getSend()) { + continue; + } EndpointTarget endpointTarget = endpointDefinition.getEndpointTarget(); if (endpointTarget.isTokenEndpointTarget() && endpointTarget.asTokenEndpointTarget().getToken().equals(this.getTarget())) { diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast index c0037c7..30e6d5a 100644 --- a/ragconnect.base/src/main/jastadd/RagConnect.relast +++ b/ragconnect.base/src/main/jastadd/RagConnect.relast @@ -1,17 +1,17 @@ -RagConnect ::= ConnectSpecificationFile* Handler* Program ; +RagConnect ::= ConnectSpecificationFile* Program Handler* Configuration; -abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition* ; -ConnectSpecificationFile : ConnectSpecification ::= <FileName> ; +abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition*; +ConnectSpecificationFile : ConnectSpecification ::= <FileName>; -EndpointDefinition ::= <AlwaysApply:boolean> <IndexBasedListAccess:boolean> <WithAdd:boolean> <Send:boolean> EndpointTarget ; +EndpointDefinition ::= <AlwaysApply:boolean> <IndexBasedListAccess:boolean> <WithAdd:boolean> <Send:boolean> EndpointTarget; rel EndpointDefinition.Mapping* <-> MappingDefinition.UsedAt*; -abstract EndpointTarget ; -TokenEndpointTarget : EndpointTarget ; -rel TokenEndpointTarget.Token <-> TokenComponent.TokenEndpointTarget* ; -TypeEndpointTarget : EndpointTarget ; -rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget* ; -UntypedEndpointTarget : EndpointTarget ::= <TokenOrType> ; // only used by parser +abstract EndpointTarget; +TokenEndpointTarget : EndpointTarget; +rel TokenEndpointTarget.Token <-> TokenComponent.TokenEndpointTarget*; +TypeEndpointTarget : EndpointTarget; +rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget*; +UntypedEndpointTarget : EndpointTarget ::= <TokenOrType>; // only used by parser // to be integrated: //AttributeEndpointTarget : EndpointTarget ::= <Name> ; //RelationEndpointTarget : EndpointTarget ; @@ -21,10 +21,19 @@ DependencyDefinition ::= <ID>; rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*; rel DependencyDefinition.Target -> Component; -MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content> ; +MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content>; abstract MappingDefinitionType ::= ; -JavaMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ; -JavaArrayMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ; -DefaultMappingDefinition : MappingDefinition ; +JavaMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse; +JavaArrayMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse; +DefaultMappingDefinition : MappingDefinition; -Handler ::= <ClassName> <Construction> <AttributeName> <FieldName> <InUse:boolean>; +Handler ::= <DefinitionFileName> <ClassName> <UniqueName> <InUse:boolean>; + +Configuration ::= +<LoggingEnabledForReads:boolean> +<LoggingEnabledForWrites:boolean> +<LoggingEnabledForIncremental:boolean> +<JastAddList:String> +<IncrementalOptionActive:boolean> +<ExperimentalJastAdd329:boolean>; +rel Configuration.RootNode -> TypeDecl ; diff --git a/ragconnect.base/src/main/jastadd/RagConnect_old.relast b/ragconnect.base/src/main/jastadd/RagConnect_old.relast deleted file mode 100644 index 4301c70..0000000 --- a/ragconnect.base/src/main/jastadd/RagConnect_old.relast +++ /dev/null @@ -1,35 +0,0 @@ -RagConnect ::= ConnectSpecificationFile* Program ; - -abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition* ; -ConnectSpecificationFile : ConnectSpecification ::= <FileName> ; - -abstract EndpointDefinition ::= <AlwaysApply:boolean> ; - -rel EndpointDefinition.Mapping* <-> MappingDefinition.UsedAt*; - -abstract TokenEndpointDefinition : EndpointDefinition; -rel TokenEndpointDefinition.Token <-> TokenComponent.TokenEndpointDefinition*; - -ReceiveTokenEndpointDefinition : TokenEndpointDefinition; -SendTokenEndpointDefinition : TokenEndpointDefinition; - -abstract TypeEndpointDefinition : EndpointDefinition ::= <IndexBasedListAccess:boolean> ; -rel TypeEndpointDefinition.Type <-> TypeComponent.TypeEndpointDefinition*; - -ReceiveTypeEndpointDefinition : TypeEndpointDefinition ::= <WithAdd:boolean>; -SendTypeEndpointDefinition : TypeEndpointDefinition; - -DependencyDefinition ::= <ID>; -rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*; -rel DependencyDefinition.Target -> Component; - -MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content> ; -abstract MappingDefinitionType ::= ; -JavaMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ; -JavaArrayMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ; -DefaultMappingDefinition : MappingDefinition ; - -// only used by parser -abstract UntypedEndpointDefinition : EndpointDefinition ::= <TokenOrType> <Indexed:boolean> <WithAdd:boolean> ; -ReceiveUntypedEndpointDefinition : UntypedEndpointDefinition; -SendUntypedEndpointDefinition : UntypedEndpointDefinition; diff --git a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd index d52f627..2e0c4a9 100644 --- a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd +++ b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd @@ -6,7 +6,7 @@ Design considerations aspect AttributesForMustache { syn List<TypeComponent> RagConnect.rootTypeComponents() { List<TypeComponent> result = new ArrayList<>(); - for (Component child : rootNode.getComponentList()) { + for (Component child : rootNode().getComponentList()) { if (child.isTypeComponent()) { result.add(child.asTypeComponent()); } @@ -16,12 +16,11 @@ aspect AttributesForMustache { syn boolean RagConnect.hasRootTypeComponents() = !rootTypeComponents().isEmpty(); syn String RagConnect.closeMethodName() = "ragconnectCloseConnections"; - syn String RagConnect.mqttHandlerAttribute() = "_mqttHandler"; - syn String RagConnect.mqttHandlerField() = "_mqttHandler"; - syn String RagConnect.mqttSetupWaitUntilReadyMethodName() = "ragconnectSetupMqttWaitUntilReady"; - syn String RagConnect.restHandlerAttribute() = "_restHandler"; - syn String RagConnect.restHandlerField() = "_restHandler"; + syn String Handler.AttributeName() = "_" + getUniqueName() + "Handler"; + syn String Handler.FieldName() = "_" + getUniqueName() + "Handler"; + syn String Handler.SetupWaitUntilReadyMethodName() = "ragconnectSetup" + capitalize(getUniqueName()) + "WaitUntilReady"; + syn String Handler.ConstructionSnippet() = "new " + getClassName() + "(\"Handler for " + ragconnect().rootNodeName() + ".\" + this.hashCode())"; syn boolean RagConnect.hasTreeListEndpoints() { for (EndpointDefinition endpointDef : allEndpointDefinitionList()) { @@ -249,7 +248,7 @@ aspect AttributesForMustache { aspect AspectGeneration { // --- rootNodeName --- - syn String ASTNode.rootNodeName() = rootNode.getName(); + syn String RagConnect.rootNodeName() = getConfiguration().getRootNode().getName(); } aspect GrammarGeneration { @@ -273,13 +272,13 @@ aspect GrammarGeneration { return result; } -// coll java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() [new java.util.HashMap<>()] with put root RagConnect; + // coll java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() [new java.util.HashMap<>()] with put root RagConnect; -// TypeEndpointDefinition contributes getTokenToCreate() -// when typeIsList() && !getUseList() -// to RagConnect.additionalTokens() -//// for ragconnect() -// ; + // TypeEndpointDefinition contributes getTokenToCreate() + // when typeIsList() && !getUseList() + // to RagConnect.additionalTokens() + //// for ragconnect() + // ; syn java.util.Map<TypeDecl, TokenComponent> RagConnect.additionalTokens() { java.util.Map<TypeDecl, TokenComponent> result = new java.util.HashMap<>(); diff --git a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag b/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag index 82ca5a5..e41821f 100644 --- a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag +++ b/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag @@ -47,50 +47,50 @@ aspect DefaultMappings { syn nta DefaultMappingDefinition RagConnect.defaultBytesToTreeMapping(String typeName) { return treeDefaultMappingDefinition("byte[]", typeName, - "String content = new String(input);\n" + - "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" + - "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + - "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" + - typeName + " result = " + typeName + ".deserialize((com.fasterxml.jackson.databind.JsonNode)mapper.readTree(parser));\n" + - "parser.close();\n" + - "return result;" + "String content = new String(input);\n" + + "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" + + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + + "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" + + typeName + " result = " + typeName + ".deserialize((com.fasterxml.jackson.databind.JsonNode)mapper.readTree(parser));\n" + + "parser.close();\n" + + "return result;" ); } syn nta DefaultMappingDefinition RagConnect.defaultTreeToBytesMapping(String typeName) { return treeDefaultMappingDefinition(typeName, "byte[]", - "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" + - "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + - "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n"+ - "input.serialize(generator);\n" + - "generator.flush();\n" + - "return outputStream.toString().getBytes();" + "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" + + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + + "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n" + + "input.serialize(generator);\n" + + "generator.flush();\n" + + "return outputStream.toString().getBytes();" ); } syn nta DefaultMappingDefinition RagConnect.defaultBytesToListTreeMapping(String typeName) { - return treeDefaultMappingDefinition("byte[]", JastAddList + "<" + typeName + ">", - "String content = new String(input);\n" + - "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" + - "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + - "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" + - JastAddList + "<" + typeName + ">" + " result = " + typeName + ".deserializeList((com.fasterxml.jackson.databind.node.ArrayNode)mapper.readTree(parser));\n" + - "parser.close();\n" + - "return result;" + return treeDefaultMappingDefinition("byte[]", JastAddList() + "<" + typeName + ">", + "String content = new String(input);\n" + + "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n" + + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + + "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(content);\n" + + JastAddList() + "<" + typeName + ">" + " result = " + typeName + ".deserializeList((com.fasterxml.jackson.databind.node.ArrayNode)mapper.readTree(parser));\n" + + "parser.close();\n" + + "return result;" ); } syn nta DefaultMappingDefinition RagConnect.defaultListTreeToBytesMapping() { - return treeDefaultMappingDefinition(JastAddList, "byte[]", - "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" + - "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + - "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n"+ - "input.serialize(generator);\n" + - "generator.flush();\n" + - "return outputStream.toString().getBytes();" + return treeDefaultMappingDefinition(JastAddList(), "byte[]", + "java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();\n" + + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n" + + "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(outputStream, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n" + + "input.serialize(generator);\n" + + "generator.flush();\n" + + "return outputStream.toString().getBytes();" ); } syn nta DefaultMappingDefinition RagConnect.defaultBooleanToBytesMapping() = baseDefaultMappingDefinition( - "boolean", "byte[]", "return java.nio.ByteBuffer.allocate(1).put((byte) (input ? 1 : 0)).array();"); + "boolean", "byte[]", "return java.nio.ByteBuffer.allocate(1).put((byte) (input ? 1 : 0)).array();"); syn nta DefaultMappingDefinition RagConnect.defaultIntToBytesMapping() = baseDefaultMappingDefinition( "int", "byte[]", "return java.nio.ByteBuffer.allocate(Integer.BYTES).putInt(input).array();"); syn nta DefaultMappingDefinition RagConnect.defaultShortToBytesMapping() = baseDefaultMappingDefinition( @@ -168,7 +168,7 @@ aspect Mappings { syn boolean TokenComponent.isPrimitiveType() = effectiveJavaTypeUse().isPrimitiveType(); syn boolean JavaTypeUse.isPrimitiveType() = false; eq SimpleJavaTypeUse.isPrimitiveType() { - switch(getName()) { + switch (getName()) { case "boolean": case "int": case "short": @@ -176,8 +176,10 @@ aspect Mappings { case "float": case "double": case "char": - case "byte": return true; - default: return false; + case "byte": + return true; + default: + return false; } } syn boolean MappingDefinitionType.isPrimitiveType() = false; @@ -193,30 +195,40 @@ aspect Mappings { try { TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName()); return typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultBytesToListTreeMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName()); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } } switch (targetTypeName()) { case "boolean": - case "Boolean": return ragconnect().defaultBytesToBooleanMapping(); + case "Boolean": + return ragconnect().defaultBytesToBooleanMapping(); case "int": - case "Integer": return ragconnect().defaultBytesToIntMapping(); + case "Integer": + return ragconnect().defaultBytesToIntMapping(); case "short": - case "Short": return ragconnect().defaultBytesToShortMapping(); + case "Short": + return ragconnect().defaultBytesToShortMapping(); case "long": - case "Long": return ragconnect().defaultBytesToLongMapping(); + case "Long": + return ragconnect().defaultBytesToLongMapping(); case "float": - case "Float": return ragconnect().defaultBytesToFloatMapping(); + case "Float": + return ragconnect().defaultBytesToFloatMapping(); case "double": - case "Double": return ragconnect().defaultBytesToDoubleMapping(); + case "Double": + return ragconnect().defaultBytesToDoubleMapping(); case "char": - case "Character": return ragconnect().defaultBytesToCharMapping(); - case "String": return ragconnect().defaultBytesToStringMapping(); + case "Character": + return ragconnect().defaultBytesToCharMapping(); + case "String": + return ragconnect().defaultBytesToStringMapping(); default: try { TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName()); // TODO: also support list-types, if list is first type return ragconnect().defaultBytesToTreeMapping(typeDecl.getName()); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this); return null; } @@ -228,30 +240,40 @@ aspect Mappings { try { TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName()); return typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultListTreeToBytesMapping() : ragconnect().defaultTreeToBytesMapping(typeDecl.getName()); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } } switch (targetTypeName()) { case "boolean": - case "Boolean": return ragconnect().defaultBooleanToBytesMapping(); + case "Boolean": + return ragconnect().defaultBooleanToBytesMapping(); case "int": - case "Integer": return ragconnect().defaultIntToBytesMapping(); + case "Integer": + return ragconnect().defaultIntToBytesMapping(); case "short": - case "Short": return ragconnect().defaultShortToBytesMapping(); + case "Short": + return ragconnect().defaultShortToBytesMapping(); case "long": - case "Long": return ragconnect().defaultLongToBytesMapping(); + case "Long": + return ragconnect().defaultLongToBytesMapping(); case "float": - case "Float": return ragconnect().defaultFloatToBytesMapping(); + case "Float": + return ragconnect().defaultFloatToBytesMapping(); case "double": - case "Double": return ragconnect().defaultDoubleToBytesMapping(); + case "Double": + return ragconnect().defaultDoubleToBytesMapping(); case "char": - case "Character": return ragconnect().defaultCharToBytesMapping(); - case "String": return ragconnect().defaultStringToBytesMapping(); + case "Character": + return ragconnect().defaultCharToBytesMapping(); + case "String": + return ragconnect().defaultStringToBytesMapping(); default: try { TypeDecl typeDecl = program().resolveTypeDecl(targetTypeName()); // TODO: also support list-types, if list is last type return ragconnect().defaultTreeToBytesMapping(typeDecl.getName()); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this); return null; } @@ -273,54 +295,54 @@ aspect Mappings { eq TokenEndpointTarget.targetTypeName() = getToken().effectiveJavaTypeUse().getName(); eq TypeEndpointTarget.targetTypeName() = getType().getTypeDecl().getName(); -// 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; -// } -// } + // 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"); + // // --- isString --- + // syn boolean MappingDefinitionType.isString() = false; + // eq JavaMappingDefinitionType.isString() = getType().getName().equals("String"); // --- allMappingDefinitions --- syn java.util.List<MappingDefinition> RagConnect.allMappingDefinitions() { diff --git a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex index 11f73da..8a1eaec 100644 --- a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex +++ b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex @@ -5,8 +5,6 @@ "maps" { return sym(Terminals.MAPS); } "to" { return sym(Terminals.TO); } "as" { return sym(Terminals.AS); } -//"tree" { return sym(Terminals.TREE); } -//"list" { return sym(Terminals.LIST); } "with" { return sym(Terminals.WITH); } "indexed" { return sym(Terminals.INDEXED); } "add" { return sym(Terminals.ADD); } diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java index 1f57a7a..abddccd 100644 --- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java +++ b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java @@ -20,7 +20,7 @@ import java.util.logging.Logger; public class Compiler extends AbstractCompiler { -// private ValueOption optionOutputDir; + // private ValueOption optionOutputDir; private ValueOption optionRootNode; private ValueOption optionProtocols; private BooleanOption optionPrintYaml; @@ -68,6 +68,7 @@ public class Compiler extends AbstractCompiler { } RagConnect ragConnect = parseProgram(getConfiguration().getFiles()); + setConfiguration(ragConnect); if (!ragConnect.errors().isEmpty()) { StringBuilder sb = new StringBuilder("Errors:\n"); @@ -79,24 +80,17 @@ public class Compiler extends AbstractCompiler { } if (optionPrintYaml.value()) { - System.err.println("Currently unsupported option!"); -// 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); + System.err.println("'--printYaml' currently unsupported option!"); return 1; } LOGGER.fine("Writing output files"); - 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) { + for (Handler handler : ragConnect.getHandlerList()) { + if (!handler.getInUse()) { + continue; + } + String handlerFileName = handler.getDefinitionFileName(); try { InputStream inputStream = Compiler.class.getClassLoader().getResourceAsStream(handlerFileName); if (inputStream == null) { @@ -114,7 +108,7 @@ public class Compiler extends AbstractCompiler { writeToFile(outputFile, grammarFile.generateAbstractGrammar()); } writeToFile(getConfiguration().outputDir().toPath().resolve("RagConnect.jadd"), - generateAspect(ragConnect, optionRootNode.value())); + generateAspect(ragConnect)); return 0; } @@ -130,14 +124,14 @@ public class Compiler extends AbstractCompiler { /** * Reads the version string. - * + * <p> * The version string is read from the property file * src/main/resources/Version.properties. This * file should be generated during the build process. If it is missing * then there is some problem in the build script. * - * @author Jesper Öqvist <jesper.oqvist@cs.lth.se> * @return the read version string, or <code>version ?</code> + * @author Jesper Öqvist <jesper.oqvist@cs.lth.se> */ private String readVersion() { try { @@ -160,26 +154,26 @@ public class Compiler extends AbstractCompiler { super.initOptions(); optionRootNode = addOption( new ValueOption("rootNode", "root node in the base grammar.") - .acceptAnyValue() - .needsValue(true)); + .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") + .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)); + new BooleanOption("printYaml", "Print out YAML instead of generating files (currently unsupported)") + .defaultValue(false)); optionVerbose = addOption( new BooleanOption("verbose", "Print more messages while compiling.") - .defaultValue(false)); + .defaultValue(false)); optionLogReads = addOption( new BooleanOption("logReads", "Enable logging for every read.") - .defaultValue(false)); + .defaultValue(false)); optionLogWrites = addOption( new BooleanOption("logWrites", "Enable logging for every write.") - .defaultValue(false)); + .defaultValue(false)); optionLogIncremental = addOption( new BooleanOption("logIncremental", "Enable logging for observer in incremental dependency tracking.") .defaultValue(false)); @@ -235,20 +229,7 @@ public class Compiler extends AbstractCompiler { ragConnect.additionalRelations().forEach(ragConnectGrammarPart::addDeclaration); ragConnect.additionalTokens().forEach(TypeDecl::addComponent); - ASTNode.loggingEnabledForReads = optionLogReads.value(); - ASTNode.loggingEnabledForWrites = optionLogWrites.value(); - ASTNode.loggingEnabledForIncremental = optionLogIncremental.value(); - ASTNode.experimentalJastAdd329 = optionExperimentalJastAdd329.value(); - // reuse "--incremental" option of JastAdd - ASTNode.incrementalOptionActive = getConfiguration().incremental() && getConfiguration().traceFlush(); - LOGGER.fine(() -> "ASTNode.incrementalOptionActive = " + ASTNode.incrementalOptionActive); - - // reuse "--List" option of JastAdd - ASTNode.JastAddList = getConfiguration().listType(); - - ASTNode.usesMqtt = optionProtocols.hasValue(OPTION_PROTOCOL_MQTT); - ASTNode.usesRest = optionProtocols.hasValue(OPTION_PROTOCOL_REST); return ragConnect; } @@ -281,6 +262,7 @@ public class Compiler extends AbstractCompiler { /** * Extracts the basename of the given file, with file extension + * * @param filename the given filename * @return the basename */ @@ -288,20 +270,29 @@ public class Compiler extends AbstractCompiler { return new File(filename).getName(); } -// protected void printUsage() { -// System.out.println("Usage: java -jar ragconnect.jar [--option1] [--option2=value] ... <filename1> <filename2> ... "); -// System.out.println("Options:"); -// System.out.print(commandLine.printOptionHelp()); -// } + private void setConfiguration(RagConnect ragConnect) { + ragConnect.setConfiguration(new Configuration()); + ragConnect.getConfiguration().setLoggingEnabledForReads(optionLogReads.value()); + ragConnect.getConfiguration().setLoggingEnabledForWrites(optionLogWrites.value()); + ragConnect.getConfiguration().setLoggingEnabledForIncremental(optionLogIncremental.value()); + ragConnect.getConfiguration().setExperimentalJastAdd329(optionExperimentalJastAdd329.value()); + + // reuse "--incremental" and "--trace=flush" options of JastAdd + boolean incrementalOptionActive = this.getConfiguration().incremental() && this.getConfiguration().traceFlush(); + ragConnect.getConfiguration().setIncrementalOptionActive(incrementalOptionActive); + LOGGER.fine(() -> "ragConnect.getConfiguration().IncrementalOptionActive = " + incrementalOptionActive); + // reuse "--List" option of JastAdd + ragConnect.getConfiguration().setJastAddList(this.getConfiguration().listType()); + + ragConnect.getConfiguration().setRootNode(ragConnect.getProgram().resolveTypeDecl(optionRootNode.value())); + + // Handler ::= <ClassName> <UniqueName> <InUse:boolean>; + ragConnect.addHandler(new Handler("MqttHandler.jadd", "MqttServerHandler", "mqtt", optionProtocols.hasValue(OPTION_PROTOCOL_MQTT))); + ragConnect.addHandler(new Handler("RestHandler.jadd", "RestServerHandler", "rest", optionProtocols.hasValue(OPTION_PROTOCOL_REST))); + } - public String generateAspect(RagConnect ragConnectSpec, String rootNodeName) { - ASTNode.rootNode = ragConnectSpec.getProgram().resolveTypeDecl(rootNodeName); - // Handler ::= <ClassName> <Construction> <AttributeName> <FieldName> <InUse:boolean>; - ragConnectSpec.addHandler(new Handler("MqttServerHandler", "new MqttServerHandler(\"RagConnectMQTT\")", - ragConnectSpec.mqttHandlerAttribute(), ragConnectSpec.mqttHandlerField(), ASTNode.usesMqtt)); - ragConnectSpec.addHandler(new Handler("RestServerHandler", "new RestServerHandler(\"RagConnectREST\")", - ragConnectSpec.restHandlerAttribute(), ragConnectSpec.restHandlerField(), ASTNode.usesRest)); + public String generateAspect(RagConnect ragConnect) { StringBuilder sb = new StringBuilder(); com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() { @Override @@ -321,7 +312,7 @@ public class Compiler extends AbstractCompiler { com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory(); mf.setObjectHandler(roh); com.github.mustachejava.Mustache m = mf.compile("ragconnect.mustache"); - m.execute(new java.io.PrintWriter(new org.jastadd.ragconnect.compiler.AppendableWriter(sb)), ragConnectSpec); + m.execute(new java.io.PrintWriter(new org.jastadd.ragconnect.compiler.AppendableWriter(sb)), ragConnect); return sb.toString(); } } diff --git a/ragconnect.base/src/main/resources/MqttHandler.jadd b/ragconnect.base/src/main/resources/MqttHandler.jadd index d859d38..0651d5f 100644 --- a/ragconnect.base/src/main/resources/MqttHandler.jadd +++ b/ragconnect.base/src/main/resources/MqttHandler.jadd @@ -316,11 +316,8 @@ public class MqttHandler { needSubscribe = pairToAddTo.callbacks.isEmpty(); pairToAddTo.callbacks.add(callback); } else { // normal topic - java.util.List<java.util.function.BiConsumer<String, byte[]>> callbacksForTopic = normalCallbacks.get(topic); - if (callbacksForTopic == null) { - callbacksForTopic = new java.util.ArrayList<>(); - normalCallbacks.put(topic, callbacksForTopic); - } + java.util.List<java.util.function.BiConsumer<String, byte[]>> callbacksForTopic = normalCallbacks. + computeIfAbsent(topic, t -> new java.util.ArrayList<>()); needSubscribe = callbacksForTopic.isEmpty(); callbacksForTopic.add(callback); } @@ -346,7 +343,10 @@ public class MqttHandler { }); }); try { - operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS); + boolean finishedInTime = operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS); + if (!finishedInTime) { + return false; + } return success.get(); } catch (InterruptedException e) { return false; @@ -428,7 +428,10 @@ public class MqttHandler { }); }); try { - operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS); + boolean finishedInTime = operationFinished.await(2, java.util.concurrent.TimeUnit.SECONDS); + if (!finishedInTime) { + return false; + } } catch (InterruptedException e) { logger.catching(e); success.set(false); diff --git a/ragconnect.base/src/main/resources/handler.mustache b/ragconnect.base/src/main/resources/handler.mustache index 874eba4..32d7986 100644 --- a/ragconnect.base/src/main/resources/handler.mustache +++ b/ragconnect.base/src/main/resources/handler.mustache @@ -1,7 +1,7 @@ aspect RagConnectHandler { {{#Handlers}} {{#InUse}} - private {{ClassName}} {{rootNodeName}}.{{FieldName}} = {{{Construction}}}; + private {{ClassName}} {{rootNodeName}}.{{FieldName}} = {{{ConstructionSnippet}}}; {{#hasRootTypeComponents}}inh {{ClassName}} ASTNode.{{AttributeName}}();{{/hasRootTypeComponents}} {{#rootTypeComponents}} eq {{rootNodeName}}.get{{name}}().{{AttributeName}}() = {{FieldName}}; @@ -14,6 +14,15 @@ aspect RagConnectHandler { {{#InUse}}{{FieldName}}.close();{{/InUse}} {{/Handlers}} } + +{{#mqttHandler}} + {{#InUse}} + public void {{rootNodeName}}.{{SetupWaitUntilReadyMethodName}}(long time, java.util.concurrent.TimeUnit unit) { + {{FieldName}}.setupWaitUntilReady(time, unit); + } + {{/InUse}} +{{/mqttHandler}} + class RagConnectToken { static java.util.concurrent.atomic.AtomicLong counter = new java.util.concurrent.atomic.AtomicLong(0); final long id; diff --git a/ragconnect.base/src/main/resources/mqtt.mustache b/ragconnect.base/src/main/resources/mqtt.mustache deleted file mode 100644 index 5561c2d..0000000 --- a/ragconnect.base/src/main/resources/mqtt.mustache +++ /dev/null @@ -1,5 +0,0 @@ -aspect MQTT { - public void {{rootNodeName}}.{{mqttSetupWaitUntilReadyMethodName}}(long time, java.util.concurrent.TimeUnit unit) { - {{mqttHandlerField}}.setupWaitUntilReady(time, unit); - } -} diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache index f53cb2f..b1ee1b2 100644 --- a/ragconnect.base/src/main/resources/ragconnect.mustache +++ b/ragconnect.base/src/main/resources/ragconnect.mustache @@ -1,4 +1,3 @@ -{{#usesMqtt}}{{> mqtt}}{{/usesMqtt}} {{> handler}} aspect RagConnect { {{#allEndpointDefinitionList}} diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache index 379633d..e457306 100644 --- a/ragconnect.base/src/main/resources/receiveDefinition.mustache +++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache @@ -92,19 +92,23 @@ private boolean {{parentTypeName}}.{{internalConnectMethodName}}(String {{connec RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}"); boolean success; switch (scheme) { - {{#usesMqtt}} + {{#mqttHandler}} + {{#InUse}} case "mqtt": - success = {{mqttHandlerAttribute}}().newConnection(connectToken, consumer); + success = {{AttributeName}}().newConnection(connectToken, consumer); break; - {{/usesMqtt}} - {{#usesRest}} + {{/InUse}} + {{/mqttHandler}} + {{#restHandler}} + {{#InUse}} case "rest": - success = {{restHandlerAttribute}}().newPUTConnection(connectToken, input -> { + success = {{AttributeName}}().newPUTConnection(connectToken, input -> { // TODO wildcard-topic not supported yet consumer.accept("", input.getBytes()); }); break; - {{/usesRest}} + {{/InUse}} + {{/restHandler}} default: System.err.println("Unknown protocol '" + scheme + "'."); success = false; @@ -124,14 +128,18 @@ public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParam } RagConnectDisconnectHandlerMethod disconnectingMethod; switch (scheme) { - {{#usesMqtt}} - case "mqtt": disconnectingMethod = {{mqttHandlerAttribute}}()::disconnect; + {{#mqttHandler}} + {{#InUse}} + case "mqtt": disconnectingMethod = {{AttributeName}}()::disconnect; break; - {{/usesMqtt}} - {{#usesRest}} - case "rest": disconnectingMethod = {{restHandlerAttribute}}()::disconnect; + {{/InUse}} + {{/mqttHandler}} + {{#restHandler}} + {{#InUse}} + case "rest": disconnectingMethod = {{AttributeName}}()::disconnect; break; - {{/usesRest}} + {{/InUse}} + {{/restHandler}} default: System.err.println("Unknown protocol '" + scheme + "' in '" + {{connectParameterName}} + "' for disconnecting {{parentTypeName}}.{{entityName}}"); return false; diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache index a07265f..bc7bc5a 100644 --- a/ragconnect.base/src/main/resources/sendDefinition.mustache +++ b/ragconnect.base/src/main/resources/sendDefinition.mustache @@ -5,10 +5,11 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete RagConnectToken connectToken = new RagConnectToken(uri, "{{entityName}}"); boolean success; switch (scheme) { - {{#usesMqtt}} + {{#mqttHandler}} + {{#InUse}} case "mqtt": - final MqttHandler handler = {{mqttHandlerAttribute}}().resolveHandler(uri); - final String topic = {{mqttHandlerAttribute}}().extractTopic(uri); + final MqttHandler handler = {{AttributeName}}().resolveHandler(uri); + final String topic = {{AttributeName}}().extractTopic(uri); {{senderName}}.add(() -> { {{#loggingEnabledForWrites}} System.out.println("[Send] {{entityName}} = " + {{getterMethodName}}() + " -> " + {{connectParameterName}}); @@ -21,15 +22,18 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete } success = true; break; - {{/usesMqtt}} - {{#usesRest}} + {{/InUse}} + {{/mqttHandler}} + {{#restHandler}} + {{#InUse}} case "rest": - success = {{restHandlerAttribute}}().newGETConnection(connectToken, () -> { + success = {{AttributeName}}().newGETConnection(connectToken, () -> { {{updateMethodName}}(); return new String({{lastValue}}); }); break; - {{/usesRest}} + {{/InUse}} + {{/restHandler}} default: System.err.println("Unknown protocol '" + scheme + "'."); success = false; @@ -59,16 +63,20 @@ public boolean {{parentTypeName}}.{{disconnectMethodName}}(String {{connectParam {{/incrementalOptionActive}} RagConnectDisconnectHandlerMethod disconnectingMethod; switch (scheme) { - {{#usesMqtt}} + {{#mqttHandler}} + {{#InUse}} case "mqtt": disconnectingMethod = {{senderName}}::remove; break; - {{/usesMqtt}} - {{#usesRest}} + {{/InUse}} + {{/mqttHandler}} + {{#restHandler}} + {{#InUse}} case "rest": - disconnectingMethod = {{restHandlerAttribute}}()::disconnect; + disconnectingMethod = {{AttributeName}}()::disconnect; break; - {{/usesRest}} + {{/InUse}} + {{/restHandler}} default: System.err.println("Unknown protocol '" + scheme + "' in '" + {{connectParameterName}} + "' for disconnecting {{parentTypeName}}.{{entityName}}"); return false; -- GitLab