diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 00000000..2e7379ea
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,41 @@
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ "master", "develop" ]
+ pull_request:
+ branches: [ "master" ]
+ schedule:
+ - cron: "28 7 * * 6"
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ java ]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+ queries: +security-and-quality
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v2
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
+ with:
+ category: "/language:${{ matrix.language }}"
diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml
index 0d8244ed..2f10fa61 100644
--- a/.github/workflows/develop.yml
+++ b/.github/workflows/develop.yml
@@ -10,15 +10,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up JDK 1.8
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v3
with:
- java-version: 1.8
- java-package: jdk
+ distribution: temurin
+ java-version: 8
+ - name: Setup Gradle
+ uses: gradle/gradle-build-action@v2
- name: Gradle Plugin Publish Local
run: |
./gradlew :Packager:publishToMavenLocal
+ env:
+ PGP_KEY: ${{ secrets.PGP_KEY }}
+ PGP_PWD: ${{ secrets.PGP_PWD }}
build:
@@ -26,14 +31,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up JDK 1.8
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v3
with:
- java-version: 1.8
- java-package: jdk
- - name: Grant execute permission for gradlew
- run: chmod +x gradlew
+ distribution: temurin
+ java-version: 8
+ - name: Setup Gradle
+ uses: gradle/gradle-build-action@v2
- name: Build with Gradle
run: ./gradlew build
@@ -45,9 +50,12 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Set up JDK 1.8
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v3
with:
- java-version: 1.8
+ distribution: temurin
+ java-version: 8
+ - name: Setup Gradle
+ uses: gradle/gradle-build-action@v2
- name: Gradle Maven Publish
run: |
./gradlew publishMavenJavaPublicationToMavenRepository
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index 6aff882c..45150ab0 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -10,14 +10,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up JDK 1.8
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v3
with:
- java-version: 1.8
- java-package: jdk
- - name: Grant execute permission for gradlew
- run: chmod +x gradlew
+ distribution: temurin
+ java-version: 8
+ - name: Setup Gradle
+ uses: gradle/gradle-build-action@v2
- name: Build with Gradle
run: ./gradlew build
- name: JaCoco Test Report
@@ -33,11 +33,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v3
- name: Set up JDK 1.8
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v3
with:
- java-version: 1.8
+ distribution: temurin
+ java-version: 8
+ - name: Setup Gradle
+ uses: gradle/gradle-build-action@v2
- name: Gradle Maven Publish
run: |
./gradlew publishMavenJavaPublicationToMavenRepository
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index 3e2e265b..e73eef42 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -11,19 +11,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up JDK 1.8
- uses: actions/setup-java@v1
+ uses: actions/setup-java@v3
with:
- java-version: 1.8
- java-package: jdk
- - name: Grant execute permission for gradlew
- run: chmod +x gradlew
+ distribution: temurin
+ java-version: 8
+ - name: Setup Gradle
+ uses: gradle/gradle-build-action@v2
- name: Run unit tests
run: ./gradlew Library:test
- name: Upload Failing Unit Test Results
if: failure()
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: Unit and Int Test Failure Results
path: ./Library/build/reports/tests/**
diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Action.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Action.java
index ce829ac2..31c279bc 100644
--- a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Action.java
+++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Action.java
@@ -28,17 +28,30 @@
/**
* Action Annotation
*
- * Target is a Method
- *
- *
- * If your method only has @Data annotated parameters, it will be called automatically by the SDK.
- * If it is not the case, the SDK will call the TouchPortalPluginListener.onReceive(JsonObject jsonMessage) instead.
+ * Target is a Method or a Class extending TPAction
*
+ *
+ * -
+ * If used on a Method
+ *
+ * - If the method only has @Data annotated parameters, it will be called automatically by the SDK.
+ * - If it is not the case, the SDK will call the TouchPortalPluginListener.onReceive(JsonObject jsonMessage) instead.
+ *
+ *
+ * -
+ * If used on a Class, the SDK will
+ *
+ * - Create a new instance of the Class
+ * - Set the instance's {@link Data} annotated fields
+ * - Call the onInvoke method
+ *
+ *
+ *
*
* @see TP Documentation: Dynamic Actions
*/
@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Action {
/**
* Action id
diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslation.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslation.java
new file mode 100644
index 00000000..6338933b
--- /dev/null
+++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslation.java
@@ -0,0 +1,80 @@
+/*
+ * Touch Portal Plugin SDK
+ *
+ * Copyright 2020 Christophe Carvalho Vilas-Boas
+ * christophe.carvalhovilasboas@gmail.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.christophecvb.touchportal.annotations;
+
+import java.lang.annotation.*;
+
+/**
+ * ActionTranslation Annotation
+ *
+ * Target is a Method
+ *
+ */
+@Repeatable(ActionTranslations.class)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface ActionTranslation {
+ /**
+ * ActionTranslation Language
+ *
+ * @return Language language
+ */
+ Language language();
+
+ /**
+ * ActionTranslation name
+ *
+ * Default is ""
+ *
+ *
+ * @return String name
+ */
+ String name() default "";
+
+ /**
+ * ActionTranslation prefix
+ *
+ * Default is ""
+ *
+ *
+ * @return String prefix
+ */
+ String prefix() default "";
+
+ /**
+ * ActionTranslation description
+ *
+ * Default is ""
+ *
+ *
+ * @return String description
+ */
+ String description() default "";
+
+ /**
+ * ActionTranslation format
+ *
+ * Default is ""
+ *
+ *
+ * @return String format
+ */
+ String format() default "";
+}
diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslations.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslations.java
new file mode 100644
index 00000000..37bcd533
--- /dev/null
+++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslations.java
@@ -0,0 +1,9 @@
+package com.christophecvb.touchportal.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface ActionTranslations {
+ ActionTranslation[] value();
+}
diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Connector.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Connector.java
index d07ecfc2..5aafdd04 100644
--- a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Connector.java
+++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Connector.java
@@ -28,17 +28,30 @@
/**
* Connector Annotation
*
- * Target is a Method
- *
- *
- * If your method only has @Data annotated parameters, it will be called automatically by the SDK.
- * If it is not the case, the SDK will call the TouchPortalPluginListener.onReceive(JsonObject jsonMessage) instead.
+ * Target is a Method or a Class extending TPConnector
*
+ *
+ * -
+ * If used on a Method
+ *
+ * - If the method only has @Data annotated parameters, it will be called automatically by the SDK.
+ * - If it is not the case, the SDK will call the TouchPortalPluginListener.onReceive(JsonObject jsonMessage) instead.
+ *
+ *
+ * -
+ * If used on a Class, the SDK will
+ *
+ * - Create a new instance of the Class
+ * - Set the instance's {@link Data} annotated fields
+ * - Call the onInvoke method
+ *
+ *
+ *
*
* @see TP Documentation: Connectors
*/
@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Connector {
/**
* Connector id
diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ConnectorValue.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ConnectorValue.java
index de3eaa13..c1fdc122 100644
--- a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ConnectorValue.java
+++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ConnectorValue.java
@@ -38,6 +38,6 @@
* @see TP Documentation: Connectors
*/
@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.PARAMETER)
+@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface ConnectorValue {
}
diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Data.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Data.java
index 1a57faf5..a23a9429 100644
--- a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Data.java
+++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Data.java
@@ -34,7 +34,7 @@
* @see TP Documentation: Action Data
*/
@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.PARAMETER)
+@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface Data {
/**
* Data id
diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Language.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Language.java
new file mode 100644
index 00000000..880e2dbf
--- /dev/null
+++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Language.java
@@ -0,0 +1,25 @@
+package com.christophecvb.touchportal.annotations;
+
+/**
+ * Languages supported by Touch Portal
+ */
+public enum Language {
+ //ENGLISH("en"), This is the default language to provide in Action annotation
+ GERMAN("de"),
+ SPANISH("es"),
+ FRENCH("fr"),
+ DUTCH("nl"),
+ PORTUGUESE("pt"),
+ TURKISH("tr");
+
+
+ private final String code;
+
+ Language(String code) {
+ this.code = code;
+ }
+
+ public String getCode() {
+ return this.code;
+ }
+}
diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ParentCategory.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ParentCategory.java
new file mode 100644
index 00000000..d7611fcc
--- /dev/null
+++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ParentCategory.java
@@ -0,0 +1,25 @@
+package com.christophecvb.touchportal.annotations;
+
+/**
+ * Parent Categories supported by Touch Portal
+ */
+public enum ParentCategory {
+ AUDIO("audio"),
+ STREAMING("streaming"),
+ CONTENT("content"),
+ HOME_AUTOMATION("homeautomation"),
+ SOCIAL("social "),
+ GAMES("games"),
+ MISC("misc");
+
+
+ private final String key;
+
+ ParentCategory(String key) {
+ this.key = key;
+ }
+
+ public String getKey() {
+ return this.key;
+ }
+}
diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Plugin.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Plugin.java
index 418b32a9..8f583b96 100644
--- a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Plugin.java
+++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Plugin.java
@@ -72,4 +72,14 @@
* @return String colorLight
*/
String colorLight();
+
+ /**
+ * Plugin Parent Category
+ *
+ * Value from enum {@link ParentCategory}
+ *
+ *
+ * @return ParentCategory parentCategory
+ */
+ ParentCategory parentCategory() default ParentCategory.MISC;
}
diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Setting.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Setting.java
index eded71fb..6d276dca 100644
--- a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Setting.java
+++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Setting.java
@@ -99,4 +99,17 @@
* @return boolean isReadOnly
*/
boolean isReadOnly() default false;
+
+ /**
+ * Setting Tooltip
+ *
+ * @return {@link Tooltip} tooltip
+ */
+ Tooltip tooltip() default @Tooltip(body = "");
+
+ @interface Tooltip {
+ String title() default "";
+ String body();
+ String docUrl() default "";
+ }
}
diff --git a/AnnotationsProcessor/build.gradle b/AnnotationsProcessor/build.gradle
index 67a555eb..96232ca1 100644
--- a/AnnotationsProcessor/build.gradle
+++ b/AnnotationsProcessor/build.gradle
@@ -77,11 +77,11 @@ javadoc {
}
dependencies {
- implementation group: 'com.google.code.gson', name: 'gson', version: '2.9.0'
- implementation 'com.google.auto.service:auto-service:1.0.1'
- implementation group: 'com.squareup', name: 'javapoet', version: '1.13.0'
+ implementation libs.gson
+ implementation libs.autoservice
+ implementation libs.javapoet
implementation project(':Annotations')
implementation project(':Helpers')
- testImplementation group: 'junit', name: 'junit', version: '4.13.2'
+ testImplementation libs.junit
}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/ActionProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/ActionProcessor.java
new file mode 100644
index 00000000..5eb59706
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/ActionProcessor.java
@@ -0,0 +1,88 @@
+package com.christophecvb.touchportal.annotations.processor;
+
+import com.christophecvb.touchportal.annotations.*;
+import com.christophecvb.touchportal.annotations.processor.utils.Pair;
+import com.christophecvb.touchportal.annotations.processor.utils.SpecUtils;
+import com.christophecvb.touchportal.helpers.ActionHelper;
+import com.christophecvb.touchportal.helpers.GenericHelper;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+import java.util.Set;
+
+public class ActionProcessor {
+ /**
+ * Generates a JsonObject and a TypeSpec.Builder representing the {@link Action}
+ *
+ * @param processor {@link TouchPortalPluginAnnotationsProcessor}
+ * @param roundEnv RoundEnvironment
+ * @param pluginElement Element
+ * @param plugin {@link Plugin}
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param actionElement Element
+ * @return Pair<JsonObject, TypeSpec.Builder> actionPair
+ * @throws GenericHelper.TPTypeException If a used type is not Supported
+ */
+ public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element actionElement) throws GenericHelper.TPTypeException {
+ processor.getMessager().printMessage(Diagnostic.Kind.NOTE, "Process Action: " + actionElement.getSimpleName());
+ Action action = actionElement.getAnnotation(Action.class);
+
+ TypeSpec.Builder actionTypeSpecBuilder = SpecUtils.createActionTypeSpecBuilder(pluginElement, categoryElement, category, actionElement, action);
+
+ JsonObject jsonAction = new JsonObject();
+ jsonAction.addProperty(ActionHelper.ID, ActionHelper.getActionId(pluginElement, categoryElement, category, actionElement, action));
+ jsonAction.addProperty(ActionHelper.NAME, ActionHelper.getActionName(actionElement, action));
+ jsonAction.addProperty(ActionHelper.PREFIX, action.prefix());
+ jsonAction.addProperty(ActionHelper.TYPE, action.type());
+ if (!action.description().isEmpty()) {
+ jsonAction.addProperty(ActionHelper.DESCRIPTION, action.description());
+ }
+ if (!action.format().isEmpty()) {
+ jsonAction.addProperty(ActionHelper.FORMAT, action.format());
+ jsonAction.addProperty(ActionHelper.TRY_INLINE, true);
+ }
+ jsonAction.addProperty(ActionHelper.HAS_HOLD_FUNCTIONALITY, action.hasHoldFunctionality());
+
+ ActionTranslation[] actionTranslations = actionElement.getAnnotationsByType(ActionTranslation.class);
+ if (actionTranslations.length > 0) {
+ for (ActionTranslation actionTranslation : actionTranslations) {
+ String languageCode = actionTranslation.language().getCode();
+ if (!actionTranslation.name().isEmpty()) {
+ jsonAction.addProperty(ActionHelper.NAME + "_" + languageCode, actionTranslation.name());
+ }
+ if (!actionTranslation.prefix().isEmpty()) {
+ jsonAction.addProperty(ActionHelper.PREFIX + "_" + languageCode, actionTranslation.prefix());
+ }
+ if (!actionTranslation.description().isEmpty()) {
+ jsonAction.addProperty(ActionHelper.DESCRIPTION + "_" + languageCode, actionTranslation.description());
+ }
+ if (!actionTranslation.format().isEmpty()) {
+ jsonAction.addProperty(ActionHelper.FORMAT + "_" + languageCode, actionTranslation.format());
+ }
+ }
+ }
+
+ JsonArray jsonActionData = new JsonArray();
+ Set extends Element> dataElements = roundEnv.getElementsAnnotatedWith(Data.class);
+ for (Element dataElement : dataElements) {
+ Element enclosingElement = dataElement.getEnclosingElement();
+ if (actionElement.equals(enclosingElement)) {
+ Pair actionDataResult = DataProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement, category, actionElement, action, jsonAction, dataElement);
+ jsonActionData.add(actionDataResult.first);
+ if (actionDataResult.second != null) {
+ actionTypeSpecBuilder.addType(actionDataResult.second.build());
+ }
+ }
+ }
+ if (jsonActionData.size() > 0) {
+ jsonAction.add(ActionHelper.DATA, jsonActionData);
+ }
+
+ return Pair.create(jsonAction, actionTypeSpecBuilder);
+ }
+}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java
new file mode 100644
index 00000000..acd990f1
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java
@@ -0,0 +1,110 @@
+package com.christophecvb.touchportal.annotations.processor;
+
+import com.christophecvb.touchportal.annotations.*;
+import com.christophecvb.touchportal.annotations.processor.utils.Pair;
+import com.christophecvb.touchportal.annotations.processor.utils.SpecUtils;
+import com.christophecvb.touchportal.helpers.CategoryHelper;
+import com.christophecvb.touchportal.helpers.GenericHelper;
+import com.christophecvb.touchportal.helpers.PluginHelper;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+import javax.tools.Diagnostic;
+import java.util.Set;
+
+public class CategoryProcessor {
+ /**
+ * Generates a JsonObject and a TypeSpec.Builder representing the {@link Category}
+ *
+ * @param processor {@link TouchPortalPluginAnnotationsProcessor}
+ * @param roundEnv RoundEnvironment
+ * @param pluginElement Element
+ * @param plugin {@link Plugin}
+ * @param categoryElement Element
+ * @return Pair<JsonObject, TypeSpec.Builder> categoryPair
+ * @throws GenericHelper.TPTypeException If a used type is not Supported
+ * @throws TPAnnotationException If an Annotation is misused
+ */
+ public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement) throws GenericHelper.TPTypeException, TPAnnotationException {
+ processor.getMessager().printMessage(Diagnostic.Kind.NOTE, "Process Category: " + categoryElement.getSimpleName());
+ Category category = categoryElement.getAnnotation(Category.class);
+
+ TypeSpec.Builder categoryTypeSpecBuilder = SpecUtils.createCategoryTypeSpecBuilder(pluginElement, categoryElement, category);
+
+ JsonObject jsonCategory = new JsonObject();
+ jsonCategory.addProperty(CategoryHelper.ID, CategoryHelper.getCategoryId(pluginElement, categoryElement, category));
+ jsonCategory.addProperty(CategoryHelper.NAME, CategoryHelper.getCategoryName(categoryElement, category));
+ jsonCategory.addProperty(CategoryHelper.IMAGE_PATH, PluginHelper.TP_PLUGIN_FOLDER + pluginElement.getSimpleName() + "/" + category.imagePath());
+
+ TypeSpec.Builder actionsTypeSpecBuilder = TypeSpec.classBuilder("Actions").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ JsonArray jsonActions = new JsonArray();
+ Set extends Element> actionElements = roundEnv.getElementsAnnotatedWith(Action.class);
+ for (Element actionElement : actionElements) {
+ Action action = actionElement.getAnnotation(Action.class);
+ String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id();
+ if (categoryId.equals(action.categoryId())) {
+ Pair actionResult = ActionProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement, category, actionElement);
+ jsonActions.add(actionResult.first);
+ actionsTypeSpecBuilder.addType(actionResult.second.build());
+ }
+ }
+ categoryTypeSpecBuilder.addType(actionsTypeSpecBuilder.build());
+ jsonCategory.add(CategoryHelper.ACTIONS, jsonActions);
+
+ TypeSpec.Builder connectorsTypeSpecBuilder = TypeSpec.classBuilder("Connectors").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ JsonArray jsonConnectors = new JsonArray();
+ Set extends Element> connectorElements = roundEnv.getElementsAnnotatedWith(Connector.class);
+ for (Element connectorElement : connectorElements) {
+ Connector connector = connectorElement.getAnnotation(Connector.class);
+ String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id();
+ if (categoryId.equals(connector.categoryId())) {
+ Pair connectorResult = ConnectorProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement, category, connectorElement);
+ jsonConnectors.add(connectorResult.first);
+ connectorsTypeSpecBuilder.addType(connectorResult.second.build());
+ }
+ }
+ categoryTypeSpecBuilder.addType(connectorsTypeSpecBuilder.build());
+ jsonCategory.add(CategoryHelper.CONNECTORS, jsonConnectors);
+
+ TypeSpec.Builder statesTypeSpecBuilder = TypeSpec.classBuilder("States").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ JsonArray jsonStates = new JsonArray();
+ Set extends Element> stateElements = roundEnv.getElementsAnnotatedWith(State.class);
+ for (Element stateElement : stateElements) {
+ State state = stateElement.getAnnotation(State.class);
+ String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id();
+ if (categoryId.equals(state.categoryId())) {
+ Pair stateResult = StateProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement, category, stateElement);
+ jsonStates.add(stateResult.first);
+ statesTypeSpecBuilder.addType(stateResult.second.build());
+ }
+ }
+ categoryTypeSpecBuilder.addType(statesTypeSpecBuilder.build());
+ jsonCategory.add(CategoryHelper.STATES, jsonStates);
+
+ TypeSpec.Builder eventsTypeSpecBuilder = TypeSpec.classBuilder("Events").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ JsonArray jsonEvents = new JsonArray();
+ Set extends Element> eventElements = roundEnv.getElementsAnnotatedWith(Event.class);
+ for (Element eventElement : eventElements) {
+ State state = eventElement.getAnnotation(State.class);
+ String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id();
+ if (state != null) {
+ if (categoryId.equals(state.categoryId())) {
+ Pair eventResult = EventProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement, category, eventElement);
+ jsonEvents.add(eventResult.first);
+ eventsTypeSpecBuilder.addType(eventResult.second.build());
+ }
+ }
+ else {
+ throw new TPAnnotationException.Builder(State.class).isMissing(true).forElement(eventElement).build();
+ }
+ }
+ categoryTypeSpecBuilder.addType(eventsTypeSpecBuilder.build());
+ jsonCategory.add(CategoryHelper.EVENTS, jsonEvents);
+
+ return Pair.create(jsonCategory, categoryTypeSpecBuilder);
+ }
+}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/ConnectorProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/ConnectorProcessor.java
new file mode 100644
index 00000000..45388fb1
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/ConnectorProcessor.java
@@ -0,0 +1,81 @@
+package com.christophecvb.touchportal.annotations.processor;
+
+import com.christophecvb.touchportal.annotations.*;
+import com.christophecvb.touchportal.annotations.processor.utils.Pair;
+import com.christophecvb.touchportal.annotations.processor.utils.SpecUtils;
+import com.christophecvb.touchportal.helpers.ConnectorHelper;
+import com.christophecvb.touchportal.helpers.GenericHelper;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+import java.util.Set;
+
+public class ConnectorProcessor {
+
+ /**
+ * Generates a JsonObject and a TypeSpec.Builder representing the {@link Connector}
+ *
+ * @param processor {@link TouchPortalPluginAnnotationsProcessor}
+ * @param roundEnv RoundEnvironment
+ * @param pluginElement Element
+ * @param plugin {@link Plugin}
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param connectorElement Element
+ * @return Pair<JsonObject, TypeSpec.Builder> actionPair
+ * @throws GenericHelper.TPTypeException If a used type is not Supported
+ * @throws TPAnnotationException If an Annotation is misused
+ */
+ public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element connectorElement) throws GenericHelper.TPTypeException, TPAnnotationException {
+ processor.getMessager().printMessage(Diagnostic.Kind.NOTE, "Process Connector: " + connectorElement.getSimpleName());
+ Connector connector = connectorElement.getAnnotation(Connector.class);
+
+ TypeSpec.Builder connectorTypeSpecBuilder = SpecUtils.createConnectorTypeSpecBuilder(pluginElement, categoryElement, category, connectorElement, connector);
+
+ JsonObject jsonConnector = new JsonObject();
+ jsonConnector.addProperty(ConnectorHelper.ID, ConnectorHelper.getConnectorId(pluginElement, categoryElement, category, connectorElement, connector));
+ jsonConnector.addProperty(ConnectorHelper.NAME, ConnectorHelper.getConnectorName(connectorElement, connector));
+ jsonConnector.addProperty(ConnectorHelper.FORMAT, connector.format());
+
+ String classMethod = pluginElement.getSimpleName() + "." + connectorElement.getSimpleName();
+ boolean connectorValueFound = false;
+ Set extends Element> connectorValueElements = roundEnv.getElementsAnnotatedWith(ConnectorValue.class);
+ for (Element connectorValueElement : connectorValueElements) {
+ Element enclosingElement = connectorValueElement.getEnclosingElement();
+ if (connectorElement.equals(enclosingElement)) {
+ String classMethodParameter = classMethod + "(" + connectorValueElement.getSimpleName() + ")";
+ String desiredType = GenericHelper.getTouchPortalType(classMethodParameter, connectorValueElement);
+ if (!desiredType.equals(GenericHelper.TP_TYPE_NUMBER)) {
+ throw new GenericHelper.TPTypeException.Builder(classMethodParameter).typeUnsupported(desiredType).forAnnotation(GenericHelper.TPTypeException.ForAnnotation.CONNECTOR_VALUE).build();
+ }
+ connectorValueFound = true;
+ break;
+ }
+ }
+ if (!connectorValueFound) {
+ throw new TPAnnotationException.Builder(ConnectorValue.class).isMissing(true).forElement(connectorElement).build();
+ }
+
+ JsonArray jsonConnectorData = new JsonArray();
+ Set extends Element> dataElements = roundEnv.getElementsAnnotatedWith(Data.class);
+ for (Element dataElement : dataElements) {
+ Element enclosingElement = dataElement.getEnclosingElement();
+ if (connectorElement.equals(enclosingElement)) {
+ Pair connectorDataResult = DataProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement, category, connectorElement, connector, jsonConnector, dataElement);
+ jsonConnectorData.add(connectorDataResult.first);
+ if (connectorDataResult.second != null) {
+ connectorTypeSpecBuilder.addType(connectorDataResult.second.build());
+ }
+ }
+ }
+ if (jsonConnectorData.size() > 0) {
+ jsonConnector.add(ConnectorHelper.DATA, jsonConnectorData);
+ }
+
+ return Pair.create(jsonConnector, connectorTypeSpecBuilder);
+ }
+}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/DataProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/DataProcessor.java
new file mode 100644
index 00000000..7d8af7ce
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/DataProcessor.java
@@ -0,0 +1,169 @@
+package com.christophecvb.touchportal.annotations.processor;
+
+import com.christophecvb.touchportal.annotations.*;
+import com.christophecvb.touchportal.annotations.processor.utils.Pair;
+import com.christophecvb.touchportal.annotations.processor.utils.SpecUtils;
+import com.christophecvb.touchportal.helpers.*;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class DataProcessor {
+ /**
+ * Generates a JsonObject and a TypeSpec.Builder representing the {@link Data} for an {@link Action} or a {@link Connector}
+ *
+ * @param Action or Connector Annotation
+ * @param processor {@link TouchPortalPluginAnnotationsProcessor}
+ * @param roundEnv RoundEnvironment
+ * @param pluginElement Element
+ * @param plugin {@link Plugin}
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param targetElement Element
+ * @param annotation {@link Action} or {@link Connector}
+ * @param jsonElement JsonObject
+ * @param dataElement Element
+ * @return Pair<JsonObject, TypeSpec.Builder> dataPair
+ * @throws GenericHelper.TPTypeException If a used type is not Supported
+ */
+ public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element targetElement, T annotation, JsonObject jsonElement, Element dataElement) throws GenericHelper.TPTypeException {
+ String annotationName = annotation.annotationType().getSimpleName();
+ boolean isAction = annotation instanceof Action;
+
+ processor.getMessager().printMessage(Diagnostic.Kind.NOTE, "Process " + annotationName + " Data: " + dataElement.getSimpleName());
+ Data data = dataElement.getAnnotation(Data.class);
+
+ TypeSpec.Builder dataTypeSpecBuilder = isAction ?
+ SpecUtils.createActionDataTypeSpecBuilder(pluginElement, categoryElement, category, targetElement, (Action) annotation, dataElement, data) :
+ SpecUtils.createConnectorDataTypeSpecBuilder(pluginElement, categoryElement, category, targetElement, (Connector) annotation, dataElement, data);
+
+ Element method = dataElement.getEnclosingElement();
+ String className = method.getEnclosingElement().getSimpleName() + "." + annotationName + "(" + dataElement.getSimpleName() + ")";
+
+ JsonObject jsonData = new JsonObject();
+ String desiredTPType = GenericHelper.getTouchPortalType(className, dataElement);
+ jsonData.addProperty(DataHelper.TYPE, desiredTPType);
+ jsonData.addProperty(DataHelper.LABEL, DataHelper.getDataLabel(dataElement, data));
+ // Default Value
+ switch (desiredTPType) {
+ case GenericHelper.TP_TYPE_NUMBER:
+ double defaultValue = 0;
+ try {
+ defaultValue = Double.parseDouble(data.defaultValue());
+ }
+ catch (NumberFormatException ignored) {}
+ jsonData.addProperty(DataHelper.DEFAULT, defaultValue);
+ break;
+
+ case GenericHelper.TP_TYPE_SWITCH:
+ jsonData.addProperty(DataHelper.DEFAULT, data.defaultValue().equals("true"));
+ break;
+
+ default:
+ jsonData.addProperty(DataHelper.DEFAULT, data.defaultValue());
+ break;
+ }
+ AtomicReference dataId = new AtomicReference<>( isAction ?
+ DataHelper.getActionDataId(pluginElement, categoryElement, category, targetElement, (Action) annotation, dataElement, data) :
+ DataHelper.getConnectorDataId(pluginElement, categoryElement, category, targetElement, (Connector) annotation, dataElement, data));
+ // Specific properties
+ switch (desiredTPType) {
+ case GenericHelper.TP_TYPE_CHOICE:
+ JsonArray dataValueChoices = new JsonArray();
+ if (!data.stateId().isEmpty()) {
+ Optional extends Element> optionalStateElement = roundEnv.getElementsAnnotatedWith(State.class).stream().filter(element -> {
+ State state = element.getAnnotation(State.class);
+ String shortStateId = !state.id().isEmpty() ? state.id() : element.getSimpleName().toString();
+ return shortStateId.equals(data.stateId());
+ }).findFirst();
+ if (optionalStateElement.isPresent()) {
+ dataTypeSpecBuilder = null;
+
+ Element stateElement = optionalStateElement.get();
+ State state = stateElement.getAnnotation(State.class);
+ dataId.set(StateHelper.getStateId(pluginElement, categoryElement, category, stateElement, state));
+ for (String valueChoice : state.valueChoices()) {
+ dataValueChoices.add(valueChoice);
+ }
+ jsonData.addProperty(DataHelper.DEFAULT, data.defaultValue().isEmpty() ? state.defaultValue() : data.defaultValue());
+ }
+ else {
+ for (String valueChoice : data.valueChoices()) {
+ dataValueChoices.add(valueChoice);
+ }
+ }
+ }
+ else {
+ for (String valueChoice : data.valueChoices()) {
+ dataValueChoices.add(valueChoice);
+ }
+ }
+ jsonData.add(DataHelper.VALUE_CHOICES, dataValueChoices);
+ break;
+
+ case GenericHelper.TP_TYPE_FILE:
+ if (data.isDirectory()) {
+ jsonData.addProperty(DataHelper.TYPE, GenericHelper.TP_TYPE_DIRECTORY);
+ }
+ else {
+ JsonArray jsonExtensions = new JsonArray();
+ for (String extension : data.extensions()) {
+ if (extension.matches(DataHelper.EXTENSION_FORMAT)) {
+ jsonExtensions.add(extension);
+ }
+ else {
+ processor.getMessager().printMessage(Diagnostic.Kind.ERROR, annotationName + " Data Extension: [" + extension + "] format is not valid");
+ }
+ }
+ jsonData.add(DataHelper.EXTENSIONS, jsonExtensions);
+ }
+ break;
+
+ case GenericHelper.TP_TYPE_TEXT:
+ if (data.isColor()) {
+ jsonData.addProperty(DataHelper.TYPE, GenericHelper.TP_TYPE_COLOR);
+ if (!data.defaultValue().isEmpty()) {
+ if (!data.defaultValue().matches(DataHelper.COLOR_FORMAT)) {
+ processor.getMessager().printMessage(Diagnostic.Kind.ERROR, annotationName + " Data Color Default value: [" + data.defaultValue() + "] format is not valid");
+ }
+ }
+ }
+ break;
+
+ case GenericHelper.TP_TYPE_NUMBER:
+ try {
+ double defaultValue = jsonData.get(DataHelper.DEFAULT).getAsDouble();
+ if (defaultValue < data.minValue() || defaultValue > data.maxValue()) {
+ throw new GenericHelper.TPTypeException.Builder(className).defaultNotInRange().build();
+ }
+ }
+ catch (NumberFormatException numberFormatException) {
+ throw new GenericHelper.TPTypeException.Builder(className).defaultInvalid(data.defaultValue()).build();
+ }
+ jsonData.addProperty(DataHelper.ALLOW_DECIMALS, GenericHelper.getTouchPortalTypeNumberAllowDecimals(dataElement.asType().toString()));
+ if (data.minValue() > Double.NEGATIVE_INFINITY) {
+ jsonData.addProperty(DataHelper.MIN_VALUE, data.minValue());
+ }
+ if (data.maxValue() < Double.POSITIVE_INFINITY) {
+ jsonData.addProperty(DataHelper.MAX_VALUE, data.maxValue());
+ }
+ break;
+ }
+ jsonData.addProperty(DataHelper.ID, dataId.get());
+ String format = isAction ? ((Action) annotation).format() : ((Connector) annotation).format();
+ if (!format.isEmpty()) {
+ // Replace wildcards
+ String rawFormat = jsonElement.get(isAction ? ActionHelper.FORMAT : ConnectorHelper.FORMAT).getAsString();
+ jsonElement.addProperty(isAction ? ActionHelper.FORMAT : ConnectorHelper.FORMAT, rawFormat.replace("{$" + (data.id().isEmpty() ? dataElement.getSimpleName().toString() : data.id()) + "$}", "{$" + dataId.get() + "$}"));
+ }
+
+ return Pair.create(jsonData, dataTypeSpecBuilder);
+ }
+}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/EventProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/EventProcessor.java
new file mode 100644
index 00000000..f5d7a44d
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/EventProcessor.java
@@ -0,0 +1,69 @@
+package com.christophecvb.touchportal.annotations.processor;
+
+import com.christophecvb.touchportal.annotations.Category;
+import com.christophecvb.touchportal.annotations.Event;
+import com.christophecvb.touchportal.annotations.Plugin;
+import com.christophecvb.touchportal.annotations.State;
+import com.christophecvb.touchportal.annotations.processor.utils.Pair;
+import com.christophecvb.touchportal.annotations.processor.utils.SpecUtils;
+import com.christophecvb.touchportal.helpers.EventHelper;
+import com.christophecvb.touchportal.helpers.GenericHelper;
+import com.christophecvb.touchportal.helpers.StateHelper;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+
+public class EventProcessor {
+ /**
+ * Generates a JsonObject and a TypeSpec.Builder representing the {@link Event}
+ *
+ * @param processor {@link TouchPortalPluginAnnotationsProcessor}
+ * @param roundEnv RoundEnvironment
+ * @param pluginElement Element
+ * @param plugin {@link Plugin}
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param eventElement Element
+ * @return Pair<JsonObject, TypeSpec.Builder> eventPair
+ * @throws GenericHelper.TPTypeException If a used type is not Supported
+ * @throws TPAnnotationException If an Annotation is misused
+ */
+ public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element eventElement) throws GenericHelper.TPTypeException, TPAnnotationException {
+ processor.getMessager().printMessage(Diagnostic.Kind.NOTE, "Process Event: " + eventElement.getSimpleName());
+ State state = eventElement.getAnnotation(State.class);
+ Event event = eventElement.getAnnotation(Event.class);
+
+ String reference = eventElement.getEnclosingElement().getSimpleName() + "." + eventElement.getSimpleName();
+
+ if (state == null) {
+ throw new TPAnnotationException.Builder(State.class).isMissing(true).forElement(eventElement).build();
+ }
+
+ TypeSpec.Builder eventTypeSpecBuilder = SpecUtils.createEventTypeSpecBuilder(pluginElement, categoryElement, category, eventElement, event);
+
+ JsonObject jsonEvent = new JsonObject();
+ jsonEvent.addProperty(EventHelper.ID, EventHelper.getEventId(pluginElement, categoryElement, category, eventElement, event));
+ jsonEvent.addProperty(EventHelper.TYPE, EventHelper.TYPE_COMMUNICATE);
+ jsonEvent.addProperty(EventHelper.NAME, EventHelper.getEventName(eventElement, event));
+ jsonEvent.addProperty(EventHelper.FORMAT, event.format());
+ String desiredTPType = GenericHelper.getTouchPortalType(reference, eventElement);
+ if (desiredTPType.equals(StateHelper.TYPE_TEXT)) {
+ jsonEvent.addProperty(EventHelper.VALUE_TYPE, EventHelper.VALUE_TYPE_CHOICE);
+ JsonArray eventValueChoices = new JsonArray();
+ for (String valueChoice : event.valueChoices()) {
+ eventValueChoices.add(valueChoice);
+ }
+ jsonEvent.add(EventHelper.VALUE_CHOICES, eventValueChoices);
+ jsonEvent.addProperty(EventHelper.VALUE_STATE_ID, StateHelper.getStateId(pluginElement, categoryElement, category, eventElement, state));
+ }
+ else {
+ throw new GenericHelper.TPTypeException.Builder(reference).typeUnsupported(desiredTPType).forAnnotation(GenericHelper.TPTypeException.ForAnnotation.EVENT).build();
+ }
+
+ return Pair.create(jsonEvent, eventTypeSpecBuilder);
+ }
+}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java
new file mode 100644
index 00000000..f68dcde0
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java
@@ -0,0 +1,81 @@
+package com.christophecvb.touchportal.annotations.processor;
+
+import com.christophecvb.touchportal.annotations.Category;
+import com.christophecvb.touchportal.annotations.Plugin;
+import com.christophecvb.touchportal.annotations.Setting;
+import com.christophecvb.touchportal.annotations.processor.utils.Pair;
+import com.christophecvb.touchportal.annotations.processor.utils.SpecUtils;
+import com.christophecvb.touchportal.helpers.GenericHelper;
+import com.christophecvb.touchportal.helpers.PluginHelper;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+import javax.tools.Diagnostic;
+import java.util.Set;
+
+public class PluginProcessor {
+ /**
+ * Generates a JsonObject and a TypeSpec.Builder representing the {@link Plugin}
+ *
+ * @param processor {@link TouchPortalPluginAnnotationsProcessor}
+ * @param roundEnv RoundEnvironment
+ * @param pluginElement Element
+ * @return Pair<JsonObject, TypeSpec.Builder> pluginPair
+ * @throws GenericHelper.TPTypeException If a used type is not Supported
+ * @throws TPAnnotationException If an Annotation is misused
+ */
+ public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement) throws GenericHelper.TPTypeException, TPAnnotationException {
+ processor.getMessager().printMessage(Diagnostic.Kind.NOTE, "Process Plugin: " + pluginElement.getSimpleName());
+ Plugin plugin = pluginElement.getAnnotation(Plugin.class);
+
+ TypeSpec.Builder pluginTypeSpecBuilder = SpecUtils.createPluginTypeSpecBuilder(pluginElement, plugin);
+
+ JsonObject jsonPlugin = new JsonObject();
+ jsonPlugin.addProperty(PluginHelper.API, PluginHelper.TOUCH_PORTAL_PLUGIN_VERSION);
+ jsonPlugin.addProperty(PluginHelper.VERSION, plugin.version());
+ jsonPlugin.addProperty(PluginHelper.NAME, PluginHelper.getPluginName(pluginElement, plugin));
+ jsonPlugin.addProperty(PluginHelper.ID, PluginHelper.getPluginId(pluginElement));
+ JsonObject jsonConfiguration = new JsonObject();
+ jsonConfiguration.addProperty(PluginHelper.CONFIGURATION_COLOR_DARK, plugin.colorDark());
+ jsonConfiguration.addProperty(PluginHelper.CONFIGURATION_COLOR_LIGHT, plugin.colorLight());
+ jsonConfiguration.addProperty(PluginHelper.CONFIGURATION_PARENT_CATEGORY, plugin.parentCategory().getKey());
+ jsonPlugin.add(PluginHelper.CONFIGURATION, jsonConfiguration);
+ jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND, "java -Dapple.awt.UIElement=true -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START);
+ jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND + PluginHelper.PLUGIN_START_COMMAND_SUFFIX_WIN, "java -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START);
+ jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND + PluginHelper.PLUGIN_START_COMMAND_SUFFIX_MACOS, "java -Dapple.awt.UIElement=true -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START);
+ jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND + PluginHelper.PLUGIN_START_COMMAND_SUFFIX_LINUX, "java -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START);
+
+ TypeSpec.Builder settingsTypeSpecBuilder = TypeSpec.classBuilder("Settings").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ JsonArray jsonSettings = new JsonArray();
+ Set extends Element> settingElements = roundEnv.getElementsAnnotatedWith(Setting.class);
+ for (Element settingElement : settingElements) {
+ Pair settingResult = SettingProcessor.process(processor, settingElement);
+ jsonSettings.add(settingResult.first);
+ settingsTypeSpecBuilder.addType(settingResult.second.build());
+ }
+ if (jsonSettings.size() > 0) {
+ jsonPlugin.add(PluginHelper.SETTINGS, jsonSettings);
+ }
+ pluginTypeSpecBuilder.addType(settingsTypeSpecBuilder.build());
+
+ JsonArray jsonCategories = new JsonArray();
+ Set extends Element> categoryElements = roundEnv.getElementsAnnotatedWith(Category.class);
+ for (Element categoryElement : categoryElements) {
+ Pair categoryResult = CategoryProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement);
+ jsonCategories.add(categoryResult.first);
+ pluginTypeSpecBuilder.addType(categoryResult.second.build());
+ }
+ if (jsonCategories.size() > 0) {
+ jsonPlugin.add(PluginHelper.CATEGORIES, jsonCategories);
+ }
+ else {
+ throw new TPAnnotationException.Builder(Category.class).isMissing(true).build();
+ }
+
+ return Pair.create(jsonPlugin, pluginTypeSpecBuilder);
+ }
+}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java
new file mode 100644
index 00000000..6e27f229
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java
@@ -0,0 +1,87 @@
+package com.christophecvb.touchportal.annotations.processor;
+
+import com.christophecvb.touchportal.annotations.Setting;
+import com.christophecvb.touchportal.annotations.processor.utils.Pair;
+import com.christophecvb.touchportal.annotations.processor.utils.SpecUtils;
+import com.christophecvb.touchportal.helpers.GenericHelper;
+import com.christophecvb.touchportal.helpers.SettingHelper;
+import com.google.gson.JsonObject;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+
+public class SettingProcessor {
+ /**
+ * Generates a JsonObject and a TypeSpec.Builder representing the {@link Setting}
+ *
+ * @param processor {@link TouchPortalPluginAnnotationsProcessor}
+ * @param settingElement Element
+ * @return Pair<JsonObject, TypeSpec.Builder> statePair
+ * @throws GenericHelper.TPTypeException If a used type is not Supported
+ */
+ public static Pair process(TouchPortalPluginAnnotationsProcessor processor, Element settingElement) throws GenericHelper.TPTypeException {
+ processor.getMessager().printMessage(Diagnostic.Kind.NOTE, "Process Setting: " + settingElement.getSimpleName());
+ Setting setting = settingElement.getAnnotation(Setting.class);
+
+ TypeSpec.Builder settingTypeSpecBuilder = SpecUtils.createSettingTypeSpecBuilder(settingElement, setting);
+
+ String className = settingElement.getEnclosingElement().getSimpleName() + "." + settingElement.getSimpleName();
+
+ JsonObject jsonSetting = new JsonObject();
+ jsonSetting.addProperty(SettingHelper.NAME, SettingHelper.getSettingName(settingElement, setting));
+ String desiredTPType = GenericHelper.getTouchPortalType(className, settingElement);
+ jsonSetting.addProperty(SettingHelper.TYPE, desiredTPType);
+ jsonSetting.addProperty(SettingHelper.DEFAULT, setting.defaultValue());
+ jsonSetting.addProperty(SettingHelper.IS_READ_ONLY, setting.isReadOnly());
+
+ if (!setting.tooltip().body().isEmpty()) {
+ JsonObject tooltip = new JsonObject();
+
+ tooltip.addProperty(SettingHelper.Tooltip.BODY, setting.tooltip().body());
+
+ if (!setting.tooltip().title().isEmpty()) {
+ tooltip.addProperty(SettingHelper.Tooltip.TITLE, setting.tooltip().title());
+ }
+ if (!setting.tooltip().docUrl().isEmpty()) {
+ tooltip.addProperty(SettingHelper.Tooltip.DOC_URL, setting.tooltip().docUrl());
+ }
+
+ jsonSetting.add(SettingHelper.TOOLTIP, tooltip);
+ }
+
+ switch (desiredTPType) {
+ case SettingHelper.TYPE_TEXT:
+ if (setting.maxLength() > 0) {
+ jsonSetting.addProperty(SettingHelper.MAX_LENGTH, setting.maxLength());
+ }
+ if (setting.isPassword()) {
+ jsonSetting.addProperty(SettingHelper.IS_PASSWORD, true);
+ }
+ break;
+
+ case SettingHelper.TYPE_NUMBER:
+ try {
+ double defaultValue = Double.parseDouble(setting.defaultValue());
+ if (defaultValue < setting.minValue() || defaultValue > setting.maxValue()) {
+ throw new GenericHelper.TPTypeException.Builder(className).defaultNotInRange().build();
+ }
+ }
+ catch (NumberFormatException numberFormatException) {
+ throw new GenericHelper.TPTypeException.Builder(className).defaultInvalid(setting.defaultValue()).build();
+ }
+ if (setting.minValue() > Double.NEGATIVE_INFINITY) {
+ jsonSetting.addProperty(SettingHelper.MIN_VALUE, setting.minValue());
+ }
+ if (setting.maxValue() < Double.POSITIVE_INFINITY) {
+ jsonSetting.addProperty(SettingHelper.MAX_VALUE, setting.maxValue());
+ }
+ break;
+
+ default:
+ throw new GenericHelper.TPTypeException.Builder(className).typeUnsupported(desiredTPType).forAnnotation(GenericHelper.TPTypeException.ForAnnotation.SETTING).build();
+ }
+
+ return Pair.create(jsonSetting, settingTypeSpecBuilder);
+ }
+}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/StateProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/StateProcessor.java
new file mode 100644
index 00000000..2e398027
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/StateProcessor.java
@@ -0,0 +1,66 @@
+package com.christophecvb.touchportal.annotations.processor;
+
+import com.christophecvb.touchportal.annotations.Category;
+import com.christophecvb.touchportal.annotations.Event;
+import com.christophecvb.touchportal.annotations.Plugin;
+import com.christophecvb.touchportal.annotations.State;
+import com.christophecvb.touchportal.annotations.processor.utils.Pair;
+import com.christophecvb.touchportal.annotations.processor.utils.SpecUtils;
+import com.christophecvb.touchportal.helpers.GenericHelper;
+import com.christophecvb.touchportal.helpers.StateHelper;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+
+public class StateProcessor {
+ /**
+ * Generates a JsonObject and a TypeSpec.Builder representing the {@link State}
+ *
+ * @param processor {@link TouchPortalPluginAnnotationsProcessor}
+ * @param roundEnv RoundEnvironment
+ * @param pluginElement Element
+ * @param plugin {@link Plugin}
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param stateElement Element
+ * @return Pair<JsonObject, TypeSpec.Builder> statePair
+ * @throws GenericHelper.TPTypeException If a used type is not Supported
+ * @throws TPAnnotationException If an Annotation is misused
+ */
+ public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element stateElement) throws GenericHelper.TPTypeException, TPAnnotationException {
+ processor.getMessager().printMessage(Diagnostic.Kind.NOTE, "Process State: " + stateElement.getSimpleName());
+ State state = stateElement.getAnnotation(State.class);
+
+ TypeSpec.Builder stateTypeSpecBuilder = SpecUtils.createStateTypeSpecBuilder(pluginElement, categoryElement, category, stateElement, state);
+
+ String className = stateElement.getEnclosingElement().getSimpleName() + "." + stateElement.getSimpleName();
+
+ JsonObject jsonState = new JsonObject();
+ jsonState.addProperty(StateHelper.ID, StateHelper.getStateId(pluginElement, categoryElement, category, stateElement, state));
+ String desiredTPType = GenericHelper.getTouchPortalType(className, stateElement);
+ jsonState.addProperty(StateHelper.TYPE, desiredTPType);
+ jsonState.addProperty(StateHelper.DESC, StateHelper.getStateDesc(stateElement, state));
+ jsonState.addProperty(StateHelper.DEFAULT, state.defaultValue());
+ if (desiredTPType.equals(StateHelper.TYPE_CHOICE)) {
+ JsonArray stateValueChoices = new JsonArray();
+ for (String valueChoice : state.valueChoices()) {
+ stateValueChoices.add(valueChoice);
+ }
+ jsonState.add(StateHelper.VALUE_CHOICES, stateValueChoices);
+ }
+ else if (!desiredTPType.equals(StateHelper.TYPE_TEXT)) {
+ throw new GenericHelper.TPTypeException.Builder(className).typeUnsupported(desiredTPType).forAnnotation(GenericHelper.TPTypeException.ForAnnotation.STATE).build();
+ }
+
+ Event event = stateElement.getAnnotation(Event.class);
+ if (event != null && !desiredTPType.equals(StateHelper.TYPE_TEXT)) {
+ throw new TPAnnotationException.Builder(State.class).typeFor(desiredTPType, className, "the field is also Annotated with Event. Only the type " + StateHelper.TYPE_TEXT + " is supported for a State that has an Event Annotation.").build();
+ }
+
+ return Pair.create(jsonState, stateTypeSpecBuilder);
+ }
+}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TPAnnotationException.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TPAnnotationException.java
new file mode 100644
index 00000000..c680bbf4
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TPAnnotationException.java
@@ -0,0 +1,51 @@
+package com.christophecvb.touchportal.annotations.processor;
+
+import javax.lang.model.element.Element;
+import java.lang.annotation.Annotation;
+
+public class TPAnnotationException extends Exception {
+ private TPAnnotationException(Class extends Annotation> annotationType, Boolean isMissing, Integer count, Element element, String typeFor) {
+ super(annotationType.getSimpleName() + " Annotation"
+ + (isMissing != null && isMissing ? " is missing" : "")
+ + (count != null ? " count cannot be " + count : "")
+ + (typeFor != null ? " " + typeFor : "")
+ + (element != null ? " for element " + element.getSimpleName() : "")
+ );
+ }
+
+ public static class Builder {
+ private final Class extends Annotation> annotationType;
+ private Boolean isMissing;
+ private Integer count;
+ private Element element;
+ private String typeFor;
+
+ public Builder(Class extends Annotation> annotationType) {
+ this.annotationType = annotationType;
+ }
+
+ public Builder isMissing(Boolean isMissing) {
+ this.isMissing = isMissing;
+ return this;
+ }
+
+ public Builder forElement(Element element) {
+ this.element = element;
+ return this;
+ }
+
+ public Builder typeFor(String type, String forElement, String because) {
+ this.typeFor = "type for " + forElement + " cannot be " + type + " because " + because;
+ return this;
+ }
+
+ public Builder count(int count) {
+ this.count = count;
+ return this;
+ }
+
+ public TPAnnotationException build() {
+ return new TPAnnotationException(this.annotationType, this.isMissing, this.count, this.element, this.typeFor);
+ }
+ }
+}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java
deleted file mode 100644
index 08ac3d90..00000000
--- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java
+++ /dev/null
@@ -1,1091 +0,0 @@
-/*
- * Touch Portal Plugin SDK
- *
- * Copyright 2020 Christophe Carvalho Vilas-Boas
- * christophe.carvalhovilasboas@gmail.com
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package com.christophecvb.touchportal.annotations.processor;
-
-import com.christophecvb.touchportal.annotations.*;
-import com.christophecvb.touchportal.annotations.processor.utils.Pair;
-import com.christophecvb.touchportal.helpers.*;
-import com.google.auto.service.AutoService;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonObject;
-import com.squareup.javapoet.ArrayTypeName;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.JavaFile;
-import com.squareup.javapoet.TypeSpec;
-
-import javax.annotation.processing.*;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.PackageElement;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic;
-import javax.tools.FileObject;
-import javax.tools.StandardLocation;
-import java.io.Writer;
-import java.lang.annotation.AnnotationFormatError;
-import java.util.LinkedHashSet;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Touch Portal Plugin Annotation Processor
- */
-@AutoService(Processor.class)
-public class TouchPortalPluginAnnotationProcessor extends AbstractProcessor {
- private Filer filer;
- private Messager messager;
-
- public static String capitalize(String str) {
- if (str == null || str.isEmpty()) {
- return str;
- }
-
- return str.substring(0, 1).toUpperCase() + str.substring(1);
- }
-
- @Override
- public synchronized void init(ProcessingEnvironment processingEnv) {
- super.init(processingEnv);
- this.filer = processingEnv.getFiler();
- this.messager = processingEnv.getMessager();
- }
-
- @Override
- public Set getSupportedAnnotationTypes() {
- Set annotations = new LinkedHashSet<>();
- annotations.add(Plugin.class.getCanonicalName());
- annotations.add(Setting.class.getCanonicalName());
- annotations.add(Category.class.getCanonicalName());
- annotations.add(Action.class.getCanonicalName());
- annotations.add(Data.class.getCanonicalName());
- annotations.add(State.class.getCanonicalName());
- annotations.add(Event.class.getCanonicalName());
- annotations.add(Connector.class.getCanonicalName());
- return annotations;
- }
-
- @Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latestSupported();
- }
-
- @Override
- public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
- if (roundEnv.processingOver() || annotations.size() == 0) {
- return false;
- }
- this.messager.printMessage(Diagnostic.Kind.NOTE, this.getClass().getSimpleName() + ".process");
-
- try {
- Set extends Element> plugins = roundEnv.getElementsAnnotatedWith(Plugin.class);
- if (plugins.size() != 1) {
- throw new Exception("You need 1(one) @Plugin Annotation, you have " + plugins.size());
- }
- for (Element pluginElement : plugins) {
- Pair pluginPair = this.processPlugin(roundEnv, pluginElement);
-
- String actionFileName = "resources/" + PluginHelper.ENTRY_TP;
- FileObject actionFileObject = this.filer.createResource(StandardLocation.SOURCE_OUTPUT, "", actionFileName, pluginElement);
- Writer writer = actionFileObject.openWriter();
- writer.write(pluginPair.first.toString());
- writer.flush();
- writer.close();
-
- TypeSpec pluginTypeSpec = pluginPair.second.build();
- String packageName = ((PackageElement) pluginElement.getEnclosingElement()).getQualifiedName().toString();
- JavaFile javaConstantsFile = JavaFile.builder(packageName, pluginTypeSpec).build();
- javaConstantsFile.writeTo(this.filer);
- }
- }
- catch (Exception exception) {
- this.messager.printMessage(Diagnostic.Kind.ERROR, exception.getMessage());
- }
-
- return true;
- }
-
- /**
- * Generates a JsonObject and a TypeSpec.Builder representing the {@link Plugin}
- *
- * @param roundEnv RoundEnvironment
- * @param pluginElement Element
- * @return Pair pluginPair
- * @throws GenericHelper.TPTypeException If a used type is not Supported
- */
- private Pair processPlugin(RoundEnvironment roundEnv, Element pluginElement) throws Exception {
- this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Plugin: " + pluginElement.getSimpleName());
- Plugin plugin = pluginElement.getAnnotation(Plugin.class);
-
- TypeSpec.Builder pluginTypeSpecBuilder = this.createPluginTypeSpecBuilder(pluginElement, plugin);
-
- JsonObject jsonPlugin = new JsonObject();
- jsonPlugin.addProperty(PluginHelper.SDK, PluginHelper.TOUCH_PORTAL_PLUGIN_VERSION);
- jsonPlugin.addProperty(PluginHelper.VERSION, plugin.version());
- jsonPlugin.addProperty(PluginHelper.NAME, PluginHelper.getPluginName(pluginElement, plugin));
- jsonPlugin.addProperty(PluginHelper.ID, PluginHelper.getPluginId(pluginElement));
- JsonObject jsonConfiguration = new JsonObject();
- jsonConfiguration.addProperty(PluginHelper.CONFIGURATION_COLOR_DARK, plugin.colorDark());
- jsonConfiguration.addProperty(PluginHelper.CONFIGURATION_COLOR_LIGHT, plugin.colorLight());
- jsonPlugin.add(PluginHelper.CONFIGURATION, jsonConfiguration);
- jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND, "java -Dapple.awt.UIElement=true -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START);
-
- TypeSpec.Builder settingsTypeSpecBuilder = TypeSpec.classBuilder("Settings").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- JsonArray jsonSettings = new JsonArray();
- Set extends Element> settingElements = roundEnv.getElementsAnnotatedWith(Setting.class);
- for (Element settingElement : settingElements) {
- Pair settingResult = this.processSetting(settingElement);
- jsonSettings.add(settingResult.first);
- settingsTypeSpecBuilder.addType(settingResult.second.build());
- }
- if (jsonSettings.size() > 0) {
- jsonPlugin.add(PluginHelper.SETTINGS, jsonSettings);
- }
- pluginTypeSpecBuilder.addType(settingsTypeSpecBuilder.build());
-
- JsonArray jsonCategories = new JsonArray();
- Set extends Element> categoryElements = roundEnv.getElementsAnnotatedWith(Category.class);
- for (Element categoryElement : categoryElements) {
- Pair categoryResult = this.processCategory(roundEnv, pluginElement, plugin, categoryElement);
- jsonCategories.add(categoryResult.first);
- pluginTypeSpecBuilder.addType(categoryResult.second.build());
- }
- if (jsonCategories.size() > 0) {
- jsonPlugin.add(PluginHelper.CATEGORIES, jsonCategories);
- }
- else {
- throw new Exception("Category Annotation missing");
- }
-
- return Pair.create(jsonPlugin, pluginTypeSpecBuilder);
- }
-
- /**
- * Generates a JsonObject and a TypeSpec.Builder representing the {@link Setting}
- *
- * @param settingElement Element
- * @return Pair statePair
- * @throws GenericHelper.TPTypeException If a used type is not Supported
- */
- private Pair processSetting(Element settingElement) throws Exception {
- this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Setting: " + settingElement.getSimpleName());
- Setting setting = settingElement.getAnnotation(Setting.class);
-
- TypeSpec.Builder settingTypeSpecBuilder = this.createSettingTypeSpecBuilder(settingElement, setting);
-
- String className = settingElement.getEnclosingElement().getSimpleName() + "." + settingElement.getSimpleName();
-
- JsonObject jsonSetting = new JsonObject();
- jsonSetting.addProperty(SettingHelper.NAME, SettingHelper.getSettingName(settingElement, setting));
- String desiredTPType = GenericHelper.getTouchPortalType(className, settingElement);
- jsonSetting.addProperty(SettingHelper.TYPE, desiredTPType);
- jsonSetting.addProperty(SettingHelper.DEFAULT, setting.defaultValue());
- jsonSetting.addProperty(SettingHelper.IS_READ_ONLY, setting.isReadOnly());
- switch (desiredTPType) {
- case SettingHelper.TYPE_TEXT:
- if (setting.maxLength() > 0) {
- jsonSetting.addProperty(SettingHelper.MAX_LENGTH, setting.maxLength());
- }
- if (setting.isPassword()) {
- jsonSetting.addProperty(SettingHelper.IS_PASSWORD, true);
- }
- break;
-
- case SettingHelper.TYPE_NUMBER:
- try {
- double defaultValue = Double.parseDouble(setting.defaultValue());
- if (defaultValue < setting.minValue() || defaultValue > setting.maxValue()) {
- throw new GenericHelper.TPTypeException.Builder(className).defaultNotInRange().build();
- }
- }
- catch (NumberFormatException numberFormatException) {
- throw new GenericHelper.TPTypeException.Builder(className).defaultInvalid(setting.defaultValue()).build();
- }
- if (setting.minValue() > Double.NEGATIVE_INFINITY) {
- jsonSetting.addProperty(SettingHelper.MIN_VALUE, setting.minValue());
- }
- if (setting.maxValue() < Double.POSITIVE_INFINITY) {
- jsonSetting.addProperty(SettingHelper.MAX_VALUE, setting.maxValue());
- }
- break;
-
- default:
- throw new GenericHelper.TPTypeException.Builder(className).typeUnsupported(desiredTPType).forAnnotation(GenericHelper.TPTypeException.ForAnnotation.SETTING).build();
- }
-
- return Pair.create(jsonSetting, settingTypeSpecBuilder);
- }
-
- /**
- * Generates a JsonObject and a TypeSpec.Builder representing the {@link Category}
- *
- * @param roundEnv RoundEnvironment
- * @param pluginElement Element
- * @param plugin {@link Plugin}
- * @param categoryElement Element
- * @return Pair categoryPair
- * @throws GenericHelper.TPTypeException If a used type is not Supported
- */
- private Pair processCategory(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement) throws Exception {
- this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Category: " + categoryElement.getSimpleName());
- Category category = categoryElement.getAnnotation(Category.class);
-
- TypeSpec.Builder categoryTypeSpecBuilder = this.createCategoryTypeSpecBuilder(pluginElement, categoryElement, category);
-
- JsonObject jsonCategory = new JsonObject();
- jsonCategory.addProperty(CategoryHelper.ID, CategoryHelper.getCategoryId(pluginElement, categoryElement, category));
- jsonCategory.addProperty(CategoryHelper.NAME, CategoryHelper.getCategoryName(categoryElement, category));
- jsonCategory.addProperty(CategoryHelper.IMAGE_PATH, PluginHelper.TP_PLUGIN_FOLDER + pluginElement.getSimpleName() + "/" + category.imagePath());
-
- TypeSpec.Builder actionsTypeSpecBuilder = TypeSpec.classBuilder("Actions").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- JsonArray jsonActions = new JsonArray();
- Set extends Element> actionElements = roundEnv.getElementsAnnotatedWith(Action.class);
- for (Element actionElement : actionElements) {
- Action action = actionElement.getAnnotation(Action.class);
- String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id();
- if (categoryId.equals(action.categoryId())) {
- Pair actionResult = this.processAction(roundEnv, pluginElement, plugin, categoryElement, category, actionElement);
- jsonActions.add(actionResult.first);
- actionsTypeSpecBuilder.addType(actionResult.second.build());
- }
- }
- categoryTypeSpecBuilder.addType(actionsTypeSpecBuilder.build());
- jsonCategory.add(CategoryHelper.ACTIONS, jsonActions);
-
- TypeSpec.Builder connectorsTypeSpecBuilder = TypeSpec.classBuilder("Connectors").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- JsonArray jsonConnectors = new JsonArray();
- Set extends Element> connectorElements = roundEnv.getElementsAnnotatedWith(Connector.class);
- for (Element connectorElement : connectorElements) {
- Connector connector = connectorElement.getAnnotation(Connector.class);
- String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id();
- if (categoryId.equals(connector.categoryId())) {
- Pair connectorResult = this.processConnector(roundEnv, pluginElement, plugin, categoryElement, category, connectorElement);
- jsonConnectors.add(connectorResult.first);
- connectorsTypeSpecBuilder.addType(connectorResult.second.build());
- }
- }
- categoryTypeSpecBuilder.addType(connectorsTypeSpecBuilder.build());
- jsonCategory.add(CategoryHelper.CONNECTORS, jsonConnectors);
-
- TypeSpec.Builder statesTypeSpecBuilder = TypeSpec.classBuilder("States").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- JsonArray jsonStates = new JsonArray();
- Set extends Element> stateElements = roundEnv.getElementsAnnotatedWith(State.class);
- for (Element stateElement : stateElements) {
- State state = stateElement.getAnnotation(State.class);
- String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id();
- if (categoryId.equals(state.categoryId())) {
- Pair stateResult = this.processState(roundEnv, pluginElement, plugin, categoryElement, category, stateElement);
- jsonStates.add(stateResult.first);
- statesTypeSpecBuilder.addType(stateResult.second.build());
- }
- }
- categoryTypeSpecBuilder.addType(statesTypeSpecBuilder.build());
- jsonCategory.add(CategoryHelper.STATES, jsonStates);
-
- TypeSpec.Builder eventsTypeSpecBuilder = TypeSpec.classBuilder("Events").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- JsonArray jsonEvents = new JsonArray();
- Set extends Element> eventElements = roundEnv.getElementsAnnotatedWith(Event.class);
- for (Element eventElement : eventElements) {
- State state = eventElement.getAnnotation(State.class);
- String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id();
- if (state != null) {
- if (categoryId.equals(state.categoryId())) {
- Pair eventResult = this.processEvent(roundEnv, pluginElement, plugin, categoryElement, category, eventElement);
- jsonEvents.add(eventResult.first);
- eventsTypeSpecBuilder.addType(eventResult.second.build());
- }
- }
- else {
- throw new AnnotationFormatError("The State Annotation is missing for element " + eventElement.getSimpleName());
- }
- }
- categoryTypeSpecBuilder.addType(eventsTypeSpecBuilder.build());
- jsonCategory.add(CategoryHelper.EVENTS, jsonEvents);
-
- return Pair.create(jsonCategory, categoryTypeSpecBuilder);
- }
-
- /**
- * Generates a JsonObject and a TypeSpec.Builder representing the {@link Action}
- *
- * @param roundEnv RoundEnvironment
- * @param pluginElement Element
- * @param plugin {@link Plugin}
- * @param categoryElement Element
- * @param category {@link Category}
- * @param actionElement Element
- * @return Pair actionPair
- */
- private Pair processAction(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element actionElement) throws Exception {
- this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Action: " + actionElement.getSimpleName());
- Action action = actionElement.getAnnotation(Action.class);
-
- TypeSpec.Builder actionTypeSpecBuilder = this.createActionTypeSpecBuilder(pluginElement, categoryElement, category, actionElement, action);
-
- JsonObject jsonAction = new JsonObject();
- jsonAction.addProperty(ActionHelper.ID, ActionHelper.getActionId(pluginElement, categoryElement, category, actionElement, action));
- jsonAction.addProperty(ActionHelper.NAME, ActionHelper.getActionName(actionElement, action));
- jsonAction.addProperty(ActionHelper.PREFIX, action.prefix());
- jsonAction.addProperty(ActionHelper.TYPE, action.type());
- if (!action.description().isEmpty()) {
- jsonAction.addProperty(ActionHelper.DESCRIPTION, action.description());
- }
- if (!action.format().isEmpty()) {
- jsonAction.addProperty(ActionHelper.FORMAT, action.format());
- jsonAction.addProperty(ActionHelper.TRY_INLINE, true);
- }
- jsonAction.addProperty(ActionHelper.HAS_HOLD_FUNCTIONALITY, action.hasHoldFunctionality());
-
- JsonArray jsonActionData = new JsonArray();
- Set extends Element> dataElements = roundEnv.getElementsAnnotatedWith(Data.class);
- for (Element dataElement : dataElements) {
- Element enclosingElement = dataElement.getEnclosingElement();
- if (actionElement.equals(enclosingElement)) {
- Pair actionDataResult = this.processActionData(roundEnv, pluginElement, plugin, categoryElement, category, actionElement, action, jsonAction, dataElement);
- jsonActionData.add(actionDataResult.first);
- if (actionDataResult.second != null) {
- actionTypeSpecBuilder.addType(actionDataResult.second.build());
- }
- }
- }
- if (jsonActionData.size() > 0) {
- jsonAction.add(ActionHelper.DATA, jsonActionData);
- }
-
- return Pair.create(jsonAction, actionTypeSpecBuilder);
- }
-
- /**
- * Generates a JsonObject and a TypeSpec.Builder representing the {@link Connector}
- *
- * @param roundEnv RoundEnvironment
- * @param pluginElement Element
- * @param plugin {@link Plugin}
- * @param categoryElement Element
- * @param category {@link Category}
- * @param connectorElement Element
- * @return Pair actionPair
- */
- private Pair processConnector(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element connectorElement) throws Exception {
- this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Connector: " + connectorElement.getSimpleName());
- Connector connector = connectorElement.getAnnotation(Connector.class);
-
- TypeSpec.Builder connectorTypeSpecBuilder = this.createConnectorTypeSpecBuilder(pluginElement, categoryElement, category, connectorElement, connector);
-
- JsonObject jsonConnector = new JsonObject();
- jsonConnector.addProperty(ConnectorHelper.ID, ConnectorHelper.getConnectorId(pluginElement, categoryElement, category, connectorElement, connector));
- jsonConnector.addProperty(ConnectorHelper.NAME, ConnectorHelper.getConnectorName(connectorElement, connector));
- jsonConnector.addProperty(ConnectorHelper.FORMAT, connector.format());
-
- String classMethod = pluginElement.getSimpleName() + "." + connectorElement.getSimpleName();
- boolean connectorValueFound = false;
- Set extends Element> connectorValueElements = roundEnv.getElementsAnnotatedWith(ConnectorValue.class);
- for (Element connectorValueElement : connectorValueElements) {
- Element enclosingElement = connectorValueElement.getEnclosingElement();
- if (connectorElement.equals(enclosingElement)) {
- String classMethodParameter = classMethod + "(" + connectorValueElement.getSimpleName() + ")";
- String desiredType = GenericHelper.getTouchPortalType(classMethodParameter, connectorValueElement);
- if (!desiredType.equals(GenericHelper.TP_TYPE_NUMBER)) {
- throw new GenericHelper.TPTypeException.Builder(classMethodParameter).typeUnsupported(desiredType).forAnnotation(GenericHelper.TPTypeException.ForAnnotation.CONNECTOR_VALUE).build();
- }
- connectorValueFound = true;
- break;
- }
- }
- if (!connectorValueFound) {
- throw new IllegalArgumentException("Connector " + classMethod + " has no declared ConnectorValue");
- }
-
- JsonArray jsonConnectorData = new JsonArray();
- Set extends Element> dataElements = roundEnv.getElementsAnnotatedWith(Data.class);
- for (Element dataElement : dataElements) {
- Element enclosingElement = dataElement.getEnclosingElement();
- if (connectorElement.equals(enclosingElement)) {
- Pair connectorDataResult = this.processConnectorData(roundEnv, pluginElement, plugin, categoryElement, category, connectorElement, connector, jsonConnector, dataElement);
- jsonConnectorData.add(connectorDataResult.first);
- if (connectorDataResult.second != null) {
- connectorTypeSpecBuilder.addType(connectorDataResult.second.build());
- }
- }
- }
- if (jsonConnectorData.size() > 0) {
- jsonConnector.add(ConnectorHelper.DATA, jsonConnectorData);
- }
-
- return Pair.create(jsonConnector, connectorTypeSpecBuilder);
- }
-
- /**
- * Generates a JsonObject and a TypeSpec.Builder representing the {@link State}
- *
- * @param roundEnv RoundEnvironment
- * @param pluginElement Element
- * @param plugin {@link Plugin}
- * @param categoryElement Element
- * @param category {@link Category}
- * @param stateElement Element
- * @return Pair statePair
- * @throws GenericHelper.TPTypeException If a used type is not Supported
- */
- private Pair processState(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element stateElement) throws Exception {
- this.messager.printMessage(Diagnostic.Kind.NOTE, "Process State: " + stateElement.getSimpleName());
- State state = stateElement.getAnnotation(State.class);
-
- TypeSpec.Builder stateTypeSpecBuilder = this.createStateTypeSpecBuilder(pluginElement, categoryElement, category, stateElement, state);
-
- String className = stateElement.getEnclosingElement().getSimpleName() + "." + stateElement.getSimpleName();
-
- JsonObject jsonState = new JsonObject();
- jsonState.addProperty(StateHelper.ID, StateHelper.getStateId(pluginElement, categoryElement, category, stateElement, state));
- String desiredTPType = GenericHelper.getTouchPortalType(className, stateElement);
- jsonState.addProperty(StateHelper.TYPE, desiredTPType);
- jsonState.addProperty(StateHelper.DESC, StateHelper.getStateDesc(stateElement, state));
- jsonState.addProperty(StateHelper.DEFAULT, state.defaultValue());
- if (desiredTPType.equals(StateHelper.TYPE_CHOICE)) {
- JsonArray stateValueChoices = new JsonArray();
- for (String valueChoice : state.valueChoices()) {
- stateValueChoices.add(valueChoice);
- }
- jsonState.add(StateHelper.VALUE_CHOICES, stateValueChoices);
- }
- else if (!desiredTPType.equals(StateHelper.TYPE_TEXT)) {
- throw new GenericHelper.TPTypeException.Builder(className).typeUnsupported(desiredTPType).forAnnotation(GenericHelper.TPTypeException.ForAnnotation.STATE).build();
- }
-
- Event event = stateElement.getAnnotation(Event.class);
- if (event != null && !desiredTPType.equals(StateHelper.TYPE_TEXT)) {
- throw new Exception("The type of the State Annotation for " + className + " cannot be " + desiredTPType + " because the field is also Annotated with Event. Only the type " + StateHelper.TYPE_TEXT + " is supported for a State that has an Event Annotation.");
- }
-
- return Pair.create(jsonState, stateTypeSpecBuilder);
- }
-
- /**
- * Generates a JsonObject and a TypeSpec.Builder representing the {@link Event}
- *
- * @param roundEnv RoundEnvironment
- * @param pluginElement Element
- * @param plugin {@link Plugin}
- * @param categoryElement Element
- * @param category {@link Category}
- * @param eventElement Element
- * @return Pair eventPair
- * @throws GenericHelper.TPTypeException If any used type is not Supported
- */
- private Pair processEvent(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element eventElement) throws Exception {
- this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Event: " + eventElement.getSimpleName());
- State state = eventElement.getAnnotation(State.class);
- Event event = eventElement.getAnnotation(Event.class);
-
- String reference = eventElement.getEnclosingElement().getSimpleName() + "." + eventElement.getSimpleName();
-
- if (state == null) {
- throw new Exception("The Event Annotation on " + reference + " must be used with the State Annotation");
- }
-
- TypeSpec.Builder eventTypeSpecBuilder = this.createEventTypeSpecBuilder(pluginElement, categoryElement, category, eventElement, event);
-
- JsonObject jsonEvent = new JsonObject();
- jsonEvent.addProperty(EventHelper.ID, EventHelper.getEventId(pluginElement, categoryElement, category, eventElement, event));
- jsonEvent.addProperty(EventHelper.TYPE, EventHelper.TYPE_COMMUNICATE);
- jsonEvent.addProperty(EventHelper.NAME, EventHelper.getEventName(eventElement, event));
- jsonEvent.addProperty(EventHelper.FORMAT, event.format());
- String desiredTPType = GenericHelper.getTouchPortalType(reference, eventElement);
- if (desiredTPType.equals(StateHelper.TYPE_TEXT)) {
- jsonEvent.addProperty(EventHelper.VALUE_TYPE, EventHelper.VALUE_TYPE_CHOICE);
- JsonArray eventValueChoices = new JsonArray();
- for (String valueChoice : event.valueChoices()) {
- eventValueChoices.add(valueChoice);
- }
- jsonEvent.add(EventHelper.VALUE_CHOICES, eventValueChoices);
- jsonEvent.addProperty(EventHelper.VALUE_STATE_ID, StateHelper.getStateId(pluginElement, categoryElement, category, eventElement, state));
- }
- else {
- throw new GenericHelper.TPTypeException.Builder(reference).typeUnsupported(desiredTPType).forAnnotation(GenericHelper.TPTypeException.ForAnnotation.EVENT).build();
- }
-
- return Pair.create(jsonEvent, eventTypeSpecBuilder);
- }
-
- /**
- * Generates a JsonObject and a TypeSpec.Builder representing the {@link Data} for an {@link Action}
- *
- * @param roundEnv RoundEnvironment
- * @param pluginElement Element
- * @param plugin {@link Plugin}
- * @param categoryElement Element
- * @param category {@link Category}
- * @param actionElement Element
- * @param action {@link Action}
- * @param jsonAction JsonObject
- * @param dataElement Element
- * @return Pair dataPair
- */
- private Pair processActionData(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element actionElement, Action action, JsonObject jsonAction, Element dataElement) throws Exception {
- this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Action Data: " + dataElement.getSimpleName());
- Data data = dataElement.getAnnotation(Data.class);
-
- TypeSpec.Builder actionDataTypeSpecBuilder = this.createActionDataTypeSpecBuilder(pluginElement, categoryElement, category, actionElement, action, dataElement, data);
-
- Element method = dataElement.getEnclosingElement();
- String className = method.getEnclosingElement().getSimpleName() + "." + method.getSimpleName() + "(" + dataElement.getSimpleName() + ")";
-
- JsonObject jsonData = new JsonObject();
- String desiredTPType = GenericHelper.getTouchPortalType(className, dataElement);
- jsonData.addProperty(DataHelper.TYPE, desiredTPType);
- jsonData.addProperty(DataHelper.LABEL, DataHelper.getDataLabel(dataElement, data));
- // Default Value
- switch (desiredTPType) {
- case GenericHelper.TP_TYPE_NUMBER:
- double defaultValue = 0;
- try {
- defaultValue = Double.parseDouble(data.defaultValue());
- }
- catch (NumberFormatException ignored) {}
- jsonData.addProperty(DataHelper.DEFAULT, defaultValue);
- break;
-
- case GenericHelper.TP_TYPE_SWITCH:
- jsonData.addProperty(DataHelper.DEFAULT, data.defaultValue().equals("true"));
- break;
-
- default:
- jsonData.addProperty(DataHelper.DEFAULT, data.defaultValue());
- break;
- }
- AtomicReference dataId = new AtomicReference<>(DataHelper.getActionDataId(pluginElement, categoryElement, category, actionElement, action, dataElement, data));
- // Specific properties
- switch (desiredTPType) {
- case GenericHelper.TP_TYPE_CHOICE:
- JsonArray dataValueChoices = new JsonArray();
- if (!data.stateId().isEmpty()) {
- Optional extends Element> optionalStateElement = roundEnv.getElementsAnnotatedWith(State.class).stream().filter(element -> {
- State state = element.getAnnotation(State.class);
- String shortStateId = !state.id().isEmpty() ? state.id() : element.getSimpleName().toString();
- return shortStateId.equals(data.stateId());
- }).findFirst();
- if (optionalStateElement.isPresent()) {
- actionDataTypeSpecBuilder = null;
-
- Element stateElement = optionalStateElement.get();
- State state = stateElement.getAnnotation(State.class);
- dataId.set(StateHelper.getStateId(pluginElement, categoryElement, category, stateElement, state));
- for (String valueChoice : state.valueChoices()) {
- dataValueChoices.add(valueChoice);
- }
- jsonData.addProperty(DataHelper.DEFAULT, data.defaultValue().isEmpty() ? state.defaultValue() : data.defaultValue());
- }
- else {
- for (String valueChoice : data.valueChoices()) {
- dataValueChoices.add(valueChoice);
- }
- }
- }
- else {
- for (String valueChoice : data.valueChoices()) {
- dataValueChoices.add(valueChoice);
- }
- }
- jsonData.add(DataHelper.VALUE_CHOICES, dataValueChoices);
- break;
-
- case GenericHelper.TP_TYPE_FILE:
- if (data.isDirectory()) {
- jsonData.addProperty(DataHelper.TYPE, GenericHelper.TP_TYPE_DIRECTORY);
- }
- else {
- JsonArray jsonExtensions = new JsonArray();
- for (String extension : data.extensions()) {
- if (extension.matches(DataHelper.EXTENSION_FORMAT)) {
- jsonExtensions.add(extension);
- }
- else {
- this.messager.printMessage(Diagnostic.Kind.ERROR, "Action Data Extension: [" + extension + "] format is not valid");
- }
- }
- jsonData.add(DataHelper.EXTENSIONS, jsonExtensions);
- }
- break;
-
- case GenericHelper.TP_TYPE_TEXT:
- if (data.isColor()) {
- jsonData.addProperty(DataHelper.TYPE, GenericHelper.TP_TYPE_COLOR);
- if (!data.defaultValue().isEmpty()) {
- if (!data.defaultValue().matches(DataHelper.COLOR_FORMAT)) {
- this.messager.printMessage(Diagnostic.Kind.ERROR, "Action Data Color Default value: [" + data.defaultValue() + "] format is not valid");
- }
- }
- }
- break;
-
- case GenericHelper.TP_TYPE_NUMBER:
- try {
- double defaultValue = jsonData.get(DataHelper.DEFAULT).getAsDouble();
- if (defaultValue < data.minValue() || defaultValue > data.maxValue()) {
- throw new GenericHelper.TPTypeException.Builder(className).defaultNotInRange().build();
- }
- }
- catch (NumberFormatException numberFormatException) {
- throw new GenericHelper.TPTypeException.Builder(className).defaultInvalid(data.defaultValue()).build();
- }
- jsonData.addProperty(DataHelper.ALLOW_DECIMALS, GenericHelper.getTouchPortalTypeNumberAllowDecimals(dataElement.asType().toString()));
- if (data.minValue() > Double.NEGATIVE_INFINITY) {
- jsonData.addProperty(DataHelper.MIN_VALUE, data.minValue());
- }
- if (data.maxValue() < Double.POSITIVE_INFINITY) {
- jsonData.addProperty(DataHelper.MAX_VALUE, data.maxValue());
- }
- break;
- }
- jsonData.addProperty(DataHelper.ID, dataId.get());
- if (!action.format().isEmpty()) {
- // Replace wildcards
- String rawFormat = jsonAction.get(ActionHelper.FORMAT).getAsString();
- jsonAction.addProperty(ActionHelper.FORMAT, rawFormat.replace("{$" + (data.id().isEmpty() ? dataElement.getSimpleName().toString() : data.id()) + "$}", "{$" + dataId.get() + "$}"));
- }
-
- return Pair.create(jsonData, actionDataTypeSpecBuilder);
- }
-
- /**
- * Generates a JsonObject and a TypeSpec.Builder representing the {@link Data} for a {@link Connector}
- *
- * @param roundEnv RoundEnvironment
- * @param pluginElement Element
- * @param plugin {@link Plugin}
- * @param categoryElement Element
- * @param category {@link Category}
- * @param connectorElement Element
- * @param connector {@link Connector}
- * @param jsonConnector JsonObject
- * @param dataElement Element
- * @return Pair dataPair
- */
- private Pair processConnectorData(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element connectorElement, Connector connector, JsonObject jsonConnector, Element dataElement) throws Exception {
- this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Connector Data: " + dataElement.getSimpleName());
- Data data = dataElement.getAnnotation(Data.class);
-
- TypeSpec.Builder connectorDataTypeSpecBuilder = this.createConnectorDataTypeSpecBuilder(pluginElement, categoryElement, category, connectorElement, connector, dataElement, data);
-
- Element method = dataElement.getEnclosingElement();
- String className = method.getEnclosingElement().getSimpleName() + "." + method.getSimpleName() + "(" + dataElement.getSimpleName() + ")";
-
- JsonObject jsonData = new JsonObject();
- String desiredTPType = GenericHelper.getTouchPortalType(className, dataElement);
- jsonData.addProperty(DataHelper.TYPE, desiredTPType);
- jsonData.addProperty(DataHelper.LABEL, DataHelper.getDataLabel(dataElement, data));
- // Default Value
- switch (desiredTPType) {
- case GenericHelper.TP_TYPE_NUMBER:
- double defaultValue = 0;
- try {
- defaultValue = Double.parseDouble(data.defaultValue());
- }
- catch (NumberFormatException ignored) {}
- jsonData.addProperty(DataHelper.DEFAULT, defaultValue);
- break;
-
- case GenericHelper.TP_TYPE_SWITCH:
- jsonData.addProperty(DataHelper.DEFAULT, data.defaultValue().equals("true"));
- break;
-
- default:
- jsonData.addProperty(DataHelper.DEFAULT, data.defaultValue());
- break;
- }
- AtomicReference dataId = new AtomicReference<>(DataHelper.getConnectorDataId(pluginElement, categoryElement, category, connectorElement, connector, dataElement, data));
- // Specific properties
- switch (desiredTPType) {
- case GenericHelper.TP_TYPE_CHOICE:
- JsonArray dataValueChoices = new JsonArray();
- if (!data.stateId().isEmpty()) {
- Optional extends Element> optionalStateElement = roundEnv.getElementsAnnotatedWith(State.class).stream().filter(element -> {
- State state = element.getAnnotation(State.class);
- String shortStateId = !state.id().isEmpty() ? state.id() : element.getSimpleName().toString();
- return shortStateId.equals(data.stateId());
- }).findFirst();
- if (optionalStateElement.isPresent()) {
- connectorDataTypeSpecBuilder = null;
-
- Element stateElement = optionalStateElement.get();
- State state = stateElement.getAnnotation(State.class);
- dataId.set(StateHelper.getStateId(pluginElement, categoryElement, category, stateElement, state));
- for (String valueChoice : state.valueChoices()) {
- dataValueChoices.add(valueChoice);
- }
- jsonData.addProperty(DataHelper.DEFAULT, data.defaultValue().isEmpty() ? state.defaultValue() : data.defaultValue());
- }
- else {
- for (String valueChoice : data.valueChoices()) {
- dataValueChoices.add(valueChoice);
- }
- }
- }
- else {
- for (String valueChoice : data.valueChoices()) {
- dataValueChoices.add(valueChoice);
- }
- }
- jsonData.add(DataHelper.VALUE_CHOICES, dataValueChoices);
- break;
-
- case GenericHelper.TP_TYPE_FILE:
- if (data.isDirectory()) {
- jsonData.addProperty(DataHelper.TYPE, GenericHelper.TP_TYPE_DIRECTORY);
- }
- else {
- JsonArray jsonExtensions = new JsonArray();
- for (String extension : data.extensions()) {
- if (extension.matches(DataHelper.EXTENSION_FORMAT)) {
- jsonExtensions.add(extension);
- }
- else {
- this.messager.printMessage(Diagnostic.Kind.ERROR, "Action Data Extension: [" + extension + "] format is not valid");
- }
- }
- jsonData.add(DataHelper.EXTENSIONS, jsonExtensions);
- }
- break;
-
- case GenericHelper.TP_TYPE_TEXT:
- if (data.isColor()) {
- jsonData.addProperty(DataHelper.TYPE, GenericHelper.TP_TYPE_COLOR);
- if (!data.defaultValue().isEmpty()) {
- if (!data.defaultValue().matches(DataHelper.COLOR_FORMAT)) {
- this.messager.printMessage(Diagnostic.Kind.ERROR, "Action Data Color Default value: [" + data.defaultValue() + "] format is not valid");
- }
- }
- }
- break;
-
- case GenericHelper.TP_TYPE_NUMBER:
- try {
- double defaultValue = jsonData.get(DataHelper.DEFAULT).getAsDouble();
- if (defaultValue < data.minValue() || defaultValue > data.maxValue()) {
- throw new GenericHelper.TPTypeException.Builder(className).defaultNotInRange().build();
- }
- }
- catch (NumberFormatException numberFormatException) {
- throw new GenericHelper.TPTypeException.Builder(className).defaultInvalid(data.defaultValue()).build();
- }
- jsonData.addProperty(DataHelper.ALLOW_DECIMALS, GenericHelper.getTouchPortalTypeNumberAllowDecimals(dataElement.asType().toString()));
- if (data.minValue() > Double.NEGATIVE_INFINITY) {
- jsonData.addProperty(DataHelper.MIN_VALUE, data.minValue());
- }
- if (data.maxValue() < Double.POSITIVE_INFINITY) {
- jsonData.addProperty(DataHelper.MAX_VALUE, data.maxValue());
- }
- break;
- }
- jsonData.addProperty(DataHelper.ID, dataId.get());
- if (!connector.format().isEmpty()) {
- // Replace wildcards
- String rawFormat = jsonConnector.get(ConnectorHelper.FORMAT).getAsString();
- jsonConnector.addProperty(ConnectorHelper.FORMAT, rawFormat.replace("{$" + (data.id().isEmpty() ? dataElement.getSimpleName().toString() : data.id()) + "$}", "{$" + dataId.get() + "$}"));
- }
-
- return Pair.create(jsonData, connectorDataTypeSpecBuilder);
- }
-
- /**
- * Generates a TypeSpec.Builder with Constants for the {@link Plugin}
- *
- * @param pluginElement Element
- * @param plugin {@link Plugin}
- * @return TypeSpec.Builder pluginTypeSpecBuilder
- */
- private TypeSpec.Builder createPluginTypeSpecBuilder(Element pluginElement, Plugin plugin) {
- String simpleClassName = pluginElement.getSimpleName().toString() + "Constants";
-
- TypeSpec.Builder pluginTypeSpecBuilder = TypeSpec.classBuilder(TouchPortalPluginAnnotationProcessor.capitalize(simpleClassName));
- pluginTypeSpecBuilder.addModifiers(Modifier.PUBLIC);
-
- pluginTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("id", PluginHelper.getPluginId(pluginElement)));
- pluginTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("name", plugin.name()));
- pluginTypeSpecBuilder.addField(this.getStaticFinalLongFieldSpec("version", plugin.version()));
-
- return pluginTypeSpecBuilder;
- }
-
- /**
- * Generates a TypeSpec.Builder with Constants for the {@link Category}
- *
- * @param pluginElement Element
- * @param categoryElement Element
- * @param category {@link Category}
- * @return TypeSpec.Builder pluginTypeSpecBuilder
- */
- private TypeSpec.Builder createCategoryTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category) {
- String simpleClassName = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id();
-
- TypeSpec.Builder categoryTypeSpecBuilder = TypeSpec.classBuilder(TouchPortalPluginAnnotationProcessor.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- categoryTypeSpecBuilder.addModifiers(Modifier.PUBLIC);
-
- categoryTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("id", CategoryHelper.getCategoryId(pluginElement, categoryElement, category)));
- categoryTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("name", category.name()));
- categoryTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("image_path", category.imagePath()));
-
- return categoryTypeSpecBuilder;
- }
-
- /**
- * Generates a TypeSpec.Builder with Constants for the {@link Action}
- *
- * @param pluginElement Element
- * @param categoryElement Element
- * @param category {@link Category}
- * @param actionElement Element
- * @param action {@link Action}
- * @return TypeSpec.Builder actionTypeSpecBuilder
- */
- private TypeSpec.Builder createActionTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element actionElement, Action action) {
- String simpleClassName = action.id().isEmpty() ? actionElement.getSimpleName().toString() : action.id();
-
- TypeSpec.Builder actionTypeSpecBuilder = TypeSpec.classBuilder(TouchPortalPluginAnnotationProcessor.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- actionTypeSpecBuilder.addModifiers(Modifier.PUBLIC);
-
- actionTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("id", ActionHelper.getActionId(pluginElement, categoryElement, category, actionElement, action)));
- actionTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("name", ActionHelper.getActionName(actionElement, action)));
- actionTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("prefix", action.prefix()));
- actionTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("description", action.description()));
- actionTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("type", action.type()));
- actionTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("format", action.format()));
- actionTypeSpecBuilder.addField(this.getStaticFinalBooleanFieldSpec("has_hold_functionality", action.hasHoldFunctionality()));
-
- return actionTypeSpecBuilder;
- }
-
- /**
- * Generates a TypeSpec.Builder with Constants for the {@link Action}
- *
- * @param pluginElement Element
- * @param categoryElement Element
- * @param category {@link Category}
- * @param connectorElement Element
- * @param connector {@link Action}
- * @return TypeSpec.Builder actionTypeSpecBuilder
- */
- private TypeSpec.Builder createConnectorTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element connectorElement, Connector connector) {
- String simpleClassName = connector.id().isEmpty() ? connectorElement.getSimpleName().toString() : connector.id();
-
- TypeSpec.Builder actionTypeSpecBuilder = TypeSpec.classBuilder(TouchPortalPluginAnnotationProcessor.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- actionTypeSpecBuilder.addModifiers(Modifier.PUBLIC);
-
- actionTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("id", ConnectorHelper.getConnectorId(pluginElement, categoryElement, category, connectorElement, connector)));
- actionTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("name", ConnectorHelper.getConnectorName(connectorElement, connector)));
- actionTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("format", connector.format()));
-
- return actionTypeSpecBuilder;
- }
-
- /**
- * Generates a TypeSpec.Builder with Constants for the {@link Data} for an {@link Action}
- *
- * @param pluginElement Element
- * @param categoryElement Element
- * @param category {@link Category}
- * @param actionElement Element
- * @param action {@link Action}
- * @param dataElement Element
- * @param data {@link Data}
- * @return TypeSpec.Builder dataTypeSpecBuilder
- */
- private TypeSpec.Builder createActionDataTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element actionElement, Action action, Element dataElement, Data data) {
- String simpleClassName = data.id().isEmpty() ? dataElement.getSimpleName().toString() : data.id();
-
- TypeSpec.Builder actionDataTypeSpecBuilder = TypeSpec.classBuilder(TouchPortalPluginAnnotationProcessor.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- actionDataTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("id", DataHelper.getActionDataId(pluginElement, categoryElement, category, actionElement, action, dataElement, data)));
- actionDataTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("label", DataHelper.getDataLabel(dataElement, data)));
- actionDataTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("default_value", data.defaultValue()));
- actionDataTypeSpecBuilder.addField(this.getStaticFinalStringArrayFieldSpec("value_choices", data.valueChoices()));
- actionDataTypeSpecBuilder.addField(this.getStaticFinalStringArrayFieldSpec("extensions", data.extensions()));
- actionDataTypeSpecBuilder.addField(this.getStaticFinalBooleanFieldSpec("is_directory", data.isDirectory()));
- actionDataTypeSpecBuilder.addField(this.getStaticFinalBooleanFieldSpec("is_color", data.isColor()));
- if (data.minValue() > Double.NEGATIVE_INFINITY) {
- actionDataTypeSpecBuilder.addField(this.getStaticFinalDoubleFieldSpec("min_value", data.minValue()));
- }
- if (data.maxValue() < Double.POSITIVE_INFINITY) {
- actionDataTypeSpecBuilder.addField(this.getStaticFinalDoubleFieldSpec("max_value", data.maxValue()));
- }
-
- return actionDataTypeSpecBuilder;
- }
-
- /**
- * Generates a TypeSpec.Builder with Constants for the {@link Data} for a {@link Connector}
- *
- * @param pluginElement Element
- * @param categoryElement Element
- * @param category {@link Category}
- * @param connectorElement Element
- * @param connector {@link Connector}
- * @param dataElement Element
- * @param data {@link Data}
- * @return TypeSpec.Builder dataTypeSpecBuilder
- */
- private TypeSpec.Builder createConnectorDataTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element connectorElement, Connector connector, Element dataElement, Data data) {
- String simpleClassName = data.id().isEmpty() ? dataElement.getSimpleName().toString() : data.id();
-
- TypeSpec.Builder connectorDataTypeSpecBuilder = TypeSpec.classBuilder(TouchPortalPluginAnnotationProcessor.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- connectorDataTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("id", DataHelper.getConnectorDataId(pluginElement, categoryElement, category, connectorElement, connector, dataElement, data)));
- connectorDataTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("label", DataHelper.getDataLabel(dataElement, data)));
- connectorDataTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("default_value", data.defaultValue()));
- connectorDataTypeSpecBuilder.addField(this.getStaticFinalStringArrayFieldSpec("value_choices", data.valueChoices()));
- connectorDataTypeSpecBuilder.addField(this.getStaticFinalStringArrayFieldSpec("extensions", data.extensions()));
- connectorDataTypeSpecBuilder.addField(this.getStaticFinalBooleanFieldSpec("is_directory", data.isDirectory()));
- connectorDataTypeSpecBuilder.addField(this.getStaticFinalBooleanFieldSpec("is_color", data.isColor()));
- if (data.minValue() > Double.NEGATIVE_INFINITY) {
- connectorDataTypeSpecBuilder.addField(this.getStaticFinalDoubleFieldSpec("min_value", data.minValue()));
- }
- if (data.maxValue() < Double.POSITIVE_INFINITY) {
- connectorDataTypeSpecBuilder.addField(this.getStaticFinalDoubleFieldSpec("max_value", data.maxValue()));
- }
-
- return connectorDataTypeSpecBuilder;
- }
-
- /**
- * Generates a TypeSpec.Builder with Constants for the {@link Setting}
- *
- * @param settingElement Element
- * @param setting {@link Setting}
- * @return TypeSpec.Builder stateTypeSpecBuilder
- */
- private TypeSpec.Builder createSettingTypeSpecBuilder(Element settingElement, Setting setting) {
- String simpleClassName = settingElement.getSimpleName().toString();
-
- TypeSpec.Builder stateTypeSpecBuilder = TypeSpec.classBuilder(TouchPortalPluginAnnotationProcessor.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- stateTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("name", SettingHelper.getSettingName(settingElement, setting)));
- stateTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("default", setting.defaultValue()));
- stateTypeSpecBuilder.addField(this.getStaticFinalDoubleFieldSpec("max_length", setting.maxLength()));
- stateTypeSpecBuilder.addField(this.getStaticFinalBooleanFieldSpec("is_password", setting.isPassword()));
- if (setting.minValue() > Double.NEGATIVE_INFINITY) {
- stateTypeSpecBuilder.addField(this.getStaticFinalDoubleFieldSpec("min_value", setting.minValue()));
- }
- if (setting.maxValue() < Double.POSITIVE_INFINITY) {
- stateTypeSpecBuilder.addField(this.getStaticFinalDoubleFieldSpec("max_value", setting.maxValue()));
- }
-
- return stateTypeSpecBuilder;
- }
-
- /**
- * Generates a TypeSpec.Builder with Constants for the {@link State}
- *
- * @param pluginElement Element
- * @param categoryElement Element
- * @param category {@link Category}
- * @param stateElement Element
- * @param state {@link State}
- * @return TypeSpec.Builder stateTypeSpecBuilder
- */
- private TypeSpec.Builder createStateTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element stateElement, State state) {
- String simpleClassName = state.id().isEmpty() ? stateElement.getSimpleName().toString() : state.id();
-
- TypeSpec.Builder stateTypeSpecBuilder = TypeSpec.classBuilder(TouchPortalPluginAnnotationProcessor.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- stateTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("id", StateHelper.getStateId(pluginElement, categoryElement, category, stateElement, state)));
- stateTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("desc", StateHelper.getStateDesc(stateElement, state)));
- stateTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("default_value", state.defaultValue()));
- stateTypeSpecBuilder.addField(this.getStaticFinalStringArrayFieldSpec("value_choices", state.valueChoices()));
-
- return stateTypeSpecBuilder;
- }
-
- /**
- * Generates a TypeSpec.Builder with Constants for the {@link Event}
- *
- * @param pluginElement Element
- * @param categoryElement Element
- * @param category {@link Category}
- * @param eventElement Element
- * @param event {@link Event}
- * @return TypeSpec.Builder eventTypeSpecBuilder
- */
- private TypeSpec.Builder createEventTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element eventElement, Event event) {
- String simpleClassName = event.id().isEmpty() ? eventElement.getSimpleName().toString() : event.id();
-
- TypeSpec.Builder eventTypeSpecBuilder = TypeSpec.classBuilder(TouchPortalPluginAnnotationProcessor.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
- eventTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("id", EventHelper.getEventId(pluginElement, categoryElement, category, eventElement, event)));
- eventTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("name", EventHelper.getEventName(eventElement, event)));
- eventTypeSpecBuilder.addField(this.getStaticFinalStringFieldSpec("format", event.format()));
- eventTypeSpecBuilder.addField(this.getStaticFinalStringArrayFieldSpec("value_choices", event.valueChoices()));
-
- return eventTypeSpecBuilder;
- }
-
- /**
- * Internal Get a Static Final String Field initialised with value
- *
- * @param fieldName String
- * @param value String
- * @return FieldSpec fieldSpec
- */
- private FieldSpec getStaticFinalStringFieldSpec(String fieldName, String value) {
- return FieldSpec.builder(String.class, fieldName.toUpperCase()).addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC).initializer("$S", value).build();
- }
-
- /**
- * Internal Get a Static Final long Field initialised with value
- *
- * @param fieldName String
- * @param value long
- * @return FieldSpec fieldSpec
- */
- private FieldSpec getStaticFinalDoubleFieldSpec(String fieldName, double value) {
- return FieldSpec.builder(double.class, fieldName.toUpperCase()).addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC).initializer("$L", value).build();
- }
-
- /**
- * Internal Get a Static Final long Field initialised with value
- *
- * @param fieldName String
- * @param value long
- * @return FieldSpec fieldSpec
- */
- private FieldSpec getStaticFinalLongFieldSpec(String fieldName, long value) {
- return FieldSpec.builder(long.class, fieldName.toUpperCase()).addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC).initializer("$L", value).build();
- }
-
- /**
- * Internal Get a Static Final boolean Field initialised with value
- *
- * @param fieldName String
- * @param value boolean
- * @return FieldSpec fieldSpec
- */
- private FieldSpec getStaticFinalBooleanFieldSpec(String fieldName, boolean value) {
- return FieldSpec.builder(boolean.class, fieldName.toUpperCase()).addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC).initializer("$L", value).build();
- }
-
- /**
- * Internal Get a Static Final boolean Field initialised with value
- *
- * @param fieldName String
- * @param values String[]
- * @return FieldSpec fieldSpec
- */
- private FieldSpec getStaticFinalStringArrayFieldSpec(String fieldName, String[] values) {
- ArrayTypeName stringArray = ArrayTypeName.of(String.class);
- String literal = "{\"" + String.join("\",\"", values) + "\"}";
- return FieldSpec.builder(String[].class, fieldName.toUpperCase()).addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC).initializer("new $1T $2L", stringArray, literal).build();
- }
-}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java
new file mode 100644
index 00000000..79bcd45e
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java
@@ -0,0 +1,121 @@
+/*
+ * Touch Portal Plugin SDK
+ *
+ * Copyright 2020 Christophe Carvalho Vilas-Boas
+ * christophe.carvalhovilasboas@gmail.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.christophecvb.touchportal.annotations.processor;
+
+import com.christophecvb.touchportal.annotations.*;
+import com.christophecvb.touchportal.annotations.processor.utils.Pair;
+import com.christophecvb.touchportal.annotations.processor.utils.SpecUtils;
+import com.christophecvb.touchportal.helpers.*;
+import com.google.auto.service.AutoService;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.annotation.processing.*;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+import java.io.Writer;
+import java.util.LinkedHashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Touch Portal Plugin Annotations Processor
+ */
+@AutoService(Processor.class)
+public class TouchPortalPluginAnnotationsProcessor extends AbstractProcessor {
+ private Filer filer;
+ private Messager messager;
+
+ public Messager getMessager() {
+ return this.messager;
+ }
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ this.filer = processingEnv.getFiler();
+ this.messager = processingEnv.getMessager();
+ }
+
+ @Override
+ public Set getSupportedAnnotationTypes() {
+ Set annotations = new LinkedHashSet<>();
+ annotations.add(Plugin.class.getCanonicalName());
+ annotations.add(Setting.class.getCanonicalName());
+ annotations.add(Category.class.getCanonicalName());
+ annotations.add(Action.class.getCanonicalName());
+ annotations.add(ActionTranslation.class.getCanonicalName());
+ annotations.add(ActionTranslations.class.getCanonicalName());
+ annotations.add(Data.class.getCanonicalName());
+ annotations.add(State.class.getCanonicalName());
+ annotations.add(Event.class.getCanonicalName());
+ annotations.add(Connector.class.getCanonicalName());
+ return annotations;
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (roundEnv.processingOver() || annotations.size() == 0) {
+ return false;
+ }
+ this.messager.printMessage(Diagnostic.Kind.NOTE, this.getClass().getSimpleName() + ".process");
+
+ try {
+ Set extends Element> plugins = roundEnv.getElementsAnnotatedWith(Plugin.class);
+ if (plugins.size() != 1) {
+ throw new TPAnnotationException.Builder(Plugin.class).count(plugins.size()).build();
+ }
+ for (Element pluginElement : plugins) {
+ Pair pluginPair = PluginProcessor.process(this, roundEnv, pluginElement);
+
+ String entryFileName = "resources/" + PluginHelper.ENTRY_TP;
+ FileObject actionFileObject = this.filer.createResource(StandardLocation.SOURCE_OUTPUT, "", entryFileName, pluginElement);
+ Writer writer = actionFileObject.openWriter();
+ writer.write(pluginPair.first.toString());
+ writer.flush();
+ writer.close();
+
+ TypeSpec pluginTypeSpec = pluginPair.second.build();
+ String packageName = ((PackageElement) pluginElement.getEnclosingElement()).getQualifiedName().toString();
+ JavaFile javaConstantsFile = JavaFile.builder(packageName, pluginTypeSpec).build();
+ javaConstantsFile.writeTo(this.filer);
+ }
+ }
+ catch (Exception exception) {
+ this.messager.printMessage(Diagnostic.Kind.ERROR, exception.getMessage());
+ }
+
+ return true;
+ }
+}
diff --git a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/utils/SpecUtils.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/utils/SpecUtils.java
new file mode 100644
index 00000000..044022ff
--- /dev/null
+++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/utils/SpecUtils.java
@@ -0,0 +1,325 @@
+package com.christophecvb.touchportal.annotations.processor.utils;
+
+import com.christophecvb.touchportal.annotations.*;
+import com.christophecvb.touchportal.helpers.*;
+import com.squareup.javapoet.ArrayTypeName;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeSpec;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+
+public class SpecUtils {
+ private static String capitalize(String str) {
+ if (str == null || str.isEmpty()) {
+ return str;
+ }
+
+ return str.substring(0, 1).toUpperCase() + str.substring(1);
+ }
+
+ /**
+ * Generates a TypeSpec.Builder with Constants for the {@link Plugin}
+ *
+ * @param pluginElement Element
+ * @param plugin {@link Plugin}
+ * @return TypeSpec.Builder pluginTypeSpecBuilder
+ */
+ public static TypeSpec.Builder createPluginTypeSpecBuilder(Element pluginElement, Plugin plugin) {
+ String simpleClassName = pluginElement.getSimpleName().toString() + "Constants";
+
+ TypeSpec.Builder pluginTypeSpecBuilder = TypeSpec.classBuilder(SpecUtils.capitalize(simpleClassName));
+ pluginTypeSpecBuilder.addModifiers(Modifier.PUBLIC);
+
+ pluginTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("id", PluginHelper.getPluginId(pluginElement)));
+ pluginTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("name", plugin.name()));
+ pluginTypeSpecBuilder.addField(SpecUtils.getStaticFinalLongFieldSpec("version", plugin.version()));
+
+ return pluginTypeSpecBuilder;
+ }
+
+ /**
+ * Generates a TypeSpec.Builder with Constants for the {@link Category}
+ *
+ * @param pluginElement Element
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @return TypeSpec.Builder pluginTypeSpecBuilder
+ */
+ public static TypeSpec.Builder createCategoryTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category) {
+ String simpleClassName = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id();
+
+ TypeSpec.Builder categoryTypeSpecBuilder = TypeSpec.classBuilder(SpecUtils.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ categoryTypeSpecBuilder.addModifiers(Modifier.PUBLIC);
+
+ categoryTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("id", CategoryHelper.getCategoryId(pluginElement, categoryElement, category)));
+ categoryTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("name", category.name()));
+ categoryTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("image_path", category.imagePath()));
+
+ return categoryTypeSpecBuilder;
+ }
+
+ /**
+ * Generates a TypeSpec.Builder with Constants for the {@link Action}
+ *
+ * @param pluginElement Element
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param actionElement Element
+ * @param action {@link Action}
+ * @return TypeSpec.Builder actionTypeSpecBuilder
+ */
+ public static TypeSpec.Builder createActionTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element actionElement, Action action) {
+ String simpleClassName = action.id().isEmpty() ? actionElement.getSimpleName().toString() : action.id();
+
+ TypeSpec.Builder actionTypeSpecBuilder = TypeSpec.classBuilder(SpecUtils.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ actionTypeSpecBuilder.addModifiers(Modifier.PUBLIC);
+
+ actionTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("id", ActionHelper.getActionId(pluginElement, categoryElement, category, actionElement, action)));
+ actionTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("name", ActionHelper.getActionName(actionElement, action)));
+ actionTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("prefix", action.prefix()));
+ actionTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("description", action.description()));
+ actionTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("type", action.type()));
+ actionTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("format", action.format()));
+ actionTypeSpecBuilder.addField(SpecUtils.getStaticFinalBooleanFieldSpec("has_hold_functionality", action.hasHoldFunctionality()));
+
+ return actionTypeSpecBuilder;
+ }
+
+ /**
+ * Generates a TypeSpec.Builder with Constants for the {@link Action}
+ *
+ * @param pluginElement Element
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param connectorElement Element
+ * @param connector {@link Action}
+ * @return TypeSpec.Builder actionTypeSpecBuilder
+ */
+ public static TypeSpec.Builder createConnectorTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element connectorElement, Connector connector) {
+ String simpleClassName = connector.id().isEmpty() ? connectorElement.getSimpleName().toString() : connector.id();
+
+ TypeSpec.Builder actionTypeSpecBuilder = TypeSpec.classBuilder(SpecUtils.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ actionTypeSpecBuilder.addModifiers(Modifier.PUBLIC);
+
+ actionTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("id", ConnectorHelper.getConnectorId(pluginElement, categoryElement, category, connectorElement, connector)));
+ actionTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("name", ConnectorHelper.getConnectorName(connectorElement, connector)));
+ actionTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("format", connector.format()));
+
+ return actionTypeSpecBuilder;
+ }
+
+ /**
+ * Generates a TypeSpec.Builder with Constants for the {@link Data} for an {@link Action}
+ *
+ * @param pluginElement Element
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param actionElement Element
+ * @param action {@link Action}
+ * @param dataElement Element
+ * @param data {@link Data}
+ * @return TypeSpec.Builder dataTypeSpecBuilder
+ */
+ public static TypeSpec.Builder createActionDataTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element actionElement, Action action, Element dataElement, Data data) {
+ String simpleClassName = data.id().isEmpty() ? dataElement.getSimpleName().toString() : data.id();
+
+ TypeSpec.Builder actionDataTypeSpecBuilder = TypeSpec.classBuilder(SpecUtils.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ actionDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("id", DataHelper.getActionDataId(pluginElement, categoryElement, category, actionElement, action, dataElement, data)));
+ actionDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("label", DataHelper.getDataLabel(dataElement, data)));
+ actionDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("default_value", data.defaultValue()));
+ actionDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringArrayFieldSpec("value_choices", data.valueChoices()));
+ actionDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringArrayFieldSpec("extensions", data.extensions()));
+ actionDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalBooleanFieldSpec("is_directory", data.isDirectory()));
+ actionDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalBooleanFieldSpec("is_color", data.isColor()));
+ if (data.minValue() > Double.NEGATIVE_INFINITY) {
+ actionDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalDoubleFieldSpec("min_value", data.minValue()));
+ }
+ if (data.maxValue() < Double.POSITIVE_INFINITY) {
+ actionDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalDoubleFieldSpec("max_value", data.maxValue()));
+ }
+
+ return actionDataTypeSpecBuilder;
+ }
+
+ /**
+ * Generates a TypeSpec.Builder with Constants for the {@link Data} for a {@link Connector}
+ *
+ * @param pluginElement Element
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param connectorElement Element
+ * @param connector {@link Connector}
+ * @param dataElement Element
+ * @param data {@link Data}
+ * @return TypeSpec.Builder dataTypeSpecBuilder
+ */
+ public static TypeSpec.Builder createConnectorDataTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element connectorElement, Connector connector, Element dataElement, Data data) {
+ String simpleClassName = data.id().isEmpty() ? dataElement.getSimpleName().toString() : data.id();
+
+ TypeSpec.Builder connectorDataTypeSpecBuilder = TypeSpec.classBuilder(SpecUtils.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ connectorDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("id", DataHelper.getConnectorDataId(pluginElement, categoryElement, category, connectorElement, connector, dataElement, data)));
+ connectorDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("label", DataHelper.getDataLabel(dataElement, data)));
+ connectorDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("default_value", data.defaultValue()));
+ connectorDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringArrayFieldSpec("value_choices", data.valueChoices()));
+ connectorDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringArrayFieldSpec("extensions", data.extensions()));
+ connectorDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalBooleanFieldSpec("is_directory", data.isDirectory()));
+ connectorDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalBooleanFieldSpec("is_color", data.isColor()));
+ if (data.minValue() > Double.NEGATIVE_INFINITY) {
+ connectorDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalDoubleFieldSpec("min_value", data.minValue()));
+ }
+ if (data.maxValue() < Double.POSITIVE_INFINITY) {
+ connectorDataTypeSpecBuilder.addField(SpecUtils.getStaticFinalDoubleFieldSpec("max_value", data.maxValue()));
+ }
+
+ return connectorDataTypeSpecBuilder;
+ }
+
+ /**
+ * Generates a TypeSpec.Builder with Constants for the {@link Setting}
+ *
+ * @param settingElement Element
+ * @param setting {@link Setting}
+ * @return TypeSpec.Builder stateTypeSpecBuilder
+ */
+ public static TypeSpec.Builder createSettingTypeSpecBuilder(Element settingElement, Setting setting) {
+ String simpleClassName = settingElement.getSimpleName().toString();
+
+ TypeSpec.Builder stateTypeSpecBuilder = TypeSpec.classBuilder(SpecUtils.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ stateTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("name", SettingHelper.getSettingName(settingElement, setting)));
+ stateTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("default", setting.defaultValue()));
+ stateTypeSpecBuilder.addField(SpecUtils.getStaticFinalDoubleFieldSpec("max_length", setting.maxLength()));
+ stateTypeSpecBuilder.addField(SpecUtils.getStaticFinalBooleanFieldSpec("is_password", setting.isPassword()));
+ if (setting.minValue() > Double.NEGATIVE_INFINITY) {
+ stateTypeSpecBuilder.addField(SpecUtils.getStaticFinalDoubleFieldSpec("min_value", setting.minValue()));
+ }
+ if (setting.maxValue() < Double.POSITIVE_INFINITY) {
+ stateTypeSpecBuilder.addField(SpecUtils.getStaticFinalDoubleFieldSpec("max_value", setting.maxValue()));
+ }
+
+ if (!setting.tooltip().body().isEmpty()) {
+ stateTypeSpecBuilder.addType(createSettingTooltipTypeSpecBuilder(setting.tooltip()).build());
+ }
+
+ return stateTypeSpecBuilder;
+ }
+
+ /**
+ * Generates a TypeSpec.Builder with Constants for the {@link Setting.Tooltip}
+ *
+ * @param tooltip {@link Setting.Tooltip}
+ * @return TypeSpec.Builder tooltipTypeSpecBuilder
+ */
+ public static TypeSpec.Builder createSettingTooltipTypeSpecBuilder(Setting.Tooltip tooltip) {
+ TypeSpec.Builder tooltipTypeSpecBuilder = TypeSpec.classBuilder("Tooltip").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ tooltipTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("body", tooltip.body()));
+ if (!tooltip.title().isEmpty()) {
+ tooltipTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("title", tooltip.title()));
+ }
+ if (!tooltip.title().isEmpty()) {
+ tooltipTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("docUrl", tooltip.docUrl()));
+ }
+ return tooltipTypeSpecBuilder;
+ }
+
+ /**
+ * Generates a TypeSpec.Builder with Constants for the {@link State}
+ *
+ * @param pluginElement Element
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param stateElement Element
+ * @param state {@link State}
+ * @return TypeSpec.Builder stateTypeSpecBuilder
+ */
+ public static TypeSpec.Builder createStateTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element stateElement, State state) {
+ String simpleClassName = state.id().isEmpty() ? stateElement.getSimpleName().toString() : state.id();
+
+ TypeSpec.Builder stateTypeSpecBuilder = TypeSpec.classBuilder(SpecUtils.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ stateTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("id", StateHelper.getStateId(pluginElement, categoryElement, category, stateElement, state)));
+ stateTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("desc", StateHelper.getStateDesc(stateElement, state)));
+ stateTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("default_value", state.defaultValue()));
+ stateTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringArrayFieldSpec("value_choices", state.valueChoices()));
+
+ return stateTypeSpecBuilder;
+ }
+
+ /**
+ * Generates a TypeSpec.Builder with Constants for the {@link Event}
+ *
+ * @param pluginElement Element
+ * @param categoryElement Element
+ * @param category {@link Category}
+ * @param eventElement Element
+ * @param event {@link Event}
+ * @return TypeSpec.Builder eventTypeSpecBuilder
+ */
+ public static TypeSpec.Builder createEventTypeSpecBuilder(Element pluginElement, Element categoryElement, Category category, Element eventElement, Event event) {
+ String simpleClassName = event.id().isEmpty() ? eventElement.getSimpleName().toString() : event.id();
+
+ TypeSpec.Builder eventTypeSpecBuilder = TypeSpec.classBuilder(SpecUtils.capitalize(simpleClassName)).addModifiers(Modifier.PUBLIC, Modifier.STATIC);
+ eventTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("id", EventHelper.getEventId(pluginElement, categoryElement, category, eventElement, event)));
+ eventTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("name", EventHelper.getEventName(eventElement, event)));
+ eventTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringFieldSpec("format", event.format()));
+ eventTypeSpecBuilder.addField(SpecUtils.getStaticFinalStringArrayFieldSpec("value_choices", event.valueChoices()));
+
+ return eventTypeSpecBuilder;
+ }
+
+ /**
+ * Internal Get a Static Final String Field initialised with value
+ *
+ * @param fieldName String
+ * @param value String
+ * @return FieldSpec fieldSpec
+ */
+ public static FieldSpec getStaticFinalStringFieldSpec(String fieldName, String value) {
+ return FieldSpec.builder(String.class, fieldName.toUpperCase()).addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC).initializer("$S", value).build();
+ }
+
+ /**
+ * Internal Get a Static Final long Field initialised with value
+ *
+ * @param fieldName String
+ * @param value long
+ * @return FieldSpec fieldSpec
+ */
+ public static FieldSpec getStaticFinalDoubleFieldSpec(String fieldName, double value) {
+ return FieldSpec.builder(double.class, fieldName.toUpperCase()).addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC).initializer("$L", value).build();
+ }
+
+ /**
+ * Internal Get a Static Final long Field initialised with value
+ *
+ * @param fieldName String
+ * @param value long
+ * @return FieldSpec fieldSpec
+ */
+ public static FieldSpec getStaticFinalLongFieldSpec(String fieldName, long value) {
+ return FieldSpec.builder(long.class, fieldName.toUpperCase()).addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC).initializer("$L", value).build();
+ }
+
+ /**
+ * Internal Get a Static Final boolean Field initialised with value
+ *
+ * @param fieldName String
+ * @param value boolean
+ * @return FieldSpec fieldSpec
+ */
+ public static FieldSpec getStaticFinalBooleanFieldSpec(String fieldName, boolean value) {
+ return FieldSpec.builder(boolean.class, fieldName.toUpperCase()).addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC).initializer("$L", value).build();
+ }
+
+ /**
+ * Internal Get a Static Final boolean Field initialised with value
+ *
+ * @param fieldName String
+ * @param values String[]
+ * @return FieldSpec fieldSpec
+ */
+ public static FieldSpec getStaticFinalStringArrayFieldSpec(String fieldName, String[] values) {
+ ArrayTypeName stringArray = ArrayTypeName.of(String.class);
+ String literal = "{\"" + String.join("\",\"", values) + "\"}";
+ return FieldSpec.builder(String[].class, fieldName.toUpperCase()).addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC).initializer("new $1T $2L", stringArray, literal).build();
+ }
+}
diff --git a/AnnotationsProcessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/AnnotationsProcessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
index 85ca834c..ebd05a33 100644
--- a/AnnotationsProcessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/AnnotationsProcessor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -1 +1 @@
-com.christophecvb.touchportal.annotations.processor.TouchPortalPluginAnnotationProcessor
+com.christophecvb.touchportal.annotations.processor.TouchPortalPluginAnnotationsProcessor
diff --git a/Helpers/build.gradle b/Helpers/build.gradle
index 5d24d203..b19d282c 100644
--- a/Helpers/build.gradle
+++ b/Helpers/build.gradle
@@ -77,6 +77,6 @@ javadoc {
}
dependencies {
- api group: 'com.google.code.gson', name: 'gson', version: '2.9.0'
+ api libs.gson
api project(':Annotations')
}
diff --git a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ActionHelper.java b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ActionHelper.java
index 907ae911..4ded488e 100644
--- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ActionHelper.java
+++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ActionHelper.java
@@ -24,6 +24,7 @@
import com.christophecvb.touchportal.annotations.Category;
import javax.lang.model.element.Element;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
@@ -109,6 +110,24 @@ public static String getActionId(Class> pluginClass, Method actionMethod) {
return actionId;
}
+ /**
+ * Get the generated Action ID
+ *
+ * @param pluginClass Class
+ * @param actionField Field
+ * @return String actionId
+ */
+ public static String getActionId(Class> pluginClass, Field actionField) {
+ String actionId = "";
+
+ if (actionField.getDeclaringClass().isAnnotationPresent(Action.class)) {
+ Action action = actionField.getDeclaringClass().getDeclaredAnnotation(Action.class);
+ actionId = ActionHelper._getActionId(CategoryHelper.getCategoryId(pluginClass, action.categoryId()), (!action.id().isEmpty() ? action.id() : actionField.getDeclaringClass().getSimpleName()));
+ }
+
+ return actionId;
+ }
+
/**
* Internal - Get the formatted Action ID
*
diff --git a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ConnectorHelper.java b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ConnectorHelper.java
index 6e0e20ca..993075f6 100644
--- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ConnectorHelper.java
+++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ConnectorHelper.java
@@ -24,6 +24,7 @@
import com.christophecvb.touchportal.annotations.Connector;
import javax.lang.model.element.Element;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
@@ -105,6 +106,24 @@ public static String getConnectorId(Class> pluginClass, Method connectorMethod
return connectorId;
}
+ /**
+ * Get the generated Connector ID
+ *
+ * @param pluginClass Class
+ * @param connectorField Field
+ * @return String connectorId
+ */
+ public static String getConnectorId(Class> pluginClass, Field connectorField) {
+ String connectorId = "";
+
+ if (connectorField.getDeclaringClass().isAnnotationPresent(Connector.class)) {
+ Connector connector = connectorField.getDeclaringClass().getDeclaredAnnotation(Connector.class);
+ connectorId = ConnectorHelper.getConnectorId(pluginClass, connectorField.getDeclaringClass(), connector);
+ }
+
+ return connectorId;
+ }
+
/**
* Get the generated Connector ID
*
@@ -117,6 +136,18 @@ public static String getConnectorId(Class> pluginClass, Method connectorMethod
return ConnectorHelper._getConnectorId(CategoryHelper.getCategoryId(pluginClass, connector.categoryId()), !connector.id().isEmpty() ? connector.id() : connectorMethod.getName());
}
+ /**
+ * Get the generated Connector ID
+ *
+ * @param pluginClass Class
+ * @param connectorClass Class
+ * @param connector {@link Connector}
+ * @return String connectorId
+ */
+ public static String getConnectorId(Class> pluginClass, Class connectorClass, Connector connector) {
+ return ConnectorHelper._getConnectorId(CategoryHelper.getCategoryId(pluginClass, connector.categoryId()), !connector.id().isEmpty() ? connector.id() : connectorClass.getSimpleName());
+ }
+
/**
* Internal - Get the formatted Connector ID
*
diff --git a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/DataHelper.java b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/DataHelper.java
index c9e0da87..6bce8c9d 100644
--- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/DataHelper.java
+++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/DataHelper.java
@@ -23,6 +23,7 @@
import com.christophecvb.touchportal.annotations.*;
import javax.lang.model.element.Element;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
@@ -150,6 +151,45 @@ else if (method.isAnnotationPresent(Connector.class)) {
return dataId;
}
+ /**
+ * Get the generated Data Id
+ *
+ * @param pluginClass Class
+ * @param field Field
+ * @return String dataId
+ */
+ public static String getDataId(Class> pluginClass, Field field) {
+ String dataId = "";
+
+ if (field.isAnnotationPresent(Data.class)) {
+ Data data = field.getAnnotation(Data.class);
+ if (!data.stateId().isEmpty()) {
+ String categoryId = null;
+ if (field.getDeclaringClass().isAnnotationPresent(Action.class)) {
+ Action action = field.getDeclaringClass().getAnnotation(Action.class);
+ categoryId = action.categoryId();
+ }
+ else if (field.getDeclaringClass().isAnnotationPresent(Connector.class)) {
+ Connector connector = field.getDeclaringClass().getAnnotation(Connector.class);
+ categoryId = connector.categoryId();
+ }
+ if (categoryId != null) {
+ dataId = StateHelper.getStateId(pluginClass, categoryId, data.stateId());
+ }
+ }
+ else {
+ if (field.getDeclaringClass().isAnnotationPresent(Action.class)) {
+ dataId = DataHelper._getDataId(ActionHelper.getActionId(pluginClass, field), data.id().isEmpty() ? field.getName() : data.id());
+ }
+ else if (field.getDeclaringClass().isAnnotationPresent(Connector.class)) {
+ dataId = DataHelper._getDataId(ConnectorHelper.getConnectorId(pluginClass, field), data.id().isEmpty() ? field.getName() : data.id());
+ }
+ }
+ }
+
+ return dataId;
+ }
+
/**
* Internal - Get the formatted Data Id
*
diff --git a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java
index b4706d68..fc777ec1 100644
--- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java
+++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java
@@ -29,21 +29,25 @@
* Touch Portal Plugin Plugin Helper
*/
public class PluginHelper {
- public static final String SDK = "sdk";
+ public static final String API = "api";
public static final String VERSION = "version";
public static final String NAME = GenericHelper.NAME;
public static final String ID = GenericHelper.ID;
public static final String CONFIGURATION = "configuration";
public static final String CONFIGURATION_COLOR_DARK = "colorDark";
public static final String CONFIGURATION_COLOR_LIGHT = "colorLight";
+ public static final String CONFIGURATION_PARENT_CATEGORY = "parentCategory";
public static final String PLUGIN_START_COMMAND = "plugin_start_cmd";
+ public static final String PLUGIN_START_COMMAND_SUFFIX_WIN = "_windows";
+ public static final String PLUGIN_START_COMMAND_SUFFIX_MACOS = "_mac";
+ public static final String PLUGIN_START_COMMAND_SUFFIX_LINUX = "_linux";
public static final String CATEGORIES = "categories";
public static final String SETTINGS = "settings";
/**
* Touch Portal Plugin System version
*/
- public static final int TOUCH_PORTAL_PLUGIN_VERSION = 6;
+ public static final int TOUCH_PORTAL_PLUGIN_VERSION = 7;
/**
* Argument passed to the jar to start the plugin
*/
@@ -52,6 +56,10 @@ public class PluginHelper {
* Touch Portal Plugin System TP_PLUGIN_FOLDER
*/
public static final String TP_PLUGIN_FOLDER = "%TP_PLUGIN_FOLDER%";
+ /**
+ * Touch Portal Plugin System TP_JAVA_FILE
+ */
+ public static final String TP_JAVA = "%TP_JAVA_FILE%";
/**
* Touch Portal entry file
*/
diff --git a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ReceivedMessageHelper.java b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ReceivedMessageHelper.java
index 2af3cedc..b6e7c96a 100644
--- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ReceivedMessageHelper.java
+++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/ReceivedMessageHelper.java
@@ -40,7 +40,7 @@ public class ReceivedMessageHelper {
public static final String TYPE_CONNECTOR_CHANGE = "connectorChange";
public static final String TYPE_SHORT_CONNECTOR_ID_NOTIFICATION = "shortConnectorIdNotification";
public static final String TYPE_INFO = "info";
- public static final String TYPE_LIST_CHANGE = "listChange";
+ public static final String TYPE_LIST_CHANGED = "listChange";
public static final String TYPE_CLOSE_PLUGIN = "closePlugin";
public static final String TYPE_BROADCAST = "broadcast";
public static final String TYPE_SETTINGS = "settings";
@@ -87,7 +87,7 @@ public static boolean isTypeAction(JsonObject jsonMessage) {
* @return boolean isMessageAListChange
*/
public static boolean isTypeListChange(JsonObject jsonMessage) {
- return ReceivedMessageHelper.TYPE_LIST_CHANGE.equals(ReceivedMessageHelper.getType(jsonMessage));
+ return ReceivedMessageHelper.TYPE_LIST_CHANGED.equals(ReceivedMessageHelper.getType(jsonMessage));
}
/**
diff --git a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SentMessageHelper.java b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SentMessageHelper.java
index a852b127..afa854a1 100644
--- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SentMessageHelper.java
+++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SentMessageHelper.java
@@ -34,6 +34,7 @@ public class SentMessageHelper {
public static final String TYPE_SETTING_UPDATE = "settingUpdate";
public static final String TYPE_SHOW_NOTIFICATION = "showNotification";
public static final String TYPE_CONNECTOR_UPDATE = "connectorUpdate";
+ public static final String TYPE_TRIGGER_EVENT = "triggerEvent";
public static final String INSTANCE_ID = "instanceId";
public static final String ID = GenericHelper.ID;
public static final String VALUE = GenericHelper.VALUE;
@@ -48,5 +49,8 @@ public class SentMessageHelper {
public static final String CONNECTOR_ID = "connectorId";
public static final String SHORT_ID = "shortId";
public static final String PARENT_GROUP = "parentGroup";
+ public static final String FORCE_UPDATE = "forceUpdate";
+ public static final String EVENT_ID = "eventId";
+ public static final String STATES = "states";
}
\ No newline at end of file
diff --git a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SettingHelper.java b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SettingHelper.java
index e559e987..a78eff38 100644
--- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SettingHelper.java
+++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SettingHelper.java
@@ -39,6 +39,13 @@ public class SettingHelper {
public static final String MIN_VALUE = "minValue";
public static final String MAX_VALUE = "maxValue";
public static final String IS_READ_ONLY = "readOnly";
+ public static final String TOOLTIP = "tooltip";
+
+ public static class Tooltip {
+ public static final String TITLE = "title";
+ public static final String BODY = "body";
+ public static final String DOC_URL = "docUrl";
+ }
/**
* Get the generated Setting Name
diff --git a/Library/build.gradle b/Library/build.gradle
index 31acac36..2125853f 100644
--- a/Library/build.gradle
+++ b/Library/build.gradle
@@ -81,11 +81,11 @@ dependencies {
api project(':Annotations')
api project(':Helpers')
- api group: 'com.google.code.gson', name: 'gson', version: '2.9.0'
+ api libs.gson
- implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.9.3'
+ implementation libs.okhttp
- testImplementation group: 'junit', name: 'junit', version: '4.13.2'
+ testImplementation libs.junit
testAnnotationProcessor project(':AnnotationsProcessor')
}
diff --git a/Library/src/main/java/com/christophecvb/touchportal/TPAction.java b/Library/src/main/java/com/christophecvb/touchportal/TPAction.java
new file mode 100644
index 00000000..054a147f
--- /dev/null
+++ b/Library/src/main/java/com/christophecvb/touchportal/TPAction.java
@@ -0,0 +1,12 @@
+package com.christophecvb.touchportal;
+
+public abstract class TPAction extends TPInvokable {
+
+ public TPAction(T touchPortalPlugin) {
+ super(touchPortalPlugin);
+ }
+
+ protected Boolean isBeingHeld(String actionId) {
+ return this.touchPortalPlugin.isActionBeingHeld(actionId);
+ }
+}
\ No newline at end of file
diff --git a/Library/src/main/java/com/christophecvb/touchportal/TPConnector.java b/Library/src/main/java/com/christophecvb/touchportal/TPConnector.java
new file mode 100644
index 00000000..8e76c7af
--- /dev/null
+++ b/Library/src/main/java/com/christophecvb/touchportal/TPConnector.java
@@ -0,0 +1,8 @@
+package com.christophecvb.touchportal;
+
+public abstract class TPConnector extends TPInvokable {
+
+ public TPConnector(T touchPortalPlugin) {
+ super(touchPortalPlugin);
+ }
+}
\ No newline at end of file
diff --git a/Library/src/main/java/com/christophecvb/touchportal/TPInvokable.java b/Library/src/main/java/com/christophecvb/touchportal/TPInvokable.java
new file mode 100644
index 00000000..8a5ae7f3
--- /dev/null
+++ b/Library/src/main/java/com/christophecvb/touchportal/TPInvokable.java
@@ -0,0 +1,15 @@
+package com.christophecvb.touchportal;
+
+import com.christophecvb.touchportal.model.TPListChangedMessage;
+
+abstract class TPInvokable {
+ protected final T touchPortalPlugin;
+
+ public TPInvokable(T touchPortalPlugin) {
+ this.touchPortalPlugin = touchPortalPlugin;
+ }
+
+ public abstract void onInvoke();
+
+ public abstract void onListChanged(TPListChangedMessage tpListChangedMessage);
+}
\ No newline at end of file
diff --git a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java
index 37865d03..7beec982 100644
--- a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java
+++ b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java
@@ -27,19 +27,17 @@
import com.google.gson.*;
import okhttp3.*;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
import java.io.*;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Parameter;
+import java.lang.reflect.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
+import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
+import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.*;
@@ -135,10 +133,18 @@ public abstract class TouchPortalPlugin {
* Current Held Actions States
*/
private final HashMap heldActionsStates = new HashMap<>();
+ /**
+ * Map containing image urls and their base64 representation
+ */
+ private final HashMap base64Images = new HashMap<>();
/**
* Executor Service for callbacks
*/
private final ExecutorService callbacksExecutor;
+ /**
+ * Registered {@link TPInvokable}s
+ */
+ private final HashMap> registeredInvokables = new HashMap<>();
/**
* Internal Gson Serializer/Deserializer
@@ -184,7 +190,7 @@ private Thread createListenerThread() {
TPMessageDeserializer tpMessageDeserializer = new TPMessageDeserializer();
tpMessageDeserializer.registerTPMessageType(ReceivedMessageHelper.TYPE_CLOSE_PLUGIN, TPClosePluginMessage.class);
tpMessageDeserializer.registerTPMessageType(ReceivedMessageHelper.TYPE_INFO, TPInfoMessage.class);
- tpMessageDeserializer.registerTPMessageType(ReceivedMessageHelper.TYPE_LIST_CHANGE, TPListChangeMessage.class);
+ tpMessageDeserializer.registerTPMessageType(ReceivedMessageHelper.TYPE_LIST_CHANGED, TPListChangedMessage.class);
tpMessageDeserializer.registerTPMessageType(ReceivedMessageHelper.TYPE_BROADCAST, TPBroadcastMessage.class);
tpMessageDeserializer.registerTPMessageType(ReceivedMessageHelper.TYPE_SETTINGS, TPSettingsMessage.class);
tpMessageDeserializer.registerTPMessageType(ReceivedMessageHelper.TYPE_ACTION, TPActionMessage.class);
@@ -224,21 +230,40 @@ private void onMessage(String socketMessage) throws SocketException, JsonParseEx
this.updateSettingFields(this.tpInfoMessage.settings);
if (this.touchPortalPluginListener != null) {
- this.touchPortalPluginListener.onInfo(this.tpInfoMessage);
+ this.callbacksExecutor.submit(() -> {
+ this.touchPortalPluginListener.onInfo(this.tpInfoMessage);
+ });
}
break;
- case ReceivedMessageHelper.TYPE_LIST_CHANGE:
- TPListChangeMessage listChangeMessage = (TPListChangeMessage) tpMessage;
+ case ReceivedMessageHelper.TYPE_LIST_CHANGED:
+ TPListChangedMessage tpListChangedMessage = (TPListChangedMessage) tpMessage;
+ if (this.registeredInvokables.containsKey(tpListChangedMessage.actionId)) {
+ Class extends TPInvokable> invokableClass = this.registeredInvokables.get(tpListChangedMessage.actionId);
+ try {
+ TPInvokable tpInvokable = this.instantiateTPInvokable(invokableClass);
+
+ this.callbacksExecutor.submit(() -> {
+ tpInvokable.onListChanged(tpListChangedMessage);
+ });
+ }
+ catch (ReflectiveOperationException e) {
+ TouchPortalPlugin.LOGGER.log(Level.WARNING, "Invokable could not be created or its onListChanged could not be invoked", e);
+ }
+ }
if (this.touchPortalPluginListener != null) {
- this.touchPortalPluginListener.onListChanged(listChangeMessage);
+ this.callbacksExecutor.submit(() -> {
+ this.touchPortalPluginListener.onListChanged(tpListChangedMessage);
+ });
}
break;
case ReceivedMessageHelper.TYPE_BROADCAST:
TPBroadcastMessage tpBroadcastMessage = (TPBroadcastMessage) tpMessage;
if (this.touchPortalPluginListener != null) {
- this.touchPortalPluginListener.onBroadcast(tpBroadcastMessage);
+ this.callbacksExecutor.submit(() -> {
+ this.touchPortalPluginListener.onBroadcast(tpBroadcastMessage);
+ });
}
break;
@@ -248,14 +273,18 @@ private void onMessage(String socketMessage) throws SocketException, JsonParseEx
this.updateSettingFields(tpSettingsMessage.settings);
if (this.touchPortalPluginListener != null) {
- this.touchPortalPluginListener.onSettings(tpSettingsMessage);
+ this.callbacksExecutor.submit(() -> {
+ this.touchPortalPluginListener.onSettings(tpSettingsMessage);
+ });
}
break;
case ReceivedMessageHelper.TYPE_NOTIFICATION_OPTION_CLICKED:
TPNotificationOptionClickedMessage tpNotificationOptionClickedMessage = (TPNotificationOptionClickedMessage) tpMessage;
if (this.touchPortalPluginListener != null) {
- this.touchPortalPluginListener.onNotificationOptionClicked(tpNotificationOptionClickedMessage);
+ this.callbacksExecutor.submit(() -> {
+ this.touchPortalPluginListener.onNotificationOptionClicked(tpNotificationOptionClickedMessage);
+ });
}
break;
@@ -287,7 +316,9 @@ private void onMessage(String socketMessage) throws SocketException, JsonParseEx
}
if (!called) {
if (this.touchPortalPluginListener != null) {
- this.touchPortalPluginListener.onReceived(jsonMessage);
+ this.callbacksExecutor.submit(() -> {
+ this.touchPortalPluginListener.onReceived(jsonMessage);
+ });
}
}
}
@@ -297,6 +328,13 @@ private void onMessage(String socketMessage) throws SocketException, JsonParseEx
}
}
+ private TPInvokable instantiateTPInvokable(Class extends TPInvokable> invokableClass) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
+ Class extends TouchPortalPlugin> typedTouchPortalPlugin = (Class extends TouchPortalPlugin>) ((ParameterizedType) invokableClass.getGenericSuperclass()).getActualTypeArguments()[0];
+ Constructor extends TPInvokable> constructor = invokableClass.getConstructor(typedTouchPortalPlugin);
+ TPInvokable tpInvokable = constructor.newInstance(this);
+ return tpInvokable;
+ }
+
private void updateSettingFields(HashMap settings) {
Field[] pluginSettingFields = Arrays.stream(this.pluginClass.getDeclaredFields()).filter(field -> field.isAnnotationPresent(Setting.class)).toArray(Field[]::new);
for (String settingName : settings.keySet()) {
@@ -316,105 +354,194 @@ private void updateSettingFields(HashMap settings) {
}
private boolean onActionReceived(TPActionMessage tpActionMessage, JsonObject jsonAction, Boolean held) {
- boolean called = false;
+ boolean invoked = false;
if (tpActionMessage.actionId != null && !tpActionMessage.actionId.isEmpty()) {
- Method[] pluginActionMethods = Arrays.stream(this.pluginClass.getDeclaredMethods()).filter(method -> method.isAnnotationPresent(Action.class)).toArray(Method[]::new);
- for (Method method : pluginActionMethods) {
- String methodActionId = ActionHelper.getActionId(this.pluginClass, method);
- if (tpActionMessage.actionId.equals(methodActionId)) {
- try {
- Parameter[] parameters = method.getParameters();
- Object[] arguments = new Object[parameters.length];
- for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
- Parameter parameter = parameters[parameterIndex];
- if (parameter.isAnnotationPresent(Data.class)) {
- arguments[parameterIndex] = tpActionMessage.getTypedDataValue(this.pluginClass, method, parameter);
- }
- else if (parameter.getType().isAssignableFrom(JsonObject.class)) {
- arguments[parameterIndex] = jsonAction;
- }
- else if (parameter.getType().isAssignableFrom(TPActionMessage.class)) {
- arguments[parameterIndex] = tpActionMessage;
- }
- if (arguments[parameterIndex] == null) {
- throw new MethodDataParameterException(method, parameter);
- }
+ if (this.registeredInvokables.containsKey(tpActionMessage.actionId)) {
+ Class extends TPInvokable> invokableClass = this.registeredInvokables.get(tpActionMessage.actionId);
+ try {
+ TPInvokable tpInvokable = this.instantiateTPInvokable(invokableClass);
+
+ for (Field declaredField : invokableClass.getDeclaredFields()) {
+ if (declaredField.isAnnotationPresent(Data.class)) {
+ declaredField.setAccessible(true);
+ Object fieldValue = tpActionMessage.getTypedDataValue(this.pluginClass, declaredField);
+ declaredField.set(tpInvokable, fieldValue);
}
- this.heldActionsStates.put(tpActionMessage.actionId, held);
- this.callbacksExecutor.submit(() -> {
- try {
- method.setAccessible(true);
- method.invoke(this, arguments);
- }
- catch (Exception e) {
- TouchPortalPlugin.LOGGER.log(Level.SEVERE, "Action method could not be invoked", e);
+ else if (declaredField.getType().isAssignableFrom(JsonObject.class)) {
+ declaredField.setAccessible(true);
+ declaredField.set(tpInvokable, jsonAction);
+ }
+ else if (declaredField.getType().isAssignableFrom(TPActionMessage.class)) {
+ declaredField.setAccessible(true);
+ declaredField.set(tpInvokable, tpActionMessage);
+ }
+ }
+
+ this.heldActionsStates.put(tpActionMessage.actionId, held);
+ this.callbacksExecutor.submit(() -> {
+ try {
+ tpInvokable.onInvoke();
+ }
+ catch (Exception e) {
+ TouchPortalPlugin.LOGGER.log(Level.SEVERE, "Action could not be invoked", e);
+ }
+ finally {
+ if (held == null || !held) {
+ this.heldActionsStates.remove(tpActionMessage.actionId);
}
- finally {
- if (held == null || !held) {
- this.heldActionsStates.remove(tpActionMessage.actionId);
+ }
+ });
+
+ invoked = true;
+ }
+ catch (ReflectiveOperationException e) {
+ TouchPortalPlugin.LOGGER.log(Level.WARNING, "Action could not be instanced", e);
+ }
+ }
+ else {
+ Method[] pluginActionMethods = Arrays.stream(this.pluginClass.getDeclaredMethods()).filter(method -> method.isAnnotationPresent(Action.class)).toArray(Method[]::new);
+ for (Method method : pluginActionMethods) {
+ String methodActionId = ActionHelper.getActionId(this.pluginClass, method);
+ if (tpActionMessage.actionId.equals(methodActionId)) {
+ try {
+ Parameter[] parameters = method.getParameters();
+ Object[] arguments = new Object[parameters.length];
+ for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
+ Parameter parameter = parameters[parameterIndex];
+ if (parameter.isAnnotationPresent(Data.class)) {
+ arguments[parameterIndex] = tpActionMessage.getTypedDataValue(this.pluginClass, method, parameter);
+ }
+ else if (parameter.getType().isAssignableFrom(JsonObject.class)) {
+ arguments[parameterIndex] = jsonAction;
+ }
+ else if (parameter.getType().isAssignableFrom(TPActionMessage.class)) {
+ arguments[parameterIndex] = tpActionMessage;
+ }
+ if (arguments[parameterIndex] == null) {
+ throw new MethodDataParameterException(method, parameter);
}
}
- });
- called = true;
- }
- catch (MethodDataParameterException e) {
- TouchPortalPlugin.LOGGER.log(Level.WARNING, e.getMessage(), e);
+ this.heldActionsStates.put(tpActionMessage.actionId, held);
+ this.callbacksExecutor.submit(() -> {
+ try {
+ method.setAccessible(true);
+ method.invoke(this, arguments);
+ }
+ catch (Exception e) {
+ TouchPortalPlugin.LOGGER.log(Level.SEVERE, "Action method could not be invoked", e);
+ }
+ finally {
+ if (held == null || !held) {
+ this.heldActionsStates.remove(tpActionMessage.actionId);
+ }
+ }
+ });
+ invoked = true;
+ }
+ catch (MethodDataParameterException e) {
+ TouchPortalPlugin.LOGGER.log(Level.WARNING, "Action method data parameters could not be retrieved", e);
+ }
+ break;
}
- break;
}
}
}
- return called;
+ return invoked;
}
- private boolean onConnectorChangeReceived(TPConnectorChangeMessage tpConnectorChangeMessage, JsonObject jsonAction) {
- boolean called = false;
+ private boolean onConnectorChangeReceived(TPConnectorChangeMessage tpConnectorChangeMessage, JsonObject jsonConnectorChange) {
+ boolean invoked = false;
if (tpConnectorChangeMessage.connectorId != null && !tpConnectorChangeMessage.connectorId.isEmpty()) {
- Method[] pluginConnectorMethods = Arrays.stream(this.pluginClass.getDeclaredMethods()).filter(method -> method.isAnnotationPresent(Connector.class)).toArray(Method[]::new);
- for (Method method : pluginConnectorMethods) {
- String methodConnectorId = ConnectorHelper.getConnectorId(this.pluginClass, method);
- if (tpConnectorChangeMessage.connectorId.equals(methodConnectorId)) {
- try {
- Parameter[] parameters = method.getParameters();
- Object[] arguments = new Object[parameters.length];
- for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
- Parameter parameter = parameters[parameterIndex];
- if (parameter.isAnnotationPresent(Data.class)) {
- arguments[parameterIndex] = tpConnectorChangeMessage.getTypedDataValue(this.pluginClass, method, parameter);
- }
- else if (parameter.isAnnotationPresent(ConnectorValue.class)) {
- arguments[parameterIndex] = tpConnectorChangeMessage.value;
- }
- else if (parameter.getType().isAssignableFrom(JsonObject.class)) {
- arguments[parameterIndex] = jsonAction;
- }
- else if (parameter.getType().isAssignableFrom(TPConnectorChangeMessage.class)) {
- arguments[parameterIndex] = tpConnectorChangeMessage;
- }
- if (arguments[parameterIndex] == null) {
- throw new MethodDataParameterException(method, parameter);
- }
+ if (this.registeredInvokables.containsKey(tpConnectorChangeMessage.connectorId)) {
+ Class extends TPInvokable> invokableClass = this.registeredInvokables.get(tpConnectorChangeMessage.connectorId);
+ try {
+ TPInvokable tpInvokable = this.instantiateTPInvokable(invokableClass);
+
+ for (Field declaredField : invokableClass.getDeclaredFields()) {
+ if (declaredField.isAnnotationPresent(Data.class)) {
+ declaredField.setAccessible(true);
+ Object fieldValue = tpConnectorChangeMessage.getTypedDataValue(this.pluginClass, declaredField);
+ declaredField.set(tpInvokable, fieldValue);
+ }
+ else if (declaredField.isAnnotationPresent(ConnectorValue.class)) {
+ declaredField.setAccessible(true);
+ declaredField.set(tpInvokable, tpConnectorChangeMessage.value);
+ }
+ else if (declaredField.getType().isAssignableFrom(JsonObject.class)) {
+ declaredField.setAccessible(true);
+ declaredField.set(tpInvokable, jsonConnectorChange);
+ }
+ else if (declaredField.getType().isAssignableFrom(TPConnectorChangeMessage.class)) {
+ declaredField.setAccessible(true);
+ declaredField.set(tpInvokable, tpConnectorChangeMessage);
}
- this.currentConnectorValues.put(tpConnectorChangeMessage.getConstructedId(), tpConnectorChangeMessage.value);
- this.callbacksExecutor.submit(() -> {
- try {
- method.setAccessible(true);
- method.invoke(this, arguments);
- }
- catch (Exception e) {
- TouchPortalPlugin.LOGGER.log(Level.SEVERE, "Connector method could not be invoked", e);
- }
- });
- called = true;
}
- catch (MethodDataParameterException e) {
- TouchPortalPlugin.LOGGER.log(Level.WARNING, e.getMessage(), e);
+
+ this.currentConnectorValues.put(tpConnectorChangeMessage.getConstructedId(), tpConnectorChangeMessage.value);
+ this.callbacksExecutor.submit(() -> {
+ try {
+ tpInvokable.onInvoke();
+ }
+ catch (Exception e) {
+ TouchPortalPlugin.LOGGER.log(Level.SEVERE, "Connector could not be invoked", e);
+ }
+ });
+
+ invoked = true;
+ }
+ catch (ReflectiveOperationException e) {
+ TouchPortalPlugin.LOGGER.log(Level.WARNING, "Connector could not be created or invoked", e);
+ }
+ }
+ else {
+ Method[] pluginConnectorMethods = Arrays.stream(this.pluginClass.getDeclaredMethods()).filter(method -> method.isAnnotationPresent(Connector.class)).toArray(Method[]::new);
+ for (Method method : pluginConnectorMethods) {
+ String methodConnectorId = ConnectorHelper.getConnectorId(this.pluginClass, method);
+ if (tpConnectorChangeMessage.connectorId.equals(methodConnectorId)) {
+ try {
+ Parameter[] parameters = method.getParameters();
+ Object[] arguments = new Object[parameters.length];
+ for (int parameterIndex = 0; parameterIndex < parameters.length; parameterIndex++) {
+ Parameter parameter = parameters[parameterIndex];
+ if (parameter.isAnnotationPresent(Data.class)) {
+ arguments[parameterIndex] = tpConnectorChangeMessage.getTypedDataValue(this.pluginClass, method, parameter);
+ }
+ else if (parameter.isAnnotationPresent(ConnectorValue.class)) {
+ arguments[parameterIndex] = tpConnectorChangeMessage.value;
+ }
+ else if (parameter.getType().isAssignableFrom(JsonObject.class)) {
+ arguments[parameterIndex] = jsonConnectorChange;
+ }
+ else if (parameter.getType().isAssignableFrom(TPConnectorChangeMessage.class)) {
+ arguments[parameterIndex] = tpConnectorChangeMessage;
+ }
+ if (arguments[parameterIndex] == null) {
+ throw new MethodDataParameterException(method, parameter);
+ }
+ }
+
+ this.currentConnectorValues.put(tpConnectorChangeMessage.getConstructedId(), tpConnectorChangeMessage.value);
+ this.callbacksExecutor.submit(() -> {
+ try {
+ method.setAccessible(true);
+ method.invoke(this, arguments);
+ }
+ catch (Exception e) {
+ TouchPortalPlugin.LOGGER.log(Level.SEVERE, "Connector method could not be invoked", e);
+ }
+ });
+
+ invoked = true;
+ }
+ catch (MethodDataParameterException e) {
+ TouchPortalPlugin.LOGGER.log(Level.WARNING, e.getMessage(), e);
+ }
+ break;
}
- break;
}
}
}
- return called;
+ return invoked;
}
/**
@@ -433,6 +560,16 @@ private boolean sendPair() {
return paired;
}
+ /**
+ * Register a {@link TPAction} or {@link TPConnector}
+ *
+ * @param invokableId String
+ * @param invokableClass Class<{@link TPInvokable}>
+ */
+ protected void registerInvokable(String invokableId, Class extends TPInvokable> invokableClass) {
+ this.registeredInvokables.put(invokableId, invokableClass);
+ }
+
/**
* Get TPInfo
*
@@ -767,6 +904,7 @@ public boolean sendCreateState(String categoryId, String stateId, String parentG
createStateMessage.addProperty(SentMessageHelper.ID, stateId);
createStateMessage.addProperty(SentMessageHelper.DESCRIPTION, description);
createStateMessage.addProperty(SentMessageHelper.DEFAULT_VALUE, valueStr);
+ createStateMessage.addProperty(SentMessageHelper.FORCE_UPDATE, true);
if (parentGroup == null || parentGroup.isEmpty()) {
classLoop:
for (Class> subClass : this.pluginClass.getDeclaredClasses()) {
@@ -912,6 +1050,36 @@ public boolean sendShowNotification(String notificationId, String title, String
return sent;
}
+ /**
+ * Send a Trigger Event message to the Touch Portal Plugin System
+ *
+ * @param eventId String
+ * @param states Map<String, Object> Key value pair of state id and value
+ * @return Boolean sent
+ */
+ public boolean sendTriggerEvent(String eventId, Map states) {
+ boolean sent = false;
+ if (eventId != null && !eventId.isEmpty()) {
+ JsonObject triggerEventMessage = new JsonObject();
+ triggerEventMessage.addProperty(SentMessageHelper.TYPE, SentMessageHelper.TYPE_TRIGGER_EVENT);
+ triggerEventMessage.addProperty(SentMessageHelper.EVENT_ID, eventId);
+
+ if (states != null && !states.isEmpty()) {
+ JsonObject jsonStates = new JsonObject();
+
+ states.forEach((id, value) -> {
+ jsonStates.addProperty(id, String.valueOf(value));
+ });
+ triggerEventMessage.add(SentMessageHelper.STATES, jsonStates);
+ }
+
+ sent = this.send(triggerEventMessage);
+ TouchPortalPlugin.LOGGER.info("Trigger Event [" + eventId + "] Sent [" + sent + "]");
+ }
+
+ return sent;
+ }
+
/**
* Send a Connector Update Message to the Touch Portal Plugin System
*
@@ -1197,6 +1365,41 @@ public Boolean isActionBeingHeld(String actionId) {
return this.heldActionsStates.get(actionId);
}
+ /**
+ * Convert an image url to a base64 representation
+ *
+ * @param imageUrl String
+ * @return String base64Image
+ */
+ public String getBase64ImageFromUrl(String imageUrl) {
+ String base64Image = "";
+ if (this.base64Images.containsKey(imageUrl)) {
+ base64Image = this.base64Images.get(imageUrl);
+ }
+ else {
+ ByteArrayOutputStream byteArrayOutputStream = null;
+ try {
+ BufferedImage bufferedImage = ImageIO.read(new URL(imageUrl));
+
+ ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream = new ByteArrayOutputStream());
+ base64Image = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
+ this.base64Images.put(imageUrl, base64Image);
+ }
+ catch (Exception exception) {
+ LOGGER.warning(exception.getMessage() + " for Image URL: " + imageUrl);
+ }
+ finally {
+ if (byteArrayOutputStream != null) {
+ try {
+ byteArrayOutputStream.close();
+ }
+ catch (IOException ignored) {}
+ }
+ }
+ }
+ return base64Image;
+ }
+
/**
* Interface Definition for Callbacks
*/
@@ -1225,9 +1428,9 @@ public interface TouchPortalPluginListener {
/**
* Called when a List Change Message is received
*
- * @param tpListChangeMessage TPListChangeMessage
+ * @param tpListChangedMessage TPListChangeMessage
*/
- void onListChanged(TPListChangeMessage tpListChangeMessage);
+ void onListChanged(TPListChangedMessage tpListChangedMessage);
/**
* Called when a Broadcast Message is received
@@ -1268,7 +1471,7 @@ public MethodDataParameterException(Method method, Parameter parameter) {
/**
* Custom ConsoleHandler
*/
- private static class CustomConsoleHandler extends ConsoleHandler {
+ public static class CustomConsoleHandler extends ConsoleHandler {
public CustomConsoleHandler() {
super();
setFormatter(new SimpleFormatter() {
@@ -1278,7 +1481,7 @@ public CustomConsoleHandler() {
public synchronized String format(LogRecord lr) {
String[] path = lr.getSourceClassName().split("\\.");
return String.format(format,
- lr.getLevel().getLocalizedName(),
+ lr.getLevel().getName(),
path[path.length - 1] + "." + lr.getSourceMethodName(),
lr.getMessage()
);
diff --git a/Library/src/main/java/com/christophecvb/touchportal/model/TPActionMessage.java b/Library/src/main/java/com/christophecvb/touchportal/model/TPActionMessage.java
index ea6e1f05..2ecf1a4d 100644
--- a/Library/src/main/java/com/christophecvb/touchportal/model/TPActionMessage.java
+++ b/Library/src/main/java/com/christophecvb/touchportal/model/TPActionMessage.java
@@ -23,6 +23,7 @@
import com.christophecvb.touchportal.helpers.DataHelper;
import com.christophecvb.touchportal.helpers.ReceivedMessageHelper;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
@@ -41,6 +42,10 @@ public Object getTypedDataValue(Class> pluginClass, Method actionMethod, Param
return this.getTypedDataValue(actionMethodParameter.getParameterizedType().getTypeName(), DataHelper.getDataId(pluginClass, actionMethod, actionMethodParameter));
}
+ public Object getTypedDataValue(Class> pluginClass, Field actionField) {
+ return this.getTypedDataValue(actionField.getType().getTypeName(), DataHelper.getDataId(pluginClass, actionField));
+ }
+
public Object getTypedDataValue(String actionDataType, String actionDataId) {
Object value = null;
diff --git a/Library/src/main/java/com/christophecvb/touchportal/model/TPConnectorChangeMessage.java b/Library/src/main/java/com/christophecvb/touchportal/model/TPConnectorChangeMessage.java
index ae5db536..b865a254 100644
--- a/Library/src/main/java/com/christophecvb/touchportal/model/TPConnectorChangeMessage.java
+++ b/Library/src/main/java/com/christophecvb/touchportal/model/TPConnectorChangeMessage.java
@@ -24,6 +24,7 @@
import com.christophecvb.touchportal.helpers.DataHelper;
import com.christophecvb.touchportal.helpers.ReceivedMessageHelper;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
@@ -44,6 +45,10 @@ public Object getTypedDataValue(Class> pluginClass, Method actionMethod, Param
return this.getTypedDataValue(connectorMethodParameter.getParameterizedType().getTypeName(), DataHelper.getDataId(pluginClass, actionMethod, connectorMethodParameter));
}
+ public Object getTypedDataValue(Class> pluginClass, Field actionField) {
+ return this.getTypedDataValue(actionField.getType().getTypeName(), DataHelper.getDataId(pluginClass, actionField));
+ }
+
public Object getTypedDataValue(String connectorDataType, String connectorDataId) {
Object value = null;
diff --git a/Library/src/main/java/com/christophecvb/touchportal/model/TPListChangeMessage.java b/Library/src/main/java/com/christophecvb/touchportal/model/TPListChangedMessage.java
similarity index 94%
rename from Library/src/main/java/com/christophecvb/touchportal/model/TPListChangeMessage.java
rename to Library/src/main/java/com/christophecvb/touchportal/model/TPListChangedMessage.java
index 83b2c773..f0243901 100644
--- a/Library/src/main/java/com/christophecvb/touchportal/model/TPListChangeMessage.java
+++ b/Library/src/main/java/com/christophecvb/touchportal/model/TPListChangedMessage.java
@@ -20,7 +20,7 @@
package com.christophecvb.touchportal.model;
-public class TPListChangeMessage extends TPMessage {
+public class TPListChangedMessage extends TPMessage {
public String pluginId;
public String actionId;
public String listId;
diff --git a/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java b/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java
index 2f85477c..dc16d5ae 100644
--- a/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java
+++ b/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java
@@ -46,10 +46,14 @@
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import static org.junit.Assert.*;
public class LibraryTests {
+ private static final Logger LOGGER = Logger.getLogger(LibraryTests.class.getSimpleName());
+ private static final long REASONABLE_TIME = 100;
private ServerSocket serverSocket;
private Socket serverSocketClient;
private TouchPortalPluginTest touchPortalPluginTest;
@@ -67,7 +71,7 @@ public void onInfo(TPInfoMessage tpInfoMessage) {
}
@Override
- public void onListChanged(TPListChangeMessage tpListChangeMessage) {
+ public void onListChanged(TPListChangedMessage tpListChangedMessage) {
}
@Override
@@ -83,6 +87,11 @@ public void onNotificationOptionClicked(TPNotificationOptionClickedMessage tpNot
}
};
+ static {
+ LOGGER.setUseParentHandlers(false);
+ LOGGER.addHandler(new TouchPortalPlugin.CustomConsoleHandler());
+ }
+
@Before
public void initialize() throws IOException {
// Mock Server
@@ -113,7 +122,7 @@ private void serverSocketAccept() {
}
}).start();
try {
- Thread.sleep(100);
+ Thread.sleep(REASONABLE_TIME);
}
catch (InterruptedException ignored) {}
}
@@ -137,7 +146,7 @@ public void close() {
ioException.printStackTrace();
}
try {
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
}
catch (InterruptedException e) {
e.printStackTrace();
@@ -149,8 +158,9 @@ public void close() {
@Test
public void testConnection() {
+ LOGGER.log(Level.FINE, "Now");
try {
- Thread.sleep(500);
+ Thread.sleep(REASONABLE_TIME);
}
catch (InterruptedException e) {
e.printStackTrace();
@@ -161,6 +171,7 @@ public void testConnection() {
@Test
public void testConnectionFail() {
+ LOGGER.log(Level.FINE, "Now");
this.close();
boolean connectedPairedAndListening = this.touchPortalPluginTest.connectThenPairAndListen(null);
@@ -176,6 +187,7 @@ public void testConnectionFail() {
@Test
public void testMultipleConnect() {
+ LOGGER.log(Level.FINE, "Now");
// A connectThenPairAndListen is already done in the @Before
assertTrue(this.touchPortalPluginTest.connectThenPairAndListen(null));
assertTrue(this.touchPortalPluginTest.connectThenPairAndListen(this.touchPortalPluginListener));
@@ -183,6 +195,7 @@ public void testMultipleConnect() {
@Test
public void testCloseAndReConnect() {
+ LOGGER.log(Level.FINE, "Now");
this.touchPortalPluginTest.close(null);
boolean connectedPairedAndListening = this.touchPortalPluginTest.connectThenPairAndListen(null);
@@ -192,6 +205,7 @@ public void testCloseAndReConnect() {
@Test
public void testConnectionNoListener() {
+ LOGGER.log(Level.FINE, "Now");
this.touchPortalPluginTest.close(null);
boolean connectedPairedAndListening = this.touchPortalPluginTest.connectThenPairAndListen(null);
@@ -201,19 +215,22 @@ public void testConnectionNoListener() {
@Test
public void testClose() {
+ LOGGER.log(Level.FINE, "Now");
this.touchPortalPluginTest.close(null);
}
@Test
public void testServerSocketCloses() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
this.serverSocketClient.close();
this.serverSocket.close();
- Thread.sleep(100);
+ Thread.sleep(REASONABLE_TIME);
assertFalse(this.touchPortalPluginTest.isConnected());
}
@Test
public void testSend() {
+ LOGGER.log(Level.FINE, "Now");
// Send State Update by ID from Constants
assertTrue(this.touchPortalPluginTest.sendStateUpdate(TouchPortalPluginTestConstants.BaseCategory.States.CustomState.ID, "New Value 01"));
@@ -226,6 +243,7 @@ public void testSend() {
@Test
public void testSendStates() {
+ LOGGER.log(Level.FINE, "Now");
assertFalse(this.touchPortalPluginTest.sendStateUpdate(null, null));
assertFalse(this.touchPortalPluginTest.sendStateUpdate("", null));
assertFalse(this.touchPortalPluginTest.sendStateUpdate("", ""));
@@ -239,6 +257,7 @@ public void testSendStates() {
@Test
public void testSendChoices() {
+ LOGGER.log(Level.FINE, "Now");
assertFalse(this.touchPortalPluginTest.sendChoiceUpdate(null, null));
assertFalse(this.touchPortalPluginTest.sendChoiceUpdate("", new String[0]));
assertFalse(this.touchPortalPluginTest.sendChoiceUpdate(null, new String[0]));
@@ -253,6 +272,7 @@ public void testSendChoices() {
@Test
public void testSendSpecificChoices() {
+ LOGGER.log(Level.FINE, "Now");
assertFalse(this.touchPortalPluginTest.sendSpecificChoiceUpdate(null, null, null));
assertFalse(this.touchPortalPluginTest.sendSpecificChoiceUpdate("", null, null));
assertFalse(this.touchPortalPluginTest.sendSpecificChoiceUpdate("", "", null));
@@ -272,6 +292,7 @@ public void testSendSpecificChoices() {
@Test
public void testSendActionDataUpdate() {
+ LOGGER.log(Level.FINE, "Now");
HashMap props = new HashMap<>();
assertFalse(this.touchPortalPluginTest.sendActionDataUpdate(null, null, null));
assertFalse(this.touchPortalPluginTest.sendActionDataUpdate("", null, null));
@@ -290,6 +311,7 @@ public void testSendActionDataUpdate() {
@Test
public void testLastStateValue() {
+ LOGGER.log(Level.FINE, "Now");
String stateValue = System.currentTimeMillis() + "";
assertTrue(this.touchPortalPluginTest.sendStateUpdate(TouchPortalPluginTestConstants.BaseCategory.States.CustomState.ID, stateValue));
assertFalse(this.touchPortalPluginTest.sendStateUpdate(TouchPortalPluginTestConstants.BaseCategory.States.CustomState.ID, stateValue));
@@ -298,6 +320,7 @@ public void testLastStateValue() {
@Test
public void testShowNotification() {
+ LOGGER.log(Level.FINE, "Now");
assertFalse (this.touchPortalPluginTest.sendShowNotification(null, null, null, null));
assertFalse(this.touchPortalPluginTest.sendShowNotification("", null, null, null));
assertFalse(this.touchPortalPluginTest.sendShowNotification("", "", null, null));
@@ -313,6 +336,7 @@ public void testShowNotification() {
@Test
public void testDynamicStates() {
+ LOGGER.log(Level.FINE, "Now");
assertFalse(this.touchPortalPluginTest.sendCreateState(null, null, null, null));
assertFalse(this.touchPortalPluginTest.sendCreateState("", null, null, null));
@@ -362,6 +386,7 @@ public void testDynamicStates() {
@Test
public void testSendFail() {
+ LOGGER.log(Level.FINE, "Now");
this.touchPortalPluginTest.close(null);
assertFalse(this.touchPortalPluginTest.sendStateUpdate(TouchPortalPluginTestConstants.BaseCategory.States.CustomState.ID, "New Value"));
assertFalse(this.touchPortalPluginTest.sendChoiceUpdate("listId", null, true));
@@ -370,15 +395,30 @@ public void testSendFail() {
assertFalse(this.touchPortalPluginTest.sendRemoveState("BaseCategory", "StateId"));
}
+ @Test
+ public void testTriggerEvent() {
+ LOGGER.log(Level.FINE, "Now");
+ assertFalse(this.touchPortalPluginTest.sendTriggerEvent(null, null));
+ assertFalse(this.touchPortalPluginTest.sendTriggerEvent("", null));
+
+ assertTrue(this.touchPortalPluginTest.sendTriggerEvent(TouchPortalPluginTestConstants.BaseCategory.Events.CustomState.ID, null));
+
+ HashMap states = new HashMap<>();
+ states.put(TouchPortalPluginTestConstants.BaseCategory.States.CustomState.ID, "StateValue");
+ assertTrue(this.touchPortalPluginTest.sendTriggerEvent(TouchPortalPluginTestConstants.BaseCategory.Events.CustomState.ID, states));
+ }
+
@Test
public void testReceiveActionNoId() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_ACTION);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -386,13 +426,15 @@ public void testReceiveActionNoId() throws IOException, InterruptedException {
@Test
public void testReceiveConnectorNoId() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_CONNECTOR_CHANGE);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -400,14 +442,16 @@ public void testReceiveConnectorNoId() throws IOException, InterruptedException
@Test
public void testReceiveActionEmptyId() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_ACTION);
jsonMessage.addProperty(ReceivedMessageHelper.ACTION_ID, "");
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -415,14 +459,16 @@ public void testReceiveActionEmptyId() throws IOException, InterruptedException
@Test
public void testReceiveConnectorEmptyId() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_CONNECTOR_CHANGE);
jsonMessage.addProperty(ReceivedMessageHelper.CONNECTOR_ID, "");
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -430,6 +476,7 @@ public void testReceiveConnectorEmptyId() throws IOException, InterruptedExcepti
@Test
public void testReceiveShortConnectorIdNotification() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
Map dataReceived = new HashMap<>();
dataReceived.put(TouchPortalPluginTestConstants.BaseCategory.Connectors.ConnectorForSliderWithData.Text.ID + "0", "Text0!");
dataReceived.put(TouchPortalPluginTestConstants.BaseCategory.Connectors.ConnectorForSliderWithData.Text.ID, "Text!");
@@ -440,10 +487,11 @@ public void testReceiveShortConnectorIdNotification() throws IOException, Interr
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_SHORT_CONNECTOR_ID_NOTIFICATION);
jsonMessage.addProperty(ReceivedMessageHelper.CONNECTOR_ID, ConnectorHelper.getConstructedId(TouchPortalPluginTestConstants.ID, TouchPortalPluginTestConstants.BaseCategory.Connectors.ConnectorForSliderWithData.ID, 0, dataReceived));
jsonMessage.addProperty(ReceivedMessageHelper.SHORT_ID, "SHORT_ID");
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -457,6 +505,7 @@ public void testReceiveShortConnectorIdNotification() throws IOException, Interr
@Test
public void testReceiveDummyWithDataTextAndNumberAction() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_ACTION);
@@ -471,10 +520,11 @@ public void testReceiveDummyWithDataTextAndNumberAction() throws IOException, In
numberDataItem.addProperty(ReceivedMessageHelper.ACTION_DATA_VALUE, 42);
data.add(numberDataItem);
jsonMessage.add(ActionHelper.DATA, data);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -482,6 +532,7 @@ public void testReceiveDummyWithDataTextAndNumberAction() throws IOException, In
@Test
public void testReceiveDummyWithDataFileAction() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_ACTION);
@@ -497,55 +548,62 @@ public void testReceiveDummyWithDataFileAction() throws IOException, Interrupted
directoryDataItem.addProperty(ReceivedMessageHelper.ACTION_DATA_VALUE, "/");
data.add(directoryDataItem);
jsonMessage.add(ActionHelper.DATA, data);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
}
@Test
- public void testReceiveDummyDummyWithJsonObject() throws IOException, InterruptedException {
+ public void testReceiveDummyWithJsonObject() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_ACTION);
jsonMessage.addProperty(ReceivedMessageHelper.ACTION_ID, TouchPortalPluginTestConstants.BaseCategory.Actions.DummyWithJsonObject.ID);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
}
@Test
- public void testReceiveDummyDummyWithTPActionMessage() throws IOException, InterruptedException {
+ public void testReceiveDummyWithTPActionMessage() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_ACTION);
jsonMessage.addProperty(ReceivedMessageHelper.ACTION_ID, TouchPortalPluginTestConstants.BaseCategory.Actions.DummyWithTPActionMessage.ID);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
}
@Test
- public void testReceiveDummyDummyWithParam() throws IOException, InterruptedException {
+ public void testReceiveDummyWithParamNotData() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_ACTION);
jsonMessage.addProperty(ReceivedMessageHelper.ACTION_ID, TouchPortalPluginTestConstants.BaseCategory.Actions.DummyWithParam.ID);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -553,15 +611,17 @@ public void testReceiveDummyDummyWithParam() throws IOException, InterruptedExce
@Test
public void testReceiveActionHoldableDownAndUp() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
JsonObject jsonMessageHoldDown = new JsonObject();
jsonMessageHoldDown.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessageHoldDown.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_HOLD_DOWN);
jsonMessageHoldDown.addProperty(ReceivedMessageHelper.ACTION_ID, TouchPortalPluginTestConstants.BaseCategory.Actions.ActionHoldable.ID);
+
out.println(jsonMessageHoldDown);
- Thread.sleep(150);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isActionBeingHeld(TouchPortalPluginTestConstants.BaseCategory.Actions.ActionHoldable.ID));
@@ -569,9 +629,10 @@ public void testReceiveActionHoldableDownAndUp() throws IOException, Interrupted
jsonMessageHoldUp.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessageHoldUp.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_HOLD_UP);
jsonMessageHoldUp.addProperty(ReceivedMessageHelper.ACTION_ID, TouchPortalPluginTestConstants.BaseCategory.Actions.ActionHoldable.ID);
+
out.println(jsonMessageHoldUp);
- Thread.sleep(150);
+ Thread.sleep(REASONABLE_TIME);
assertNull(this.touchPortalPluginTest.isActionBeingHeld(TouchPortalPluginTestConstants.BaseCategory.Actions.ActionHoldable.ID));
@@ -581,15 +642,17 @@ public void testReceiveActionHoldableDownAndUp() throws IOException, Interrupted
@Test
public void testReceiveActionHoldablePress() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
JsonObject jsonMessageHoldDown = new JsonObject();
jsonMessageHoldDown.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessageHoldDown.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_ACTION);
jsonMessageHoldDown.addProperty(ReceivedMessageHelper.ACTION_ID, TouchPortalPluginTestConstants.BaseCategory.Actions.ActionHoldable.ID);
+
out.println(jsonMessageHoldDown);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertNull(this.touchPortalPluginTest.isActionBeingHeld(TouchPortalPluginTestConstants.BaseCategory.Actions.ActionHoldable.ID));
@@ -599,6 +662,7 @@ public void testReceiveActionHoldablePress() throws IOException, InterruptedExce
@Test
public void testReceiveConnectorForSlider() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
JsonObject jsonMessageConnectorForSlider = new JsonObject();
@@ -609,7 +673,7 @@ public void testReceiveConnectorForSlider() throws IOException, InterruptedExcep
out.println(jsonMessageConnectorForSlider);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -617,6 +681,7 @@ public void testReceiveConnectorForSlider() throws IOException, InterruptedExcep
@Test
public void testReceiveConnectorForSliderWithData() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
JsonObject jsonMessageConnectorForSliderWithData = new JsonObject();
@@ -628,11 +693,12 @@ public void testReceiveConnectorForSliderWithData() throws IOException, Interrup
JsonObject dataText = new JsonObject();
dataText.addProperty(ReceivedMessageHelper.ACTION_DATA_ID, TouchPortalPluginTestConstants.BaseCategory.Connectors.ConnectorForSliderWithData.Text.ID);
dataText.addProperty(ReceivedMessageHelper.ACTION_DATA_VALUE, "Sliding!");
+ data.add(dataText);
jsonMessageConnectorForSliderWithData.add(ConnectorHelper.DATA, data);
out.println(jsonMessageConnectorForSliderWithData);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -640,6 +706,7 @@ public void testReceiveConnectorForSliderWithData() throws IOException, Interrup
@Test
public void testUpdateConnectorValue() {
+ LOGGER.log(Level.FINE, "Now");
HashMap data = new HashMap<>();
data.put("dataId", "value");
assertFalse(this.touchPortalPluginTest.sendConnectorUpdate(null, null, null, null));
@@ -658,6 +725,7 @@ public void testUpdateConnectorValue() {
@Test
public void testReceiveConnectorForSliderWithNonData() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
JsonObject jsonMessageConnectorForSliderWithNonData = new JsonObject();
@@ -668,7 +736,7 @@ public void testReceiveConnectorForSliderWithNonData() throws IOException, Inter
out.println(jsonMessageConnectorForSliderWithNonData);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -676,13 +744,15 @@ public void testReceiveConnectorForSliderWithNonData() throws IOException, Inter
@Test
public void testReceiveListChange() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
- jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_LIST_CHANGE);
+ jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_LIST_CHANGED);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -690,14 +760,16 @@ public void testReceiveListChange() throws IOException, InterruptedException {
@Test
public void testReceiveListChangeNoListener() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
this.touchPortalPluginTest.connectThenPairAndListen(null);
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
- jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_LIST_CHANGE);
+ jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_LIST_CHANGED);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -705,14 +777,16 @@ public void testReceiveListChangeNoListener() throws IOException, InterruptedExc
@Test
public void testReceiveActionNoListener() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
this.touchPortalPluginTest.connectThenPairAndListen(null);
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_ACTION);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -720,13 +794,15 @@ public void testReceiveActionNoListener() throws IOException, InterruptedExcepti
@Test
public void testReceiveBadPlugin() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, "falsePluginId");
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_ACTION);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -734,12 +810,14 @@ public void testReceiveBadPlugin() throws IOException, InterruptedException {
@Test
public void testReceiveNoMessageType() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -747,13 +825,15 @@ public void testReceiveNoMessageType() throws IOException, InterruptedException
@Test
public void testReceiveUnknownMessageType() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, "unknown");
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -761,6 +841,7 @@ public void testReceiveUnknownMessageType() throws IOException, InterruptedExcep
@Test
public void testReceiveInfo() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
TPInfoMessage sentTPInfoMessage = new TPInfoMessage();
sentTPInfoMessage.status = "paired";
sentTPInfoMessage.sdkVersion = 3L;
@@ -782,7 +863,7 @@ public void testReceiveInfo() throws IOException, InterruptedException {
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -799,13 +880,15 @@ public void testReceiveInfo() throws IOException, InterruptedException {
@Test
public void testReceiveInfoMissingPropsAndNoListener() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
this.touchPortalPluginTest.connectThenPairAndListen(null);
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_INFO);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -821,14 +904,16 @@ public void testReceiveInfoMissingPropsAndNoListener() throws IOException, Inter
@Test
public void testReceiveClose() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_CLOSE_PLUGIN);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
// Wait for the listenerThread to catch up
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertFalse(this.touchPortalPluginTest.isConnected());
assertFalse(this.touchPortalPluginTest.isListening());
@@ -836,10 +921,11 @@ public void testReceiveClose() throws IOException, InterruptedException {
@Test
public void testReceiveJSONFail() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println("Not a JSON Object");
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -847,10 +933,11 @@ public void testReceiveJSONFail() throws IOException, InterruptedException {
@Test
public void testReceiveEmpty() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println();
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -858,6 +945,7 @@ public void testReceiveEmpty() throws IOException, InterruptedException {
@Test
public void testReceivePart() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.print("This");
out.print("is");
@@ -865,7 +953,7 @@ public void testReceivePart() throws IOException, InterruptedException {
out.print("data");
out.println();
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -873,25 +961,30 @@ public void testReceivePart() throws IOException, InterruptedException {
@Test
public void testReceiveBroadcast() throws IOException {
+ LOGGER.log(Level.FINE, "Now");
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_BROADCAST);
jsonMessage.addProperty(ReceivedMessageHelper.EVENT, ReceivedMessageHelper.EVENT_PAGE_CHANGE);
jsonMessage.addProperty(ReceivedMessageHelper.PAGE_NAME, "Page ONE");
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
}
@Test
public void testReceiveBroadcastMissingPropsAndNoListener() throws IOException {
+ LOGGER.log(Level.FINE, "Now");
this.touchPortalPluginTest.connectThenPairAndListen(null);
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_BROADCAST);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
}
@Test
public void testReceiveSettings() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
JsonArray jsonSettings = new JsonArray();
JsonObject jsonSettingIP = new JsonObject();
@@ -911,7 +1004,7 @@ public void testReceiveSettings() throws IOException, InterruptedException {
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -922,14 +1015,16 @@ public void testReceiveSettings() throws IOException, InterruptedException {
@Test
public void testReceiveSettingsNoListener() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
this.touchPortalPluginTest.connectThenPairAndListen(null);
JsonObject jsonMessage = new JsonObject();
jsonMessage.addProperty(ReceivedMessageHelper.PLUGIN_ID, TouchPortalPluginTestConstants.ID);
jsonMessage.addProperty(ReceivedMessageHelper.TYPE, ReceivedMessageHelper.TYPE_SETTINGS);
+
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertTrue(this.touchPortalPluginTest.isConnected());
assertTrue(this.touchPortalPluginTest.isListening());
@@ -937,6 +1032,7 @@ public void testReceiveSettingsNoListener() throws IOException, InterruptedExcep
@Test
public void testSendSettingUpdate() throws IOException, InterruptedException {
+ LOGGER.log(Level.FINE, "Now");
assertFalse(this.touchPortalPluginTest.sendSettingUpdate("DOES NOT EXISTS", "VALUE", false));
TPInfoMessage sentTPInfoMessage = new TPInfoMessage();
@@ -958,7 +1054,7 @@ public void testSendSettingUpdate() throws IOException, InterruptedException {
PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true);
out.println(jsonMessage);
- Thread.sleep(10);
+ Thread.sleep(REASONABLE_TIME);
assertFalse(this.touchPortalPluginTest.sendSettingUpdate(null, null, false));
assertFalse(this.touchPortalPluginTest.sendSettingUpdate("", null, false));
@@ -981,6 +1077,7 @@ public void testSendSettingUpdate() throws IOException, InterruptedException {
@Test
public void testAnnotations() {
+ LOGGER.log(Level.FINE, "Now");
assertEquals(TouchPortalPluginTestConstants.ID, "com.christophecvb.touchportal.test.TouchPortalPluginTest");
assertEquals(TouchPortalPluginTestConstants.BaseCategory.ID, "com.christophecvb.touchportal.test.TouchPortalPluginTest.BaseCategory");
assertEquals(TouchPortalPluginTestConstants.BaseCategory.Actions.DummyWithDataTextAndNumber.ID, "com.christophecvb.touchportal.test.TouchPortalPluginTest.BaseCategory.action.dummyWithDataTextAndNumber");
@@ -991,6 +1088,7 @@ public void testAnnotations() {
@Test
public void testEntryTPAndConstants() throws IOException {
+ LOGGER.log(Level.FINE, "Now");
File testGeneratedResourcesDirectory = new File("../../../../build/generated/sources/annotationProcessor/java/test/resources");
BufferedReader reader = Files.newBufferedReader(Paths.get(new File(testGeneratedResourcesDirectory.getAbsolutePath() + "/entry.tp").getAbsolutePath()));
@@ -1070,6 +1168,7 @@ public void testEntryTPAndConstants() throws IOException {
@Test
public void testProperties() {
+ LOGGER.log(Level.FINE, "Now");
// Test reloadProperties without a Properties File
assertFalse(this.touchPortalPluginTest.reloadProperties());
@@ -1098,11 +1197,13 @@ public void testProperties() {
@Test
public void testPropertiesFail() {
+ LOGGER.log(Level.FINE, "Now");
assertFalse(this.touchPortalPluginTest.loadProperties("doesNot.exists"));
}
@Test
public void testPropertiesAccess() {
+ LOGGER.log(Level.FINE, "Now");
// No Loaded Properties File
assertNull(this.touchPortalPluginTest.removeProperty("non existent"));
assertNull(this.touchPortalPluginTest.getProperty("non existent"));
@@ -1111,6 +1212,7 @@ public void testPropertiesAccess() {
@Test
public void testIsUpdateAvailable() {
+ LOGGER.log(Level.FINE, "Now");
assertFalse(this.touchPortalPluginTest.isUpdateAvailable("", 0));
assertFalse(this.touchPortalPluginTest.isUpdateAvailable("https://raw.githubusercontent.com/ChristopheCVB/TouchPortalPluginSDK/master/Library/src/test/resources/TouchPortalPluginTest/plugin.config", 1)); // Uses plugin.version
assertTrue(this.touchPortalPluginTest.isUpdateAvailable("https://raw.githubusercontent.com/ChristopheCVB/TouchPortalPluginSDK/master/Library/src/test/resources/TouchPortalPluginTest/plugin.config", 0)); // Uses plugin.version
@@ -1118,6 +1220,7 @@ public void testIsUpdateAvailable() {
@Test
public void testOAuth2() throws IOException {
+ LOGGER.log(Level.FINE, "Now");
String host = "localhost";
String callbackPath = "/oauth";
int port = -1;
diff --git a/Library/src/test/resources/TouchPortalPluginTest/plugin.config b/Library/src/test/resources/TouchPortalPluginTest/plugin.config
index 09a26035..ec09544b 100644
--- a/Library/src/test/resources/TouchPortalPluginTest/plugin.config
+++ b/Library/src/test/resources/TouchPortalPluginTest/plugin.config
@@ -1,4 +1,4 @@
#TouchPortalPluginTest
-#Mon May 23 10:54:55 CEST 2022
+#Sun Dec 04 22:41:39 CET 2022
samplekey=Sample Value
plugin.version=1
diff --git a/Packager/build.gradle b/Packager/build.gradle
index 42d58ab0..a24250cb 100644
--- a/Packager/build.gradle
+++ b/Packager/build.gradle
@@ -1,13 +1,12 @@
plugins {
- id 'groovy-gradle-plugin'
- id 'com.gradle.plugin-publish' version '0.15.0'
- id 'maven-publish'
+ id 'com.gradle.plugin-publish' version '1.0.0'
id 'signing'
+ id 'groovy-gradle-plugin'
}
group 'com.christophecvb.touchportal'
def localArchiveBaseName = 'plugin-packager'
-version versionName
+version isRelease ? versionName : versionName.replace('-SNAPSHOT', '-' + System.currentTimeSeconds())
pluginBundle {
website = 'https://github.com/ChristopheCVB/TouchPortalPluginSDK'
@@ -68,7 +67,7 @@ publishing {
repositories {
maven {
- url = version.endsWith('SNAPSHOT') ? 'https://s01.oss.sonatype.org/content/repositories/snapshots/' : 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/'
+ url = version.contains('-') ? 'https://s01.oss.sonatype.org/content/repositories/snapshots/' : 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/'
credentials {
username = envOrPropOrEmpty('OSSRH_USERNAME')
password = envOrPropOrEmpty('OSSRH_PASSWORD')
diff --git a/Packager/src/main/groovy/com/christophecvb/touchportal/packager/TouchPortalPluginPackager.groovy b/Packager/src/main/groovy/com/christophecvb/touchportal/packager/TouchPortalPluginPackager.groovy
index 453e64e7..c111e8c1 100644
--- a/Packager/src/main/groovy/com/christophecvb/touchportal/packager/TouchPortalPluginPackager.groovy
+++ b/Packager/src/main/groovy/com/christophecvb/touchportal/packager/TouchPortalPluginPackager.groovy
@@ -17,8 +17,9 @@ class TouchPortalPluginPackager implements Plugin {
project.tasks.withType(JavaCompile) { task ->
task.doFirst {
- println('Adding -parameters to Compiler Args')
+ println('Adding -parameters to Compiler Args and setting encoding to UTF-8')
options.compilerArgs.add('-parameters')
+ options.encoding = "UTF-8"
}
}
@@ -53,6 +54,8 @@ class TouchPortalPluginPackager implements Plugin {
}
def copyResources = project.tasks.register('copyResources', Copy) {
+ dependsOn project.processResources
+
group = 'Touch Portal Plugin'
from(project.file("${project.buildDir}/resources/main/"))
into("${project.buildDir}/plugin/${extension.mainClassSimpleName.get()}/")
diff --git a/README.md b/README.md
index 7546e47c..6445d727 100644
--- a/README.md
+++ b/README.md
@@ -18,26 +18,24 @@ Once you have cloned this project, you can run the `gradlew javaDoc` and browse
## Releases
-Latest is 8.0.0
-
-Go to [releases](https://github.com/ChristopheCVB/TouchPortalPluginSDK/releases)
+Go to [releases](https://github.com/ChristopheCVB/TouchPortalPluginSDK/releases) to check which is the latest
### Maven Central
-Latest version is `8.0.0`
-
-Prior versions were not published to Maven Central
+Versions before `7.0.0` were not published to Maven Central
#### Gradle
```groovy
plugins {
- id 'com.christophecvb.touchportal.plugin-packager' version '8.0.0'
+ id 'com.christophecvb.touchportal.plugin-packager' version 'X.Y.Z'
}
+tpPlugin.mainClassSimpleName = 'MyTouchPortalPlugin'
+
dependencies {
- implementation 'com.christophecvb.touchportal:plugin-sdk:8.0.0'
- annotationProcessor 'com.christophecvb.touchportal:plugin-sdk-annotations-processor:8.0.0'
+ implementation 'com.christophecvb.touchportal:plugin-sdk:X.Y.Z'
+ annotationProcessor 'com.christophecvb.touchportal:plugin-sdk-annotations-processor:X.Y.Z'
}
```
@@ -93,7 +91,7 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPorta
/**
* Called when a List Change Message is received
*/
- public void onListChanged(TPListChangeMessage tpListChangeMessage) { }
+ public void onListChanged(TPListChangeMessage tpListChangedMessage) { }
/**
* Called when a Broadcast Message is received
@@ -105,16 +103,15 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPorta
*/
public void onSettings(TPSettingsMessage tpSettingsMessage) { }
- /**
- * Called when a Notification Option Clicked Message is received
- */
- public void onNotificationOptionClicked(TPNotificationOptionClickedMessage tpNotificationOptionClickedMessage) {}
+ /**
+ * Called when a Notification Option Clicked Message is received
+ */
+ public void onNotificationOptionClicked(TPNotificationOptionClickedMessage tpNotificationOptionClickedMessage) { }
}
```
## Development and Interaction
-
-- The SDK will automatically callback your action methods if they only contain `@Data` annotated parameters
+The SDK will automatically invoke your action methods if they contain only `@Data` annotated parameters
```java
public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPortalPlugin.TouchPortalPluginListener {
@@ -127,14 +124,14 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPorta
*/
@Action(description = "Long Description of Dummy Action with Data Text", format = "Set text to {$text$}", categoryId = "BaseCategory")
private void actionWithText(@Data String text) {
- TouchPortalSamplePlugin.LOGGER.log(Level.INFO, "Action actionWithText received: " + text);
+ MyTouchPortalPlugin.LOGGER.log(Level.INFO, "Action actionWithText received: " + text);
}
// ...
}
```
-- Otherwise, call your actions manually in the `onReceived(JsonObject jsonMessage)` method
+Otherwise, call your actions manually in the `onReceived(JsonObject jsonMessage)` method
```java
public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPortalPlugin.TouchPortalPluginListener {
@@ -159,7 +156,7 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPorta
}
```
-- Don't forget to initialize all your services once you receive the onInfo event. The TPInfoMessage will also contain the initial values of your settings.
+Don't forget to initialize all your services once you receive the onInfo event. The TPInfoMessage will also contain the initial values of your settings.
```java
public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPortalPlugin.TouchPortalPluginListener {
@@ -167,7 +164,7 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPorta
public void onInfo(TPInfoMessage tpInfoMessage) {
// TPInfoMessage will contain the initial settings stored by TP
- // -> Note that your annotated Settings fields will be up to date at this point
+ // -> Note that your annotated Settings fields will be up-to-date at this point
// continue plugin initialization
}
@@ -176,7 +173,7 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPorta
}
```
-- Finally, send messages back to TouchPortal when you want to update your states
+Finally, send messages back to TouchPortal when you want to update your states
```java
public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPortalPlugin.TouchPortalPluginListener {
@@ -196,16 +193,24 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin implements TouchPorta
}
```
-## Use Annotations to describe your plugin and package it
+## Use Annotations to describe your plugin
+
+The provided Annotations help you in the automatic generation of the `entry.tp` file (necessary for packaging and deployment of your plugin)
+
+Current supported annotations include: Plugin, Category, Action, Data, State, Event and Setting
-- The provided Annotations help you in the automatic generation of the `entry.tp` file (necessary for packaging and deployment of your plugin)
-- Current supported annotations include: Plugin, Category, Action, Data, State, Event and Setting
-- More examples can be found in the sample modules
+More examples can be found in the sample modules
```java
// ...
-@Plugin(version = BuildConfig.VERSION_CODE, colorDark = "#203060", colorLight = "#4070F0", name = "My Touch Portal Plugin")
+@Plugin(
+ name = "My Touch Portal Plugin",
+ version = BuildConfig.VERSION_CODE,
+ colorDark = "#203060",
+ colorLight = "#4070F0",
+ parentCategory = ParentCategory.MISC
+)
public class MyTouchPortalPlugin extends TouchPortalPlugin {
//...
@@ -214,7 +219,11 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin {
*
* @param text String
*/
- @Action(description = "Long Description of Dummy Action with Data", format = "Set text to {$text$}", categoryId = "BaseCategory")
+ @Action(
+ description = "Long Description of Dummy Action with Data",
+ format = "Set text to {$text$}",
+ categoryId = "BaseCategory"
+ )
private void dummyWithData(@Data String text) {
LOGGER.log(Level.Info, "Action dummyWithData received: " + text);
}
@@ -240,13 +249,16 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin {
## Prepackaging
-- Add the Plugin icon and extra resources into the `src/main/resources/` directory of your module
+Add the Plugin icon and extra resources into the `src/main/resources/` directory of your module
-## Build
+## Clean, Build and Package
-- Use the common `gradlew clean` task to clean your build directories.
-- Use the common `gradlew build` task to build your project with the Annotations.
-- Use the `gradlew packagePlugin` task to pack your plugin into a `.tpp` file. Output files will be in your module `build/plugin` directory.
+- Clean Project
+ - `gradlew clean`
+- Build Project
+ - `gradlew build`
+- Package Project
+ - `gradlew packagePlugin` task to pack your plugin into a `.tpp` file. Output files will be in your module's `build/plugin` directory.
## Debugging tips
@@ -261,8 +273,4 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin {
- Main Class: `your.package.YourTouchPortalPlugin`
- Arguments: `start`
- Working Directory: `YourModule/build/plugin/YourTouchPortalPlugin`
-[![Touch Portal Plugin SDK Gradle Application Configuration](https://raw.githubusercontent.com/ChristopheCVB/TouchPortalPluginSDK/master/resources/TP%20Plugin%20SDK%20Gradle%20Application%20Configuration.png)](#debugging-tips)
-
-## ROADMAP
-
-The roadmap can be found [here](https://github.com/ChristopheCVB/TouchPortalPluginSDK/projects/1)
+![Touch Portal Plugin SDK Gradle Application Configuration](https://raw.githubusercontent.com/ChristopheCVB/TouchPortalPluginSDK/master/resources/TP%20Plugin%20SDK%20Gradle%20Application%20Configuration.png)
diff --git a/SampleJava/build.gradle b/SampleJava/build.gradle
index bcae488a..306d47b9 100644
--- a/SampleJava/build.gradle
+++ b/SampleJava/build.gradle
@@ -1,7 +1,7 @@
plugins {
id 'java'
- id 'com.github.gmazzo.buildconfig' version '3.0.0'
- id 'com.christophecvb.touchportal.plugin-packager' version "$versionName+"
+ id 'com.github.gmazzo.buildconfig' version '3.1.0'
+ id 'com.christophecvb.touchportal.plugin-packager' version "+"
}
def mainClassSimpleName = 'TouchPortalSampleJavaPlugin'
diff --git a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java
index 38ade3ff..0f968730 100644
--- a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java
+++ b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java
@@ -20,19 +20,27 @@
package com.christophecvb.touchportal.samplejava;
+import com.christophecvb.touchportal.TouchPortalPlugin;
import com.christophecvb.touchportal.annotations.*;
import com.christophecvb.touchportal.helpers.PluginHelper;
import com.christophecvb.touchportal.helpers.ReceivedMessageHelper;
-import com.christophecvb.touchportal.TouchPortalPlugin;
import com.christophecvb.touchportal.model.*;
+import com.christophecvb.touchportal.samplejava.invokable.action.ExampleClassAction;
+import com.christophecvb.touchportal.samplejava.invokable.connector.ExampleClassConnector;
import com.google.gson.JsonObject;
import java.io.File;
+import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
-@SuppressWarnings("unused")
-@Plugin(version = BuildConfig.VERSION_CODE, colorDark = "#203060", colorLight = "#4070F0", name = "Touch Portal Plugin Example")
+@Plugin(
+ version = BuildConfig.VERSION_CODE,
+ colorDark = "#203060",
+ colorLight = "#4070F0",
+ name = BuildConfig.NAME,
+ parentCategory = ParentCategory.CONTENT
+)
public class TouchPortalSampleJavaPlugin extends TouchPortalPlugin implements TouchPortalPlugin.TouchPortalPluginListener {
/**
* Logger
@@ -71,14 +79,18 @@ private enum Categories {
/**
* Setting of type text definition example
*/
- @Setting(name = "IP", defaultValue = "localhost", maxLength = 15)
+ @Setting(name = "IP", defaultValue = "localhost", maxLength = 15, tooltip = @Setting.Tooltip(
+ title = "IP address",
+ body = "ip address to connect to",
+ docUrl = "https://example.com"
+ ))
private String ipSetting;
/**
* Setting of type number definition example
*/
@Setting(name = "Update Delay", defaultValue = "10", minValue = 10, maxValue = 30)
- private int updateDelaySetting;
+ private int updateDelaySetting = 10;
/**
* Setting of type String and is read only definition example
@@ -98,20 +110,29 @@ public static void main(String... args) {
if (PluginHelper.COMMAND_START.equals(args[0])) {
// Initialize the Plugin
TouchPortalSampleJavaPlugin touchPortalSampleJavaPlugin = new TouchPortalSampleJavaPlugin();
+
+ // Register Invokable
+ touchPortalSampleJavaPlugin.registerInvokable(TouchPortalSampleJavaPluginConstants.BaseCategory.Actions.ExampleClassAction.ID, ExampleClassAction.class);
+ touchPortalSampleJavaPlugin.registerInvokable(TouchPortalSampleJavaPluginConstants.BaseCategory.Connectors.ExampleClassConnector.ID, ExampleClassConnector.class);
+
// Load a properties File
touchPortalSampleJavaPlugin.loadProperties("plugin.config");
+
// Get a property
TouchPortalSampleJavaPlugin.LOGGER.log(Level.INFO, touchPortalSampleJavaPlugin.getProperty("samplekey"));
+
// Set a property
touchPortalSampleJavaPlugin.setProperty("samplekey", "Value set from Plugin");
+
// Store the properties
touchPortalSampleJavaPlugin.storeProperties();
+
// Initiate the connection with the Touch Portal Plugin System
boolean connectedPairedAndListening = touchPortalSampleJavaPlugin.connectThenPairAndListen(touchPortalSampleJavaPlugin);
if (connectedPairedAndListening) {
// Update a State with the ID from the Generated Constants Class
- boolean stateUpdated = touchPortalSampleJavaPlugin.sendStateUpdate(TouchPortalSampleJavaPluginConstants.BaseCategory.States.CustomStateWithEvent.ID, "2");
+ touchPortalSampleJavaPlugin.sendStateUpdate(TouchPortalSampleJavaPluginConstants.BaseCategory.States.CustomStateWithEvent.ID, "2");
// Create a new State
touchPortalSampleJavaPlugin.sendCreateState("BaseCategory", "createdState1", "Created State 01", System.currentTimeMillis() + "1");
@@ -136,8 +157,13 @@ public static void main(String... args) {
touchPortalSampleJavaPlugin.sendConnectorUpdate(TouchPortalSampleJavaPluginConstants.ID, TouchPortalSampleJavaPluginConstants.BaseCategory.Connectors.ConnectorSimple.ID, 90, null);
try {
Thread.sleep(1000);
- } catch (InterruptedException ignored) {}
+ } catch (InterruptedException ignored) {
+ }
touchPortalSampleJavaPlugin.sendConnectorUpdate(TouchPortalSampleJavaPluginConstants.ID, TouchPortalSampleJavaPluginConstants.BaseCategory.Connectors.ConnectorSimple.ID, 50, null);
+
+ HashMap states = new HashMap<>();
+ states.put(TouchPortalSampleJavaPluginConstants.BaseCategory.States.CustomStateWithEvent.ID, "Value");
+ touchPortalSampleJavaPlugin.sendTriggerEvent(TouchPortalSampleJavaPluginConstants.BaseCategory.Events.CustomStateWithEvent.ID, states);
}
}
}
@@ -147,6 +173,8 @@ public static void main(String... args) {
* Action example with no parameter
*/
@Action(description = "Long Description of Action Simple", format = "Do a simple action", categoryId = "BaseCategory")
+ @ActionTranslation(language = Language.FRENCH, description = "Description longue de Action Simple", format = "Exécute une action simple", prefix = "Mon préfixe", name = "Action Simple")
+ @ActionTranslation(language = Language.PORTUGUESE, description = "Descrição longa da Acção Simples", format = "Realiza uma acção simples", prefix = "Meu prefixo", name = "Acção Simples")
private void actionSimple() {
TouchPortalSampleJavaPlugin.LOGGER.log(Level.INFO, "Action actionSimple received");
}
@@ -298,7 +326,7 @@ public void onInfo(TPInfoMessage tpInfoMessage) {
}
@Override
- public void onListChanged(TPListChangeMessage tpListChangeMessage) {
+ public void onListChanged(TPListChangedMessage tpListChangedMessage) {
}
@Override
diff --git a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/invokable/action/ExampleClassAction.java b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/invokable/action/ExampleClassAction.java
new file mode 100644
index 00000000..d484e1ee
--- /dev/null
+++ b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/invokable/action/ExampleClassAction.java
@@ -0,0 +1,43 @@
+package com.christophecvb.touchportal.samplejava.invokable.action;
+
+import com.christophecvb.touchportal.TPAction;
+import com.christophecvb.touchportal.TouchPortalPlugin;
+import com.christophecvb.touchportal.annotations.Action;
+import com.christophecvb.touchportal.annotations.ActionTranslation;
+import com.christophecvb.touchportal.annotations.Data;
+import com.christophecvb.touchportal.annotations.Language;
+import com.christophecvb.touchportal.model.TPListChangedMessage;
+import com.christophecvb.touchportal.samplejava.TouchPortalSampleJavaPlugin;
+
+import java.util.logging.Logger;
+
+@Action(
+ name = "Example Class Action",
+ format = "Example Class Action with param {$param$}",
+ categoryId = "BaseCategory"
+)
+@ActionTranslation(
+ language = Language.FRENCH,
+ name = "Exemple d'Action via une Classe",
+ format = "Exemple d'Action via une Classe avec le paramètre {$param$}"
+)
+public class ExampleClassAction extends TPAction {
+ private final static Logger LOGGER = Logger.getLogger(TouchPortalPlugin.class.getName());
+
+ @Data
+ private String param;
+
+ public ExampleClassAction(TouchPortalSampleJavaPlugin touchPortalPlugin) {
+ super(touchPortalPlugin);
+ }
+
+ @Override
+ public void onInvoke() {
+ LOGGER.info("ExampleClassAction.onInvoke this.param=" + this.param);
+ }
+
+ @Override
+ public void onListChanged(TPListChangedMessage tpListChangedMessage) {
+ LOGGER.info("ExampleClassAction.onListChanged");
+ }
+}
diff --git a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/invokable/connector/ExampleClassConnector.java b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/invokable/connector/ExampleClassConnector.java
new file mode 100644
index 00000000..8ffc3875
--- /dev/null
+++ b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/invokable/connector/ExampleClassConnector.java
@@ -0,0 +1,39 @@
+package com.christophecvb.touchportal.samplejava.invokable.connector;
+
+import com.christophecvb.touchportal.TPConnector;
+import com.christophecvb.touchportal.TouchPortalPlugin;
+import com.christophecvb.touchportal.annotations.Connector;
+import com.christophecvb.touchportal.annotations.ConnectorValue;
+import com.christophecvb.touchportal.annotations.Data;
+import com.christophecvb.touchportal.model.TPListChangedMessage;
+import com.christophecvb.touchportal.samplejava.TouchPortalSampleJavaPlugin;
+
+import java.util.logging.Logger;
+
+@Connector(
+ name = "Example Class Connector",
+ categoryId = "BaseCategory",
+ format = "Connect Example Class Connector with param {$param$}"
+)
+public class ExampleClassConnector extends TPConnector {
+ private final static Logger LOGGER = Logger.getLogger(TouchPortalPlugin.class.getName());
+
+ @ConnectorValue
+ private Integer value;
+ @Data
+ private String param;
+
+ public ExampleClassConnector(TouchPortalSampleJavaPlugin touchPortalPlugin) {
+ super(touchPortalPlugin);
+ }
+
+ @Override
+ public void onInvoke() {
+ LOGGER.info("Example Class Connector with param=" + this.param + " and value=" + this.value);
+ }
+
+ @Override
+ public void onListChanged(TPListChangedMessage tpListChangedMessage) {
+ LOGGER.info("ExampleClassConnector.onListChanged");
+ }
+}
diff --git a/SampleKotlin/build.gradle b/SampleKotlin/build.gradle
index 05162251..1faf69de 100644
--- a/SampleKotlin/build.gradle
+++ b/SampleKotlin/build.gradle
@@ -2,8 +2,8 @@ plugins {
id 'org.jetbrains.kotlin.jvm' version '1.5.0'
id 'org.jetbrains.kotlin.kapt' version '1.5.0'
id 'java'
- id 'com.github.gmazzo.buildconfig' version '3.0.0'
- id 'com.christophecvb.touchportal.plugin-packager' version "$versionName+"
+ id 'com.github.gmazzo.buildconfig' version '3.1.0'
+ id 'com.christophecvb.touchportal.plugin-packager' version "+"
}
def mainClassSimpleName = 'TouchPortalSampleKotlinPlugin'
@@ -24,7 +24,7 @@ buildConfig {
}
dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
+ implementation libs.kotlinstdlibjdk8
implementation project(':Library')
diff --git a/SampleKotlin/src/main/kotlin/com/christophecvb/touchportal/samplekotlin/TouchPortalSampleKotlinPlugin.kt b/SampleKotlin/src/main/kotlin/com/christophecvb/touchportal/samplekotlin/TouchPortalSampleKotlinPlugin.kt
index 470fa007..7e23c6c0 100644
--- a/SampleKotlin/src/main/kotlin/com/christophecvb/touchportal/samplekotlin/TouchPortalSampleKotlinPlugin.kt
+++ b/SampleKotlin/src/main/kotlin/com/christophecvb/touchportal/samplekotlin/TouchPortalSampleKotlinPlugin.kt
@@ -20,25 +20,27 @@
package com.christophecvb.touchportal.samplekotlin
-import com.christophecvb.touchportal.annotations.Action
-import com.christophecvb.touchportal.annotations.Category
-import com.christophecvb.touchportal.annotations.Plugin
import com.christophecvb.touchportal.helpers.PluginHelper
import com.christophecvb.touchportal.TouchPortalPlugin
-import com.christophecvb.touchportal.annotations.Data
+import com.christophecvb.touchportal.annotations.*
import com.christophecvb.touchportal.model.*
import com.google.gson.JsonObject
import java.util.logging.Level
import java.util.logging.Logger
import kotlin.system.exitProcess
-@Suppress("unused")
-@Plugin(version = BuildConfig.VERSION_CODE, colorDark = "#556677", colorLight = "#112233")
+@Plugin(
+ name = BuildConfig.NAME,
+ version = BuildConfig.VERSION_CODE,
+ colorDark = "#556677",
+ colorLight = "#112233",
+ parentCategory = ParentCategory.MISC
+)
class TouchPortalSampleKotlinPlugin(parallelizeActions: Boolean) : TouchPortalPlugin(parallelizeActions), TouchPortalPlugin.TouchPortalPluginListener {
companion object {
/**
- * Logger
+ * Logger used within the plugin
*/
private val LOGGER = Logger.getLogger(TouchPortalPlugin::class.java.name)
@@ -51,7 +53,7 @@ class TouchPortalSampleKotlinPlugin(parallelizeActions: Boolean) : TouchPortalPl
// Initiate the connection with the Touch Portal Plugin System
val connectedPairedAndListening = touchPortalSampleKotlinPlugin.connectThenPairAndListen(touchPortalSampleKotlinPlugin)
- @Suppress("ControlFlowWithEmptyBody")
+
if (connectedPairedAndListening) {
// Let's go!
LOGGER.log(Level.INFO, "Plugin with ID[${TouchPortalSampleKotlinPluginConstants.ID}] Connected and Paired!")
@@ -61,7 +63,6 @@ class TouchPortalSampleKotlinPlugin(parallelizeActions: Boolean) : TouchPortalPl
}
}
- @Suppress("unused")
enum class Categories {
@Category(imagePath = "images/icon-24.png")
BaseCategory
@@ -82,7 +83,7 @@ class TouchPortalSampleKotlinPlugin(parallelizeActions: Boolean) : TouchPortalPl
}
override fun onReceived(jsonMessage: JsonObject) {}
- override fun onListChanged(tpListChangeMessage: TPListChangeMessage) {}
+ override fun onListChanged(tpListChangedMessage: TPListChangedMessage) {}
override fun onInfo(tpInfoMessage: TPInfoMessage) {}
override fun onBroadcast(tpBroadcastMessage: TPBroadcastMessage) {}
override fun onSettings(tpSettingsMessage: TPSettingsMessage) {}
diff --git a/build.gradle b/build.gradle
index f66e5da4..8bff2046 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,8 +4,8 @@ allprojects {
}
}
-ext.versionMajor = 8
-ext.versionMinor = 3
+ext.versionMajor = 9
+ext.versionMinor = 0
ext.versionPatch = 0
ext.isRelease = System.getenv('IS_RELEASE') == 'YES'
diff --git a/settings.gradle b/settings.gradle
index 2fcdf047..37ba134b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -6,6 +6,19 @@ pluginManagement {
}
}
+dependencyResolutionManagement {
+ versionCatalogs {
+ libs {
+ library('gson', 'com.google.code.gson:gson:2.9.0')
+ library('okhttp', 'com.squareup.okhttp3:okhttp:4.9.3')
+ library('autoservice', 'com.google.auto.service:auto-service:1.0.1')
+ library('javapoet', 'com.squareup:javapoet:1.13.0')
+ library('junit', 'junit:junit:4.13.2')
+ library('kotlinstdlibjdk8', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.10')
+ }
+ }
+}
+
rootProject.name = 'TouchPortalPluginSDK'
include 'Annotations'
include 'Helpers'