Skip to content
Snippets Groups Projects
Commit a14db74f authored by René Schöne's avatar René Schöne
Browse files

working on concise grammar

- auto-format aspects
- some refactorings for #39
- unified handler setup
- cleanup
- update extending.md for new process (fewer steps now)
parent 8dd7a3fa
No related branches found
No related tags found
2 merge requests!22Resolve "Feature: Add context-free connect",!21Resolve "Make grammar(s) more concise"
Pipeline #12018 passed
This commit is part of merge request !21. Comments created here will be created in the context of that merge request.
Showing
with 388 additions and 363 deletions
# 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) {
......
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();
......@@ -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) {
......@@ -70,20 +33,28 @@ aspect Analysis {
syn String JavaTypeUse.primitivePrettyPrint() {
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();
}
}
......
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();
}
......@@ -19,8 +19,9 @@ aspect Errors {
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() + ")!")
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())
......@@ -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;
}
......
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;
}
}
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) {
......
......@@ -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())) {
......
RagConnect ::= ConnectSpecificationFile* Handler* Program ;
RagConnect ::= ConnectSpecificationFile* Program Handler* Configuration;
abstract ConnectSpecification ::= EndpointDefinition* DependencyDefinition* MappingDefinition*;
ConnectSpecificationFile : ConnectSpecification ::= <FileName>;
......@@ -27,4 +27,13 @@ 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 ;
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;
......@@ -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 {
......
......@@ -68,18 +68,18 @@ aspect DefaultMappings {
}
syn nta DefaultMappingDefinition RagConnect.defaultBytesToListTreeMapping(String typeName) {
return treeDefaultMappingDefinition("byte[]", JastAddList + "<" + 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" +
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[]",
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" +
......@@ -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;
}
......
......@@ -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); }
......@@ -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 {
......@@ -169,7 +163,7 @@ public class Compiler extends AbstractCompiler {
.addAcceptedValue(OPTION_PROTOCOL_REST, "Enable REST")
);
optionPrintYaml = addOption(
new BooleanOption("printYaml", "Print out YAML instead of generating files")
new BooleanOption("printYaml", "Print out YAML instead of generating files (currently unsupported)")
.defaultValue(false));
optionVerbose = addOption(
new BooleanOption("verbose", "Print more messages while compiling.")
......@@ -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();
}
}
......@@ -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);
......
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;
......
aspect MQTT {
public void {{rootNodeName}}.{{mqttSetupWaitUntilReadyMethodName}}(long time, java.util.concurrent.TimeUnit unit) {
{{mqttHandlerField}}.setupWaitUntilReady(time, unit);
}
}
{{#usesMqtt}}{{> mqtt}}{{/usesMqtt}}
{{> handler}}
aspect RagConnect {
{{#allEndpointDefinitionList}}
......
......@@ -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;
......
......@@ -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;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment