Skip to content

Commit 65d9b25

Browse files
javier-godoypaodb
authored andcommitted
fix: implement specific interfaces in ElementalNode classes
Close #29
1 parent 6239275 commit 65d9b25

File tree

10 files changed

+279
-14
lines changed

10 files changed

+279
-14
lines changed

pom.xml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,30 @@
136136
</pluginManagement>
137137

138138
<plugins>
139+
<plugin>
140+
<groupId>org.codehaus.mojo</groupId>
141+
<artifactId>exec-maven-plugin</artifactId>
142+
<version>3.1.0</version>
143+
<executions>
144+
<execution>
145+
<phase>process-classes</phase>
146+
<goals>
147+
<goal>java</goal>
148+
</goals>
149+
<configuration>
150+
<mainClass>com.flowingcode.vaadin.jsonmigration.ElementalNodeAsmPostProcessor</mainClass>
151+
<arguments>
152+
<argument>${project.build.outputDirectory}/com/flowingcode/vaadin/jsonmigration/ElementalArrayNode.class</argument>
153+
<argument>${project.build.outputDirectory}/com/flowingcode/vaadin/jsonmigration/ElementalBooleanNode.class</argument>
154+
<argument>${project.build.outputDirectory}/com/flowingcode/vaadin/jsonmigration/ElementalNullNode.class</argument>
155+
<argument>${project.build.outputDirectory}/com/flowingcode/vaadin/jsonmigration/ElementalNumberNode.class</argument>
156+
<argument>${project.build.outputDirectory}/com/flowingcode/vaadin/jsonmigration/ElementalObjectNode.class</argument>
157+
<argument>${project.build.outputDirectory}/com/flowingcode/vaadin/jsonmigration/ElementalStringNode.class</argument>
158+
</arguments>
159+
</configuration>
160+
</execution>
161+
</executions>
162+
</plugin>
139163
<plugin>
140164
<groupId>org.apache.maven.plugins</groupId>
141165
<artifactId>maven-jar-plugin</artifactId>
@@ -216,6 +240,9 @@
216240
<quiet>true</quiet>
217241
<doclint>none</doclint>
218242
<failOnWarnings>true</failOnWarnings>
243+
<sourceFileExcludes>
244+
<sourceFileExclude>**/ElementalNodeAsmPostProcessor.java</sourceFileExclude>
245+
</sourceFileExcludes>
219246
<links>
220247
<link>https://javadoc.io/doc/com.vaadin/vaadin-platform-javadoc/${vaadin.version}</link>
221248
</links>
@@ -229,6 +256,8 @@
229256
<!-- Generated file that shouldn't be included in add-ons -->
230257
<excludes>
231258
<exclude>META-INF/VAADIN/config/flow-build-info.json</exclude>
259+
<exclude>com/flowingcode/vaadin/jsonmigration/ElementalNodeAsmPostProcessor.class</exclude>
260+
<exclude>com/flowingcode/vaadin/jsonmigration/ElementalNodeAsmPostProcessor$*.class</exclude>
232261
</excludes>
233262
</configuration>
234263
</plugin>

src/main/java/com/flowingcode/vaadin/jsonmigration/ElementalArrayNode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* #%L
33
* Json Migration Helper
44
* %%
5-
* Copyright (C) 2025 Flowing Code
5+
* Copyright (C) 2025 - 2026 Flowing Code
66
* %%
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@
2929
import tools.jackson.databind.node.JsonNodeFactory;
3030

