From e86b16f2d558139cc845b0a4becb54df5579700e Mon Sep 17 00:00:00 2001 From: Pjiesco <55349095+Pjiesco@users.noreply.github.com> Date: Thu, 19 May 2022 11:54:12 +0200 Subject: [PATCH 01/41] [Feature] Dynamic state parent group (#48) * feat: add parentGroup field in createState message * bump SDK and TP Api version * Change parameter order * feat: use the name from the category of the categoryId if parentGroup isn't specified * fix: typo's in stateId fix: use correct categoryId * fix: add check for category id from constants * fix: break outer loop * feat: add CategoryHelper.getCategoryShortId method --- .../touchportal/annotations/Category.java | 2 +- .../touchportal/helpers/CategoryHelper.java | 23 ++++++++ .../touchportal/helpers/PluginHelper.java | 2 +- .../helpers/SentMessageHelper.java | 1 + .../touchportal/TouchPortalPlugin.java | 50 +++++++++++++++++- .../touchportal/test/LibraryTests.java | 52 +++++++++++-------- build.gradle | 4 +- 7 files changed, 106 insertions(+), 28 deletions(-) diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Category.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Category.java index 43c843f..e6b622b 100644 --- a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Category.java +++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/Category.java @@ -33,7 +33,7 @@ * * @see TP Documentation: Categories */ -@Retention(RetentionPolicy.SOURCE) +@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Category { /** diff --git a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/CategoryHelper.java b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/CategoryHelper.java index 42e7bb6..c4eba79 100644 --- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/CategoryHelper.java +++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/CategoryHelper.java @@ -23,6 +23,7 @@ import com.christophecvb.touchportal.annotations.Category; import javax.lang.model.element.Element; +import java.lang.reflect.Field; /** * Touch Portal Plugin Category Helper @@ -48,6 +49,17 @@ public static String getCategoryId(Element pluginElement, Element categoryElemen return CategoryHelper._getCategoryId(PluginHelper.getPluginId(pluginElement), category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id()); } + /** + * Get Category Short ID + * + * @param categoryField {@link Field} + * @param category {@link Category} + * @return String categoryId + */ + public static String getCategoryShortId(Field categoryField, Category category) { + return category.id().isEmpty() ? categoryField.getName() : category.id(); + } + /** * Get the generated Category Name * @@ -59,6 +71,17 @@ public static String getCategoryName(Element categoryElement, Category category) return category.name().isEmpty() ? categoryElement.getSimpleName().toString() : category.name(); } + /** + * Get the generated Category Name + * + * @param categoryField {@link Field} + * @param category {@link Category} + * @return String categoryName + */ + public static String getCategoryName(Field categoryField, Category category) { + return category.name().isEmpty() ? categoryField.getName() : category.name(); + } + /** * Get the generated Category 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 0c615b3..b4706d6 100644 --- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java +++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java @@ -43,7 +43,7 @@ public class PluginHelper { /** * Touch Portal Plugin System version */ - public static final int TOUCH_PORTAL_PLUGIN_VERSION = 5; + public static final int TOUCH_PORTAL_PLUGIN_VERSION = 6; /** * Argument passed to the jar to start the plugin */ 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 a5a8d2c..a852b12 100644 --- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SentMessageHelper.java +++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SentMessageHelper.java @@ -47,5 +47,6 @@ public class SentMessageHelper { public static final String OPTIONS = "options"; public static final String CONNECTOR_ID = "connectorId"; public static final String SHORT_ID = "shortId"; + public static final String PARENT_GROUP = "parentGroup"; } \ 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 ae58cf0..37865d0 100644 --- a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java +++ b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java @@ -712,7 +712,21 @@ public boolean sendStateUpdate(String stateId, Object value, boolean allowEmptyV * @return boolean stateUpdateMessageSent */ public boolean sendCreateState(String categoryId, String stateId, String description, Object value) { - return this.sendCreateState(categoryId, stateId, description, value, false, false); + return this.sendCreateState(categoryId, stateId, null, description, value, false, false); + } + + /** + * Send a Create a State Message to the Touch Portal Plugin System not allowing empty value + * + * @param categoryId String + * @param stateId String + * @param parentGroup String + * @param description String + * @param value Object + * @return boolean stateUpdateMessageSent + */ + public boolean sendCreateState(String categoryId, String stateId, String parentGroup, String description, Object value) { + return this.sendCreateState(categoryId, stateId, parentGroup, description, value, false, false); } /** @@ -727,6 +741,22 @@ public boolean sendCreateState(String categoryId, String stateId, String descrip * @return boolean stateCreateSent */ public boolean sendCreateState(String categoryId, String stateId, String description, Object value, boolean allowEmptyValue, boolean forceUpdate) { + return this.sendCreateState(categoryId, stateId, null, description, value, allowEmptyValue, forceUpdate); + } + + /** + * Send a Create a State Message to the Touch Portal Plugin System + * + * @param categoryId String + * @param stateId String + * @param parentGroup String + * @param description String + * @param value Object + * @param allowEmptyValue boolean + * @param forceUpdate boolean + * @return boolean stateCreateSent + */ + public boolean sendCreateState(String categoryId, String stateId, String parentGroup, String description, Object value, boolean allowEmptyValue, boolean forceUpdate) { boolean sent = false; String valueStr = value != null ? String.valueOf(value) : null; if (categoryId != null && !categoryId.isEmpty() && stateId != null && !stateId.isEmpty() && description != null && !description.isEmpty() && valueStr != null && (allowEmptyValue || !valueStr.isEmpty())) { @@ -737,6 +767,24 @@ public boolean sendCreateState(String categoryId, String stateId, String descrip createStateMessage.addProperty(SentMessageHelper.ID, stateId); createStateMessage.addProperty(SentMessageHelper.DESCRIPTION, description); createStateMessage.addProperty(SentMessageHelper.DEFAULT_VALUE, valueStr); + if (parentGroup == null || parentGroup.isEmpty()) { + classLoop: + for (Class subClass : this.pluginClass.getDeclaredClasses()) { + for (Field subClassField : subClass.getDeclaredFields()) { + if (subClassField.isAnnotationPresent(Category.class)) { + Category category = subClassField.getAnnotation(Category.class); + + if(categoryId.equals(CategoryHelper.getCategoryShortId(subClassField, category))) { + createStateMessage.addProperty(SentMessageHelper.PARENT_GROUP, CategoryHelper.getCategoryName(subClassField, category)); + break classLoop; + } + } + } + } + } else { + createStateMessage.addProperty(SentMessageHelper.PARENT_GROUP, parentGroup); + } + sent = this.send(createStateMessage); if (sent) { this.currentStates.put(stateId, valueStr); 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 db68892..2f85477 100644 --- a/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java +++ b/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java @@ -321,37 +321,43 @@ public void testDynamicStates() { assertFalse(this.touchPortalPluginTest.sendCreateState(null, null, null, "")); assertFalse(this.touchPortalPluginTest.sendCreateState("", "", "", "")); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", null, null, null)); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "", null, null)); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", null, "", null)); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", null, null, "")); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "", "", "")); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", null, null, null)); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "", null, null)); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", null, "", null)); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", null, null, "")); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "", "", "")); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "SateId", null, null)); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "SateId", "", null)); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "SateId", null, "")); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "SateId", "", "")); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", null, null)); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "", null)); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", null, "")); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "", "")); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "SateId", "Dynamically Created State", null)); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "SateId", "Dynamically Created State", "")); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Dynamically Created State", null)); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Dynamically Created State", "")); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "StateId", "Dynamically Created State", null, true, false)); - assertTrue(this.touchPortalPluginTest.sendCreateState("CategoryId", "StateId", "Dynamically Created State", "", true, false)); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "StateId", "Dynamically Created State", "", true, false)); - assertTrue(this.touchPortalPluginTest.sendCreateState("CategoryId", "StateId", "Dynamically Created State", "", true, true)); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Dynamically Created State", null, true, false)); + assertTrue(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Dynamically Created State", "", true, false)); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Dynamically Created State", "", true, false)); + assertTrue(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Dynamically Created State", "", true, true)); + + assertTrue(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Dynamically Created State", "Default Value 01")); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Dynamically Created State", "Default Value 01")); + assertTrue(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Dynamically Created State", "Default Value 02")); + + assertTrue(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Parent", "Dynamically Created State", "Default Value 01")); + assertTrue(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "", "Dynamically Created State", "Default Value 02")); + assertTrue(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", null, "Dynamically Created State", "Default Value 03")); + assertTrue(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Parent", "Dynamically Created State", "Default Value 04", true, false)); - assertTrue(this.touchPortalPluginTest.sendCreateState("CategoryId", "SateId", "Dynamically Created State", "Default Value 01")); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "SateId", "Dynamically Created State", "Default Value 01")); - assertTrue(this.touchPortalPluginTest.sendCreateState("CategoryId", "SateId", "Dynamically Created State", "Default Value 02")); assertFalse(this.touchPortalPluginTest.sendRemoveState(null, null)); assertFalse(this.touchPortalPluginTest.sendRemoveState("", null)); assertFalse(this.touchPortalPluginTest.sendRemoveState(null, "")); assertFalse(this.touchPortalPluginTest.sendRemoveState("", "")); - assertFalse(this.touchPortalPluginTest.sendRemoveState("CategoryId", null)); - assertFalse(this.touchPortalPluginTest.sendRemoveState("CategoryId", "")); + assertFalse(this.touchPortalPluginTest.sendRemoveState("BaseCategory", null)); + assertFalse(this.touchPortalPluginTest.sendRemoveState("BaseCategory", "")); - assertTrue(this.touchPortalPluginTest.sendRemoveState("CategoryId", "SateId")); + assertTrue(this.touchPortalPluginTest.sendRemoveState("BaseCategory", "StateId")); } @Test @@ -360,8 +366,8 @@ public void testSendFail() { assertFalse(this.touchPortalPluginTest.sendStateUpdate(TouchPortalPluginTestConstants.BaseCategory.States.CustomState.ID, "New Value")); assertFalse(this.touchPortalPluginTest.sendChoiceUpdate("listId", null, true)); assertFalse(this.touchPortalPluginTest.sendSpecificChoiceUpdate("listId", "instanceId", null, true)); - assertFalse(this.touchPortalPluginTest.sendCreateState("CategoryId", "SateId", "Dynamically Created State", "Default Value 01")); - assertFalse(this.touchPortalPluginTest.sendRemoveState("CategoryId", "SateId")); + assertFalse(this.touchPortalPluginTest.sendCreateState("BaseCategory", "StateId", "Dynamically Created State", "Default Value 01")); + assertFalse(this.touchPortalPluginTest.sendRemoveState("BaseCategory", "StateId")); } @Test diff --git a/build.gradle b/build.gradle index 03f26fb..0af9408 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ allprojects { } ext.versionMajor = 8 -ext.versionMinor = 1 -ext.versionPatch = 2 +ext.versionMinor = 2 +ext.versionPatch = 0 ext.isRelease = System.getenv('IS_RELEASE') == 'YES' From dbc0a35bed92677f6d3ebc7f51b2090c5dde3f52 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Mon, 23 May 2022 10:55:46 +0200 Subject: [PATCH 02/41] core: Upgrade dependencies --- AnnotationsProcessor/build.gradle | 8 ++++---- Helpers/build.gradle | 2 +- Library/build.gradle | 6 +++--- .../test/resources/TouchPortalPluginTest/plugin.config | 2 +- SampleKotlin/build.gradle | 2 +- build.gradle | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/AnnotationsProcessor/build.gradle b/AnnotationsProcessor/build.gradle index 0b26306..67a555e 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.8.6' - implementation 'com.google.auto.service:auto-service:1.0-rc7' - implementation group: 'com.squareup', name: 'javapoet', version: '1.12.1' + 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 project(':Annotations') implementation project(':Helpers') - testImplementation group: 'junit', name: 'junit', version: '4.12' + testImplementation group: 'junit', name: 'junit', version: '4.13.2' } diff --git a/Helpers/build.gradle b/Helpers/build.gradle index 1e8e3f0..5d24d20 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.8.6' + api group: 'com.google.code.gson', name: 'gson', version: '2.9.0' api project(':Annotations') } diff --git a/Library/build.gradle b/Library/build.gradle index 368be89..31acac3 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.8.6' + api group: 'com.google.code.gson', name: 'gson', version: '2.9.0' - implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.7.2' + implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.9.3' - testImplementation group: 'junit', name: 'junit', version: '4.12' + testImplementation group: 'junit', name: 'junit', version: '4.13.2' testAnnotationProcessor project(':AnnotationsProcessor') } diff --git a/Library/src/test/resources/TouchPortalPluginTest/plugin.config b/Library/src/test/resources/TouchPortalPluginTest/plugin.config index e646ad3..09a2603 100644 --- a/Library/src/test/resources/TouchPortalPluginTest/plugin.config +++ b/Library/src/test/resources/TouchPortalPluginTest/plugin.config @@ -1,4 +1,4 @@ #TouchPortalPluginTest -#Fri Jan 21 19:02:31 CET 2022 +#Mon May 23 10:54:55 CEST 2022 samplekey=Sample Value plugin.version=1 diff --git a/SampleKotlin/build.gradle b/SampleKotlin/build.gradle index 87e1fd0..67b7048 100644 --- a/SampleKotlin/build.gradle +++ b/SampleKotlin/build.gradle @@ -24,7 +24,7 @@ buildConfig { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' implementation project(':Library') diff --git a/build.gradle b/build.gradle index 0af9408..1218721 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ allprojects { ext.versionMajor = 8 ext.versionMinor = 2 -ext.versionPatch = 0 +ext.versionPatch = 1 ext.isRelease = System.getenv('IS_RELEASE') == 'YES' From 821de0218ee3d83896e0bc1574f36aa8a3c4f081 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Thu, 15 Sep 2022 20:09:50 +0200 Subject: [PATCH 03/41] core: Upgrade Gradle version to 7.5.1 --- .../packager/TouchPortalPluginPackager.groovy | 2 ++ build.gradle | 6 +++--- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 9 ++++++++- 4 files changed, 14 insertions(+), 5 deletions(-) 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 d96b273..453e64e 100644 --- a/Packager/src/main/groovy/com/christophecvb/touchportal/packager/TouchPortalPluginPackager.groovy +++ b/Packager/src/main/groovy/com/christophecvb/touchportal/packager/TouchPortalPluginPackager.groovy @@ -37,6 +37,8 @@ class TouchPortalPluginPackager implements Plugin { project.tasks.withType(Jar) { task -> task.dependsOn project.configurations.runtimeClasspath + task.duplicatesStrategy = DuplicatesStrategy.EXCLUDE + task.doFirst { manifest { attributes 'Implementation-Title': "${extension.mainClassSimpleName.get()}", diff --git a/build.gradle b/build.gradle index 1218721..f66e5da 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ allprojects { } ext.versionMajor = 8 -ext.versionMinor = 2 -ext.versionPatch = 1 +ext.versionMinor = 3 +ext.versionPatch = 0 ext.isRelease = System.getenv('IS_RELEASE') == 'YES' @@ -16,4 +16,4 @@ ext.versionName = "${versionMajor}.${versionMinor}.${versionPatch}" + (ext.isRel ext.envOrPropOrEmpty = { String name -> def value = System.getenv(name) ? System.getenv(name) : findProperty(name) return value ? value.toString() : '' -} \ No newline at end of file +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 28ff446..ae04661 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index 1702027..2fcdf04 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,11 @@ +pluginManagement { + repositories { + mavenLocal() + mavenCentral() + gradlePluginPortal() + } +} + rootProject.name = 'TouchPortalPluginSDK' include 'Annotations' include 'Helpers' @@ -6,4 +14,3 @@ include 'Library' include 'SampleJava' include 'SampleKotlin' include 'Packager' - From 038c1786b0098fb4ccb7ba0955060ee49ae0a5e3 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Thu, 15 Sep 2022 20:20:57 +0200 Subject: [PATCH 04/41] core: Upgrade Gradle version to 7.5.1 --- SampleJava/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SampleJava/build.gradle b/SampleJava/build.gradle index c392e71..c1c0707 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 '8.0.0-SNAPSHOT' + id 'com.christophecvb.touchportal.plugin-packager' version '8.3.0-SNAPSHOT' } def mainClassSimpleName = 'TouchPortalSampleJavaPlugin' From d0432b599cd005fd181c0c832ecd67d86c47462b Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Thu, 15 Sep 2022 20:32:02 +0200 Subject: [PATCH 05/41] core: Upgrade Gradle version to 7.5.1 --- .github/workflows/develop.yml | 16 ++++++++++++++++ SampleJava/build.gradle | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 6f35ad6..0d8244e 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -5,8 +5,24 @@ on: branches: [ develop ] jobs: + prepare-maven-local: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + java-package: jdk + - name: Gradle Plugin Publish Local + run: | + ./gradlew :Packager:publishToMavenLocal + build: + needs: prepare-maven-local runs-on: ubuntu-latest steps: diff --git a/SampleJava/build.gradle b/SampleJava/build.gradle index c1c0707..f79a792 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 '8.3.0-SNAPSHOT' + id 'com.christophecvb.touchportal.plugin-packager' version '8.3.0-SNAPSHOT+' } def mainClassSimpleName = 'TouchPortalSampleJavaPlugin' From 03ca14a7139bf09ded21c029106d65678d9d22f4 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Thu, 15 Sep 2022 20:39:52 +0200 Subject: [PATCH 06/41] core: Upgrade Gradle version to 7.5.1 --- SampleJava/build.gradle | 2 +- SampleKotlin/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SampleJava/build.gradle b/SampleJava/build.gradle index f79a792..bcae488 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 '8.3.0-SNAPSHOT+' + id 'com.christophecvb.touchportal.plugin-packager' version "$versionName+" } def mainClassSimpleName = 'TouchPortalSampleJavaPlugin' diff --git a/SampleKotlin/build.gradle b/SampleKotlin/build.gradle index 67b7048..0516225 100644 --- a/SampleKotlin/build.gradle +++ b/SampleKotlin/build.gradle @@ -3,7 +3,7 @@ plugins { 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 '8.0.0-SNAPSHOT' + id 'com.christophecvb.touchportal.plugin-packager' version "$versionName+" } def mainClassSimpleName = 'TouchPortalSampleKotlinPlugin' From 324fb2e49338ec4360351b65b66c97b7f01031aa Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Thu, 15 Sep 2022 20:48:33 +0200 Subject: [PATCH 07/41] core: Upgrade Gradle version to 7.5.1 --- Packager/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packager/build.gradle b/Packager/build.gradle index bc372b3..42d58ab 100644 --- a/Packager/build.gradle +++ b/Packager/build.gradle @@ -7,7 +7,7 @@ plugins { group 'com.christophecvb.touchportal' def localArchiveBaseName = 'plugin-packager' -version versionName + (isRelease ? '' : '-' + System.currentTimeMillis()) +version versionName pluginBundle { website = 'https://github.com/ChristopheCVB/TouchPortalPluginSDK' From 25da3315b26783d03930bf1dfb44cbf275c92ac4 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 12:14:03 +0100 Subject: [PATCH 08/41] core: Dependencies management --- AnnotationsProcessor/build.gradle | 8 ++++---- Helpers/build.gradle | 2 +- Library/build.gradle | 6 +++--- SampleKotlin/build.gradle | 2 +- build.gradle | 2 +- settings.gradle | 13 +++++++++++++ 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/AnnotationsProcessor/build.gradle b/AnnotationsProcessor/build.gradle index 67a555e..96232ca 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/Helpers/build.gradle b/Helpers/build.gradle index 5d24d20..b19d282 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/Library/build.gradle b/Library/build.gradle index 31acac3..2125853 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/SampleKotlin/build.gradle b/SampleKotlin/build.gradle index 0516225..affc3a1 100644 --- a/SampleKotlin/build.gradle +++ b/SampleKotlin/build.gradle @@ -24,7 +24,7 @@ buildConfig { } dependencies { - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + implementation libs.kotlinstdlibjdk8 implementation project(':Library') diff --git a/build.gradle b/build.gradle index f66e5da..0d0f82d 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ allprojects { } ext.versionMajor = 8 -ext.versionMinor = 3 +ext.versionMinor = 4 ext.versionPatch = 0 ext.isRelease = System.getenv('IS_RELEASE') == 'YES' diff --git a/settings.gradle b/settings.gradle index 2fcdf04..2388470 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.+') + } + } +} + rootProject.name = 'TouchPortalPluginSDK' include 'Annotations' include 'Helpers' From 8f92768ff0cd3ed782fbd55a1420a45f6a7592d7 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 12:14:42 +0100 Subject: [PATCH 09/41] core: ActionTranslation annotation --- .../annotations/ActionTranslation.java | 79 +++++++++++++++++++ .../annotations/ActionTranslations.java | 5 ++ .../touchportal/annotations/Language.java | 25 ++++++ .../TouchPortalPluginAnnotationProcessor.java | 19 +++++ .../packager/TouchPortalPluginPackager.groovy | 2 + .../TouchPortalSampleJavaPlugin.java | 2 + 6 files changed, 132 insertions(+) create mode 100644 Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslation.java create mode 100644 Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslations.java create mode 100644 Annotations/src/main/java/com/christophecvb/touchportal/annotations/Language.java 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 0000000..1830a22 --- /dev/null +++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslation.java @@ -0,0 +1,79 @@ +/* + * 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) +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 0000000..3efd044 --- /dev/null +++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslations.java @@ -0,0 +1,5 @@ +package com.christophecvb.touchportal.annotations; + +public @interface ActionTranslations { + ActionTranslation[] value(); +} 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 0000000..880e2db --- /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/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java index 08ac3d9..d3344ed 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java @@ -355,6 +355,25 @@ private Pair processAction(RoundEnvironment roundE } 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 dataElements = roundEnv.getElementsAnnotatedWith(Data.class); for (Element dataElement : dataElements) { 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 453e64e..4a1d305 100644 --- a/Packager/src/main/groovy/com/christophecvb/touchportal/packager/TouchPortalPluginPackager.groovy +++ b/Packager/src/main/groovy/com/christophecvb/touchportal/packager/TouchPortalPluginPackager.groovy @@ -53,6 +53,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/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java index 38ade3f..ca03977 100644 --- a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java +++ b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java @@ -147,6 +147,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"); } From 0af69e12b746fc8515eeebafd32242ddf3eac813 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 12:20:13 +0100 Subject: [PATCH 10/41] fix: Github Workflow --- SampleJava/build.gradle | 2 +- SampleKotlin/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SampleJava/build.gradle b/SampleJava/build.gradle index bcae488..8f0e6de 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.christophecvb.touchportal.plugin-packager' version "+" } def mainClassSimpleName = 'TouchPortalSampleJavaPlugin' diff --git a/SampleKotlin/build.gradle b/SampleKotlin/build.gradle index affc3a1..6c2dda3 100644 --- a/SampleKotlin/build.gradle +++ b/SampleKotlin/build.gradle @@ -3,7 +3,7 @@ plugins { 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.christophecvb.touchportal.plugin-packager' version "+" } def mainClassSimpleName = 'TouchPortalSampleKotlinPlugin' From 531bcb1384379c17224c42e8350ceccbaa1bd8b6 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 12:30:35 +0100 Subject: [PATCH 11/41] fix: Github Workflows --- .github/workflows/develop.yml | 23 ++++++++++++++--------- .github/workflows/master.yml | 17 ++++++++++------- .github/workflows/pr.yml | 12 ++++++------ settings.gradle | 2 +- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 0d8244e..ac557fd 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -10,12 +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: + distribution: temurin java-version: 1.8 - java-package: jdk + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Gradle Plugin Publish Local run: | ./gradlew :Packager:publishToMavenLocal @@ -26,14 +28,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: + distribution: temurin java-version: 1.8 - java-package: jdk - - name: Grant execute permission for gradlew - run: chmod +x gradlew + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Build with Gradle run: ./gradlew build @@ -45,9 +47,12 @@ jobs: steps: - uses: actions/checkout@v1 - name: Set up JDK 1.8 - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: temurin java-version: 1.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 6aff882..a09512a 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: + distribution: temurin java-version: 1.8 - java-package: jdk - - name: Grant execute permission for gradlew - run: chmod +x gradlew + - 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: + distribution: temurin java-version: 1.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 3e2e265..f82ce60 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: + distribution: temurin java-version: 1.8 - java-package: jdk - - name: Grant execute permission for gradlew - run: chmod +x gradlew + - 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/settings.gradle b/settings.gradle index 2388470..c96738d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,7 +14,7 @@ dependencyResolutionManagement { 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.+') + library('kotlinstdlibjdk8', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.1') } } } From 7bdbf1108bc8373cfcb1cec5243517896f38e24c Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 12:31:52 +0100 Subject: [PATCH 12/41] fix: Github Workflows --- .github/workflows/develop.yml | 6 +++--- .github/workflows/master.yml | 4 ++-- .github/workflows/pr.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index ac557fd..8032c23 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 1.8 + java-version: 8 - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Gradle Plugin Publish Local @@ -33,7 +33,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 1.8 + java-version: 8 - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Build with Gradle @@ -50,7 +50,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 1.8 + java-version: 8 - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Gradle Maven Publish diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index a09512a..45150ab 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 1.8 + java-version: 8 - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Build with Gradle @@ -38,7 +38,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 1.8 + java-version: 8 - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Gradle Maven Publish diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index f82ce60..e73eef4 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -16,7 +16,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 1.8 + java-version: 8 - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Run unit tests From b2bb0a4740a8045977b8c7465cad0441d66cf66c Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 12:37:48 +0100 Subject: [PATCH 13/41] fix: Github Workflows --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index c96738d..37ba134 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,7 +14,7 @@ dependencyResolutionManagement { 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.1') + library('kotlinstdlibjdk8', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.10') } } } From 336b35a6c2c3a4531148ccb702556ba4e9911f2b Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 12:50:29 +0100 Subject: [PATCH 14/41] fix: Tests wait time --- .../touchportal/test/LibraryTests.java | 71 ++++++++++--------- 1 file changed, 36 insertions(+), 35 deletions(-) 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 2f85477..f2f8bf2 100644 --- a/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java +++ b/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java @@ -50,6 +50,7 @@ import static org.junit.Assert.*; public class LibraryTests { + private static final long REASONABLE_TIME = 100; private ServerSocket serverSocket; private Socket serverSocketClient; private TouchPortalPluginTest touchPortalPluginTest; @@ -113,7 +114,7 @@ private void serverSocketAccept() { } }).start(); try { - Thread.sleep(100); + Thread.sleep(REASONABLE_TIME); } catch (InterruptedException ignored) {} } @@ -137,7 +138,7 @@ public void close() { ioException.printStackTrace(); } try { - Thread.sleep(10); + Thread.sleep(REASONABLE_TIME); } catch (InterruptedException e) { e.printStackTrace(); @@ -150,7 +151,7 @@ public void close() { @Test public void testConnection() { try { - Thread.sleep(500); + Thread.sleep(REASONABLE_TIME); } catch (InterruptedException e) { e.printStackTrace(); @@ -208,7 +209,7 @@ public void testClose() { public void testServerSocketCloses() throws IOException, InterruptedException { this.serverSocketClient.close(); this.serverSocket.close(); - Thread.sleep(100); + Thread.sleep(REASONABLE_TIME); assertFalse(this.touchPortalPluginTest.isConnected()); } @@ -378,7 +379,7 @@ public void testReceiveActionNoId() 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()); @@ -392,7 +393,7 @@ public void testReceiveConnectorNoId() 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()); @@ -407,7 +408,7 @@ public void testReceiveActionEmptyId() 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()); @@ -422,7 +423,7 @@ public void testReceiveConnectorEmptyId() throws IOException, InterruptedExcepti 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()); @@ -443,7 +444,7 @@ public void testReceiveShortConnectorIdNotification() throws IOException, Interr 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()); @@ -474,7 +475,7 @@ public void testReceiveDummyWithDataTextAndNumberAction() throws IOException, In 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()); @@ -500,7 +501,7 @@ public void testReceiveDummyWithDataFileAction() throws IOException, Interrupted 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()); @@ -515,7 +516,7 @@ public void testReceiveDummyDummyWithJsonObject() throws IOException, Interrupte 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()); @@ -530,7 +531,7 @@ public void testReceiveDummyDummyWithTPActionMessage() throws IOException, Inter 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()); @@ -545,7 +546,7 @@ public void testReceiveDummyDummyWithParam() throws IOException, InterruptedExce 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()); @@ -561,7 +562,7 @@ public void testReceiveActionHoldableDownAndUp() throws IOException, Interrupted 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)); @@ -571,7 +572,7 @@ public void testReceiveActionHoldableDownAndUp() throws IOException, Interrupted 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)); @@ -589,7 +590,7 @@ public void testReceiveActionHoldablePress() throws IOException, InterruptedExce 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)); @@ -609,7 +610,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()); @@ -632,7 +633,7 @@ public void testReceiveConnectorForSliderWithData() throws IOException, Interrup out.println(jsonMessageConnectorForSliderWithData); - Thread.sleep(10); + Thread.sleep(REASONABLE_TIME); assertTrue(this.touchPortalPluginTest.isConnected()); assertTrue(this.touchPortalPluginTest.isListening()); @@ -668,7 +669,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()); @@ -682,7 +683,7 @@ public void testReceiveListChange() 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()); @@ -697,7 +698,7 @@ public void testReceiveListChangeNoListener() throws IOException, InterruptedExc 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()); @@ -712,7 +713,7 @@ public void testReceiveActionNoListener() throws IOException, InterruptedExcepti 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()); @@ -726,7 +727,7 @@ public void testReceiveBadPlugin() 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()); @@ -739,7 +740,7 @@ public void testReceiveNoMessageType() 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()); @@ -753,7 +754,7 @@ public void testReceiveUnknownMessageType() throws IOException, InterruptedExcep 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()); @@ -782,7 +783,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()); @@ -805,7 +806,7 @@ public void testReceiveInfoMissingPropsAndNoListener() throws IOException, Inter 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()); @@ -828,7 +829,7 @@ public void testReceiveClose() throws IOException, InterruptedException { 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()); @@ -839,7 +840,7 @@ public void testReceiveJSONFail() throws IOException, InterruptedException { 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()); @@ -850,7 +851,7 @@ public void testReceiveEmpty() throws IOException, InterruptedException { 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()); @@ -865,7 +866,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()); @@ -911,7 +912,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()); @@ -929,7 +930,7 @@ public void testReceiveSettingsNoListener() throws IOException, InterruptedExcep 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()); @@ -958,7 +959,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)); From 93b8668e38b78ca91534d7d1870ec475ffeae981 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 13:07:42 +0100 Subject: [PATCH 15/41] fix: Packager SNAPSHOT publication --- Packager/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packager/build.gradle b/Packager/build.gradle index 42d58ab..386cb06 100644 --- a/Packager/build.gradle +++ b/Packager/build.gradle @@ -7,7 +7,7 @@ plugins { group 'com.christophecvb.touchportal' def localArchiveBaseName = 'plugin-packager' -version versionName +version versionName + (isRelease ? '' : '-' + System.currentTimeSeconds()) pluginBundle { website = 'https://github.com/ChristopheCVB/TouchPortalPluginSDK' From dbaa8cc4761943e2918d607e5e61390e6bbed6ab Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 13:15:49 +0100 Subject: [PATCH 16/41] fix: Packager SNAPSHOT publication --- Packager/build.gradle | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Packager/build.gradle b/Packager/build.gradle index 386cb06..62564ee 100644 --- a/Packager/build.gradle +++ b/Packager/build.gradle @@ -1,13 +1,14 @@ plugins { id 'groovy-gradle-plugin' - id 'com.gradle.plugin-publish' version '0.15.0' + id 'com.gradle.plugin-publish' version '1.0.0' id 'maven-publish' id 'signing' } group 'com.christophecvb.touchportal' def localArchiveBaseName = 'plugin-packager' -version versionName + (isRelease ? '' : '-' + System.currentTimeSeconds()) +//version versionName + (isRelease ? '' : '-' + System.currentTimeSeconds()) +version isRelease ? versionName : versionName.replace('-SNAPSHOT', '-' + System.currentTimeSeconds()) pluginBundle { website = 'https://github.com/ChristopheCVB/TouchPortalPluginSDK' From 7b6f062037d72f70f302f756694fea8ce3663295 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 13:20:27 +0100 Subject: [PATCH 17/41] fix: Packager SNAPSHOT publication --- Packager/build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Packager/build.gradle b/Packager/build.gradle index 62564ee..7d32b03 100644 --- a/Packager/build.gradle +++ b/Packager/build.gradle @@ -1,13 +1,12 @@ plugins { id 'groovy-gradle-plugin' - id 'com.gradle.plugin-publish' version '1.0.0' + id 'com.gradle.plugin-publish' version '0.15.0' id 'maven-publish' id 'signing' } group 'com.christophecvb.touchportal' def localArchiveBaseName = 'plugin-packager' -//version versionName + (isRelease ? '' : '-' + System.currentTimeSeconds()) version isRelease ? versionName : versionName.replace('-SNAPSHOT', '-' + System.currentTimeSeconds()) pluginBundle { From 925e94f6be8263f22dd757f6ba352c84dc9981fb Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 19:53:54 +0100 Subject: [PATCH 18/41] fix: Packager SNAPSHOT publication --- Packager/build.gradle | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Packager/build.gradle b/Packager/build.gradle index 7d32b03..a24250c 100644 --- a/Packager/build.gradle +++ b/Packager/build.gradle @@ -1,8 +1,7 @@ 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' @@ -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') From 8be5356af7c203cb8e3228947299e2b35cabe4ab Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 19:57:17 +0100 Subject: [PATCH 19/41] fix: Packager SNAPSHOT publication --- .github/workflows/develop.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 8032c23..2f10fa6 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -21,6 +21,9 @@ jobs: - name: Gradle Plugin Publish Local run: | ./gradlew :Packager:publishToMavenLocal + env: + PGP_KEY: ${{ secrets.PGP_KEY }} + PGP_PWD: ${{ secrets.PGP_PWD }} build: From c073d8357676cdfd7e5e0f4271905e4f2c7138c5 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 1 Nov 2022 20:47:22 +0100 Subject: [PATCH 20/41] feat: Plugin ParentCategory --- .../annotations/ParentCategory.java | 25 +++++++++++++++++++ .../touchportal/annotations/Plugin.java | 10 ++++++++ .../TouchPortalPluginAnnotationProcessor.java | 1 + .../touchportal/helpers/PluginHelper.java | 3 ++- .../TouchPortalSampleJavaPlugin.java | 2 +- 5 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 Annotations/src/main/java/com/christophecvb/touchportal/annotations/ParentCategory.java 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 0000000..d7611fc --- /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 418b32a..8f583b9 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/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java index d3344ed..161c768 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java @@ -146,6 +146,7 @@ private Pair processPlugin(RoundEnvironment roundE 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); 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 b4706d6..94191b8 100644 --- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java +++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java @@ -36,6 +36,7 @@ public class PluginHelper { 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 CATEGORIES = "categories"; public static final String SETTINGS = "settings"; @@ -43,7 +44,7 @@ public class PluginHelper { /** * 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 */ 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 ca03977..7383fbe 100644 --- a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java +++ b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java @@ -32,7 +32,7 @@ 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 = "Touch Portal Plugin Example", parentCategory = ParentCategory.CONTENT) public class TouchPortalSampleJavaPlugin extends TouchPortalPlugin implements TouchPortalPlugin.TouchPortalPluginListener { /** * Logger From 829133b32eaee1290b181cde95c73982d8211bd2 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Wed, 2 Nov 2022 01:25:37 +0100 Subject: [PATCH 21/41] feat: TouchPortalPlugin.getBase64ImageFromUrl closes #38 --- .../touchportal/TouchPortalPlugin.java | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java index 37865d0..cc829bb 100644 --- a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java +++ b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java @@ -27,6 +27,8 @@ 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; @@ -34,12 +36,10 @@ 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,6 +135,10 @@ 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 */ @@ -1197,6 +1201,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 */ From e7f9a6e719d32f0d16401c2eb19df320d4e7d23d Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Mon, 14 Nov 2022 23:06:02 +0100 Subject: [PATCH 22/41] fix: Set encoding to UTF-8 --- .../touchportal/packager/TouchPortalPluginPackager.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 4a1d305..c111e8c 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" } } From 14abfe7234a0f38583fc54ad20ee105c222712e1 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Mon, 14 Nov 2022 23:07:35 +0100 Subject: [PATCH 23/41] feat: TP Plugin API 7 Platform start command --- .../TouchPortalPluginAnnotationProcessor.java | 3 +++ .../touchportal/helpers/PluginHelper.java | 3 +++ README.md | 20 +++++++------------ SampleJava/build.gradle | 2 +- .../TouchPortalSampleJavaPlugin.java | 2 +- SampleKotlin/build.gradle | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) 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 index 161c768..26489b0 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java @@ -149,6 +149,9 @@ private Pair processPlugin(RoundEnvironment roundE 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(); 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 94191b8..e8819d7 100644 --- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java +++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java @@ -38,6 +38,9 @@ public class PluginHelper { 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"; diff --git a/README.md b/README.md index 7546e47..d06b1ae 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' } ``` @@ -262,7 +260,3 @@ public class MyTouchPortalPlugin extends TouchPortalPlugin { - 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) diff --git a/SampleJava/build.gradle b/SampleJava/build.gradle index 8f0e6de..306d47b 100644 --- a/SampleJava/build.gradle +++ b/SampleJava/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'com.github.gmazzo.buildconfig' version '3.0.0' + id 'com.github.gmazzo.buildconfig' version '3.1.0' id 'com.christophecvb.touchportal.plugin-packager' version "+" } 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 7383fbe..bdfeca5 100644 --- a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java +++ b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java @@ -20,10 +20,10 @@ 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.google.gson.JsonObject; diff --git a/SampleKotlin/build.gradle b/SampleKotlin/build.gradle index 6c2dda3..1faf69d 100644 --- a/SampleKotlin/build.gradle +++ b/SampleKotlin/build.gradle @@ -2,7 +2,7 @@ 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.github.gmazzo.buildconfig' version '3.1.0' id 'com.christophecvb.touchportal.plugin-packager' version "+" } From c39594bed9e052e89d490a86bbf1f144e6405fb4 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Sun, 27 Nov 2022 02:39:31 +0100 Subject: [PATCH 24/41] core: Refactor Annotations Processor core: TP_JAVA_FILE --- .../processor/CategoryProcessor.java | 108 ++ .../processor/PluginProcessor.java | 79 ++ .../processor/SettingProcessor.java | 70 ++ .../processor/TPAnnotationException.java | 51 + .../TouchPortalPluginAnnotationProcessor.java | 1114 ----------------- ...TouchPortalPluginAnnotationsProcessor.java | 620 +++++++++ .../processor/utils/SpecUtils.java | 303 +++++ .../javax.annotation.processing.Processor | 2 +- .../touchportal/helpers/PluginHelper.java | 4 + .../TouchPortalPluginTest/plugin.config | 2 +- 10 files changed, 1237 insertions(+), 1116 deletions(-) create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TPAnnotationException.java delete mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/utils/SpecUtils.java 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 0000000..5270f0e --- /dev/null +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java @@ -0,0 +1,108 @@ +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 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 + */ + public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement) throws Exception { + 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 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 = processor.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 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 = processor.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 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 = processor.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 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 = processor.processEvent(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/PluginProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java new file mode 100644 index 0000000..7e4e505 --- /dev/null +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java @@ -0,0 +1,79 @@ +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 roundEnv RoundEnvironment + * @param pluginElement Element + * @return Pair pluginPair + * @throws GenericHelper.TPTypeException If a used type is not Supported + */ + public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement) throws Exception { + 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.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()); + jsonConfiguration.addProperty(PluginHelper.CONFIGURATION_PARENT_CATEGORY, plugin.parentCategory().getKey()); + jsonPlugin.add(PluginHelper.CONFIGURATION, jsonConfiguration); + jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND, PluginHelper.TP_JAVA + " -Dapple.awt.UIElement=true -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START); + jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND + PluginHelper.PLUGIN_START_COMMAND_SUFFIX_WIN, PluginHelper.TP_JAVA + " -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START); + jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND + PluginHelper.PLUGIN_START_COMMAND_SUFFIX_MACOS, PluginHelper.TP_JAVA + " -Dapple.awt.UIElement=true -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START); + jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND + PluginHelper.PLUGIN_START_COMMAND_SUFFIX_LINUX, PluginHelper.TP_JAVA + " -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START); + + TypeSpec.Builder settingsTypeSpecBuilder = TypeSpec.classBuilder("Settings").addModifiers(Modifier.PUBLIC, Modifier.STATIC); + JsonArray jsonSettings = new JsonArray(); + Set 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 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 0000000..d5c636f --- /dev/null +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java @@ -0,0 +1,70 @@ +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 settingElement Element + * @return Pair statePair + * @throws GenericHelper.TPTypeException If a used type is not Supported + */ + public static Pair process(TouchPortalPluginAnnotationsProcessor processor, Element settingElement) throws Exception { + 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()); + 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/TPAnnotationException.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TPAnnotationException.java new file mode 100644 index 0000000..c680bbf --- /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 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 annotationType; + private Boolean isMissing; + private Integer count; + private Element element; + private String typeFor; + + public Builder(Class 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 26489b0..0000000 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationProcessor.java +++ /dev/null @@ -1,1114 +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 annotations, RoundEnvironment roundEnv) { - if (roundEnv.processingOver() || annotations.size() == 0) { - return false; - } - this.messager.printMessage(Diagnostic.Kind.NOTE, this.getClass().getSimpleName() + ".process"); - - try { - Set 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()); - 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 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 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 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 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 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 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()); - - 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 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 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 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 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 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 0000000..d0a71f0 --- /dev/null +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java @@ -0,0 +1,620 @@ +/* + * 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(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 annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver() || annotations.size() == 0) { + return false; + } + this.messager.printMessage(Diagnostic.Kind.NOTE, this.getClass().getSimpleName() + ".process"); + + try { + Set 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; + } + + /** + * 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 + */ + public 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 = 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 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 + */ + public 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 = 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 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 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 + */ + public 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 = 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); + } + + /** + * 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 + */ + public 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 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); + } + + /** + * 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 = SpecUtils.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 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 = SpecUtils.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 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); + } +} 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 0000000..c681c16 --- /dev/null +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/utils/SpecUtils.java @@ -0,0 +1,303 @@ +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())); + } + + 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 + */ + 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 85ca834..ebd05a3 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/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java index e8819d7..66febb3 100644 --- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java +++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java @@ -56,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/Library/src/test/resources/TouchPortalPluginTest/plugin.config b/Library/src/test/resources/TouchPortalPluginTest/plugin.config index 09a2603..10fc40b 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 +#Sat Nov 26 23:11:43 CET 2022 samplekey=Sample Value plugin.version=1 From ed082fa159f38fd64cbb8f6d823828feb7b3020d Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Sun, 27 Nov 2022 02:48:08 +0100 Subject: [PATCH 25/41] fix: JavaDoc --- .../annotations/processor/CategoryProcessor.java | 2 +- .../annotations/processor/PluginProcessor.java | 2 +- .../annotations/processor/SettingProcessor.java | 2 +- .../TouchPortalPluginAnnotationsProcessor.java | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) 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 index 5270f0e..ec55530 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java @@ -24,7 +24,7 @@ public class CategoryProcessor { * @param pluginElement Element * @param plugin {@link Plugin} * @param categoryElement Element - * @return Pair categoryPair + * @return Pair<JsonObject, TypeSpec.Builder> categoryPair * @throws GenericHelper.TPTypeException If a used type is not Supported */ public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement) throws Exception { 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 index 7e4e505..b837ebf 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java @@ -23,7 +23,7 @@ public class PluginProcessor { * * @param roundEnv RoundEnvironment * @param pluginElement Element - * @return Pair pluginPair + * @return Pair<JsonObject, TypeSpec.Builder> pluginPair * @throws GenericHelper.TPTypeException If a used type is not Supported */ public static Pair process(TouchPortalPluginAnnotationsProcessor processor, RoundEnvironment roundEnv, Element pluginElement) throws Exception { 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 index d5c636f..939fd69 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java @@ -16,7 +16,7 @@ public class SettingProcessor { * Generates a JsonObject and a TypeSpec.Builder representing the {@link Setting} * * @param settingElement Element - * @return Pair statePair + * @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 Exception { 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 index d0a71f0..8b7a633 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java @@ -126,7 +126,7 @@ public boolean process(Set annotations, RoundEnvironment * @param categoryElement Element * @param category {@link Category} * @param actionElement Element - * @return Pair actionPair + * @return Pair<JsonObject, TypeSpec.Builder> actionPair */ public 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()); @@ -195,7 +195,7 @@ public Pair processAction(RoundEnvironment roundEn * @param categoryElement Element * @param category {@link Category} * @param connectorElement Element - * @return Pair actionPair + * @return Pair<JsonObject, TypeSpec.Builder> actionPair */ public 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()); @@ -255,7 +255,7 @@ public Pair processConnector(RoundEnvironment roun * @param categoryElement Element * @param category {@link Category} * @param stateElement Element - * @return Pair statePair + * @return Pair<JsonObject, TypeSpec.Builder> statePair * @throws GenericHelper.TPTypeException If a used type is not Supported */ public Pair processState(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element stateElement) throws Exception { @@ -300,7 +300,7 @@ else if (!desiredTPType.equals(StateHelper.TYPE_TEXT)) { * @param categoryElement Element * @param category {@link Category} * @param eventElement Element - * @return Pair eventPair + * @return Pair<JsonObject, TypeSpec.Builder> eventPair * @throws GenericHelper.TPTypeException If any used type is not Supported */ public Pair processEvent(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element eventElement) throws Exception { @@ -350,7 +350,7 @@ public Pair processEvent(RoundEnvironment roundEnv * @param action {@link Action} * @param jsonAction JsonObject * @param dataElement Element - * @return Pair dataPair + * @return Pair<JsonObject, TypeSpec.Builder> 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()); @@ -490,7 +490,7 @@ private Pair processActionData(RoundEnvironment ro * @param connector {@link Connector} * @param jsonConnector JsonObject * @param dataElement Element - * @return Pair dataPair + * @return Pair<JsonObject, TypeSpec.Builder> 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()); From a7f8bcc8c3381c4e8a0672d8cecb8de53079ec17 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Sun, 27 Nov 2022 03:00:31 +0100 Subject: [PATCH 26/41] fix: JavaDoc --- .../processor/CategoryProcessor.java | 3 ++- .../processor/PluginProcessor.java | 3 ++- .../processor/SettingProcessor.java | 2 +- ...TouchPortalPluginAnnotationsProcessor.java | 21 ++++++++++++------- 4 files changed, 19 insertions(+), 10 deletions(-) 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 index ec55530..b0ee179 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java @@ -26,8 +26,9 @@ public class CategoryProcessor { * @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 Exception { + 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); 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 index b837ebf..1e2705a 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java @@ -25,8 +25,9 @@ public class PluginProcessor { * @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 Exception { + 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); 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 index 939fd69..408fec3 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java @@ -19,7 +19,7 @@ public class SettingProcessor { * @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 Exception { + 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); 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 index 8b7a633..15ef8ab 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java @@ -127,8 +127,9 @@ public boolean process(Set annotations, RoundEnvironment * @param category {@link Category} * @param actionElement Element * @return Pair<JsonObject, TypeSpec.Builder> actionPair + * @throws GenericHelper.TPTypeException If a used type is not Supported */ - public Pair processAction(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element actionElement) throws Exception { + public Pair processAction(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element actionElement) throws GenericHelper.TPTypeException { this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Action: " + actionElement.getSimpleName()); Action action = actionElement.getAnnotation(Action.class); @@ -196,8 +197,10 @@ public Pair processAction(RoundEnvironment roundEn * @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 Pair processConnector(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element connectorElement) throws Exception { + public Pair processConnector(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element connectorElement) throws GenericHelper.TPTypeException, TPAnnotationException { this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Connector: " + connectorElement.getSimpleName()); Connector connector = connectorElement.getAnnotation(Connector.class); @@ -257,8 +260,9 @@ public Pair processConnector(RoundEnvironment roun * @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 Pair processState(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element stateElement) throws Exception { + public Pair processState(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element stateElement) throws GenericHelper.TPTypeException, TPAnnotationException { this.messager.printMessage(Diagnostic.Kind.NOTE, "Process State: " + stateElement.getSimpleName()); State state = stateElement.getAnnotation(State.class); @@ -301,9 +305,10 @@ else if (!desiredTPType.equals(StateHelper.TYPE_TEXT)) { * @param category {@link Category} * @param eventElement Element * @return Pair<JsonObject, TypeSpec.Builder> eventPair - * @throws GenericHelper.TPTypeException If any used type is not Supported + * @throws GenericHelper.TPTypeException If a used type is not Supported + * @throws TPAnnotationException If an Annotation is misused */ - public Pair processEvent(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element eventElement) throws Exception { + public Pair processEvent(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element eventElement) throws GenericHelper.TPTypeException, TPAnnotationException { this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Event: " + eventElement.getSimpleName()); State state = eventElement.getAnnotation(State.class); Event event = eventElement.getAnnotation(Event.class); @@ -351,8 +356,9 @@ public Pair processEvent(RoundEnvironment roundEnv * @param jsonAction JsonObject * @param dataElement Element * @return Pair<JsonObject, TypeSpec.Builder> dataPair + * @throws GenericHelper.TPTypeException If a used type is not Supported */ - private Pair processActionData(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element actionElement, Action action, JsonObject jsonAction, Element dataElement) throws Exception { + private Pair processActionData(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element actionElement, Action action, JsonObject jsonAction, Element dataElement) throws GenericHelper.TPTypeException { this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Action Data: " + dataElement.getSimpleName()); Data data = dataElement.getAnnotation(Data.class); @@ -491,8 +497,9 @@ private Pair processActionData(RoundEnvironment ro * @param jsonConnector JsonObject * @param dataElement Element * @return Pair<JsonObject, TypeSpec.Builder> dataPair + * @throws GenericHelper.TPTypeException If a used type is not Supported */ - private Pair processConnectorData(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element connectorElement, Connector connector, JsonObject jsonConnector, Element dataElement) throws Exception { + private Pair processConnectorData(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element connectorElement, Connector connector, JsonObject jsonConnector, Element dataElement) throws GenericHelper.TPTypeException { this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Connector Data: " + dataElement.getSimpleName()); Data data = dataElement.getAnnotation(Data.class); From 2f4cec16f765e253f12e550f570d16965fd27b7f Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Sun, 27 Nov 2022 03:04:16 +0100 Subject: [PATCH 27/41] fix: JavaDoc --- .../touchportal/annotations/processor/CategoryProcessor.java | 1 + .../touchportal/annotations/processor/PluginProcessor.java | 1 + .../touchportal/annotations/processor/SettingProcessor.java | 1 + 3 files changed, 3 insertions(+) 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 index b0ee179..ef32fdc 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java @@ -20,6 +20,7 @@ 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} 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 index 1e2705a..eb5182b 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java @@ -21,6 +21,7 @@ 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 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 index 408fec3..589f1c3 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java @@ -15,6 +15,7 @@ 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 From f47e49fe72a8e20a699f5a5f5449c83ef7ade9c6 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Sun, 27 Nov 2022 18:13:44 +0100 Subject: [PATCH 28/41] core: Refactor Annotations Processor --- .../processor/ActionProcessor.java | 88 +++ .../processor/CategoryProcessor.java | 8 +- .../processor/ConnectorProcessor.java | 81 +++ .../annotations/processor/DataProcessor.java | 168 ++++++ .../annotations/processor/EventProcessor.java | 69 +++ .../annotations/processor/StateProcessor.java | 66 +++ ...TouchPortalPluginAnnotationsProcessor.java | 510 +----------------- 7 files changed, 478 insertions(+), 512 deletions(-) create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/ActionProcessor.java create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/ConnectorProcessor.java create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/DataProcessor.java create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/EventProcessor.java create mode 100644 AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/StateProcessor.java 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 0000000..5eb5970 --- /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 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 index ef32fdc..acd990f 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/CategoryProcessor.java @@ -47,7 +47,7 @@ public static Pair process(TouchPortalPluginAnnota Action action = actionElement.getAnnotation(Action.class); String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id(); if (categoryId.equals(action.categoryId())) { - Pair actionResult = processor.processAction(roundEnv, pluginElement, plugin, categoryElement, category, actionElement); + Pair actionResult = ActionProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement, category, actionElement); jsonActions.add(actionResult.first); actionsTypeSpecBuilder.addType(actionResult.second.build()); } @@ -62,7 +62,7 @@ public static Pair process(TouchPortalPluginAnnota Connector connector = connectorElement.getAnnotation(Connector.class); String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id(); if (categoryId.equals(connector.categoryId())) { - Pair connectorResult = processor.processConnector(roundEnv, pluginElement, plugin, categoryElement, category, connectorElement); + Pair connectorResult = ConnectorProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement, category, connectorElement); jsonConnectors.add(connectorResult.first); connectorsTypeSpecBuilder.addType(connectorResult.second.build()); } @@ -77,7 +77,7 @@ public static Pair process(TouchPortalPluginAnnota State state = stateElement.getAnnotation(State.class); String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id(); if (categoryId.equals(state.categoryId())) { - Pair stateResult = processor.processState(roundEnv, pluginElement, plugin, categoryElement, category, stateElement); + Pair stateResult = StateProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement, category, stateElement); jsonStates.add(stateResult.first); statesTypeSpecBuilder.addType(stateResult.second.build()); } @@ -93,7 +93,7 @@ public static Pair process(TouchPortalPluginAnnota String categoryId = category.id().isEmpty() ? categoryElement.getSimpleName().toString() : category.id(); if (state != null) { if (categoryId.equals(state.categoryId())) { - Pair eventResult = processor.processEvent(roundEnv, pluginElement, plugin, categoryElement, category, eventElement); + Pair eventResult = EventProcessor.process(processor, roundEnv, pluginElement, plugin, categoryElement, category, eventElement); jsonEvents.add(eventResult.first); eventsTypeSpecBuilder.addType(eventResult.second.build()); } 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 0000000..45388fb --- /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 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 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 0000000..e1ebb7f --- /dev/null +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/DataProcessor.java @@ -0,0 +1,168 @@ +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 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 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 0000000..f5d7a44 --- /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/StateProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/StateProcessor.java new file mode 100644 index 0000000..2e39802 --- /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/TouchPortalPluginAnnotationsProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java index 15ef8ab..79bcd45 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/TouchPortalPluginAnnotationsProcessor.java @@ -70,6 +70,8 @@ public Set getSupportedAnnotationTypes() { 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()); @@ -116,512 +118,4 @@ public boolean process(Set annotations, RoundEnvironment return true; } - - /** - * 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<JsonObject, TypeSpec.Builder> actionPair - * @throws GenericHelper.TPTypeException If a used type is not Supported - */ - public Pair processAction(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element actionElement) throws GenericHelper.TPTypeException { - this.messager.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 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<JsonObject, TypeSpec.Builder> actionPair - * @throws GenericHelper.TPTypeException If a used type is not Supported - * @throws TPAnnotationException If an Annotation is misused - */ - public Pair processConnector(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element connectorElement) throws GenericHelper.TPTypeException, TPAnnotationException { - this.messager.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 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 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<JsonObject, TypeSpec.Builder> statePair - * @throws GenericHelper.TPTypeException If a used type is not Supported - * @throws TPAnnotationException If an Annotation is misused - */ - public Pair processState(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element stateElement) throws GenericHelper.TPTypeException, TPAnnotationException { - this.messager.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); - } - - /** - * 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<JsonObject, TypeSpec.Builder> eventPair - * @throws GenericHelper.TPTypeException If a used type is not Supported - * @throws TPAnnotationException If an Annotation is misused - */ - public Pair processEvent(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element eventElement) throws GenericHelper.TPTypeException, TPAnnotationException { - 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 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); - } - - /** - * 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<JsonObject, TypeSpec.Builder> dataPair - * @throws GenericHelper.TPTypeException If a used type is not Supported - */ - private Pair processActionData(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element actionElement, Action action, JsonObject jsonAction, Element dataElement) throws GenericHelper.TPTypeException { - this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Action Data: " + dataElement.getSimpleName()); - Data data = dataElement.getAnnotation(Data.class); - - TypeSpec.Builder actionDataTypeSpecBuilder = SpecUtils.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 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<JsonObject, TypeSpec.Builder> dataPair - * @throws GenericHelper.TPTypeException If a used type is not Supported - */ - private Pair processConnectorData(RoundEnvironment roundEnv, Element pluginElement, Plugin plugin, Element categoryElement, Category category, Element connectorElement, Connector connector, JsonObject jsonConnector, Element dataElement) throws GenericHelper.TPTypeException { - this.messager.printMessage(Diagnostic.Kind.NOTE, "Process Connector Data: " + dataElement.getSimpleName()); - Data data = dataElement.getAnnotation(Data.class); - - TypeSpec.Builder connectorDataTypeSpecBuilder = SpecUtils.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 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); - } } From 89f49fdfd6e12cc759a6ea01bf88a5edf16e1ec4 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Sun, 27 Nov 2022 20:25:10 +0100 Subject: [PATCH 29/41] core: Add logs in tests --- .../touchportal/TouchPortalPlugin.java | 4 +- .../touchportal/test/LibraryTests.java | 95 ++++++++++++++++++- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java index cc829bb..deb2940 100644 --- a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java +++ b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java @@ -1307,7 +1307,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() { @@ -1317,7 +1317,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/test/java/com/christophecvb/touchportal/test/LibraryTests.java b/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java index f2f8bf2..b271368 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,13 @@ 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; @@ -84,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 @@ -150,6 +158,7 @@ public void close() { @Test public void testConnection() { + LOGGER.log(Level.FINE, "Now"); try { Thread.sleep(REASONABLE_TIME); } @@ -162,6 +171,7 @@ public void testConnection() { @Test public void testConnectionFail() { + LOGGER.log(Level.FINE, "Now"); this.close(); boolean connectedPairedAndListening = this.touchPortalPluginTest.connectThenPairAndListen(null); @@ -177,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)); @@ -184,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); @@ -193,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); @@ -202,11 +215,13 @@ 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(REASONABLE_TIME); @@ -215,6 +230,7 @@ public void testServerSocketCloses() throws IOException, InterruptedException { @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")); @@ -227,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("", "")); @@ -240,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])); @@ -254,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)); @@ -273,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)); @@ -291,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)); @@ -299,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)); @@ -314,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)); @@ -363,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)); @@ -373,9 +397,11 @@ public void testSendFail() { @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); @@ -387,9 +413,11 @@ 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); @@ -401,10 +429,12 @@ 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); @@ -416,10 +446,12 @@ 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); @@ -431,6 +463,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!"); @@ -441,6 +474,7 @@ 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); @@ -458,6 +492,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); @@ -472,6 +507,7 @@ 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); @@ -483,6 +519,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); @@ -498,6 +535,7 @@ 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); @@ -508,11 +546,13 @@ public void testReceiveDummyWithDataFileAction() throws IOException, Interrupted } @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); @@ -523,11 +563,13 @@ public void testReceiveDummyDummyWithJsonObject() throws IOException, Interrupte } @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); @@ -538,11 +580,13 @@ public void testReceiveDummyDummyWithTPActionMessage() throws IOException, Inter } @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); @@ -554,12 +598,14 @@ 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(REASONABLE_TIME); @@ -570,6 +616,7 @@ 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(REASONABLE_TIME); @@ -582,12 +629,14 @@ 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(REASONABLE_TIME); @@ -600,6 +649,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(); @@ -618,6 +668,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(); @@ -629,6 +680,7 @@ 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); @@ -641,6 +693,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)); @@ -659,6 +712,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(); @@ -677,9 +731,11 @@ 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); + PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true); out.println(jsonMessage); @@ -691,10 +747,12 @@ 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); + PrintWriter out = new PrintWriter(this.serverSocketClient.getOutputStream(), true); out.println(jsonMessage); @@ -706,10 +764,12 @@ 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); @@ -721,9 +781,11 @@ 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); @@ -735,8 +797,10 @@ 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); @@ -748,9 +812,11 @@ 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); @@ -762,6 +828,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; @@ -800,9 +867,11 @@ 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); @@ -822,9 +891,11 @@ 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); @@ -837,6 +908,7 @@ 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"); @@ -848,6 +920,7 @@ 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(); @@ -859,6 +932,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"); @@ -874,25 +948,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(); @@ -923,10 +1002,12 @@ 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); @@ -938,6 +1019,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(); @@ -982,6 +1064,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"); @@ -992,6 +1075,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())); @@ -1071,6 +1155,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()); @@ -1099,11 +1184,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")); @@ -1112,6 +1199,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 @@ -1119,6 +1207,7 @@ public void testIsUpdateAvailable() { @Test public void testOAuth2() throws IOException { + LOGGER.log(Level.FINE, "Now"); String host = "localhost"; String callbackPath = "/oauth"; int port = -1; From bfd7e4ac2b4f506d0ea690fd472c1c9b3e5a3451 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Sun, 4 Dec 2022 22:42:27 +0100 Subject: [PATCH 30/41] core: (WIP) Modularization closes #54 --- .../touchportal/annotations/Action.java | 25 +- .../annotations/ActionTranslation.java | 1 + .../annotations/ActionTranslations.java | 4 + .../touchportal/annotations/Connector.java | 25 +- .../annotations/ConnectorValue.java | 2 +- .../touchportal/annotations/Data.java | 2 +- .../annotations/processor/DataProcessor.java | 1 + .../touchportal/helpers/ActionHelper.java | 19 ++ .../touchportal/helpers/ConnectorHelper.java | 31 +++ .../touchportal/helpers/DataHelper.java | 40 +++ .../christophecvb/touchportal/TPAction.java | 12 + .../touchportal/TPConnector.java | 8 + .../touchportal/TPInvokable.java | 15 + .../touchportal/TouchPortalPlugin.java | 260 ++++++++++++------ .../touchportal/model/TPActionMessage.java | 5 + .../model/TPConnectorChangeMessage.java | 5 + ...Message.java => TPListChangedMessage.java} | 2 +- .../touchportal/test/LibraryTests.java | 2 +- .../TouchPortalPluginTest/plugin.config | 2 +- README.md | 2 +- .../TouchPortalSampleJavaPlugin.java | 13 +- .../invokable/action/ExampleClassAction.java | 43 +++ .../connector/ExampleClassConnector.java | 39 +++ .../TouchPortalSampleKotlinPlugin.kt | 2 +- 24 files changed, 451 insertions(+), 109 deletions(-) create mode 100644 Library/src/main/java/com/christophecvb/touchportal/TPAction.java create mode 100644 Library/src/main/java/com/christophecvb/touchportal/TPConnector.java create mode 100644 Library/src/main/java/com/christophecvb/touchportal/TPInvokable.java rename Library/src/main/java/com/christophecvb/touchportal/model/{TPListChangeMessage.java => TPListChangedMessage.java} (94%) create mode 100644 SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/invokable/action/ExampleClassAction.java create mode 100644 SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/invokable/connector/ExampleClassConnector.java 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 ce829ac..31c279b 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 index 1830a22..6338933 100644 --- a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslation.java +++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslation.java @@ -29,6 +29,7 @@ *

*/ @Repeatable(ActionTranslations.class) +@Target({ElementType.METHOD, ElementType.TYPE}) public @interface ActionTranslation { /** * ActionTranslation Language diff --git a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslations.java b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslations.java index 3efd044..37bcd53 100644 --- a/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslations.java +++ b/Annotations/src/main/java/com/christophecvb/touchportal/annotations/ActionTranslations.java @@ -1,5 +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 d07ecfc..5aafdd0 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 de3eaa1..c1fdc12 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 1a57faf..a23a942 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/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/DataProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/DataProcessor.java index e1ebb7f..7d8af7c 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/DataProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/DataProcessor.java @@ -19,6 +19,7 @@ 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 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 907ae91..4ded488 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 6e0e20c..993075f 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 c9e0da8..6bce8c9 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/Library/src/main/java/com/christophecvb/touchportal/TPAction.java b/Library/src/main/java/com/christophecvb/touchportal/TPAction.java new file mode 100644 index 0000000..054a147 --- /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 0000000..8e76c7a --- /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 0000000..8a5ae7f --- /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 deb2940..def900f 100644 --- a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java +++ b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java @@ -30,9 +30,7 @@ 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; @@ -143,6 +141,10 @@ public abstract class TouchPortalPlugin { * Executor Service for callbacks */ private final ExecutorService callbacksExecutor; + /** + * Registered {@link TPInvokable}s + */ + private final HashMap> registeredInvokables = new HashMap<>(); /** * Internal Gson Serializer/Deserializer @@ -188,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_CHANGE, TPListChangedMessage.class); tpMessageDeserializer.registerTPMessageType(ReceivedMessageHelper.TYPE_BROADCAST, TPBroadcastMessage.class); tpMessageDeserializer.registerTPMessageType(ReceivedMessageHelper.TYPE_SETTINGS, TPSettingsMessage.class); tpMessageDeserializer.registerTPMessageType(ReceivedMessageHelper.TYPE_ACTION, TPActionMessage.class); @@ -233,7 +235,7 @@ private void onMessage(String socketMessage) throws SocketException, JsonParseEx break; case ReceivedMessageHelper.TYPE_LIST_CHANGE: - TPListChangeMessage listChangeMessage = (TPListChangeMessage) tpMessage; + TPListChangedMessage listChangeMessage = (TPListChangedMessage) tpMessage; if (this.touchPortalPluginListener != null) { this.touchPortalPluginListener.onListChanged(listChangeMessage); } @@ -320,105 +322,175 @@ 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 invokableClass = this.registeredInvokables.get(tpActionMessage.actionId); + try { + Class typedTouchPortalPlugin = (Class) ((ParameterizedType) invokableClass.getGenericSuperclass()).getActualTypeArguments()[0]; + Constructor constructor = invokableClass.getConstructor(typedTouchPortalPlugin); + TPInvokable tpInvokable = constructor.newInstance(this); + + 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); - } - finally { - if (held == null || !held) { - this.heldActionsStates.remove(tpActionMessage.actionId); + 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); + } + } + + tpInvokable.onInvoke(); + + invoked = true; + } + catch (ReflectiveOperationException e) { + TouchPortalPlugin.LOGGER.log(Level.SEVERE, "Action could not be created or invoked", 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, e.getMessage(), 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 invokableClass = this.registeredInvokables.get(tpConnectorChangeMessage.connectorId); + try { + Class typedTouchPortalPlugin = (Class) ((ParameterizedType) invokableClass.getGenericSuperclass()).getActualTypeArguments()[0]; + Constructor constructor = invokableClass.getConstructor(typedTouchPortalPlugin); + TPInvokable tpInvokable = constructor.newInstance(this); + + 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); + + tpInvokable.onInvoke(); + + invoked = true; + } + catch (ReflectiveOperationException e) { + TouchPortalPlugin.LOGGER.log(Level.SEVERE, "Action 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; } /** @@ -437,6 +509,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 invokableClass) { + this.registeredInvokables.put(invokableId, invokableClass); + } + /** * Get TPInfo * @@ -1264,9 +1346,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 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 ea6e1f0..2ecf1a4 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 ae5db53..b865a25 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 83b2c77..f024390 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 b271368..55e2cf3 100644 --- a/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java +++ b/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java @@ -71,7 +71,7 @@ public void onInfo(TPInfoMessage tpInfoMessage) { } @Override - public void onListChanged(TPListChangeMessage tpListChangeMessage) { + public void onListChanged(TPListChangedMessage tpListChangedMessage) { } @Override diff --git a/Library/src/test/resources/TouchPortalPluginTest/plugin.config b/Library/src/test/resources/TouchPortalPluginTest/plugin.config index 10fc40b..ec09544 100644 --- a/Library/src/test/resources/TouchPortalPluginTest/plugin.config +++ b/Library/src/test/resources/TouchPortalPluginTest/plugin.config @@ -1,4 +1,4 @@ #TouchPortalPluginTest -#Sat Nov 26 23:11:43 CET 2022 +#Sun Dec 04 22:41:39 CET 2022 samplekey=Sample Value plugin.version=1 diff --git a/README.md b/README.md index d06b1ae..2e76fbd 100644 --- a/README.md +++ b/README.md @@ -91,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 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 bdfeca5..14a5a97 100644 --- a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java +++ b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java @@ -25,6 +25,8 @@ import com.christophecvb.touchportal.helpers.PluginHelper; import com.christophecvb.touchportal.helpers.ReceivedMessageHelper; 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; @@ -98,14 +100,23 @@ 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); @@ -300,7 +311,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 0000000..d484e1e --- /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 0000000..8ffc387 --- /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/src/main/kotlin/com/christophecvb/touchportal/samplekotlin/TouchPortalSampleKotlinPlugin.kt b/SampleKotlin/src/main/kotlin/com/christophecvb/touchportal/samplekotlin/TouchPortalSampleKotlinPlugin.kt index 470fa00..d88020c 100644 --- a/SampleKotlin/src/main/kotlin/com/christophecvb/touchportal/samplekotlin/TouchPortalSampleKotlinPlugin.kt +++ b/SampleKotlin/src/main/kotlin/com/christophecvb/touchportal/samplekotlin/TouchPortalSampleKotlinPlugin.kt @@ -82,7 +82,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) {} From 00ca01dd90a36e41fce822068a173c80dd4495af Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Thu, 15 Dec 2022 01:56:07 +0100 Subject: [PATCH 31/41] doc: Update Samples and README.md --- README.md | 58 ++++++++++++------- .../TouchPortalSampleJavaPlugin.java | 16 +++-- .../TouchPortalSampleKotlinPlugin.kt | 19 +++--- 3 files changed, 57 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 2e76fbd..9a5241f 100644 --- a/README.md +++ b/README.md @@ -103,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 { @@ -125,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 { @@ -157,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 { @@ -174,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 { @@ -194,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 { //... @@ -212,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); } @@ -238,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 @@ -259,4 +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) +![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/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java index 14a5a97..c7f15fc 100644 --- a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java +++ b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java @@ -33,8 +33,13 @@ 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", parentCategory = ParentCategory.CONTENT) +@Plugin( + version = BuildConfig.VERSION_CODE, + colorDark = "#203060", + colorLight = "#4070F0", + name = BuildConfig.NAME, + parentCategory = ParentCategory.CONTENT +) public class TouchPortalSampleJavaPlugin extends TouchPortalPlugin implements TouchPortalPlugin.TouchPortalPluginListener { /** * Logger @@ -80,7 +85,7 @@ private enum Categories { * 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 @@ -122,7 +127,7 @@ public static void main(String... args) { 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"); @@ -147,7 +152,8 @@ 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); } } 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 d88020c..7e23c6c 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 From e775dcec812fee4b5416eaf7279524ff7e8fd959 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Fri, 16 Dec 2022 01:17:44 +0100 Subject: [PATCH 32/41] core: Call TPInvokable.onInvoke from callbacksExecutor --- .../touchportal/TouchPortalPlugin.java | 33 ++++++++++++++++--- README.md | 2 +- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java index def900f..1a75cd0 100644 --- a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java +++ b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java @@ -347,12 +347,25 @@ else if (declaredField.getType().isAssignableFrom(TPActionMessage.class)) { } } - tpInvokable.onInvoke(); + 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); + } + } + }); invoked = true; } catch (ReflectiveOperationException e) { - TouchPortalPlugin.LOGGER.log(Level.SEVERE, "Action could not be created or invoked", e); + TouchPortalPlugin.LOGGER.log(Level.WARNING, "Action could not be instanced", e); } } else { @@ -396,7 +409,7 @@ else if (parameter.getType().isAssignableFrom(TPActionMessage.class)) { invoked = true; } catch (MethodDataParameterException e) { - TouchPortalPlugin.LOGGER.log(Level.WARNING, e.getMessage(), e); + TouchPortalPlugin.LOGGER.log(Level.WARNING, "Action method data parameters could not be retrieved", e); } break; } @@ -436,12 +449,20 @@ else if (declaredField.getType().isAssignableFrom(TPConnectorChangeMessage.class } } - tpInvokable.onInvoke(); + 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.SEVERE, "Action could not be created or invoked", e); + TouchPortalPlugin.LOGGER.log(Level.WARNING, "Connector could not be created or invoked", e); } } else { @@ -470,6 +491,7 @@ else if (parameter.getType().isAssignableFrom(TPConnectorChangeMessage.class)) { throw new MethodDataParameterException(method, parameter); } } + this.currentConnectorValues.put(tpConnectorChangeMessage.getConstructedId(), tpConnectorChangeMessage.value); this.callbacksExecutor.submit(() -> { try { @@ -480,6 +502,7 @@ else if (parameter.getType().isAssignableFrom(TPConnectorChangeMessage.class)) { TouchPortalPlugin.LOGGER.log(Level.SEVERE, "Connector method could not be invoked", e); } }); + invoked = true; } catch (MethodDataParameterException e) { diff --git a/README.md b/README.md index 9a5241f..6445d72 100644 --- a/README.md +++ b/README.md @@ -164,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 } From d7f8526051577c49189f393900b100077ccf4d23 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 20 Dec 2022 02:05:07 +0100 Subject: [PATCH 33/41] core: TPInvokable.onListChanged core: Use callbacksExecutor --- .../helpers/ReceivedMessageHelper.java | 4 +- .../touchportal/TouchPortalPlugin.java | 59 ++++++++++++++----- .../touchportal/test/LibraryTests.java | 4 +- 3 files changed, 48 insertions(+), 19 deletions(-) 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 2af3ced..b6e7c96 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/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java index 1a75cd0..700c690 100644 --- a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java +++ b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java @@ -26,6 +26,7 @@ import com.christophecvb.touchportal.model.deserializer.TPMessageDeserializer; import com.google.gson.*; import okhttp3.*; +import org.jetbrains.annotations.NotNull; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; @@ -190,7 +191,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, TPListChangedMessage.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); @@ -230,21 +231,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: - TPListChangedMessage listChangeMessage = (TPListChangedMessage) tpMessage; + case ReceivedMessageHelper.TYPE_LIST_CHANGED: + TPListChangedMessage tpListChangedMessage = (TPListChangedMessage) tpMessage; + if (this.registeredInvokables.containsKey(tpListChangedMessage.actionId)) { + Class 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; @@ -254,14 +274,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; @@ -293,7 +317,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); + }); } } } @@ -303,6 +329,13 @@ private void onMessage(String socketMessage) throws SocketException, JsonParseEx } } + private TPInvokable instantiateTPInvokable(Class invokableClass) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + Class typedTouchPortalPlugin = (Class) ((ParameterizedType) invokableClass.getGenericSuperclass()).getActualTypeArguments()[0]; + Constructor 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()) { @@ -327,9 +360,7 @@ private boolean onActionReceived(TPActionMessage tpActionMessage, JsonObject jso if (this.registeredInvokables.containsKey(tpActionMessage.actionId)) { Class invokableClass = this.registeredInvokables.get(tpActionMessage.actionId); try { - Class typedTouchPortalPlugin = (Class) ((ParameterizedType) invokableClass.getGenericSuperclass()).getActualTypeArguments()[0]; - Constructor constructor = invokableClass.getConstructor(typedTouchPortalPlugin); - TPInvokable tpInvokable = constructor.newInstance(this); + TPInvokable tpInvokable = this.instantiateTPInvokable(invokableClass); for (Field declaredField : invokableClass.getDeclaredFields()) { if (declaredField.isAnnotationPresent(Data.class)) { @@ -425,9 +456,7 @@ private boolean onConnectorChangeReceived(TPConnectorChangeMessage tpConnectorCh if (this.registeredInvokables.containsKey(tpConnectorChangeMessage.connectorId)) { Class invokableClass = this.registeredInvokables.get(tpConnectorChangeMessage.connectorId); try { - Class typedTouchPortalPlugin = (Class) ((ParameterizedType) invokableClass.getGenericSuperclass()).getActualTypeArguments()[0]; - Constructor constructor = invokableClass.getConstructor(typedTouchPortalPlugin); - TPInvokable tpInvokable = constructor.newInstance(this); + TPInvokable tpInvokable = this.instantiateTPInvokable(invokableClass); for (Field declaredField : invokableClass.getDeclaredFields()) { if (declaredField.isAnnotationPresent(Data.class)) { 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 55e2cf3..d71df95 100644 --- a/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java +++ b/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java @@ -734,7 +734,7 @@ 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); @@ -751,7 +751,7 @@ public void testReceiveListChangeNoListener() throws IOException, InterruptedExc 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); From 2135ebbe10d51d5cee2fb6c78c3170f611c8ff1d Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 20 Dec 2022 16:11:26 +0100 Subject: [PATCH 34/41] core: Revert to System Java instalation --- .../com/christophecvb/touchportal/helpers/PluginHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 66febb3..34a590c 100644 --- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java +++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java @@ -59,7 +59,7 @@ public class PluginHelper { /** * Touch Portal Plugin System TP_JAVA_FILE */ - public static final String TP_JAVA = "%TP_JAVA_FILE%"; + public static final String TP_JAVA = "java"; /** * Touch Portal entry file */ From 5c77140b19cd01d07d1b92d22869a6225eeb0f2f Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 20 Dec 2022 17:05:50 +0100 Subject: [PATCH 35/41] core: Revert to System Java instalation --- .../annotations/processor/PluginProcessor.java | 8 ++++---- .../christophecvb/touchportal/helpers/PluginHelper.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) 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 index eb5182b..7ddbbdc 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java @@ -44,10 +44,10 @@ public static Pair process(TouchPortalPluginAnnota 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, PluginHelper.TP_JAVA + " -Dapple.awt.UIElement=true -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START); - jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND + PluginHelper.PLUGIN_START_COMMAND_SUFFIX_WIN, PluginHelper.TP_JAVA + " -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START); - jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND + PluginHelper.PLUGIN_START_COMMAND_SUFFIX_MACOS, PluginHelper.TP_JAVA + " -Dapple.awt.UIElement=true -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START); - jsonPlugin.addProperty(PluginHelper.PLUGIN_START_COMMAND + PluginHelper.PLUGIN_START_COMMAND_SUFFIX_LINUX, PluginHelper.TP_JAVA + " -jar ./" + pluginElement.getSimpleName() + ".jar " + PluginHelper.COMMAND_START); + 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(); 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 34a590c..66febb3 100644 --- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java +++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java @@ -59,7 +59,7 @@ public class PluginHelper { /** * Touch Portal Plugin System TP_JAVA_FILE */ - public static final String TP_JAVA = "java"; + public static final String TP_JAVA = "%TP_JAVA_FILE%"; /** * Touch Portal entry file */ From ee1d08995c2f21b2784968a9caa20e14612cd303 Mon Sep 17 00:00:00 2001 From: Christophe Carvalho Vilas-Boas Date: Tue, 20 Dec 2022 17:41:19 +0100 Subject: [PATCH 36/41] core: 9.0.0 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 0d0f82d..8bff204 100644 --- a/build.gradle +++ b/build.gradle @@ -4,8 +4,8 @@ allprojects { } } -ext.versionMajor = 8 -ext.versionMinor = 4 +ext.versionMajor = 9 +ext.versionMinor = 0 ext.versionPatch = 0 ext.isRelease = System.getenv('IS_RELEASE') == 'YES' From 170d48323f307078cba247fbe2d5cba1f4622f2b Mon Sep 17 00:00:00 2001 From: Pjiesco <55349095+Pjiesco@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:13:04 +0200 Subject: [PATCH 37/41] Feat: TriggerEvent (#63) --- .../helpers/SentMessageHelper.java | 3 ++ .../touchportal/TouchPortalPlugin.java | 31 ++++++++++++++++++- .../touchportal/test/LibraryTests.java | 13 ++++++++ .../TouchPortalSampleJavaPlugin.java | 6 ++++ 4 files changed, 52 insertions(+), 1 deletion(-) 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 a852b12..bfb1339 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,7 @@ 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 EVENT_ID = "eventId"; + public static final String STATES = "states"; } \ 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 700c690..9ba6233 100644 --- a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java +++ b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java @@ -26,7 +26,6 @@ import com.christophecvb.touchportal.model.deserializer.TPMessageDeserializer; import com.google.gson.*; import okhttp3.*; -import org.jetbrains.annotations.NotNull; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; @@ -1050,6 +1049,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 * 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 d71df95..dc16d5a 100644 --- a/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java +++ b/Library/src/test/java/com/christophecvb/touchportal/test/LibraryTests.java @@ -395,6 +395,19 @@ 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"); 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 c7f15fc..2737b69 100644 --- a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java +++ b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java @@ -30,6 +30,8 @@ import com.google.gson.JsonObject; import java.io.File; +import java.util.HashMap; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; @@ -155,6 +157,10 @@ public static void main(String... args) { } 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); } } } From 1964ce0f9cea3af972386b3c4233c986e3d757f1 Mon Sep 17 00:00:00 2001 From: Pjiesco <55349095+Pjiesco@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:14:49 +0200 Subject: [PATCH 38/41] Feat: forceUpdate (#64) Co-authored-by: Christophe Carvalho Vilas-Boas --- .../com/christophecvb/touchportal/helpers/SentMessageHelper.java | 1 + .../java/com/christophecvb/touchportal/TouchPortalPlugin.java | 1 + 2 files changed, 2 insertions(+) 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 bfb1339..afa854a 100644 --- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SentMessageHelper.java +++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/SentMessageHelper.java @@ -49,6 +49,7 @@ 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"; diff --git a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java index 9ba6233..7beec98 100644 --- a/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java +++ b/Library/src/main/java/com/christophecvb/touchportal/TouchPortalPlugin.java @@ -904,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()) { From cc5c91577aa8ec79dec329249b7971c34f5a9be7 Mon Sep 17 00:00:00 2001 From: Pjiesco <55349095+Pjiesco@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:56:03 +0200 Subject: [PATCH 39/41] Change `sdk` to `api` (#66) --- .../touchportal/annotations/processor/PluginProcessor.java | 2 +- .../com/christophecvb/touchportal/helpers/PluginHelper.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 index 7ddbbdc..f68dcde 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/PluginProcessor.java @@ -35,7 +35,7 @@ public static Pair process(TouchPortalPluginAnnota TypeSpec.Builder pluginTypeSpecBuilder = SpecUtils.createPluginTypeSpecBuilder(pluginElement, plugin); JsonObject jsonPlugin = new JsonObject(); - jsonPlugin.addProperty(PluginHelper.SDK, PluginHelper.TOUCH_PORTAL_PLUGIN_VERSION); + 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)); 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 66febb3..fc777ec 100644 --- a/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java +++ b/Helpers/src/main/java/com/christophecvb/touchportal/helpers/PluginHelper.java @@ -29,7 +29,7 @@ * 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; From ca95a1ffb65c8cd4a7a6db66b8774fded83f72b0 Mon Sep 17 00:00:00 2001 From: "lgtm-com[bot]" <43144390+lgtm-com[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 17:14:48 +0200 Subject: [PATCH 40/41] Add CodeQL workflow for GitHub code scanning (#53) Co-authored-by: LGTM Migrator --- .github/workflows/codeql.yml | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..2e7379e --- /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 }}" From b034dd9421ccc5c8de22daddf070ca9ebf9d5f77 Mon Sep 17 00:00:00 2001 From: Pjiesco <55349095+Pjiesco@users.noreply.github.com> Date: Thu, 26 Oct 2023 09:30:31 +0200 Subject: [PATCH 41/41] [API 7] Setting Tooltip (#65) * Feat: Setting Tooltip * fix: use java 8 * Remove empty lines * Check if empty * use UpperCamelCase instead of Full uppercase * Check if empty --- .../touchportal/annotations/Setting.java | 13 +++++++++++ .../processor/SettingProcessor.java | 16 ++++++++++++++ .../processor/utils/SpecUtils.java | 22 +++++++++++++++++++ .../touchportal/helpers/SettingHelper.java | 7 ++++++ .../TouchPortalSampleJavaPlugin.java | 7 ++++-- 5 files changed, 63 insertions(+), 2 deletions(-) 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 eded71f..6d276dc 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/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java index 589f1c3..6e27f22 100644 --- a/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java +++ b/AnnotationsProcessor/src/main/java/com/christophecvb/touchportal/annotations/processor/SettingProcessor.java @@ -34,6 +34,22 @@ public static Pair process(TouchPortalPluginAnnota 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) { 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 index c681c16..044022f 100644 --- 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 @@ -197,9 +197,31 @@ public static TypeSpec.Builder createSettingTypeSpecBuilder(Element settingEleme 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} * 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 e559e98..a78eff3 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/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java index 2737b69..0f96873 100644 --- a/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java +++ b/SampleJava/src/main/java/com/christophecvb/touchportal/samplejava/TouchPortalSampleJavaPlugin.java @@ -31,7 +31,6 @@ import java.io.File; import java.util.HashMap; -import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; @@ -80,7 +79,11 @@ 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; /**