├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── io │ │ └── ebean │ │ └── querybean │ │ └── generator │ │ ├── Append.java │ │ ├── Constants.java │ │ ├── FindDbName.java │ │ ├── ModuleMeta.java │ │ ├── ProcessingContext.java │ │ ├── Processor.java │ │ ├── PropertyMeta.java │ │ ├── PropertyType.java │ │ ├── PropertyTypeArray.java │ │ ├── PropertyTypeAssoc.java │ │ ├── PropertyTypeEnum.java │ │ ├── PropertyTypeMap.java │ │ ├── PropertyTypeScalar.java │ │ ├── PropertyTypeScalarComparable.java │ │ ├── ReadModuleInfo.java │ │ ├── SimpleModuleInfoWriter.java │ │ ├── SimpleQueryBeanWriter.java │ │ └── Split.java └── resources │ └── META-INF │ ├── gradle │ └── incremental.annotation.processors │ └── services │ └── javax.annotation.processing.Processor └── test └── java └── io └── ebean └── querybean └── generator └── SplitTest.java /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | indent_style = space 6 | indent_size = 2 7 | 8 | [*.xml] 9 | max_line_length = 999999 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.orig 2 | .classpath 3 | .project 4 | .settings/ 5 | target/ 6 | logs/ 7 | log/ 8 | out/ 9 | 10 | # Intellij project files 11 | *.iml 12 | *.ipr 13 | *.iws 14 | .idea/ 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # querybean-generator 2 | Java annotation processor for generating query beans for type safe query construction. 3 | 4 | Refer to the documentation at: https://ebean.io/docs/query/query-beans 5 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.ebean 6 | querybean-generator 7 | 12.4.3-SNAPSHOT 8 | 9 | 10 | org.avaje 11 | java8-oss 12 | 2.2 13 | 14 | 15 | 16 | scm:git:git@github.com:ebean-orm/querybean-generator.git 17 | HEAD 18 | 19 | 20 | 21 | 22 | 23 | org.avaje.composite 24 | composite-testing 25 | 3.1 26 | test 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.apache.maven.plugins 36 | maven-compiler-plugin 37 | 3.2 38 | 39 | 1.8 40 | 1.8 41 | 42 | -proc:none 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/Append.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import java.io.IOException; 4 | import java.io.Writer; 5 | 6 | /** 7 | * Helper that wraps a writer with some useful methods to append content. 8 | */ 9 | class Append { 10 | 11 | private final Writer writer; 12 | 13 | Append(Writer writer) { 14 | this.writer = writer; 15 | } 16 | 17 | Append append(String content) { 18 | try { 19 | writer.append(content); 20 | return this; 21 | } catch (IOException e) { 22 | throw new RuntimeException(e); 23 | } 24 | } 25 | 26 | void close() { 27 | try { 28 | writer.flush(); 29 | writer.close(); 30 | } catch (IOException e) { 31 | throw new RuntimeException(e); 32 | } 33 | } 34 | 35 | Append eol() { 36 | try { 37 | writer.append("\n"); 38 | return this; 39 | } catch (IOException e) { 40 | throw new RuntimeException(e); 41 | } 42 | } 43 | 44 | /** 45 | * Append content with formatted arguments. 46 | */ 47 | Append append(String format, Object... args) { 48 | return append(String.format(format, args)); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/Constants.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | interface Constants { 4 | 5 | String AT_GENERATED = "@Generated(\"io.ebean.querybean.generator\")"; 6 | 7 | String AT_TYPEQUERYBEAN = "@TypeQueryBean(\"v1\")"; 8 | 9 | String GENERATED_9 = "javax.annotation.processing.Generated"; 10 | String GENERATED_8 = "javax.annotation.Generated"; 11 | 12 | String MAPPED_SUPERCLASS = "javax.persistence.MappedSuperclass"; 13 | String INHERITANCE = "javax.persistence.Inheritance"; 14 | String ENTITY = "javax.persistence.Entity"; 15 | String EMBEDDABLE = "javax.persistence.Embeddable"; 16 | String CONVERTER = "javax.persistence.Converter"; 17 | String EBEAN_COMPONENT = "io.ebean.annotation.EbeanComponent"; 18 | 19 | String DBARRAY = "io.ebean.annotation.DbArray"; 20 | String DBJSON = "io.ebean.annotation.DbJson"; 21 | String DBJSONB = "io.ebean.annotation.DbJsonB"; 22 | String DBNAME = "io.ebean.annotation.DbName"; 23 | 24 | String TQROOTBEAN = "io.ebean.typequery.TQRootBean"; 25 | String TQASSOCBEAN = "io.ebean.typequery.TQAssocBean"; 26 | String TQPROPERTY = "io.ebean.typequery.TQProperty"; 27 | String TYPEQUERYBEAN = "io.ebean.typequery.TypeQueryBean"; 28 | String DATABASE = "io.ebean.Database"; 29 | String DB = "io.ebean.DB"; 30 | String FETCHGROUP = "io.ebean.FetchGroup"; 31 | String QUERY = "io.ebean.Query"; 32 | String TRANSACTION = "io.ebean.Transaction"; 33 | 34 | String MODULEINFO = "io.ebean.config.ModuleInfo"; 35 | String METAINF_MANIFEST = "META-INF/ebean-generated-info.mf"; 36 | String METAINF_SERVICES_MODULELOADER = "META-INF/services/io.ebean.config.ModuleInfoLoader"; 37 | 38 | } -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/FindDbName.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.ExecutableElement; 6 | import javax.lang.model.element.TypeElement; 7 | import javax.lang.model.type.TypeKind; 8 | import javax.lang.model.type.TypeMirror; 9 | import javax.lang.model.util.Types; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | class FindDbName { 15 | 16 | /** 17 | * Return the value of the DbName annotation or null if it isn't found on the element. 18 | */ 19 | static String value(TypeElement element, Types typeUtils) { 20 | 21 | AnnotationMirror mirror = findDbNameMirror(element); 22 | if (mirror != null) { 23 | return readDbNameValue(mirror); 24 | } 25 | final TypeMirror typeMirror = element.getSuperclass(); 26 | if (typeMirror.getKind() == TypeKind.NONE) { 27 | return null; 28 | } 29 | final TypeElement element1 = (TypeElement)typeUtils.asElement(typeMirror); 30 | return value(element1, typeUtils); 31 | } 32 | 33 | private static String readDbNameValue(AnnotationMirror mirror) { 34 | 35 | final Map elementValues = mirror.getElementValues(); 36 | final Set> entries = elementValues.entrySet(); 37 | for (Map.Entry entry : entries) { 38 | if ("value".equals(entry.getKey().getSimpleName().toString())) { 39 | return (String) entry.getValue().getValue(); 40 | } 41 | } 42 | return null; 43 | } 44 | 45 | private static AnnotationMirror findDbNameMirror(TypeElement element) { 46 | final List mirrors = element.getAnnotationMirrors(); 47 | for (AnnotationMirror mirror : mirrors) { 48 | final String name = mirror.getAnnotationType().asElement().toString(); 49 | if (Constants.DBNAME.equals(name)) { 50 | return mirror; 51 | } 52 | } 53 | return null; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/ModuleMeta.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import java.util.List; 4 | 5 | class ModuleMeta { 6 | private final List entities; 7 | private final List other; 8 | 9 | ModuleMeta(List entities, List other) { 10 | this.entities = entities; 11 | this.other = other; 12 | } 13 | 14 | List getEntities() { 15 | return entities; 16 | } 17 | 18 | List getOther() { 19 | return other; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/ProcessingContext.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import javax.annotation.processing.Filer; 4 | import javax.annotation.processing.FilerException; 5 | import javax.annotation.processing.Messager; 6 | import javax.annotation.processing.ProcessingEnvironment; 7 | import javax.lang.model.SourceVersion; 8 | import javax.lang.model.element.AnnotationMirror; 9 | import javax.lang.model.element.AnnotationValue; 10 | import javax.lang.model.element.Element; 11 | import javax.lang.model.element.ElementKind; 12 | import javax.lang.model.element.ExecutableElement; 13 | import javax.lang.model.element.Modifier; 14 | import javax.lang.model.element.TypeElement; 15 | import javax.lang.model.element.VariableElement; 16 | import javax.lang.model.type.DeclaredType; 17 | import javax.lang.model.type.TypeKind; 18 | import javax.lang.model.type.TypeMirror; 19 | import javax.lang.model.util.ElementFilter; 20 | import javax.lang.model.util.Elements; 21 | import javax.lang.model.util.Types; 22 | import javax.tools.Diagnostic; 23 | import javax.tools.FileObject; 24 | import javax.tools.JavaFileObject; 25 | import javax.tools.StandardLocation; 26 | import java.io.FileNotFoundException; 27 | import java.io.IOException; 28 | import java.io.LineNumberReader; 29 | import java.io.Reader; 30 | import java.nio.file.NoSuchFileException; 31 | import java.util.ArrayList; 32 | import java.util.HashSet; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.Set; 36 | import java.util.TreeMap; 37 | import java.util.TreeSet; 38 | 39 | /** 40 | * Context for the source generation. 41 | */ 42 | class ProcessingContext implements Constants { 43 | 44 | private final ProcessingEnvironment processingEnv; 45 | private final Types typeUtils; 46 | private final Filer filer; 47 | private final Messager messager; 48 | private final Elements elementUtils; 49 | private final String generatedAnnotation; 50 | 51 | private final PropertyTypeMap propertyTypeMap = new PropertyTypeMap(); 52 | 53 | private final ReadModuleInfo readModuleInfo; 54 | 55 | /** 56 | * All entity packages regardless of DB (for META-INF/ebean-generated-info.mf). 57 | */ 58 | private final Set allEntityPackages = new TreeSet<>(); 59 | 60 | private final Set otherClasses = new TreeSet<>(); 61 | 62 | /** 63 | * The DB name prefixed entities. 64 | */ 65 | private final Set prefixEntities = new TreeSet<>(); 66 | 67 | /** 68 | * Entity classes for the default database. 69 | */ 70 | private final Set dbEntities = new TreeSet<>(); 71 | 72 | /** 73 | * Entity classes for non default databases. 74 | */ 75 | private final Map> otherDbEntities = new TreeMap<>(); 76 | 77 | /** 78 | * All loaded entities regardless of db (to detect ones we add back from loadedPrefixEntities). 79 | */ 80 | private final Set loaded = new HashSet<>(); 81 | 82 | /** 83 | * For partial compile the previous list of prefixed entity classes. 84 | */ 85 | private List loadedPrefixEntities = new ArrayList<>(); 86 | 87 | /** 88 | * The package for the generated ModuleInfoLoader. 89 | */ 90 | private String factoryPackage; 91 | 92 | ProcessingContext(ProcessingEnvironment processingEnv) { 93 | this.processingEnv = processingEnv; 94 | this.typeUtils = processingEnv.getTypeUtils(); 95 | this.filer = processingEnv.getFiler(); 96 | this.messager = processingEnv.getMessager(); 97 | this.elementUtils = processingEnv.getElementUtils(); 98 | 99 | boolean jdk8 = processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_8) <= 0; 100 | this.generatedAnnotation = generatedAnnotation(jdk8); 101 | this.readModuleInfo = new ReadModuleInfo(this); 102 | } 103 | 104 | TypeElement entityAnnotation() { 105 | return elementUtils.getTypeElement(ENTITY); 106 | } 107 | 108 | TypeElement embeddableAnnotation() { 109 | return elementUtils.getTypeElement(EMBEDDABLE); 110 | } 111 | 112 | TypeElement converterAnnotation() { 113 | return elementUtils.getTypeElement(CONVERTER); 114 | } 115 | 116 | TypeElement componentAnnotation() { 117 | return elementUtils.getTypeElement(EBEAN_COMPONENT); 118 | } 119 | 120 | private String generatedAnnotation(boolean jdk8) { 121 | if (jdk8) { 122 | return isTypeAvailable(GENERATED_8) ? GENERATED_8 : null; 123 | } 124 | return isTypeAvailable(GENERATED_9) ? GENERATED_9 : null; 125 | } 126 | 127 | private boolean isTypeAvailable(String canonicalName) { 128 | return null != elementUtils.getTypeElement(canonicalName); 129 | } 130 | 131 | /** 132 | * Gather all the fields (properties) for the given bean element. 133 | */ 134 | List allFields(Element element) { 135 | List list = new ArrayList<>(); 136 | gatherProperties(list, element); 137 | return list; 138 | } 139 | 140 | /** 141 | * Recursively gather all the fields (properties) for the given bean element. 142 | */ 143 | private void gatherProperties(List fields, Element element) { 144 | 145 | TypeElement typeElement = (TypeElement) element; 146 | TypeMirror superclass = typeElement.getSuperclass(); 147 | Element mappedSuper = typeUtils.asElement(superclass); 148 | if (isMappedSuperOrInheritance(mappedSuper)) { 149 | gatherProperties(fields, mappedSuper); 150 | } 151 | 152 | List allFields = ElementFilter.fieldsIn(element.getEnclosedElements()); 153 | for (VariableElement field : allFields) { 154 | if (!ignoreField(field)) { 155 | fields.add(field); 156 | } 157 | } 158 | } 159 | 160 | /** 161 | * Not interested in static, transient or Ebean internal fields. 162 | */ 163 | private boolean ignoreField(VariableElement field) { 164 | return isStaticOrTransient(field) || ignoreEbeanInternalFields(field); 165 | } 166 | 167 | private boolean ignoreEbeanInternalFields(VariableElement field) { 168 | String fieldName = field.getSimpleName().toString(); 169 | return fieldName.startsWith("_ebean") || fieldName.startsWith("_EBEAN"); 170 | } 171 | 172 | private boolean isStaticOrTransient(VariableElement field) { 173 | Set modifiers = field.getModifiers(); 174 | return (modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.TRANSIENT)); 175 | } 176 | 177 | private static boolean hasAnnotations(Element element, String... annotations) { 178 | return getAnnotation(element, annotations) != null; 179 | } 180 | 181 | private static AnnotationMirror getAnnotation(Element element, String... annotations) { 182 | if (element == null) { 183 | return null; 184 | } 185 | for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { 186 | final String name = annotationMirror.getAnnotationType().asElement().toString(); 187 | for (String annotation : annotations) { 188 | if (annotation.equals(name)) { 189 | return annotationMirror; 190 | } 191 | } 192 | } 193 | return null; 194 | } 195 | 196 | private boolean isMappedSuperOrInheritance(Element mappedSuper) { 197 | return hasAnnotations(mappedSuper, MAPPED_SUPERCLASS, INHERITANCE); 198 | } 199 | 200 | private boolean isEntityOrEmbedded(Element mappedSuper) { 201 | return hasAnnotations(mappedSuper, ENTITY, EMBEDDABLE); 202 | } 203 | 204 | boolean isEntity(Element element) { 205 | return hasAnnotations(element, ENTITY); 206 | } 207 | 208 | boolean isEmbeddable(Element element) { 209 | return hasAnnotations(element, EMBEDDABLE); 210 | } 211 | 212 | /** 213 | * Find the DbName annotation and return name if found. 214 | */ 215 | String findDbName(TypeElement element) { 216 | return FindDbName.value(element, typeUtils); 217 | } 218 | 219 | /** 220 | * Return true if it is a DbJson field. 221 | */ 222 | private static boolean dbJsonField(Element field) { 223 | return hasAnnotations(field, DBJSON, DBJSONB); 224 | } 225 | 226 | /** 227 | * Return true if it is a DbArray field. 228 | */ 229 | private static boolean dbArrayField(Element field) { 230 | return hasAnnotations(field, DBARRAY); 231 | } 232 | 233 | /** 234 | * Escape the type (e.g. java.lang.String) from the TypeMirror toString(). 235 | */ 236 | private static String typeDef(TypeMirror typeMirror) { 237 | if (typeMirror.getKind() == TypeKind.DECLARED) { 238 | DeclaredType declaredType = (DeclaredType) typeMirror; 239 | return declaredType.asElement().toString(); 240 | } else { 241 | return typeMirror.toString(); 242 | } 243 | } 244 | 245 | PropertyType getPropertyType(VariableElement field) { 246 | 247 | TypeMirror typeMirror = field.asType(); 248 | 249 | TypeMirror currentType = typeMirror; 250 | while (currentType != null) { 251 | PropertyType type = propertyTypeMap.getType(typeDef(currentType)); 252 | if (type != null) { 253 | // simple scalar type 254 | return type; 255 | } 256 | // go up in class hierarchy 257 | TypeElement fieldType = (TypeElement) typeUtils.asElement(currentType); 258 | currentType = (fieldType == null) ? null : fieldType.getSuperclass(); 259 | } 260 | 261 | if (dbJsonField(field)) { 262 | return propertyTypeMap.getDbJsonType(); 263 | } 264 | 265 | if (dbArrayField(field)) { 266 | // get generic parameter type 267 | DeclaredType declaredType = (DeclaredType) typeMirror; 268 | String fullType = typeDef(declaredType.getTypeArguments().get(0)); 269 | return new PropertyTypeArray(fullType, Split.shortName(fullType)); 270 | } 271 | 272 | Element fieldType = typeUtils.asElement(typeMirror); 273 | if (fieldType == null) { 274 | return null; 275 | } 276 | 277 | // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=544288 278 | fieldType = elementUtils.getTypeElement(fieldType.toString()); 279 | 280 | if (fieldType.getKind() == ElementKind.ENUM) { 281 | String fullType = typeDef(typeMirror); 282 | return new PropertyTypeEnum(fullType, Split.shortName(fullType)); 283 | } 284 | 285 | if (isEntityOrEmbedded(fieldType)) { 286 | // public QAssocContact contacts; 287 | return createPropertyTypeAssoc(typeDef(typeMirror)); 288 | } 289 | 290 | final PropertyType result; 291 | if (typeMirror.getKind() == TypeKind.DECLARED) { 292 | result = createManyTypeAssoc(field, (DeclaredType) typeMirror); 293 | } else { 294 | result = null; 295 | } 296 | 297 | if (result != null) { 298 | return result; 299 | } else { 300 | if (typeInstanceOf(typeMirror, "java.lang.Comparable")) { 301 | return new PropertyTypeScalarComparable(typeDef(typeMirror)); 302 | } else { 303 | return new PropertyTypeScalar(typeDef(typeMirror)); 304 | } 305 | } 306 | } 307 | 308 | private boolean typeInstanceOf(final TypeMirror typeMirror, final CharSequence desiredInterface) { 309 | TypeElement typeElement = (TypeElement) typeUtils.asElement(typeMirror); 310 | if (typeElement == null || typeElement.getQualifiedName().contentEquals("java.lang.Object")) { 311 | return false; 312 | } 313 | if (typeElement.getQualifiedName().contentEquals(desiredInterface)) { 314 | return true; 315 | } 316 | 317 | return typeInstanceOf(typeElement.getSuperclass(), desiredInterface) || 318 | typeElement 319 | .getInterfaces() 320 | .stream() 321 | .anyMatch(t -> typeInstanceOf(t, desiredInterface)); 322 | } 323 | 324 | private PropertyType createManyTypeAssoc(VariableElement field, DeclaredType declaredType) { 325 | List typeArguments = declaredType.getTypeArguments(); 326 | if (typeArguments.size() == 1) { 327 | Element argElement = typeUtils.asElement(typeArguments.get(0)); 328 | if (isEntityOrEmbedded(argElement)) { 329 | return createPropertyTypeAssoc(typeDef(argElement.asType())); 330 | } else { 331 | // look for targetEntity annotation attribute 332 | final String targetEntity = readTargetEntity(field); 333 | if (targetEntity != null) { 334 | final TypeElement element = elementUtils.getTypeElement(targetEntity); 335 | if (isEntityOrEmbedded(element)) { 336 | return createPropertyTypeAssoc(typeDef(element.asType())); 337 | } 338 | } 339 | } 340 | } 341 | return null; 342 | } 343 | 344 | private String readTargetEntity(Element declaredType) { 345 | for (AnnotationMirror annotation : declaredType.getAnnotationMirrors()) { 346 | final Object targetEntity = readTargetEntityFromAnnotation(annotation); 347 | if (targetEntity != null) { 348 | return targetEntity.toString(); 349 | } 350 | } 351 | return null; 352 | } 353 | 354 | private static Object readTargetEntityFromAnnotation(AnnotationMirror mirror) { 355 | for (Map.Entry entry : mirror.getElementValues().entrySet()) { 356 | if ("targetEntity".equals(entry.getKey().getSimpleName().toString())) { 357 | return entry.getValue().getValue(); 358 | } 359 | } 360 | return null; 361 | } 362 | 363 | /** 364 | * Create the QAssoc PropertyType. 365 | */ 366 | private PropertyType createPropertyTypeAssoc(String fullName) { 367 | 368 | String[] split = Split.split(fullName); 369 | String propertyName = "QAssoc" + split[1]; 370 | String packageName = packageAppend(split[0]); 371 | return new PropertyTypeAssoc(propertyName, packageName); 372 | } 373 | 374 | /** 375 | * Prepend the package to the suffix taking null into account. 376 | */ 377 | private String packageAppend(String origPackage) { 378 | if (origPackage == null) { 379 | return "query.assoc"; 380 | } else { 381 | return origPackage + "." + "query.assoc"; 382 | } 383 | } 384 | 385 | /** 386 | * Create a file writer for the given class name. 387 | */ 388 | JavaFileObject createWriter(String factoryClassName, Element originatingElement) throws IOException { 389 | return filer.createSourceFile(factoryClassName, originatingElement); 390 | } 391 | 392 | /** 393 | * Create a file writer for the given class name without an originating element. 394 | */ 395 | JavaFileObject createWriter(String factoryClassName) throws IOException { 396 | return filer.createSourceFile(factoryClassName); 397 | } 398 | 399 | void logError(Element e, String msg, Object... args) { 400 | messager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args), e); 401 | } 402 | 403 | void logNote(String msg, Object... args) { 404 | messager.printMessage(Diagnostic.Kind.NOTE, String.format(msg, args)); 405 | } 406 | 407 | boolean isGeneratedAvailable() { 408 | return generatedAnnotation != null; 409 | } 410 | 411 | String getGeneratedAnnotation() { 412 | return generatedAnnotation; 413 | } 414 | 415 | void readModuleInfo() { 416 | String factory = loadMetaInfServices(); 417 | if (factory != null) { 418 | TypeElement factoryType = elementUtils.getTypeElement(factory); 419 | if (factoryType != null) { 420 | // previous prefixed entities to add back for partial compile 421 | final ModuleMeta read = readModuleInfo.read(factoryType); 422 | loadedPrefixEntities.addAll(read.getEntities()); 423 | otherClasses.addAll(read.getOther()); 424 | } 425 | } 426 | } 427 | 428 | /** 429 | * Register an entity with optional dbName. 430 | */ 431 | void addEntity(String beanFullName, String dbName) { 432 | 433 | loaded.add(beanFullName); 434 | final String pkg = packageOf(beanFullName); 435 | if (pkg != null) { 436 | allEntityPackages.add(pkg); 437 | updateFactoryPackage(pkg); 438 | } 439 | if (dbName != null) { 440 | prefixEntities.add(dbName + ":" + beanFullName); 441 | otherDbEntities.computeIfAbsent(dbName, s -> new TreeSet<>()).add(beanFullName); 442 | } else { 443 | prefixEntities.add(beanFullName); 444 | dbEntities.add(beanFullName); 445 | } 446 | } 447 | 448 | /** 449 | * Add back entity classes for partial compile. 450 | */ 451 | int complete() { 452 | int added = 0; 453 | for (String oldPrefixEntity : loadedPrefixEntities) { 454 | // maybe split as dbName:entityClass 455 | final String[] prefixEntityClass = oldPrefixEntity.split(":"); 456 | 457 | String dbName = null; 458 | String entityClass; 459 | if (prefixEntityClass.length > 1) { 460 | dbName = prefixEntityClass[0]; 461 | entityClass = prefixEntityClass[1]; 462 | } else { 463 | entityClass = prefixEntityClass[0]; 464 | } 465 | if (!loaded.contains(entityClass)) { 466 | addEntity(entityClass, dbName); 467 | added++; 468 | } 469 | } 470 | return added; 471 | } 472 | 473 | private String packageOf(String beanFullName) { 474 | final int pos = beanFullName.lastIndexOf('.'); 475 | if (pos > -1) { 476 | return beanFullName.substring(0, pos); 477 | } 478 | return null; 479 | } 480 | 481 | private void updateFactoryPackage(String pkg) { 482 | if (pkg != null && (factoryPackage == null || factoryPackage.length() > pkg.length())) { 483 | factoryPackage = pkg; 484 | } 485 | } 486 | 487 | FileObject createMetaInfServicesWriter() throws IOException { 488 | return createMetaInfWriter(METAINF_SERVICES_MODULELOADER); 489 | } 490 | 491 | FileObject createManifestWriter() throws IOException { 492 | return createMetaInfWriter(METAINF_MANIFEST); 493 | } 494 | 495 | FileObject createMetaInfWriter(String target) throws IOException { 496 | return filer.createResource(StandardLocation.CLASS_OUTPUT, "", target); 497 | } 498 | 499 | public boolean hasOtherClasses() { 500 | return !otherClasses.isEmpty(); 501 | } 502 | 503 | public Set getOtherClasses() { 504 | return otherClasses; 505 | } 506 | 507 | void addOther(Element element) { 508 | otherClasses.add(element.toString()); 509 | } 510 | 511 | Set getPrefixEntities() { 512 | return prefixEntities; 513 | } 514 | 515 | Set getDbEntities() { 516 | return dbEntities; 517 | } 518 | 519 | Map> getOtherDbEntities() { 520 | return otherDbEntities; 521 | } 522 | 523 | 524 | Set getAllEntityPackages() { 525 | return allEntityPackages; 526 | } 527 | 528 | String getFactoryPackage() { 529 | return factoryPackage != null ? factoryPackage : "unknown"; 530 | } 531 | 532 | /** 533 | * Return the class name of the generated ModuleInfoLoader 534 | * (such that we can read the current meta data for partial compile). 535 | */ 536 | String loadMetaInfServices() { 537 | try { 538 | FileObject fileObject = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", METAINF_SERVICES_MODULELOADER); 539 | if (fileObject != null) { 540 | Reader reader = fileObject.openReader(true); 541 | LineNumberReader lineReader = new LineNumberReader(reader); 542 | String line = lineReader.readLine(); 543 | if (line != null) { 544 | return line.trim(); 545 | } 546 | } 547 | 548 | } catch (FileNotFoundException | NoSuchFileException e) { 549 | // ignore - no services file yet 550 | } catch (FilerException e) { 551 | logNote(null, "FilerException reading services file: " + e.getMessage()); 552 | } catch (Exception e) { 553 | e.printStackTrace(); 554 | logError(null, "Error reading services file: " + e.getMessage()); 555 | } 556 | return null; 557 | } 558 | } 559 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/Processor.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import javax.annotation.processing.AbstractProcessor; 4 | import javax.annotation.processing.ProcessingEnvironment; 5 | import javax.annotation.processing.RoundEnvironment; 6 | import javax.lang.model.SourceVersion; 7 | import javax.lang.model.element.Element; 8 | import javax.lang.model.element.TypeElement; 9 | import java.util.Arrays; 10 | import java.util.LinkedHashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * Process compiled entity beans and generates 'query beans' for them. 15 | */ 16 | public class Processor extends AbstractProcessor implements Constants { 17 | 18 | private ProcessingContext processingContext; 19 | 20 | public Processor() { 21 | } 22 | 23 | @Override 24 | public synchronized void init(ProcessingEnvironment processingEnv) { 25 | super.init(processingEnv); 26 | this.processingContext = new ProcessingContext(processingEnv); 27 | } 28 | 29 | @Override 30 | public Set getSupportedAnnotationTypes() { 31 | Set annotations = new LinkedHashSet<>(); 32 | annotations.add(ENTITY); 33 | annotations.add(EMBEDDABLE); 34 | annotations.add(CONVERTER); 35 | annotations.add(EBEAN_COMPONENT); 36 | annotations.add(MODULEINFO); 37 | return annotations; 38 | } 39 | 40 | @Override 41 | public SourceVersion getSupportedSourceVersion() { 42 | return SourceVersion.latest(); 43 | } 44 | 45 | @Override 46 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 47 | processingContext.readModuleInfo(); 48 | int count = processEntities(roundEnv); 49 | processOthers(roundEnv); 50 | final int loaded = processingContext.complete(); 51 | if (roundEnv.processingOver()) { 52 | writeModuleInfoBean(); 53 | } 54 | if (count > 0) { 55 | String msg = "Ebean APT generated %s query beans, loaded %s others - META-INF/ebean-generated-info.mf entity-packages: %s"; 56 | processingContext.logNote(msg, count, loaded, processingContext.getAllEntityPackages()); 57 | } 58 | return true; 59 | } 60 | 61 | private int processEntities(RoundEnvironment roundEnv) { 62 | int count = 0; 63 | for (Element element : roundEnv.getElementsAnnotatedWith(processingContext.embeddableAnnotation())) { 64 | generateQueryBeans(element); 65 | count++; 66 | } 67 | for (Element element : roundEnv.getElementsAnnotatedWith(processingContext.entityAnnotation())) { 68 | generateQueryBeans(element); 69 | count++; 70 | } 71 | return count; 72 | } 73 | 74 | private void processOthers(RoundEnvironment round) { 75 | processOthers(round, processingContext.converterAnnotation()); 76 | processOthers(round, processingContext.componentAnnotation()); 77 | } 78 | 79 | private void processOthers(RoundEnvironment roundEnv, TypeElement otherType) { 80 | if (otherType != null) { 81 | for (Element element : roundEnv.getElementsAnnotatedWith(otherType)) { 82 | processingContext.addOther(element); 83 | } 84 | } 85 | } 86 | 87 | private void writeModuleInfoBean() { 88 | try { 89 | SimpleModuleInfoWriter writer = new SimpleModuleInfoWriter(processingContext); 90 | writer.write(); 91 | } catch (Throwable e) { 92 | e.printStackTrace(); 93 | processingContext.logError(null, "Failed to write ModuleInfoLoader error:" + e + " stack:" + Arrays.toString(e.getStackTrace())); 94 | } 95 | } 96 | 97 | private void generateQueryBeans(Element element) { 98 | try { 99 | SimpleQueryBeanWriter beanWriter = new SimpleQueryBeanWriter((TypeElement) element, processingContext); 100 | beanWriter.writeRootBean(); 101 | beanWriter.writeAssocBean(); 102 | } catch (Throwable e) { 103 | e.printStackTrace(); 104 | processingContext.logError(element, "Error generating query beans: " + e); 105 | } 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/PropertyMeta.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | /** 4 | * Meta data for a property. 5 | */ 6 | class PropertyMeta { 7 | 8 | /** 9 | * The property name. 10 | */ 11 | private final String name; 12 | 13 | /** 14 | * The property type. 15 | */ 16 | private final PropertyType type; 17 | 18 | /** 19 | * Construct given the property name and type. 20 | */ 21 | PropertyMeta(String name, PropertyType type) { 22 | this.name = name; 23 | this.type = type; 24 | } 25 | 26 | /** 27 | * Return the type definition given the type short name and flag indicating if it is an associated bean type. 28 | */ 29 | private String getTypeDefn(String shortName, boolean assoc) { 30 | return type.getTypeDefn(shortName, assoc); 31 | } 32 | 33 | void writeFieldDefn(Append writer, String shortName, boolean assoc) { 34 | 35 | writer.append(" public "); 36 | writer.append(getTypeDefn(shortName, assoc)); 37 | writer.append(" ").append(name).append(";"); 38 | } 39 | 40 | void writeFieldAliasDefn(Append writer, String shortName) { 41 | 42 | writer.append(" public static "); 43 | writer.append(getTypeDefn(shortName, false)); 44 | writer.append(" ").append(name).append(" = _alias.").append(name).append(";"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/ebean/querybean/generator/PropertyType.java: -------------------------------------------------------------------------------- 1 | package io.ebean.querybean.generator; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * Property type definition. 7 | */ 8 | class PropertyType { 9 | 10 | /** 11 | * The property type className or primitive short name. 12 | */ 13 | final String propertyType; 14 | 15 | /** 16 | * Construct with a className of primitive name for the type. 17 | */ 18 | PropertyType(String propertyType) { 19 | this.propertyType = propertyType; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return propertyType; 25 | } 26 | 27 | /** 28 | * Return the type definition for this property. 29 | * 30 | * @param shortName The short name of the property type 31 | * @param assoc flag set to true if the property is on an association bean 32 | */ 33 | String getTypeDefn(String shortName, boolean assoc) { 34 | if (assoc) { 35 | // PLong 36 | return propertyType + ""; 37 | } else { 38 | // PLong 39 | return propertyType + ""; 40 | } 41 | } 42 | 43 | /** 44 | * Add any required imports for this property to the allImports set. 45 | */ 46 | void addImports(Set allImports) { 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(Set allImports) { 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(Set allImports) { 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(Set allImports) { 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(Set allImports) { 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 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 elementValues = mirror.getElementValues(); 42 | final Set> entries = elementValues.entrySet(); 43 | 44 | for (Map.Entry 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 | Iterator importsIterator = 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%s extends 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() and fetch() ").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 | } --------------------------------------------------------------------------------