Skip to content

Commit 1d86ce5

Browse files
committed
improved schema handling in editor, new test cases for schema added (with potential issues as TODO)
1 parent fce546f commit 1d86ce5

File tree

7 files changed

+286
-20
lines changed

7 files changed

+286
-20
lines changed

jackson/src/main/java/eu/mihosoft/vmf/jackson/VMFJsonSchemaGenerator.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ private Map<String, Object> getPropertySchema(Property property) {
221221
Type elementType = VMFTypeUtils.forClass(property.getType().getName());
222222
if (!VMFTypeUtils.getSubTypes(elementType).isEmpty()) {
223223

224+
// TODO check if choices between different types are correctly detected
225+
224226
var typesToChooseFrom = VMFTypeUtils.getSubTypes(elementType);
225227
typesToChooseFrom.add(elementType);
226228
typesToChooseFrom.removeIf(Type::isInterfaceOnly);

jackson/src/main/java/eu/mihosoft/vmf/jackson/VMFTypeUtils.java

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,47 @@ public static Type forClass(Class<?> cls) {
8383
return Type.newInstance(false, false, isInterfaceOnlyType, cls.getName(), cls);
8484
}
8585

86+
8687
try {
8788
Class<?> builderClass = VMFTypeUtils.getBuilderClass(cls);
88-
Object builder = builderClass.getDeclaredMethod("newInstance").invoke(null);
8989

90-
// Build the final object
91-
Method buildMethod = builderClass.getDeclaredMethod("build");
92-
var obj = (VObject) buildMethod.invoke(builder);
90+
// check if builder has newInstance method
91+
Method newInstanceMethod = null;
92+
93+
try {
94+
newInstanceMethod = builderClass.getDeclaredMethod("newInstance");
95+
} catch (NoSuchMethodException e) {
96+
// ignore
97+
}
98+
99+
// if not there, try a different approach
100+
if(newInstanceMethod == null) {
101+
//
102+
// we check type() method on original class
103+
Method typeMethod = null;
104+
105+
try {
106+
typeMethod = cls.getDeclaredMethod("type");
107+
} catch (NoSuchMethodException e) {
108+
// ignore
109+
}
93110

94-
return obj.vmf().reflect().type();
111+
if(typeMethod == null) {
112+
throw new RuntimeException("Could not find newInstance method in builder class and type method in original class.");
113+
}
114+
115+
// get the type object
116+
return (Type) typeMethod.invoke(null);
117+
} else {
118+
119+
Object builder = builderClass.getDeclaredMethod("newInstance").invoke(null);
120+
121+
// Build the final object
122+
Method buildMethod = builderClass.getDeclaredMethod("build");
123+
var obj = (VObject) buildMethod.invoke(builder);
124+
125+
return obj.vmf().reflect().type();
126+
}
95127

96128
} catch (Exception e) {
97129
throw new RuntimeException(e);

jackson/src/test/java/eu/mihosoft/vmf/jackson/test/simple/PolymorphicModelTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
55
import eu.mihosoft.vmf.jackson.VMFJacksonModule;
6+
import eu.mihosoft.vmf.jackson.VMFJsonSchemaGenerator;
67
import org.junit.jupiter.api.Assertions;
78

89
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -190,6 +191,13 @@ void testModel() {
190191
// e.printStackTrace();
191192
// }
192193

194+
// write schema to string
195+
var schemaString = VMFJsonSchemaGenerator.newInstance(VMFJacksonModule.RUNTIME_TYPE.EXPERIMENTAL)
196+
.generateSchemaAsString(MyModel.class);
197+
198+
System.out.println("Schema:");
199+
System.out.println(schemaString);
200+
193201
}
194202

195203
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package eu.mihosoft.vmf.jackson.test.story_graph_01.json_spec_test_01;
2+
3+
4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import eu.mihosoft.vmf.jackson.VMFJacksonModule;
7+
import eu.mihosoft.vmf.jackson.VMFJsonSchemaGenerator;
8+
9+
import eu.mihosoft.vmf.jackson.test.story_graph_01.*;
10+
11+
import java.io.IOException;
12+
import java.nio.file.Files;
13+
import java.nio.file.Path;
14+
import java.util.Map;
15+
16+
public class SchemaTest01 {
17+
18+
@org.junit.jupiter.api.Test
19+
void testModelSpec() throws IOException {
20+
Map<String, Object> schema = VMFJsonSchemaGenerator.newInstance(VMFJacksonModule.RUNTIME_TYPE.EXPERIMENTAL)
21+
// .withTypeAlias("person", Person.class.getName())
22+
// .withTypeAlias("employee", Employee.class.getName())
23+
// .withTypeAlias("my-model", MyModel.class.getName())
24+
.generateSchema(StoryNode.class);
25+
ObjectMapper schemaMapper = new ObjectMapper();
26+
String jsonSchema = schemaMapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema);
27+
28+
System.out.println(jsonSchema);
29+
30+
// write schema to file
31+
Path schemaPath = Path.of("story-node-schema.json");
32+
Files.writeString(schemaPath, jsonSchema);
33+
}
34+
35+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package eu.mihosoft.vmf.jackson.test.story_graph_01.vmfmodel;
2+
3+
import eu.mihosoft.vmf.core.Contains;
4+
import eu.mihosoft.vmf.core.DelegateTo;
5+
import eu.mihosoft.vmf.core.Doc;
6+
import eu.mihosoft.vmf.core.InterfaceOnly;
7+
8+
import javax.management.Descriptor;
9+
10+
@InterfaceOnly
11+
interface WithId {
12+
@Doc
13+
String getId();
14+
}
15+
16+
@InterfaceOnly
17+
interface WithName {
18+
String getName();
19+
}
20+
21+
@InterfaceOnly
22+
interface WithDescription {
23+
String getDescription();
24+
}
25+
26+
@InterfaceOnly
27+
interface WithText {
28+
String getText();
29+
}
30+
31+
@InterfaceOnly
32+
interface WithResources {
33+
@Contains
34+
Resource[] getResources();
35+
}
36+
37+
@InterfaceOnly
38+
interface WithChoice {
39+
@Contains
40+
Choice getChoice();
41+
}
42+
43+
@InterfaceOnly
44+
interface WithMedia {
45+
String getMedia();
46+
}
47+
48+
@InterfaceOnly
49+
interface WithEvents {
50+
@Contains
51+
Event[] getEvents();
52+
}
53+
54+
@InterfaceOnly
55+
interface WithTransitions {
56+
@Contains
57+
String[] getTransitions();
58+
}
59+
60+
@InterfaceOnly
61+
interface WithOnEntryUpdates {
62+
@Contains
63+
Update[] getOnEntryUpdates();
64+
}
65+
66+
@InterfaceOnly
67+
interface WithOnEntryConditions {
68+
@Contains
69+
Condition[] getOnEntryConditions();
70+
}
71+
72+
@InterfaceOnly
73+
interface WithNodes {
74+
@Contains
75+
StoryNode[] getNodes();
76+
}
77+
78+
@Doc
79+
interface StoryNode extends WithId, WithName, WithDescription, WithText, WithResources, WithEvents, WithTransitions,
80+
WithOnEntryUpdates, WithOnEntryConditions, WithChoice {
81+
@Contains
82+
State getState();
83+
@Contains
84+
StoryNode[] getNodes();
85+
86+
String getId();
87+
String getName();
88+
String getDescription();
89+
90+
String getText();
91+
92+
@Contains
93+
Resource[] getResources();
94+
95+
@Contains
96+
Update[] getOnEntryUpdates();
97+
98+
@Contains
99+
Condition[] getOnEntryConditions();
100+
101+
@Contains
102+
Choice getChoice();
103+
104+
@Contains
105+
Event[] getEvents();
106+
107+
String[] getTransitions();
108+
}
109+
110+
111+
interface Resource extends WithId, WithName, WithDescription, WithText, WithMedia {
112+
String getId();
113+
String getName();
114+
String getDescription();
115+
116+
String getText();
117+
String getMedia();
118+
}
119+
120+
121+
interface Event extends WithId, WithName, WithDescription, WithText, WithResources {
122+
String getId();
123+
String getName();
124+
String getDescription();
125+
126+
String getText();
127+
@Contains
128+
Resource[] getResources();
129+
}
130+
131+
interface Choice extends WithText {
132+
String getText();
133+
}
134+
135+
136+
// TODO options about extending types not fully working in schema
137+
interface Update {
138+
139+
}
140+
141+
interface StateUpdate extends Update {
142+
String getKey();
143+
String getValueUpdate();
144+
}
145+
146+
147+
// TODO options about extending types not fully working in schema
148+
interface Condition {
149+
150+
}
151+
152+
interface StateCondition extends Condition {
153+
String getKey();
154+
String getValue();
155+
String getOperator();
156+
}
157+
158+
159+
//@DelegateTo(className = "StateBehavior")
160+
interface State {
161+
// <T> T fromStateByKey(String key);
162+
//
163+
// void toStateByKey(String key, Object value);
164+
//
165+
// void removeStateByKey(String key);
166+
//
167+
// void clearState();
168+
//
169+
// boolean containsByKey(String key);
170+
}

vmfedit/src/main/java/eu/mihosoft/vmf/vmfedit/JsonEditorAppController.java

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package eu.mihosoft.vmf.vmfedit;
22

3+
import javafx.application.Platform;
34
import javafx.fxml.FXML;
45
import javafx.scene.control.*;
56
import javafx.scene.web.WebView;
@@ -114,28 +115,41 @@ private void handleLoadDocument() {
114115

115116
JsonUtils.SchemaInfo schemaInfo = JsonUtils.extractSchema(content, baseURI);
116117

117-
this.schemaInfo = schemaInfo;
118-
119118
if (schemaInfo.schemaUri() != null) {
120119
schemaField.setDisable(true);
121120
browseSchemaButton.setDisable(true);
122121
schemaField.setText(schemaInfo.schemaUri().toString());
122+
this.schemaInfo = schemaInfo;
123123
} else if (schemaInfo.schemaContent() != null) {
124124
schemaField.setDisable(true);
125125
browseSchemaButton.setDisable(true);
126126
jsonEditorControl.setSchema(schemaInfo.schemaContent());
127+
this.schemaInfo = schemaInfo;
127128
} else {
128129
schemaField.setDisable(false);
129130
browseSchemaButton.setDisable(false);
130-
jsonEditorControl.setSchema(null);
131+
if(this.schemaInfo != null && this.schemaInfo.schemaUri() != null) {
132+
// convert to local filename, removing file:: prefix
133+
var url = this.schemaInfo.schemaUri().toURL().toString();
134+
if (url.startsWith("file:")) {
135+
url = url.substring(5);
136+
}
137+
schemaField.setText(url);
138+
} else {
139+
jsonEditorControl.setSchema(null);
140+
}
131141
}
132142

133-
jsonEditorControl.setValue(content);
143+
String finalContent = content;
144+
145+
Platform.runLater(() -> {
146+
jsonEditorControl.setValue(finalContent);
147+
currentFile = file;
148+
// get stage and set title
149+
Stage stage = (Stage) webView.getScene().getWindow();
150+
stage.setTitle("VMF JSON Editor - " + currentFile.getName());
151+
});
134152

135-
currentFile = file;
136-
// get stage and set title
137-
Stage stage = (Stage) webView.getScene().getWindow();
138-
stage.setTitle("VMF JSON Editor - " + currentFile.getName());
139153
} catch (IOException e) {
140154
showError("Error loading document", e.getMessage());
141155
}
@@ -169,8 +183,10 @@ private void handleSaveDocument() {
169183
return;
170184
} else {
171185
FileChooser fileChooser = new FileChooser();
172-
FileChooser.ExtensionFilter extFilterJSON = new FileChooser.ExtensionFilter("JSON files (*.json)", "*.json");
173-
FileChooser.ExtensionFilter extFilterALL = new FileChooser.ExtensionFilter("All files (*.*)", "*.*");
186+
FileChooser.ExtensionFilter extFilterJSON =
187+
new FileChooser.ExtensionFilter("JSON files (*.json)", "*.json");
188+
FileChooser.ExtensionFilter extFilterALL =
189+
new FileChooser.ExtensionFilter("All files (*.*)", "*.*");
174190
fileChooser.getExtensionFilters().addAll(extFilterJSON, extFilterALL);
175191
File file = fileChooser.showSaveDialog(webView.getScene().getWindow());
176192
if (file != null) {
@@ -199,7 +215,8 @@ private void handleSaveAsDocument() {
199215
fileChooser.setInitialDirectory(currentFile.getParentFile());
200216
}
201217

202-
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("JSON files (*.json)", "*.json");
218+
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(
219+
"JSON files (*.json)", "*.json");
203220
fileChooser.getExtensionFilters().add(extFilter);
204221
fileChooser.setTitle("Save JSON Document as");
205222
File file = fileChooser.showSaveDialog(webView.getScene().getWindow());
@@ -247,13 +264,15 @@ private void handleBrowseSchema() {
247264

248265
fileChooser.setTitle("Open JSON Schema");
249266
// set json extension filter
250-
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("JSON Schema files (*.json)", "*.json");
267+
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(
268+
"JSON Schema files (*.json)", "*.json");
251269
fileChooser.getExtensionFilters().add(extFilter);
252270
File file = fileChooser.showOpenDialog(webView.getScene().getWindow());
253271
if (file != null) {
254272
schemaField.setText(file.getAbsolutePath());
255273
// update schemaInfo
256-
this.schemaInfo = new JsonUtils.SchemaInfo(file.toURI(), null, JsonUtils.SchemaEmbeddingType.EXTERNAL);
274+
this.schemaInfo = new JsonUtils.SchemaInfo(
275+
file.toURI(), null, JsonUtils.SchemaEmbeddingType.EXTERNAL);
257276
}
258277
}
259278

0 commit comments

Comments
 (0)