Skip to content

Method: generateAttributes(MetamodelClass)

1: /*
2: * JOPA
3: * Copyright (C) 2023 Czech Technical University in Prague
4: *
5: * This library is free software; you can redistribute it and/or
6: * modify it under the terms of the GNU Lesser General Public
7: * License as published by the Free Software Foundation; either
8: * version 3.0 of the License, or (at your option) any later version.
9: *
10: * This library is distributed in the hope that it will be useful,
11: * but WITHOUT ANY WARRANTY; without even the implied warranty of
12: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13: * Lesser General Public License for more details.
14: *
15: * You should have received a copy of the GNU Lesser General Public
16: * License along with this library.
17: */
18: package cz.cvut.kbss.jopa.modelgen;
19:
20: import cz.cvut.kbss.jopa.modelgen.classmodel.Field;
21: import cz.cvut.kbss.jopa.modelgen.classmodel.MappingAnnotations;
22: import cz.cvut.kbss.jopa.modelgen.classmodel.MetamodelClass;
23: import cz.cvut.kbss.jopa.modelgen.classmodel.Type;
24: import cz.cvut.kbss.jopa.modelgen.exception.ModelGenException;
25:
26: import javax.annotation.processing.Messager;
27: import javax.tools.Diagnostic;
28: import java.io.File;
29: import java.io.IOException;
30: import java.nio.charset.StandardCharsets;
31: import java.nio.file.Files;
32: import java.nio.file.StandardOpenOption;
33: import java.util.Collection;
34: import java.util.List;
35: import java.util.Map;
36: import java.util.Objects;
37: import java.util.Set;
38:
39: /**
40: * Class for generating output files
41: */
42: public class OutputFilesGenerator {
43:
44: private final String targetDir;
45: private final boolean debugMode;
46:
47: private final Messager messager;
48:
49: public OutputFilesGenerator(String targetDir, boolean debugMode, Messager messager) {
50: this.targetDir = targetDir;
51: this.debugMode = debugMode;
52: this.messager = messager;
53: }
54:
55: /**
56: * Generates files containing Java classes representing the specified static metamodel objects.
57: *
58: * @param classes Metamodel objects
59: */
60: public void generateOutputFiles(Collection<MetamodelClass> classes) {
61: for (MetamodelClass cls : classes) {
62: generateClassFile(cls);
63: }
64: }
65:
66: private void debug(String msg) {
67: if (debugMode) {
68: messager.printMessage(Diagnostic.Kind.NOTE, msg);
69: }
70: }
71:
72: private void generateClassFile(MetamodelClass cls) {
73: final File targetFile = createTargetFile(cls);
74: final StringBuilder content = new StringBuilder(generateClassPreamble(cls));
75: content.append(generateAttributes(cls));
76: content.append(generateClassSuffix());
77: try {
78: Files.write(targetFile.toPath(), content.toString()
79: .getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING);
80: } catch (IOException e) {
81: throw new ModelGenException("Unable to output content to target file '" + targetFile + "'!", e);
82: }
83: debug("\t - File '" + targetFile.getName() + "' created.");
84: }
85:
86: private File createTargetFile(MetamodelClass cls) {
87: final StringBuilder fileName = new StringBuilder(targetDir);
88: fileName.append("/");
89: String pack = cls.getPckg();
90: while (pack.contains(".")) {
91: int index = pack.indexOf(".");
92: fileName.append(pack, 0, index).append("/");
93: pack = pack.substring(index + 1);
94: }
95: fileName.append(pack)
96: .append("/")
97: .append(cls.getName())
98: .append("_.java");
99: try {
100: File file = new File(fileName.toString());
101: file.getParentFile().mkdirs();
102: if (!file.exists()) {
103: boolean result = file.createNewFile();
104: if (!result) {
105: throw new ModelGenException("Unable to create target file '" + file + "'!");
106: }
107: }
108: return file;
109: } catch (IOException e) {
110: throw new ModelGenException("Unable to create target file '" + fileName + "'!", e);
111: }
112: }
113:
114: public String generateClassPreamble(MetamodelClass cls) {
115: String extend = "";
116: if (!cls.getExtend().isEmpty()) {
117: extend = "extends ";
118: if (cls.getExtend().contains(".")) {
119: extend += cls.getExtend().substring(cls.getExtend().lastIndexOf(".") + 1) + "_ ";
120: } else {
121: extend += cls.getExtend();
122: }
123: }
124: StringBuilder sbOut = new StringBuilder();
125:
126: if (!cls.getPckg().isEmpty()) {
127: sbOut.append("package ")
128: .append(cls.getPckg())
129: .append(";\n\n");
130: }
131: cls.getImports().forEach(imp -> sbOut.append("import ")
132: .append(imp)
133: .append(";\n"));
134: if (!cls.getExtend().isEmpty()) {
135: sbOut.append("import ")
136: .append(cls.getExtend())
137: .append("_;\n");
138: }
139: sbOut.append("\n@Generated(value = \"")
140: .append("cz.cvut.kbss.jopa.modelgen.ModelGenProcessor\")")
141: .append("\n@StaticMetamodel(")
142: .append(cls.getName())
143: .append(".class)\n")
144: .append("public ")
145: .append("abstract ")
146: .append("class ")
147: .append(cls.getName())
148: .append("_ ")
149: .append(extend)
150: .append("{\n\n");
151: return sbOut.toString();
152: }
153:
154: private static String generateAttributes(MetamodelClass cls) {
155: StringBuilder attributes = new StringBuilder();
156:• for (Field field : cls.getFields()) {
157: final String declaringClass = field.getParentName().substring(field.getParentName().lastIndexOf('.') + 1);
158: attributes.append("\t public static volatile ");
159: //@Id
160:• if (isAnnotatedWith(field, MappingAnnotations.ID)) {
161: attributes
162: .append("Identifier<")
163: .append(declaringClass)
164: .append(", ")
165: .append(field.getType().getTypeName()
166: .substring(field.getType().getTypeName().lastIndexOf(".") + 1));
167: //@Types
168:• } else if (isAnnotatedWith(field, MappingAnnotations.TYPES)) {
169: attributes
170: .append("TypesSpecification<")
171: .append(declaringClass)
172: .append(", ");
173:• if (field.getType().getIsSimple()) {
174: attributes
175: .append(field.getType().getTypeName()
176: .substring(field.getType().getTypeName().lastIndexOf(".") + 1));
177: } else {
178: attributes
179: .append(field.getType().getTypes().get(0).getTypeName()
180: .substring(field.getType().getTypes().get(0).getTypeName()
181: .lastIndexOf(".") + 1));
182: }
183: //@Properties
184:• } else if (isAnnotatedWith(field, MappingAnnotations.PROPERTIES)) {
185: attributes
186: .append("PropertiesSpecification<")
187: .append(declaringClass)
188: .append(", ");
189: Type type = field.getType();
190:• if (!Objects.equals(type.getTypeName(), Map.class.getName())) {
191: attributes
192: .append(type.getTypes().get(0).getTypeName()
193: .substring(type.getTypes().get(0).getTypeName().lastIndexOf(".") + 1));
194: } else {
195: attributes
196: .append("Map, ")
197: .append(type.getTypes().get(0).getTypeName()
198: .substring(type.getTypes().get(0).getTypeName().lastIndexOf(".") + 1))
199: .append(", ")
200: .append(type.getTypes().get(1).getTypes().get(0).getTypeName()
201: .substring(type.getTypes().get(1).getTypes().get(0).getTypeName()
202: .lastIndexOf(".") + 1));
203: }
204:• } else if (isAnnotatedWith(field, MappingAnnotations.DATA_PROPERTY)
205:• || isAnnotatedWith(field, MappingAnnotations.OBJECT_PROPERTY)
206:• || isAnnotatedWith(field, MappingAnnotations.ANNOTATION_PROPERTY)) {
207: Type type = field.getType();
208:• if (type.getIsSimple()) {
209: attributes
210: .append("SingularAttribute<")
211: .append(declaringClass)
212: .append(", ")
213: .append(type.getTypeName().substring(type.getTypeName().lastIndexOf(".") + 1));
214: } else {
215:• if (type.getTypeName().equals(List.class.getName())) {
216: attributes
217: .append("ListAttribute<");
218:• } else if (type.getTypeName().equals(Set.class.getName())) {
219: attributes
220: .append("SetAttribute<");
221: }
222: attributes
223: .append(declaringClass)
224: .append(", ")
225: .append(type.getTypes().get(0).getTypeName()
226: .substring(type.getTypes().get(0).getTypeName().lastIndexOf(".") + 1));
227: }
228: }
229: attributes
230: .append("> ")
231: .append(field.getName())
232: .append(";\n");
233: }
234: return attributes.toString();
235: }
236:
237: private static String generateClassSuffix() {
238: return "}";
239: }
240:
241: /**
242: * Checking method whether Field has at least one of the given annotations.
243: *
244: * @param field Field to check
245: * @param mappingAnnotations Annotations for which to check
246: * @return {@code true} if the field is annotated with at least one of the mapping annotations, {@code false}
247: * otherwise
248: */
249: static boolean isAnnotatedWith(Field field, MappingAnnotations mappingAnnotations) {
250: List<MappingAnnotations> annotations = field.getAnnotatedWith();
251: if (annotations.isEmpty()) {
252: return false;
253: }
254: for (MappingAnnotations an : annotations) {
255: if (an.equals(mappingAnnotations)) {
256: return true;
257: }
258: }
259: return false;
260: }
261: }