Skip to content

Method: generateClassFile(MetamodelClass)

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