Skip to content

Commit 3791095

Browse files
committed
collections
1 parent 9970d1a commit 3791095

File tree

9 files changed

+275
-74
lines changed

9 files changed

+275
-74
lines changed

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,17 @@ An example project can be found in the directory `examples/maven-example`.
8080
* Enable annotation processing for this project under `Settings`>`Build, Execution, Deployment`>`Compiler`>`Annotation Processors`>`Maven default annotation processors profile`>`json-parser-maven-example`>Enable Annotation Processing`
8181
* Run `TestClass` in `examples/maven-example/src/main/java/io/github/danthe1st/json_compile/test/TestClass`
8282

83-
### Limitations
83+
### Supported types
84+
* `String`
85+
* `int`
86+
* `long`
87+
* Wrapper classes for supported primitive types
88+
* Objects of classes annotated with `@GenerateJSON`
89+
* `Collection`s if they are not part of other collections or arrays
90+
* Arrays
8491

85-
* Currently, the only supported data types `String`, `int`, objects and arrays.
86-
* Generics are not supported in any way
92+
### Limitations
93+
* It is not possible to create an array/collection of collections
94+
* Generic objects are not supported (except generic collections)
8795
* Eclipse may not detect the annotation processor
8896
* Compile-time JSON-parser is not yet published to maven central so you will have to build it by yourself.

examples/maven-example/src/main/java/io/github/danthe1st/json_compile/test/TestClass.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package io.github.danthe1st.json_compile.test;
22

3+
import io.github.danthe1st.json_compile.api.GenerateJSON;
4+
35
import java.io.IOException;
46
import java.nio.file.Files;
57
import java.nio.file.Path;
68
import java.util.Arrays;
79

8-
import org.json.JSONObject;
9-
10-
import io.github.danthe1st.json_compile.api.GenerateJSON;
11-
1210
@GenerateJSON
1311
public class TestClass {
1412

src/main/java/io/github/danthe1st/json_compile/impl/ClassWriter.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,17 @@ public void endIf() throws IOException {
170170
endBlock();
171171
}
172172

173+
public void beginForEach(String typeName, String variableName, String iterable) throws IOException {
174+
writer.write("for (");
175+
writer.write(typeName);
176+
writer.write(" ");
177+
writer.write(variableName);
178+
writer.write(" : ");
179+
writer.write(iterable);
180+
writer.write(")");
181+
beginBlock();
182+
}
183+
173184
public void beginSimpleFor(String init,String condition,String advancement) throws IOException{
174185
writer.write("for (");
175186
writer.write(init);
@@ -184,13 +195,17 @@ public void beginSimpleFor(String init,String condition,String advancement) thro
184195
public void endFor() throws IOException{
185196
endBlock();
186197
}
187-
198+
199+
public void addStatement(String stmt) throws IOException {
200+
writer.write(stmt);
201+
endStatement();
202+
}
188203
private void endStatement() throws IOException {
189204
writer.write(";");
190205
nextLine();
191206
}
207+
192208
//endregion
193-
194209
public void beginBlock() throws IOException {
195210
writer.write("{");
196211
indentation++;
@@ -205,14 +220,14 @@ private void nextLine() throws IOException {
205220
writer.write(System.lineSeparator());
206221
indent();
207222
}
223+
208224
private void indent() throws IOException {
209225
writer.write("\t".repeat(indentation));
210226
}
211-
227+
212228
@Override
213229
public void close() throws IOException {
214-
writer.close();
230+
writer.close();
215231
}
216232

217-
218233
}

src/main/java/io/github/danthe1st/json_compile/impl/JSONCreator.java

Lines changed: 121 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,37 @@
22

33
import java.io.BufferedWriter;
44
import java.io.IOException;
5-
import java.util.ArrayList;
6-
import java.util.List;
7-
import java.util.Map;
8-
import java.util.Set;
5+
import java.util.*;
96

107
import javax.annotation.processing.AbstractProcessor;
118
import javax.annotation.processing.RoundEnvironment;
129
import javax.annotation.processing.SupportedAnnotationTypes;
1310
import javax.annotation.processing.SupportedSourceVersion;
1411
import javax.lang.model.SourceVersion;
15-
import javax.lang.model.element.Element;
16-
import javax.lang.model.element.ElementKind;
17-
import javax.lang.model.element.ExecutableElement;
18-
import javax.lang.model.element.Modifier;
19-
import javax.lang.model.element.TypeElement;
20-
import javax.lang.model.element.VariableElement;
12+
import javax.lang.model.element.*;
2113
import javax.lang.model.type.ArrayType;
2214
import javax.lang.model.type.DeclaredType;
2315
import javax.lang.model.type.TypeKind;
2416
import javax.lang.model.type.TypeMirror;
2517
import javax.tools.Diagnostic.Kind;
2618

19+
import io.github.danthe1st.json_compile.impl.data.*;
2720
import org.json.JSONArray;
2821
import org.json.JSONObject;
2922

3023
import javax.tools.JavaFileObject;
3124

3225
import io.github.danthe1st.json_compile.api.GenerateJSON;
33-
import io.github.danthe1st.json_compile.impl.data.JSONOperation;
34-
import io.github.danthe1st.json_compile.impl.data.JSONOperation.OperationType;
35-
import io.github.danthe1st.json_compile.impl.data.VariableDefinition;
3626

3727
@SupportedAnnotationTypes("io.github.danthe1st.json_compile.api.GenerateJSON")
3828
@SupportedSourceVersion(SourceVersion.RELEASE_11)
3929
public class JSONCreator extends AbstractProcessor {
4030

4131
private static final String JSONOBJECT_PARAM_NAME = "data";
4232

43-
private static final Map <String, String> simpleAssignments = Map.of("java.lang.String", "String", "int", "Int", "long", "Long");
33+
private static final Map <String, String> simpleAssignments = Map.of("java.lang.String", "String",
34+
"int", "Int","java.lang.Integer","Int",
35+
"long", "Long","java.lang.Long","Long");
4436

4537
@Override
4638
public boolean process(Set <? extends TypeElement> annotations, RoundEnvironment roundEnv) {
@@ -92,8 +84,7 @@ private void generateJSON(Element element, String fullyQualidiedClassName, Class
9284
break;
9385
case FIELD:
9486

95-
operations.add(new JSONOperation(((VariableElement) innerElement).getSimpleName().toString(),
96-
OperationType.FIELD, innerElement.asType()));
87+
operations.add(new FieldOperation(((VariableElement) innerElement).getSimpleName().toString(), innerElement.asType()));
9788
break;
9889
case METHOD:
9990
JSONOperation op = loadMethodInfo(innerElement);
@@ -148,6 +139,8 @@ private void generateJSON(Element element, String fullyQualidiedClassName, Class
148139
writer.endClass();
149140
}
150141

142+
143+
151144
//TODO arrays/collections, more primitives
152145
private void addPropertyFromJSON(ClassWriter writer, JSONOperation jsonOperation,String jsonObjectName) throws IOException {
153146
TypeMirror type = jsonOperation.getType();
@@ -157,9 +150,7 @@ private void addPropertyFromJSON(ClassWriter writer, JSONOperation jsonOperation
157150
//TODO test, add in addPropertyToJSON
158151
ArrayType arrayType = (ArrayType) type;
159152

160-
String jsonArrayName=jsonObjectName+capitalizeFirst(jsonOperation.getAttributeName()).replaceAll("\\[.*]","")+"JsonArray";
161-
String optArgument=getJSONAccessName(jsonOperation);
162-
writer.addAssignment(JSONArray.class.getCanonicalName() +" "+jsonArrayName,jsonObjectName+".optJSONArray("+optArgument+")");
153+
String jsonArrayName=addCreateJSONArrayCode(writer,jsonOperation,jsonObjectName);
163154

164155
String actualArrayName=jsonOperation.getAttributeName().replaceAll("\\[.*]","")+"DataArray";
165156
writer.addAssignment(arrayType.getComponentType().toString()+"[] "+actualArrayName,"null");
@@ -180,65 +171,94 @@ private void addPropertyFromJSON(ClassWriter writer, JSONOperation jsonOperation
180171
String counterVar=jsonArrayName+"Counter";
181172
writer.beginSimpleFor("int "+counterVar+"=0",counterVar+"<"+jsonArrayName+".length()",counterVar+"++");
182173

183-
addPropertyFromJSON(writer,new JSONOperation(actualArrayName+"["+counterVar+"]",OperationType.ARRAY_ELEMENT,arrayType.getComponentType()),jsonArrayName);
174+
addPropertyFromJSON(writer,new ArrayElementOperation(actualArrayName+"["+counterVar+"]",arrayType.getComponentType()),jsonArrayName);
184175

185176
writer.endFor();
186177
val=actualArrayName;
187178
writer.endIf();
179+
}else if(isCollection(type)){
180+
TypeMirror collectionType=getCollectionType(type);
181+
if(collectionType==null){
182+
processingEnv.getMessager().printMessage(Kind.ERROR,"Cannot infer type",type instanceof DeclaredType? ((DeclaredType) type).asElement() : null);
183+
return;
184+
}
185+
String jsonArrayName=addCreateJSONArrayCode(writer,jsonOperation,jsonObjectName);
186+
String counterVar=jsonArrayName+"Counter";
187+
188+
String dataName=jsonArrayName+"Data";
189+
writer.addAssignment(typeName+" "+dataName,jsonOperation.getAccessor("ret"));//TODO check if ret is the correct object
190+
writer.beginIf(jsonArrayName+"!=null");
191+
writer.beginSimpleFor("int "+counterVar+"=0",counterVar+"<"+jsonArrayName+".length()",counterVar+"++");
192+
193+
194+
addPropertyFromJSON(writer,new CollectionElementOperation(dataName,counterVar/*TODO what to do with collections of collections? How to create those?*/,collectionType),jsonArrayName);
195+
196+
val=dataName;
197+
198+
writer.endIf();
199+
writer.endFor();
188200
} else if(simpleAssignments.containsKey(typeName)) {
189-
String name=getJSONAccessName(jsonOperation);
201+
String name=jsonOperation.getJSONAccessName();
190202
val = jsonObjectName + ".opt" + simpleAssignments.get(typeName) + "(" + name + ")";
191203
} else {
192204
TypeElement referencedElement = processingEnv.getElementUtils().getTypeElement(typeName);
193205
if(referencedElement != null && referencedElement.getAnnotation(GenerateJSON.class) != null) {
194-
val = referencedElement.toString() + "JSONLoader.fromJSON(" + jsonObjectName + ".optJSONObject(\"" + jsonOperation.getAttributeName() + "\"))";
206+
val = referencedElement.toString() + "JSONLoader.fromJSON(" + jsonObjectName + ".optJSONObject(" + jsonOperation.getJSONAccessName() + "))";
195207
}
196208
}
197209
if(val == null) {
198210
processingEnv.getMessager().printMessage(Kind.ERROR, "type " + typeName + " is not supported");
199211
} else {
200-
switch(jsonOperation.getOpType()) {
201-
case FIELD:
202-
writer.addAssignment("ret." + jsonOperation.getAttributeName(), val);
203-
break;
204-
case PROPERTY:
205-
writer.addMethodCall("ret", "set" + capitalizeFirst(jsonOperation.getAttributeName()), val);
206-
break;
207-
case ARRAY_ELEMENT:
208-
writer.addAssignment(jsonOperation.getAttributeName(),val);
209-
break;
210-
}
212+
writer.addStatement(jsonOperation.getMutator("ret",val));
211213
}
212214
}
213215

214-
private String getJSONAccessName(JSONOperation jsonOperation){
215-
String name=jsonOperation.getAttributeName();
216-
if(jsonOperation.getOpType()==OperationType.ARRAY_ELEMENT){
217-
name=name.substring(name.indexOf('[')+1,name.indexOf(']'));
218-
}else{
219-
name='"'+name+'"';
216+
private String addCreateJSONArrayCode(ClassWriter writer, JSONOperation jsonOperation, String jsonObjectName) throws IOException {
217+
String jsonArrayName=jsonObjectName+capitalizeFirst(jsonOperation.getAttributeName()).replaceAll("\\[.*]","")+"JsonArray";
218+
String optArgument=jsonOperation.getJSONAccessName();
219+
writer.addAssignment(JSONArray.class.getCanonicalName() +" "+jsonArrayName,jsonObjectName+".optJSONArray("+optArgument+")");
220+
return jsonArrayName;
221+
}
222+
223+
private boolean isCollection(TypeMirror type) {
224+
return getCollectionType(type)!=null;//TODO this may return false with generics
225+
//return processingEnv.getTypeUtils().isSubtype(type,processingEnv.getElementUtils().getTypeElement(Collection.class.getSimpleName()).asType());
226+
}
227+
228+
private TypeMirror getCollectionType(TypeMirror type){
229+
if(!(type instanceof DeclaredType)) {
230+
return null;
231+
}
232+
TypeElement elem= (TypeElement) ((DeclaredType) type).asElement();
233+
for(TypeMirror iFace : elem.getInterfaces()) {
234+
if(Collection.class.getCanonicalName().equals(((DeclaredType)iFace).asElement().toString())){
235+
236+
List <? extends TypeMirror> typeArgs = ((DeclaredType) iFace).getTypeArguments();
237+
if(typeArgs.size()==1){
238+
TypeMirror genericArg = typeArgs.get(0);
239+
if(genericArg.getKind()==TypeKind.TYPEVAR){
240+
for(int i = 0; i < elem.getTypeParameters().size(); i++) {
241+
if(elem.getTypeParameters().get(i).getSimpleName().toString().equals(genericArg.toString())){
242+
return ((DeclaredType) type).getTypeArguments().get(i);//TODO test this
243+
}
244+
}
245+
}
246+
return genericArg;
247+
}
248+
return null;
249+
}
220250
}
221-
return name;
251+
return null;
222252
}
223253

224254
private void addPropertyToJSON(ClassWriter writer, JSONOperation jsonOperation,String jsonObjectName) throws IOException {
225255
TypeMirror type = jsonOperation.getType();
226256
String typeName = type.toString();
227-
String val = null;
228-
switch(jsonOperation.getOpType()) {
229-
case FIELD:
230-
val = "obj." + jsonOperation.getAttributeName();
231-
break;
232-
case PROPERTY:
233-
val = "obj.get" + capitalizeFirst(jsonOperation.getAttributeName()) + "()";
234-
break;
235-
case ARRAY_ELEMENT:
236-
val=jsonOperation.getAttributeName();
237-
}
257+
String val = jsonOperation.getAccessor("obj");//TODO check if obj is correct here
258+
238259
if(val == null) {
239260
processingEnv.getMessager().printMessage(Kind.ERROR, "type " + typeName + " is currenly not supported");
240261
}else if(type.getKind()==TypeKind.ARRAY){
241-
//TODO
242262
ArrayType arrayType = (ArrayType) type;
243263

244264
String jsonArrayName=jsonObjectName+jsonOperation.getAttributeName().replaceAll("\\[.*]","");
@@ -250,20 +270,62 @@ private void addPropertyToJSON(ClassWriter writer, JSONOperation jsonOperation,S
250270
String counterVar=jsonArrayName+"Counter";
251271
writer.beginSimpleFor("int "+counterVar+"=0",counterVar+"<"+arrayName+".length",counterVar+"++");
252272

253-
writer.addMethodCall(jsonArrayName,"put",arrayName+"["+counterVar+"]");//TODO recursion?
273+
addPropertyToJSON(writer, new ArrayElementOperation(arrayName+"["+counterVar+"]",arrayType.getComponentType()), jsonArrayName);
254274

255275
writer.endFor();
256-
writer.addMethodCall(jsonObjectName,"put","\""+jsonOperation.getAttributeName()+"\"",jsonArrayName);
276+
if(jsonOperation.isChildType()){
277+
writer.addMethodCall(jsonObjectName,"put",jsonArrayName);
278+
}else{
279+
writer.addMethodCall(jsonObjectName,"put","\""+jsonOperation.getAttributeName()+"\"",jsonArrayName);
280+
}
257281
writer.endIf();
282+
}else if(isCollection(type)){
283+
if(jsonOperation.isChildType()){
284+
processingEnv.getMessager().printMessage(Kind.ERROR,"Collections of collections are not supported",type instanceof DeclaredType? ((DeclaredType) type).asElement() : null);
285+
return;
286+
}
287+
TypeMirror collectionType = getCollectionType(type);
288+
if(collectionType==null){
289+
processingEnv.getMessager().printMessage(Kind.ERROR,"Cannot infer type",type instanceof DeclaredType? ((DeclaredType) type).asElement() : null);
290+
return;
291+
}
292+
293+
String dataName=jsonObjectName+capitalizeFirst(jsonOperation.getAttributeName()).replaceAll("\\[.*]","");
294+
String collectionName=dataName+"Collection";
295+
String jsonArrayName=dataName+"JSONArray";//TODO null safe
296+
297+
writer.addAssignment(JSONArray.class.getCanonicalName()+" "+jsonArrayName,"new "+JSONArray.class.getCanonicalName()+"()");
298+
writer.addAssignment(type.toString()+" "+collectionName,val);
299+
writer.beginIf(collectionName+"!=null");
300+
writer.beginForEach(collectionType.toString(),dataName,collectionName);
301+
302+
addPropertyToJSON(writer,new CollectionElementOperation(collectionName,dataName,collectionType),jsonArrayName);
303+
304+
writer.endFor();
305+
writer.endIf();
306+
307+
if(jsonOperation.isChildType()){
308+
writer.addMethodCall(jsonObjectName,"put",jsonArrayName);
309+
}else{
310+
writer.addMethodCall(jsonObjectName,"put","\""+jsonOperation.getAttributeName()+"\"",jsonArrayName);
311+
}
312+
258313
} else if(simpleAssignments.containsKey(typeName)) {
259-
writer.addMethodCall(jsonObjectName, "put", "\"" + jsonOperation.getAttributeName() + "\"", val);
314+
if(jsonOperation.isChildType()){
315+
writer.addMethodCall(jsonObjectName, "put", val);
316+
}else {
317+
writer.addMethodCall(jsonObjectName, "put", "\"" + jsonOperation.getAttributeName() + "\"", val);
318+
}
260319
} else {
261320
TypeElement referencedElement = processingEnv.getElementUtils().getTypeElement(typeName);
262321
if(referencedElement != null && referencedElement.getAnnotation(GenerateJSON.class) != null) {
263-
writer.addMethodCall(jsonObjectName, "put", "\"" + jsonOperation.getAttributeName() + "\"", referencedElement.toString() + "JSONLoader.toJSONObject(" + val + ")");
322+
if(jsonOperation.isChildType()){
323+
writer.addMethodCall(jsonObjectName, "put", referencedElement.toString() + "JSONLoader.toJSONObject(" + val + ")");
324+
}else{
325+
writer.addMethodCall(jsonObjectName, "put", "\"" + jsonOperation.getAttributeName() + "\"", referencedElement.toString() + "JSONLoader.toJSONObject(" + val + ")");
326+
}
264327
}
265328
}
266-
267329
}
268330

269331
private void addReturnIfNull(ClassWriter writer, String paramName) throws IOException {
@@ -286,15 +348,15 @@ private JSONOperation loadMethodInfo(Element element) {
286348
for(Element sibling : element.getEnclosingElement().getEnclosedElements()) {
287349
if(sibling.getKind() == ElementKind.METHOD
288350
&& ("set" + name.substring(3)).equals(sibling.getSimpleName().toString())) {
289-
return new JSONOperation(propName, OperationType.PROPERTY,
351+
return new PropertyOperation(propName,
290352
((ExecutableElement) element).getReturnType());
291353
}
292354
}
293355
}
294356
return null;
295357
}
296358

297-
private String capitalizeFirst(String toCapitalize){
359+
public static String capitalizeFirst(String toCapitalize){
298360
return Character.toUpperCase(toCapitalize.charAt(0)) + (toCapitalize.length() > 1 ? toCapitalize.substring(1):"");
299361
}
300362
}

0 commit comments

Comments
 (0)