/
ClassWriter.java
183 lines (166 loc) · 6.85 KB
/
ClassWriter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
* JBoss, Home of Professional Open Source
* Copyright 2010, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hibernate.jpamodelgen;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import javax.annotation.Generated;
import javax.annotation.processing.FilerException;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.MetaEntity;
/**
* Helper class to write the actual meta model class using the {@link javax.annotation.processing.Filer} API.
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
*/
public final class ClassWriter {
private static final String META_MODEL_CLASS_NAME_SUFFIX = "_";
private ClassWriter() {
}
public static void writeFile(MetaEntity entity, Context context) {
try {
String metaModelPackage = entity.getPackageName();
StringBuffer body = generateBody( entity, context );
FileObject fo = context.getProcessingEnvironment().getFiler().createSourceFile(
getFullyQualifiedClassName( entity, metaModelPackage )
);
OutputStream os = fo.openOutputStream();
PrintWriter pw = new PrintWriter( os );
if ( !metaModelPackage.isEmpty() ) {
pw.println( "package " + metaModelPackage + ";" );
pw.println();
}
pw.println( entity.generateImports() );
pw.println( body );
pw.flush();
pw.close();
}
catch ( FilerException filerEx ) {
context.logMessage(
Diagnostic.Kind.ERROR, "Problem with Filer: " + filerEx.getMessage()
);
}
catch ( IOException ioEx ) {
context.logMessage(
Diagnostic.Kind.ERROR,
"Problem opening file to write MetaModel for " + entity.getSimpleName() + ioEx.getMessage()
);
}
}
/**
* Generate everything after import statements.
*
* @param entity The meta entity for which to write the body
* @param context The processing context
*
* @return body content
*/
private static StringBuffer generateBody(MetaEntity entity, Context context) {
StringWriter sw = new StringWriter();
PrintWriter pw = null;
try {
pw = new PrintWriter( sw );
if ( context.isAddGeneratedAnnotation() ) {
pw.println( writeGeneratedAnnotation( entity ) );
}
pw.println( writeStaticMetaModelAnnotation( entity ) );
printClassDeclaration( entity, pw, context );
pw.println();
List<MetaAttribute> members = entity.getMembers();
for ( MetaAttribute metaMember : members ) {
pw.println( " " + metaMember.getDeclarationString() );
}
pw.println();
pw.println( "}" );
return sw.getBuffer();
}
finally {
if ( pw != null ) {
pw.close();
}
}
}
private static void printClassDeclaration(MetaEntity entity, PrintWriter pw, Context context) {
pw.print( "public abstract class " + entity.getSimpleName() + META_MODEL_CLASS_NAME_SUFFIX );
final TypeMirror superClass = entity.getTypeElement().getSuperclass();
//superclass of Object is of NoType which returns some other kind
if ( superClass.getKind() == TypeKind.DECLARED ) {
//F..king Ch...t Have those people used their horrible APIs even once?
final Element superClassElement = ( (DeclaredType) superClass ).asElement();
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) {
pw.print( " extends " + superClassName + META_MODEL_CLASS_NAME_SUFFIX );
}
}
pw.println( " {" );
}
/**
* Checks whether this metamodel class needs to extend another metamodel class.
* This methods checks whether the processor has generated a metamodel class for the super class, but it also
* allows for the possibility that the metamodel class was generated in a previous compilation (eg it could be
* part of a separate jar. See also METAGEN-35).
*
* @param superClassElement the super class element
* @param entityMetaComplete flag indicating if the entity for which the metamodel should be generarted is metamodel
* complete. If so we cannot use reflection to decide whether we have to add the extend clause
* @param context the execution context
*
* @return {@code true} in case there is super class meta model to extend from {@code false} otherwise.
*/
private static boolean extendsSuperMetaModel(Element superClassElement, boolean entityMetaComplete, Context context) {
// if we processed the superclass in the same run we definitely need to extend
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
if ( context.containsMetaEntity( superClassName )
|| context.containsMetaEmbeddable( superClassName ) ) {
return true;
}
// to allow for the case that the metamodel class for the super entity is for example contained in another
// jar file we use reflection. However, we need to consider the fact that there is xml configuration
// and annotations should be ignored
if ( !entityMetaComplete && ( superClassElement.getAnnotation( Entity.class ) != null
|| superClassElement.getAnnotation( MappedSuperclass.class ) != null ) ) {
return true;
}
return false;
}
private static String getFullyQualifiedClassName(MetaEntity entity, String metaModelPackage) {
String fullyQualifiedClassName = "";
if ( !metaModelPackage.isEmpty() ) {
fullyQualifiedClassName = fullyQualifiedClassName + metaModelPackage + ".";
}
fullyQualifiedClassName = fullyQualifiedClassName + entity.getSimpleName() + META_MODEL_CLASS_NAME_SUFFIX;
return fullyQualifiedClassName;
}
private static String writeGeneratedAnnotation(MetaEntity entity) {
return "@" + entity.importType( Generated.class.getName() ) + "(\"JPA MetaModel for " + entity.getQualifiedName() + "\")";
}
private static String writeStaticMetaModelAnnotation(MetaEntity entity) {
return "@" + entity.importType( "javax.persistence.metamodel.StaticMetamodel" ) + "(" + entity.getSimpleName() + ".class)";
}
}