"; 40 | } 41 | } 42 | 43 | /** 44 | * Add any required imports for this property to the allImports set. 45 | */ 46 | void addImports(SetallImports) { 47 | allImports.add("io.ebean.typequery." + propertyType); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/PropertyTypeArray.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | 4 | import java.util.Set; 5 | 6 | /** 7 | * Array property type. 8 | */ 9 | class PropertyTypeArray extends PropertyType { 10 | 11 | private final String elementClass; 12 | 13 | private final String elementShortName; 14 | 15 | PropertyTypeArray(String elementClass, String elementShortName) { 16 | super("PArray"); 17 | this.elementClass = elementClass; 18 | this.elementShortName = elementShortName; 19 | } 20 | 21 | @Override 22 | String getTypeDefn(String shortName, boolean assoc) { 23 | if (assoc) { 24 | return "PArray "; 25 | 26 | } else { 27 | return "PArray "; 28 | } 29 | } 30 | 31 | @Override 32 | void addImports(SetallImports) { 33 | super.addImports(allImports); 34 | allImports.add(elementClass); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/PropertyTypeAssoc.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * Property type for associated beans (OneToMany, ManyToOne etc). 7 | */ 8 | class PropertyTypeAssoc extends PropertyType { 9 | 10 | /** 11 | * The package name for this associated query bean. 12 | */ 13 | private final String assocPackage; 14 | 15 | /** 16 | * Construct given the associated bean type name and package. 17 | * 18 | * @param qAssocTypeName the associated bean type name. 19 | * @param assocPackage the associated bean package. 20 | */ 21 | PropertyTypeAssoc(String qAssocTypeName, String assocPackage) { 22 | super(qAssocTypeName); 23 | this.assocPackage = assocPackage; 24 | } 25 | 26 | /** 27 | * All required imports to the allImports set. 28 | */ 29 | @Override 30 | void addImports(Set allImports) { 31 | allImports.add(assocPackage + "." + propertyType); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/PropertyTypeEnum.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | 4 | import java.util.Set; 5 | 6 | /** 7 | * Enum property type. 8 | */ 9 | class PropertyTypeEnum extends PropertyType { 10 | 11 | private final String enumClass; 12 | 13 | private final String enumShortName; 14 | 15 | PropertyTypeEnum(String enumClass, String enumShortName) { 16 | super("PEnum"); 17 | this.enumClass = enumClass; 18 | this.enumShortName = enumShortName; 19 | } 20 | 21 | @Override 22 | String getTypeDefn(String shortName, boolean assoc) { 23 | if (assoc) { 24 | return "PEnum "; 25 | 26 | } else { 27 | return "PEnum "; 28 | } 29 | } 30 | 31 | @Override 32 | void addImports(SetallImports) { 33 | super.addImports(allImports); 34 | allImports.add(enumClass); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/PropertyTypeMap.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import java.io.File; 4 | import java.math.BigDecimal; 5 | import java.math.BigInteger; 6 | import java.net.InetAddress; 7 | import java.net.URI; 8 | import java.net.URL; 9 | import java.sql.Timestamp; 10 | import java.util.Calendar; 11 | import java.util.Currency; 12 | import java.util.HashMap; 13 | import java.util.Locale; 14 | import java.util.Map; 15 | import java.util.TimeZone; 16 | import java.util.UUID; 17 | 18 | /** 19 | * Holds the Property types and how they match to class types. 20 | */ 21 | class PropertyTypeMap { 22 | 23 | /** 24 | * Property type for Db Json properties. 25 | */ 26 | private final PropertyType dbJsonType = new PropertyType("PJson"); 27 | 28 | private Map map = new HashMap<>(); 29 | 30 | PropertyTypeMap() { 31 | 32 | map.put("boolean", new PropertyType("PBoolean")); 33 | map.put("short", new PropertyType("PShort")); 34 | map.put("int", new PropertyType("PInteger")); 35 | map.put("long", new PropertyType("PLong")); 36 | map.put("double", new PropertyType("PDouble")); 37 | map.put("float", new PropertyType("PFloat")); 38 | 39 | addType(Boolean.class); 40 | addType(Short.class); 41 | addType(Integer.class); 42 | addType(Long.class); 43 | addType(Double.class); 44 | addType(Float.class); 45 | addType(String.class); 46 | addType(Timestamp.class); 47 | map.put(java.sql.Date.class.getName(), new PropertyType("PSqlDate")); 48 | map.put(java.util.Date.class.getName(), new PropertyType("PUtilDate")); 49 | addType(java.sql.Time.class); 50 | addType(TimeZone.class); 51 | addType(BigDecimal.class); 52 | addType(BigInteger.class); 53 | addType(Calendar.class); 54 | addType(Currency.class); 55 | addType(Class.class); 56 | addType(Locale.class); 57 | map.put("java.lang.Class>", new PropertyType("PClass")); 58 | addType(File.class); 59 | addType(InetAddress.class); 60 | 61 | map.put(URI.class.getName(), new PropertyType("PUri")); 62 | map.put(URL.class.getName(), new PropertyType("PUrl")); 63 | map.put(UUID.class.getName(), new PropertyType("PUuid")); 64 | map.put("io.ebean.types.Inet", new PropertyType("PInet")); 65 | map.put("io.ebean.types.Cidr", new PropertyType("PCidr")); 66 | addJava8Types(); 67 | addJodaTypes(); 68 | } 69 | 70 | private void addType(Class> cls) { 71 | String simpleName = cls.getSimpleName(); 72 | map.put(cls.getName(), new PropertyType("P"+simpleName)); 73 | } 74 | 75 | private void addJava8Types() { 76 | 77 | try { 78 | Class.forName("java.time.Instant"); 79 | } catch (ClassNotFoundException e) { 80 | return; 81 | } 82 | addType(java.time.DayOfWeek.class); 83 | addType(java.time.Duration.class); 84 | addType(java.time.Instant.class); 85 | addType(java.time.LocalDate.class); 86 | addType(java.time.LocalDateTime.class); 87 | addType(java.time.LocalTime.class); 88 | addType(java.time.Month.class); 89 | addType(java.time.MonthDay.class); 90 | addType(java.time.OffsetDateTime.class); 91 | addType(java.time.OffsetTime.class); 92 | addType(java.time.Year.class); 93 | addType(java.time.YearMonth.class); 94 | addType(java.time.ZoneId.class); 95 | addType(java.time.ZoneOffset.class); 96 | } 97 | 98 | private void addJodaTypes() { 99 | map.put("org.joda.time.DateTime", new PropertyType("PJodaDateTime")); 100 | map.put("org.joda.time.DateMidnight", new PropertyType("PJodaDateMidnight")); 101 | map.put("org.joda.time.LocalDate", new PropertyType("PJodaLocalDate")); 102 | map.put("org.joda.time.LocalDateTime", new PropertyType("PJodaLocalDateTime")); 103 | map.put("org.joda.time.LocalTime", new PropertyType("PJodaLocalTime")); 104 | } 105 | 106 | /** 107 | * Return the property type for the given class description. 108 | */ 109 | PropertyType getType(String classDesc) { 110 | return map.get(classDesc); 111 | } 112 | 113 | /** 114 | * Return the Db Json property type (for DbJson and DbJsonB). 115 | */ 116 | PropertyType getDbJsonType() { 117 | return dbJsonType; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/PropertyTypeScalar.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * Property type for associated beans (OneToMany, ManyToOne etc). 7 | */ 8 | class PropertyTypeScalar extends PropertyType { 9 | 10 | /** 11 | * The package name for this associated query bean. 12 | */ 13 | private final String assocPackage; 14 | private final String attributeSimpleName; 15 | 16 | /** 17 | * Construct given the associated bean type name and package. 18 | * 19 | * @param attributeClass the type in the database bean that will be serialized via ScalarType 20 | */ 21 | PropertyTypeScalar(String attributeClass) { 22 | super("PScalar"); 23 | int split = attributeClass.lastIndexOf('.'); 24 | this.assocPackage = attributeClass.substring(0, split); 25 | this.attributeSimpleName = attributeClass.substring(split + 1); 26 | } 27 | 28 | @Override 29 | String getTypeDefn(String shortName, boolean assoc) { 30 | if (assoc) { 31 | // PScalarType 32 | return "PScalar "; 33 | } else { 34 | // PScalarType 35 | return "PScalar "; 36 | } 37 | } 38 | 39 | /** 40 | * All required imports to the allImports set. 41 | */ 42 | @Override 43 | void addImports(SetallImports) { 44 | super.addImports(allImports); 45 | allImports.add(assocPackage + "." + attributeSimpleName); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/PropertyTypeScalarComparable.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * Property type for associated beans (OneToMany, ManyToOne etc). 7 | */ 8 | class PropertyTypeScalarComparable extends PropertyType { 9 | 10 | /** 11 | * The package name for this associated query bean. 12 | */ 13 | private final String assocPackage; 14 | private final String attributeSimpleName; 15 | 16 | /** 17 | * Construct given the associated bean type name and package. 18 | * 19 | * @param attributeClass the type in the database bean that will be serialized via ScalarType 20 | */ 21 | PropertyTypeScalarComparable(String attributeClass) { 22 | super("PScalarComparable"); 23 | int split = attributeClass.lastIndexOf('.'); 24 | this.assocPackage = attributeClass.substring(0, split); 25 | this.attributeSimpleName = attributeClass.substring(split + 1); 26 | } 27 | 28 | @Override 29 | String getTypeDefn(String shortName, boolean assoc) { 30 | if (assoc) { 31 | // PScalarType 32 | return "PScalarComparable "; 33 | } else { 34 | // PScalarType 35 | return "PScalarComparable "; 36 | } 37 | } 38 | 39 | /** 40 | * All required imports to the allImports set. 41 | */ 42 | @Override 43 | void addImports(SetallImports) { 44 | super.addImports(allImports); 45 | allImports.add(assocPackage + "." + attributeSimpleName); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/ReadModuleInfo.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import javax.lang.model.element.AnnotationMirror; 4 | import javax.lang.model.element.AnnotationValue; 5 | import javax.lang.model.element.Element; 6 | import javax.lang.model.element.ExecutableElement; 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | /** 14 | * Helper to read the current/existing prefixed entity classes. 15 | * 16 | * These are added back on partial compile. 17 | */ 18 | class ReadModuleInfo { 19 | 20 | private final ProcessingContext ctx; 21 | 22 | public ReadModuleInfo(ProcessingContext ctx) { 23 | this.ctx = ctx; 24 | } 25 | 26 | ModuleMeta read(Element element) { 27 | final List extends AnnotationMirror> mirrors = element.getAnnotationMirrors(); 28 | for (AnnotationMirror mirror : mirrors) { 29 | final String name = mirror.getAnnotationType().asElement().toString(); 30 | if (Constants.MODULEINFO.equals(name)) { 31 | List
entities = readEntities("entities", mirror); 32 | List other = readEntities("other", mirror); 33 | return new ModuleMeta(entities, other); 34 | } 35 | } 36 | return null; 37 | } 38 | 39 | @SuppressWarnings("unchecked") 40 | private List readEntities(String key, AnnotationMirror mirror) { 41 | final Map extends ExecutableElement, ? extends AnnotationValue> elementValues = mirror.getElementValues(); 42 | final Set extends Map.Entry extends ExecutableElement, ? extends AnnotationValue>> entries = elementValues.entrySet(); 43 | 44 | for (Map.Entry extends ExecutableElement, ? extends AnnotationValue> entry : entries) { 45 | if (key.equals(entry.getKey().getSimpleName().toString())) { 46 | return readAttributes(entry.getValue()); 47 | } 48 | } 49 | return Collections.emptyList(); 50 | } 51 | 52 | @SuppressWarnings("unchecked") 53 | private List readAttributes(AnnotationValue value) { 54 | final Object entitiesValue = value.getValue(); 55 | if (entitiesValue != null) { 56 | try { 57 | List vals = new ArrayList<>(); 58 | List coll = (List ) entitiesValue; 59 | for (AnnotationValue annotationValue : coll) { 60 | vals.add((String) annotationValue.getValue()); 61 | } 62 | return vals; 63 | } catch (Exception e) { 64 | ctx.logError(null, "Error reading ModuleInfo annotation, err " + e); 65 | } 66 | } 67 | return Collections.emptyList(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/SimpleModuleInfoWriter.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import javax.tools.FileObject; 4 | import javax.tools.JavaFileObject; 5 | import java.io.IOException; 6 | import java.io.Writer; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.StringJoiner; 10 | 11 | /** 12 | * Write the source code for the factory. 13 | */ 14 | class SimpleModuleInfoWriter { 15 | 16 | private final ProcessingContext processingContext; 17 | 18 | private final String factoryPackage; 19 | private final String factoryShortName; 20 | private final String factoryFullName; 21 | 22 | private Append writer; 23 | 24 | SimpleModuleInfoWriter(ProcessingContext processingContext) { 25 | this.processingContext = processingContext; 26 | this.factoryPackage = processingContext.getFactoryPackage(); 27 | this.factoryShortName = "_ebean$ModuleInfo"; 28 | this.factoryFullName = factoryPackage + "." + factoryShortName; 29 | } 30 | 31 | void write() throws IOException { 32 | writer = new Append(createFileWriter()); 33 | writePackage(); 34 | writeStartClass(); 35 | writeEndClass(); 36 | writer.close(); 37 | writeServicesFile(); 38 | writeManifestFile(); 39 | } 40 | 41 | private void writeServicesFile() { 42 | try { 43 | FileObject jfo = processingContext.createMetaInfServicesWriter(); 44 | if (jfo != null) { 45 | Writer writer = jfo.openWriter(); 46 | writer.write(factoryFullName); 47 | writer.close(); 48 | } 49 | 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | processingContext.logError(null, "Failed to write services file " + e.getMessage()); 53 | } 54 | } 55 | 56 | private void writeManifestFile() { 57 | try { 58 | final Set allEntityPackages = processingContext.getAllEntityPackages(); 59 | if (!allEntityPackages.isEmpty()) { 60 | FileObject jfo = processingContext.createManifestWriter(); 61 | if (jfo != null) { 62 | Writer writer = jfo.openWriter(); 63 | writer.write("generated-by: Ebean query bean generator\n"); 64 | writer.write(manifestEntityPackages(allEntityPackages)); 65 | writer.write("\n"); 66 | writer.close(); 67 | } 68 | } 69 | 70 | } catch (IOException e) { 71 | e.printStackTrace(); 72 | processingContext.logError(null, "Failed to write services file " + e.getMessage()); 73 | } 74 | } 75 | 76 | private String manifestEntityPackages(Set allEntityPackages) { 77 | StringBuilder builder = new StringBuilder("entity-packages: "); 78 | for (String pkg : allEntityPackages) { 79 | // one package per line 80 | builder.append(pkg).append("\n").append(" "); 81 | } 82 | return builder.delete(builder.lastIndexOf("\n"), builder.length()).append("\n").toString(); 83 | } 84 | 85 | private void writePackage() { 86 | 87 | writer.append("package %s;", factoryPackage).eol().eol(); 88 | 89 | writer.append("import java.util.ArrayList;").eol(); 90 | writer.append("import java.util.Collections;").eol(); 91 | writer.append("import java.util.List;").eol(); 92 | final String generated = processingContext.getGeneratedAnnotation(); 93 | if (generated != null) { 94 | writer.append("import %s;", generated).eol(); 95 | } 96 | writer.eol(); 97 | writer.append("import io.ebean.config.ModuleInfo;").eol(); 98 | writer.append("import io.ebean.config.ModuleInfoLoader;").eol(); 99 | writer.eol(); 100 | } 101 | 102 | void buildAtContextModule(Append writer) { 103 | if (processingContext.isGeneratedAvailable()) { 104 | writer.append(Constants.AT_GENERATED).eol(); 105 | } 106 | writer.append("@ModuleInfo("); 107 | if (processingContext.hasOtherClasses()) { 108 | writer.append("other={%s}, ", otherClasses()); 109 | } 110 | writer.append("entities={%s}", prefixEntities()); 111 | writer.append(")").eol(); 112 | } 113 | 114 | private String otherClasses() { 115 | return quoteTypes(processingContext.getOtherClasses()); 116 | } 117 | 118 | private String prefixEntities() { 119 | return quoteTypes(processingContext.getPrefixEntities()); 120 | } 121 | 122 | private String quoteTypes(Set otherClasses) { 123 | StringJoiner sb = new StringJoiner(","); 124 | for (String fullType : otherClasses) { 125 | sb.add("\"" + fullType + "\""); 126 | } 127 | return sb.toString(); 128 | } 129 | 130 | private void writeStartClass() { 131 | 132 | buildAtContextModule(writer); 133 | 134 | writer.append("public class %s implements ModuleInfoLoader {", factoryShortName).eol().eol(); 135 | writeMethodOtherClasses(); 136 | writeMethodEntityClasses(processingContext.getDbEntities(), null); 137 | 138 | final Map > otherDbEntities = processingContext.getOtherDbEntities(); 139 | writeMethodEntityClassesFor(otherDbEntities.keySet()); 140 | 141 | for (Map.Entry > otherDb : otherDbEntities.entrySet()) { 142 | writeMethodEntityClasses(otherDb.getValue(), otherDb.getKey()); 143 | } 144 | } 145 | 146 | private void writeMethodOtherClasses() { 147 | writer.append(" private List > otherClasses() {").eol(); 148 | if (!processingContext.hasOtherClasses()) { 149 | writer.append(" return Collections.emptyList();").eol(); 150 | } else { 151 | writer.append(" List > others = new ArrayList<>();").eol(); 152 | for (String otherType : processingContext.getOtherClasses()) { 153 | writer.append(" others.add(%s.class);", otherType).eol(); 154 | } 155 | writer.append(" return others;").eol(); 156 | } 157 | writer.append(" }").eol().eol(); 158 | } 159 | 160 | private void writeMethodEntityClasses(Set dbEntities, String dbName) { 161 | 162 | String modifier = "public"; 163 | String method = "entityClasses"; 164 | 165 | if (dbName == null) { 166 | writer.append(" @Override").eol(); 167 | } else { 168 | method = dbName + "_entities"; 169 | modifier = "private"; 170 | } 171 | writer.append(" %s List > %s() {", modifier, method).eol(); 172 | writer.append(" List > entities = new ArrayList<>();").eol(); 173 | for (String dbEntity : dbEntities) { 174 | writer.append(" entities.add(%s.class);", dbEntity).eol(); 175 | } 176 | if (processingContext.hasOtherClasses()) { 177 | writer.append(" entities.addAll(otherClasses());").eol(); 178 | } 179 | writer.append(" return entities;").eol(); 180 | writer.append(" }").eol().eol(); 181 | } 182 | 183 | private void writeMethodEntityClassesFor(Set otherDbNames) { 184 | 185 | writer.append(" @Override").eol(); 186 | writer.append(" public List > entityClassesFor(String dbName) {").eol().eol(); 187 | for (String dbName : otherDbNames) { 188 | writer.append(" if (\"%s\".equals(dbName)) return %s_entities();", dbName, dbName).eol(); 189 | } 190 | writer.append(" return Collections.emptyList();").eol(); 191 | writer.append(" }").eol().eol(); 192 | } 193 | 194 | private void writeEndClass() { 195 | writer.append("}").eol(); 196 | } 197 | 198 | private Writer createFileWriter() throws IOException { 199 | JavaFileObject jfo = processingContext.createWriter(factoryFullName); 200 | return jfo.openWriter(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/SimpleQueryBeanWriter.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | 4 | import javax.lang.model.element.TypeElement; 5 | import javax.lang.model.element.VariableElement; 6 | import javax.tools.JavaFileObject; 7 | import java.io.IOException; 8 | import java.io.Writer; 9 | import java.util.ArrayList; 10 | import java.util.Iterator; 11 | import java.util.List; 12 | import java.util.Set; 13 | import java.util.TreeSet; 14 | 15 | /** 16 | * A simple implementation that generates and writes query beans. 17 | */ 18 | class SimpleQueryBeanWriter { 19 | 20 | private final Set importTypes = new TreeSet<>(); 21 | 22 | private final List properties = new ArrayList<>(); 23 | 24 | private final TypeElement element; 25 | 26 | private final ProcessingContext processingContext; 27 | 28 | private final String dbName; 29 | private final String beanFullName; 30 | private final boolean isEntity; 31 | private final boolean embeddable; 32 | private boolean writingAssocBean; 33 | 34 | private String destPackage; 35 | private String origDestPackage; 36 | 37 | private String shortName; 38 | private String origShortName; 39 | private Append writer; 40 | 41 | SimpleQueryBeanWriter(TypeElement element, ProcessingContext processingContext) { 42 | this.element = element; 43 | this.processingContext = processingContext; 44 | this.beanFullName = element.getQualifiedName().toString(); 45 | this.destPackage = derivePackage(beanFullName) + ".query"; 46 | this.shortName = deriveShortName(beanFullName); 47 | this.isEntity = processingContext.isEntity(element); 48 | this.embeddable = processingContext.isEmbeddable(element); 49 | this.dbName = findDbName(); 50 | } 51 | 52 | private String findDbName() { 53 | return processingContext.findDbName(element); 54 | } 55 | 56 | private boolean isEntity() { 57 | return isEntity; 58 | } 59 | 60 | private boolean isEmbeddable() { 61 | return embeddable; 62 | } 63 | 64 | private void gatherPropertyDetails() { 65 | final String generated = processingContext.getGeneratedAnnotation(); 66 | if (generated != null) { 67 | importTypes.add(generated); 68 | } 69 | importTypes.add(beanFullName); 70 | importTypes.add(Constants.TQROOTBEAN); 71 | importTypes.add(Constants.TYPEQUERYBEAN); 72 | importTypes.add(Constants.DATABASE); 73 | importTypes.add(Constants.FETCHGROUP); 74 | importTypes.add(Constants.QUERY); 75 | importTypes.add(Constants.TRANSACTION); 76 | 77 | if (dbName != null) { 78 | importTypes.add(Constants.DB); 79 | } 80 | addClassProperties(); 81 | } 82 | 83 | /** 84 | * Recursively add properties from the inheritance hierarchy. 85 | * 86 | * Includes properties from mapped super classes and usual inheritance. 87 | *
88 | */ 89 | private void addClassProperties() { 90 | for (VariableElement field : processingContext.allFields(element)) { 91 | PropertyType type = processingContext.getPropertyType(field); 92 | if (type != null) { 93 | type.addImports(importTypes); 94 | properties.add(new PropertyMeta(field.getSimpleName().toString(), type)); 95 | } 96 | } 97 | } 98 | 99 | /** 100 | * Write the type query bean (root bean). 101 | */ 102 | void writeRootBean() throws IOException { 103 | 104 | gatherPropertyDetails(); 105 | if (isEmbeddable()) { 106 | processingContext.addEntity(beanFullName, dbName); 107 | } else if (isEntity()) { 108 | processingContext.addEntity(beanFullName, dbName); 109 | writer = new Append(createFileWriter()); 110 | 111 | writePackage(); 112 | writeImports(); 113 | writeClass(); 114 | writeAlias(); 115 | writeFields(); 116 | writeConstructors(); 117 | writeStaticAliasClass(); 118 | writeClassEnd(); 119 | 120 | writer.close(); 121 | } 122 | } 123 | 124 | /** 125 | * Write the type query assoc bean. 126 | */ 127 | void writeAssocBean() throws IOException { 128 | writingAssocBean = true; 129 | origDestPackage = destPackage; 130 | destPackage = destPackage + ".assoc"; 131 | origShortName = shortName; 132 | shortName = "Assoc" + shortName; 133 | 134 | prepareAssocBeanImports(); 135 | 136 | writer = new Append(createFileWriter()); 137 | 138 | writePackage(); 139 | writeImports(); 140 | writeClass(); 141 | writeFields(); 142 | writeConstructors(); 143 | writeClassEnd(); 144 | 145 | writer.close(); 146 | } 147 | 148 | /** 149 | * Prepare the imports for writing assoc bean. 150 | */ 151 | private void prepareAssocBeanImports() { 152 | importTypes.remove(Constants.DB); 153 | importTypes.remove(Constants.TQROOTBEAN); 154 | importTypes.remove(Constants.DATABASE); 155 | importTypes.remove(Constants.FETCHGROUP); 156 | importTypes.remove(Constants.QUERY); 157 | importTypes.add(Constants.TQASSOCBEAN); 158 | if (isEntity()) { 159 | importTypes.add(Constants.TQPROPERTY); 160 | importTypes.add(origDestPackage + ".Q" + origShortName); 161 | } 162 | 163 | // remove imports for the same package 164 | IteratorimportsIterator = importTypes.iterator(); 165 | String checkImportStart = destPackage + ".QAssoc"; 166 | while (importsIterator.hasNext()) { 167 | String importType = importsIterator.next(); 168 | if (importType.startsWith(checkImportStart)) { 169 | importsIterator.remove(); 170 | } 171 | } 172 | } 173 | 174 | /** 175 | * Write constructors. 176 | */ 177 | private void writeConstructors() { 178 | if (writingAssocBean) { 179 | writeAssocBeanFetch(); 180 | writeAssocBeanConstructor(); 181 | } else { 182 | writeRootBeanConstructor(); 183 | } 184 | } 185 | 186 | /** 187 | * Write the constructors for 'root' type query bean. 188 | */ 189 | private void writeRootBeanConstructor() { 190 | 191 | writer.eol(); 192 | writer.append(" /**").eol(); 193 | writer.append(" * Return a query bean used to build a FetchGroup.").eol(); 194 | writer.append(" */").eol(); 195 | writer.append(" public static Q%s forFetchGroup() {", shortName).eol(); 196 | writer.append(" return new Q%s(FetchGroup.queryFor(%s.class));", shortName, shortName).eol(); 197 | writer.append(" }").eol(); 198 | writer.eol(); 199 | 200 | String name = (dbName == null) ? "default" : dbName; 201 | writer.append(" /**").eol(); 202 | writer.append(" * Construct using the %s Database.", name).eol(); 203 | writer.append(" */").eol(); 204 | writer.append(" public Q%s() {", shortName).eol(); 205 | if (dbName == null) { 206 | writer.append(" super(%s.class);", shortName).eol(); 207 | } else { 208 | writer.append(" super(%s.class, DB.byName(\"%s\"));", shortName, dbName).eol(); 209 | } 210 | writer.append(" }").eol(); 211 | writer.eol(); 212 | 213 | writer.append(" /**").eol(); 214 | writer.append(" * Construct with a given transaction.", name).eol(); 215 | writer.append(" */").eol(); 216 | writer.append(" public Q%s(Transaction transaction) {", shortName).eol(); 217 | if (dbName == null) { 218 | writer.append(" super(%s.class, transaction);", shortName).eol(); 219 | } else { 220 | writer.append(" super(%s.class, DB.byName(\"%s\"), transaction);", shortName, dbName).eol(); 221 | } 222 | writer.append(" }").eol(); 223 | 224 | writer.eol(); 225 | writer.append(" /**").eol(); 226 | writer.append(" * Construct with a given Database.").eol(); 227 | writer.append(" */").eol(); 228 | writer.append(" public Q%s(Database database) {", shortName).eol(); 229 | writer.append(" super(%s.class, database);", shortName).eol(); 230 | writer.append(" }").eol(); 231 | writer.eol(); 232 | 233 | writer.eol(); 234 | writer.append(" /**").eol(); 235 | writer.append(" * Construct for Alias.").eol(); 236 | writer.append(" */").eol(); 237 | writer.append(" private Q%s(boolean dummy) {", shortName).eol(); 238 | writer.append(" super(dummy);").eol(); 239 | writer.append(" }").eol(); 240 | 241 | writer.eol(); 242 | writer.append(" /**").eol(); 243 | writer.append(" * Private constructor for FetchGroup building.").eol(); 244 | writer.append(" */").eol(); 245 | writer.append(" private Q%s(Query<%s> fetchGroupQuery) {", shortName, shortName).eol(); 246 | writer.append(" super(fetchGroupQuery);").eol(); 247 | writer.append(" }").eol(); 248 | } 249 | 250 | private void writeAssocBeanFetch() { 251 | if (isEntity()) { 252 | writeAssocBeanFetch("", "Eagerly fetch this association loading the specified properties."); 253 | writeAssocBeanFetch("Query", "Eagerly fetch this association using a 'query join' loading the specified properties."); 254 | writeAssocBeanFetch("Cache", "Eagerly fetch this association using L2 cache."); 255 | writeAssocBeanFetch("Lazy", "Use lazy loading for this association loading the specified properties."); 256 | } 257 | } 258 | 259 | private void writeAssocBeanFetch(String fetchType, String comment) { 260 | writer.append(" /**").eol(); 261 | writer.append(" * ").append(comment).eol(); 262 | writer.append(" */").eol(); 263 | writer.append(" @SafeVarargs @SuppressWarnings(\"varargs\")").eol(); 264 | writer.append(" public final R fetch%s(TQProperty ... properties) {", fetchType, origShortName).eol(); 265 | writer.append(" return fetch%sProperties(properties);", fetchType).eol(); 266 | writer.append(" }").eol(); 267 | writer.eol(); 268 | } 269 | 270 | /** 271 | * Write constructor for 'assoc' type query bean. 272 | */ 273 | private void writeAssocBeanConstructor() { 274 | writer.append(" public Q%s(String name, R root) {", shortName).eol(); 275 | writer.append(" super(name, root);").eol(); 276 | writer.append(" }").eol().eol(); 277 | 278 | writer.append(" public Q%s(String name, R root, String prefix) {", shortName).eol(); 279 | writer.append(" super(name, root, prefix);").eol(); 280 | writer.append(" }").eol(); 281 | } 282 | 283 | /** 284 | * Write all the fields. 285 | */ 286 | private void writeFields() { 287 | for (PropertyMeta property : properties) { 288 | property.writeFieldDefn(writer, shortName, writingAssocBean); 289 | writer.eol(); 290 | } 291 | writer.eol(); 292 | } 293 | 294 | /** 295 | * Write the class definition. 296 | */ 297 | private void writeClass() { 298 | if (writingAssocBean) { 299 | writer.append("/**").eol(); 300 | writer.append(" * Association query bean for %s.", shortName).eol(); 301 | writer.append(" * ").eol(); 302 | writer.append(" * THIS IS A GENERATED OBJECT, DO NOT MODIFY THIS CLASS.").eol(); 303 | writer.append(" */").eol(); 304 | if (processingContext.isGeneratedAvailable()) { 305 | writer.append(Constants.AT_GENERATED).eol(); 306 | } 307 | writer.append(Constants.AT_TYPEQUERYBEAN).eol(); 308 | writer.append("public class Q%sextends TQAssocBean<%s,R> {", shortName, origShortName).eol(); 309 | 310 | } else { 311 | writer.append("/**").eol(); 312 | writer.append(" * Query bean for %s.", shortName).eol(); 313 | writer.append(" * ").eol(); 314 | writer.append(" * THIS IS A GENERATED OBJECT, DO NOT MODIFY THIS CLASS.").eol(); 315 | writer.append(" */").eol(); 316 | if (processingContext.isGeneratedAvailable()) { 317 | writer.append(Constants.AT_GENERATED).eol(); 318 | } 319 | writer.append(Constants.AT_TYPEQUERYBEAN).eol(); 320 | writer.append("public class Q%s extends TQRootBean<%1$s,Q%1$s> {", shortName).eol(); 321 | } 322 | 323 | writer.eol(); 324 | } 325 | 326 | private void writeAlias() { 327 | if (!writingAssocBean) { 328 | writer.append(" private static final Q%s _alias = new Q%1$s(true);", shortName).eol().eol(); 329 | 330 | writer.append(" /**").eol(); 331 | writer.append(" * Return the shared 'Alias' instance used to provide properties to ").eol(); 332 | writer.append(" * select()
andfetch()
").eol(); 333 | writer.append(" */").eol(); 334 | writer.append(" public static Q%s alias() {", shortName).eol(); 335 | writer.append(" return _alias;").eol(); 336 | writer.append(" }").eol(); 337 | writer.eol(); 338 | } 339 | } 340 | 341 | private void writeStaticAliasClass() { 342 | writer.eol(); 343 | writer.append(" /**").eol(); 344 | writer.append(" * Provides static properties to use in select() and fetch() ").eol(); 345 | writer.append(" * clauses of a query. Typically referenced via static imports. ").eol(); 346 | writer.append(" */").eol(); 347 | writer.append(" public static class Alias {").eol(); 348 | for (PropertyMeta property : properties) { 349 | property.writeFieldAliasDefn(writer, shortName); 350 | writer.eol(); 351 | } 352 | writer.append(" }").eol(); 353 | } 354 | 355 | private void writeClassEnd() { 356 | writer.append("}").eol(); 357 | } 358 | 359 | /** 360 | * Write all the imports. 361 | */ 362 | private void writeImports() { 363 | 364 | for (String importType : importTypes) { 365 | writer.append("import %s;", importType).eol(); 366 | } 367 | writer.eol(); 368 | } 369 | 370 | private void writePackage() { 371 | writer.append("package %s;", destPackage).eol().eol(); 372 | } 373 | 374 | 375 | private Writer createFileWriter() throws IOException { 376 | JavaFileObject jfo = processingContext.createWriter(destPackage + "." + "Q" + shortName, element); 377 | return jfo.openWriter(); 378 | } 379 | 380 | private String derivePackage(String name) { 381 | int pos = name.lastIndexOf('.'); 382 | if (pos == -1) { 383 | return ""; 384 | } 385 | return name.substring(0, pos); 386 | } 387 | 388 | private String deriveShortName(String name) { 389 | int pos = name.lastIndexOf('.'); 390 | if (pos == -1) { 391 | return name; 392 | } 393 | return name.substring(pos + 1); 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/Split.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | /** 4 | * Helper for splitting package and class name. 5 | */ 6 | class Split { 7 | 8 | /** 9 | * Split into package and class name. 10 | */ 11 | static String[] split(String className) { 12 | String[] result = new String[2]; 13 | int startPos = className.lastIndexOf('.'); 14 | if (startPos == -1) { 15 | result[1] = className; 16 | return result; 17 | } 18 | result[0] = className.substring(0, startPos); 19 | result[1] = className.substring(startPos + 1); 20 | return result; 21 | } 22 | 23 | /** 24 | * Trim off package to return the simple class name. 25 | */ 26 | static String shortName(String className) { 27 | int startPos = className.lastIndexOf('.'); 28 | if (startPos == -1) { 29 | return className; 30 | } 31 | return className.substring(startPos + 1); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/gradle/incremental.annotation.processors: -------------------------------------------------------------------------------- 1 | io.ebean.querybean.generator.Processor,isolating 2 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/javax.annotation.processing.Processor: -------------------------------------------------------------------------------- 1 | io.ebean.querybean.generator.Processor -------------------------------------------------------------------------------- /src/test/java/io/ebean/querybean/generator/SplitTest.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import org.testng.annotations.Test; 4 | 5 | import static org.testng.Assert.assertEquals; 6 | import static org.testng.Assert.assertNull; 7 | 8 | public class SplitTest { 9 | 10 | @Test 11 | public void shortName() throws Exception { 12 | 13 | assertEquals(Split.shortName("com.foo.domain.Customer"), "Customer"); 14 | assertEquals(Split.shortName("Customer"), "Customer"); 15 | } 16 | 17 | @Test 18 | public void split_normal() throws Exception { 19 | 20 | String[] split = Split.split("com.foo.domain.Customer"); 21 | 22 | assertEquals(split[0], "com.foo.domain"); 23 | assertEquals(split[1], "Customer"); 24 | } 25 | 26 | @Test 27 | public void split_noPackage() throws Exception { 28 | 29 | String[] split = Split.split("Customer"); 30 | 31 | assertNull(split[0]); 32 | assertEquals(split[1], "Customer"); 33 | } 34 | 35 | } --------------------------------------------------------------------------------