3131
@SuppressWarnings("serial")
32-
class ElementalArrayNode extends ArrayNode implements UnsupportedJsonValueImpl {
32+
class ElementalArrayNode extends ArrayNode implements UnsupportedJsonValueImpl<JsonArray> {
3333

3434
public ElementalArrayNode(JsonArray a) {
3535
super(JsonNodeFactory.instance, children(a));

src/main/java/com/flowingcode/vaadin/jsonmigration/ElementalBooleanNode.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* #%L
33
* Json Migration Helper
44
* %%
5-
* Copyright (C) 2025 Flowing Code
5+
* Copyright (C) 2025 - 2026 Flowing Code
66
* %%
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
@@ -19,11 +19,12 @@
1919
*/
2020
package com.flowingcode.vaadin.jsonmigration;
2121

22+
import elemental.json.JsonBoolean;
2223
import elemental.json.JsonType;
2324
import tools.jackson.databind.node.BooleanNode;
2425

2526
@SuppressWarnings("serial")
26-
class ElementalBooleanNode extends BooleanNode implements UnsupportedJsonValueImpl {
27+
class ElementalBooleanNode extends BooleanNode implements UnsupportedJsonValueImpl<JsonBoolean> {
2728

2829
public ElementalBooleanNode(boolean value) {
2930
super(value);
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*-
2+
* #%L
3+
* Json Migration Helper
4+
* %%
5+
* Copyright (C) 2025 - 2026 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.flowingcode.vaadin.jsonmigration;
21+
22+
import java.nio.file.Files;
23+
import java.nio.file.Path;
24+
import java.nio.file.Paths;
25+
import java.util.Arrays;
26+
import java.util.Optional;
27+
import lombok.NonNull;
28+
import lombok.SneakyThrows;
29+
import org.objectweb.asm.ClassReader;
30+
import org.objectweb.asm.ClassVisitor;
31+
import org.objectweb.asm.ClassWriter;
32+
import org.objectweb.asm.Opcodes;
33+
import org.objectweb.asm.Type;
34+
import org.objectweb.asm.signature.SignatureReader;
35+
import org.objectweb.asm.signature.SignatureVisitor;
36+
37+
/**
38+
* Dynamically modifies the class header to implement the JSON interface specified in the
39+
* UnsupportedJsonValueImpl<T> generic argument.
40+
*/
41+
public class ElementalNodeAsmPostProcessor {
42+
43+
public static void main(String[] args) throws Exception {
44+
for (String arg : args) {
45+
Path classPath = Paths.get(arg);
46+
byte[] b = Files.readAllBytes(classPath);
47+
48+
ClassReader cr = new ClassReader(b);
49+
ClassWriter cw = new ClassWriter(cr, 0);
50+
ClassVisitorImpl transformer = new ClassVisitorImpl(cw);
51+
52+
cr.accept(transformer, 0);
53+
54+
if (transformer.modified) {
55+
Files.write(classPath, cw.toByteArray());
56+
System.out.println("Successfully patched: " + classPath.getFileName());
57+
}
58+
}
59+
}
60+
61+
private static class ClassVisitorImpl extends ClassVisitor {
62+
63+
private final static String TARGET_INTERFACE =
64+
Type.getInternalName(UnsupportedJsonValueImpl.class);
65+
66+
boolean modified;
67+
68+
public ClassVisitorImpl(ClassVisitor cv) {
69+
super(Opcodes.ASM9, cv);
70+
}
71+
72+
@Override
73+
@SneakyThrows
74+
public void visit(int version, int access, String name, String signature, String superName,
75+
String[] interfaces) {
76+
String detectedInterface = detectInterface(signature);
77+
78+
for (String intf : interfaces) {
79+
if (intf.equals(detectedInterface)) {
80+
return;
81+
}
82+
}
83+
84+
modified = true;
85+
interfaces = Arrays.copyOf(interfaces, interfaces.length + 1);
86+
interfaces[interfaces.length - 1] = detectedInterface;
87+
super.visit(version, access, name, signature, superName, interfaces);
88+
}
89+
90+
private String detectInterface(@NonNull String signature) {
91+
// Extracts the internal name of the specific generic type argument 'T' from
92+
// the class signature implementing UnsupportedJsonValueImpl<T>.
93+
String[] detectedInterface = new String[1];
94+
SignatureReader reader = new SignatureReader(signature);
95+
reader.accept(new SignatureVisitor(Opcodes.ASM9) {
96+
private boolean insideTargetInterface = false;
97+
98+
@Override
99+
public SignatureVisitor visitTypeArgument(char wildcard) {
100+
// Move into the <T> block
101+
return super.visitTypeArgument(wildcard);
102+
}
103+
104+
@Override
105+
public SignatureVisitor visitInterface() {
106+
return this;
107+
}
108+
109+
@Override
110+
public void visitClassType(String name) {
111+
if (name.equals(TARGET_INTERFACE)) {
112+
insideTargetInterface = true;
113+
} else if (insideTargetInterface && detectedInterface[0] == null) {
114+
// This is the first class type found AFTER UnsupportedJsonValueImpl
115+
// which represents the generic argument T
116+
detectedInterface[0] = name;
117+
insideTargetInterface = false; // Stop looking
118+
}
119+
}
120+
121+
@Override
122+
public void visitEnd() {
123+
insideTargetInterface = false;
124+
super.visitEnd();
125+
}
126+
127+
});
128+
return Optional.ofNullable(detectedInterface[0])
129+
.orElseThrow(() -> new IllegalArgumentException("Failed to extract interface"));
130+
}
131+
132+
@Override
133+
public void visitEnd() {
134+
super.visitEnd();
135+
}
136+
137+
}
138+
139+
}

src/main/java/com/flowingcode/vaadin/jsonmigration/ElementalNullNode.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* #%L
33
* Json Migration Helper
44
* %%
5-
* Copyright (C) 2025 Flowing Code
5+
* Copyright (C) 2025 - 2026 Flowing Code
66
* %%
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
@@ -19,11 +19,12 @@
1919
*/
2020
package com.flowingcode.vaadin.jsonmigration;
2121

22+
import elemental.json.JsonNull;
2223
import elemental.json.JsonType;
2324
import tools.jackson.databind.node.NullNode;
2425

2526
@SuppressWarnings("serial")
26-
class ElementalNullNode extends NullNode implements UnsupportedJsonValueImpl {
27+
class ElementalNullNode extends NullNode implements UnsupportedJsonValueImpl<JsonNull> {
2728

2829
public ElementalNullNode() {
2930
super();

src/main/java/com/flowingcode/vaadin/jsonmigration/ElementalNumberNode.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* #%L
33
* Json Migration Helper
44
* %%
5-
* Copyright (C) 2025 Flowing Code
5+
* Copyright (C) 2025 - 2026 Flowing Code
66
* %%
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
@@ -19,11 +19,12 @@
1919
*/
2020
package com.flowingcode.vaadin.jsonmigration;
2121

22+
import elemental.json.JsonNumber;
2223
import elemental.json.JsonType;
2324
import tools.jackson.databind.node.DoubleNode;
2425

2526
@SuppressWarnings("serial")
26-
class ElementalNumberNode extends DoubleNode implements UnsupportedJsonValueImpl {
27+
class ElementalNumberNode extends DoubleNode implements UnsupportedJsonValueImpl<JsonNumber> {
2728

2829
public ElementalNumberNode(double value) {
2930
super(value);

src/main/java/com/flowingcode/vaadin/jsonmigration/ElementalObjectNode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* #%L
33
* Json Migration Helper
44
* %%
5-
* Copyright (C) 2025 Flowing Code
5+
* Copyright (C) 2025 - 2026 Flowing Code
66
* %%
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@
3131
import tools.jackson.databind.node.ObjectNode;
3232

3333
@SuppressWarnings("serial")
34-
class ElementalObjectNode extends ObjectNode implements UnsupportedJsonValueImpl {
34+
class ElementalObjectNode extends ObjectNode implements UnsupportedJsonValueImpl<JsonObject> {
3535

3636
public ElementalObjectNode(JsonObject o) {
3737
super(JsonNodeFactory.instance, children(o));

src/main/java/com/flowingcode/vaadin/jsonmigration/ElementalStringNode.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* #%L
33
* Json Migration Helper
44
* %%
5-
* Copyright (C) 2025 Flowing Code
5+
* Copyright (C) 2025 - 2026 Flowing Code
66
* %%
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
@@ -19,11 +19,12 @@
1919
*/
2020
package com.flowingcode.vaadin.jsonmigration;
2121

22+
import elemental.json.JsonString;
2223
import elemental.json.JsonType;
2324
import tools.jackson.databind.node.StringNode;
2425

2526
@SuppressWarnings("serial")
26-
class ElementalStringNode extends StringNode implements UnsupportedJsonValueImpl {
27+
class ElementalStringNode extends StringNode implements UnsupportedJsonValueImpl<JsonString> {
2728

2829
public ElementalStringNode(String value) {
2930
super(value);

src/main/java/com/flowingcode/vaadin/jsonmigration/UnsupportedJsonValueImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* #%L
33
* Json Migration Helper
44
* %%
5-
* Copyright (C) 2025 Flowing Code
5+
* Copyright (C) 2025 - 2026 Flowing Code
66
* %%
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@
2222
import elemental.json.JsonValue;
2323
import tools.jackson.databind.JsonNode;
2424

25-
interface UnsupportedJsonValueImpl extends JsonValue {
25+
interface UnsupportedJsonValueImpl<T extends JsonValue> extends JsonValue {
2626

2727
@Override
2828
default boolean asBoolean() {

0 commit comments

Comments
 (0)