diff --git a/pom.xml b/pom.xml index 6ef6eb5..5012a2c 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,9 @@ + src/main/resources + + . LICENSE @@ -57,7 +60,6 @@ - org.apache.maven.plugins @@ -68,6 +70,25 @@ 1.8 true + + + default-compile + + -proc:none + + ua/net/uid/utils/db/orm/Processor.java + + + + + + compile-project + compile + + compile + + + diff --git a/src/main/java/ua/net/uid/utils/db/annotation/Column.java b/src/main/java/ua/net/uid/utils/db/annotation/Column.java new file mode 100644 index 0000000..dafeca6 --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/annotation/Column.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.FIELD) +public @interface Column { + String name() default ""; + boolean auto() default false; +} diff --git a/src/main/java/ua/net/uid/utils/db/annotation/Columns.java b/src/main/java/ua/net/uid/utils/db/annotation/Columns.java new file mode 100644 index 0000000..c9a20e7 --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/annotation/Columns.java @@ -0,0 +1,26 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.FIELD) +public @interface Columns { +} diff --git a/src/main/java/ua/net/uid/utils/db/annotation/PrimaryKey.java b/src/main/java/ua/net/uid/utils/db/annotation/PrimaryKey.java new file mode 100644 index 0000000..37486c2 --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/annotation/PrimaryKey.java @@ -0,0 +1,26 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.FIELD) +public @interface PrimaryKey { +} \ No newline at end of file diff --git a/src/main/java/ua/net/uid/utils/db/annotation/Select.java b/src/main/java/ua/net/uid/utils/db/annotation/Select.java new file mode 100644 index 0000000..18aea34 --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/annotation/Select.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import ua.net.uid.utils.db.Fetcher; + +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +public @interface Select { + String sql(); + Class fetcher() default Fetcher.class; +} diff --git a/src/main/java/ua/net/uid/utils/db/annotation/Table.java b/src/main/java/ua/net/uid/utils/db/annotation/Table.java new file mode 100644 index 0000000..af94c93 --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/annotation/Table.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface Table { + String value(); +} diff --git a/src/main/java/ua/net/uid/utils/db/annotation/Update.java b/src/main/java/ua/net/uid/utils/db/annotation/Update.java new file mode 100644 index 0000000..8b05336 --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/annotation/Update.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +public @interface Update { + String sql(); + boolean returnAuto() default false; +} diff --git a/src/main/java/ua/net/uid/utils/db/orm/EntityColumn.java b/src/main/java/ua/net/uid/utils/db/orm/EntityColumn.java new file mode 100644 index 0000000..1f1eba5 --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/orm/EntityColumn.java @@ -0,0 +1,131 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.orm; + +import java.io.IOException; +import java.util.Date; +import java.util.TimeZone; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import ua.net.uid.utils.db.annotation.Column; +import ua.net.uid.utils.db.annotation.PrimaryKey; + +public class EntityColumn extends EntityField { + + private final String name; + private final boolean primary; + + public EntityColumn(VariableElement variableElement, EntityField parentField) { + super(variableElement, parentField); + Column column = variableElement.getAnnotation(Column.class); + name = "".equals(column.name()) ? variableElement.getSimpleName().toString() : column.name(); + primary = variableElement.getAnnotation(PrimaryKey.class) != null; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean hasPrimary() { + return primary; + } + + @Override + public boolean isPrimaryKey() { + return primary; + } + + @Override + public EntityField getPrimaryKey() { + return primary ? this : null; + } + + @Override + public void writeFetch(Appendable builder, String item) throws IOException { + builder.append(" ").append(item).append('.'); + final boolean dirrect = !writeSetter(builder); + builder.append(dirrect ? " = " : "("); + final TypeMirror typeMirror = getVariableElement().asType(); + switch (typeMirror.getKind()) { + case ARRAY: + final TypeMirror componentType = ((ArrayType) typeMirror).getComponentType(); + if (componentType.getKind() == TypeKind.BYTE) { + builder.append("result.getBytes(\"").append(name).append("\")"); + } else { + builder.append("result.getArray(\"").append(name).append("\").getArray()"); + } + break; + case DECLARED: + final TypeElement typeElement = (TypeElement) ((DeclaredType) typeMirror).asElement(); + if (Utils.isAssignableFrom(typeElement, TimeZone.class)) { + builder.append("java.util.TimeZone.getTimeZone(result.getString(\"").append(name).append("\"))"); + } else if (Utils.isAssignableFrom(typeElement, Date.class)) { + builder.append("result.getTimestamp(\"").append(name).append("\")"); + } else if (Utils.isAssignableFrom(typeElement, String.class)) { + builder.append("result.getString(\"").append(name).append("\")"); + } else if (typeElement.getKind() == ElementKind.ENUM) { + builder.append(typeElement.getQualifiedName()).append(".valueOf(result.getString(\"").append(name).append("\"))"); + } else { + builder.append('(').append(typeElement.getQualifiedName()).append(")result.getObject(\"").append(name).append("\")"); + } + break; + default: + switch (typeMirror.getKind()) { + case BOOLEAN: + builder.append("result.getBoolean(\"").append(name).append("\")"); + break; + case BYTE: + builder.append("result.getByte(\"").append(name).append("\")"); + break; + case CHAR: + builder.append("result.getString(\"").append(name).append("\").charAt(0)"); + break; + case SHORT: + builder.append("result.getShort(\"").append(name).append("\")"); + break; + case INT: + builder.append("result.getInt(\"").append(name).append("\")"); + break; + case LONG: + builder.append("result.getLong(\"").append(name).append("\")"); + break; + case FLOAT: + builder.append("result.getFloat(\"").append(name).append("\")"); + break; + case DOUBLE: + builder.append("result.getDouble(\"").append(name).append("\")"); + break; + default: + builder.append("result.getObject(\"").append(name).append("\")"); + break; + } + break; + } + builder.append(dirrect ? ";\n" : ");\n"); + } + + @Override + public void writeColums(Appendable builder, String postfix, String div) throws IOException { + builder.append('"').append(name).append('"').append(postfix); + } +} diff --git a/src/main/java/ua/net/uid/utils/db/orm/EntityField.java b/src/main/java/ua/net/uid/utils/db/orm/EntityField.java new file mode 100644 index 0000000..e3825a3 --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/orm/EntityField.java @@ -0,0 +1,58 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.orm; + + +import java.io.IOException; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; + +public abstract class EntityField { + private final EntityField parentField; + private final VariableElement variableElement; + + public EntityField(VariableElement variableElement, EntityField parentField) { + this.parentField = parentField; + this.variableElement = variableElement; + } + public EntityField getParent() { return parentField; } + public VariableElement getVariableElement() { return variableElement; } + public String getName() { return variableElement.toString(); } + + public abstract boolean hasPrimary(); + public abstract boolean isPrimaryKey(); + public abstract EntityField getPrimaryKey(); + + public abstract void writeFetch(Appendable builder, String item) throws IOException; + + public boolean writeSetter(Appendable builder) throws IOException { + final TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement(); + final ExecutableElement setterElement = Utils.findSetter(getName(), typeElement); + if (setterElement != null) { + builder.append(setterElement.getSimpleName().toString()); + return true; + } else if (!variableElement.getModifiers().contains(Modifier.PRIVATE)) { + builder.append(variableElement.toString()); + return false; + } else { + throw new IOException("can`t find setter for "+variableElement.toString()); + } + } + + public abstract void writeColums(Appendable builder, String postfix, String div) throws IOException; +} diff --git a/src/main/java/ua/net/uid/utils/db/orm/EntityMeta.java b/src/main/java/ua/net/uid/utils/db/orm/EntityMeta.java new file mode 100644 index 0000000..753f75e --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/orm/EntityMeta.java @@ -0,0 +1,138 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.orm; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.processing.Filer; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.tools.JavaFileObject; +import ua.net.uid.utils.db.annotation.Column; +import ua.net.uid.utils.db.annotation.Columns; +import ua.net.uid.utils.db.annotation.PrimaryKey; +import ua.net.uid.utils.db.annotation.Table; + +public class EntityMeta { + private final Processor processor; + private final TypeElement typeElement; + private final PackageElement packageElement; + private final String tableName; + private final Map columns = new LinkedHashMap<>(); + private final List fields = new ArrayList<>(); + private final EntityField primaryField; + //private final EntityColumn auto; + private final String fetcherSimpleName; + private final String fetcherQualifiedName; + private final boolean fetcherFromResulSet; + + public EntityMeta(Processor processor, TypeElement typeElement) { + this.processor = processor; + this.typeElement = typeElement; + packageElement = Utils.getPackageElement(typeElement); + + final Table table = typeElement.getAnnotation(Table.class); + tableName = table == null ? typeElement.getSimpleName().toString() : table.value(); + + boolean simple = false; + EntityField primary = null; + for (Element element : typeElement.getEnclosedElements()) { + if (element.getKind() == ElementKind.CONSTRUCTOR) { + final ExecutableElement executable = (ExecutableElement) element; + if (executable.getParameters().size() == 1 && Utils.RESULTSET_CLASS.equals(executable.getParameters().get(0).asType().toString())) { + simple = true; + } + } else if (element.getKind() == ElementKind.FIELD) { + final VariableElement variable = (VariableElement) element; + EntityField field = null; + if (variable.getAnnotation(Column.class) != null) { + field = new EntityColumn(variable, null); + columns.put(field.getName(), (EntityColumn) field); + } else if (variable.getAnnotation(PrimaryKey.class) != null || variable.getAnnotation(Columns.class) != null) { + field = new EntityObject(variable, columns, null); + } + if (field != null) { + fields.add(field); + if (field.hasPrimary()) primary = field.getPrimaryKey(); + //if (_auto == null && field.isAuto()) _auto = field.getAuto(); + } + } + } + primaryField = primary; + fetcherSimpleName = typeElement.getQualifiedName().toString() + .substring(packageElement.getQualifiedName().length() + 1) + .replace('.', '_') + Utils.FETCHER_SUFFIX; + fetcherQualifiedName = packageElement.getQualifiedName() + "." + fetcherSimpleName; + System.err.println("\t" + fetcherQualifiedName); + fetcherFromResulSet = simple; + } + + public Processor getProcessor() { return processor; } + public TypeElement getTypeElement() { return typeElement; } + public PackageElement getPackageElement() { return packageElement; } + public String getTableName() { return tableName; } + public String getFetcherSimpleName() { return fetcherSimpleName; } + public String getFetcherQualifiedName() { return fetcherQualifiedName; } + public EntityField getPrimaryKey() { return primaryField; } + + public void generateFetcher(Filer filer) throws IOException { + JavaFileObject fileObject = filer.createSourceFile(fetcherQualifiedName, packageElement, typeElement); + try (final Writer writer = fileObject.openWriter()) { + final BufferedWriter builder = new BufferedWriter(writer); + writeFetcher(builder); + builder.flush(); + } + } + + private void writeFetcher(Appendable builder) throws IOException { + String simpleName = typeElement.getSimpleName().toString(); + builder.append("package ").append(packageElement.getQualifiedName()) + .append(";\npublic class ").append(fetcherSimpleName) + .append(" implements ").append(Utils.FETCHER_CLASS) + .append('<').append(simpleName).append("> {\n @Override\n public ") + .append(simpleName).append(" fetch(final ").append(Utils.RESULTSET_CLASS) + .append(" result) throws ").append(Utils.SQLEXCEPTION_CLASS).append(" {\n"); + if (fetcherFromResulSet) { + builder.append(" return new ").append(simpleName).append("(result);\n"); + } else { + builder.append(" ").append(simpleName).append(" item = new ").append(simpleName).append("();\n"); + for (EntityField field : fields) { + field.writeFetch(builder, "item"); + } + builder.append(" return item;\n"); + } + builder.append(" }\n public static ").append(fetcherSimpleName) + .append(" getInstance() { return Holder.INSTANCE; }\n private ") + .append(fetcherSimpleName) + .append("() {}\n private static class Holder {\n private static final ") + .append(fetcherSimpleName) + .append(" INSTANCE = new ").append(fetcherSimpleName).append("();\n }\n}\n"); + } + + /*public void writeDelete(Appendable builder, String param) throws IOException { + builder.append("\"DELETE FROM \\\"").append(tableName). + }*/ + +} diff --git a/src/main/java/ua/net/uid/utils/db/orm/EntityObject.java b/src/main/java/ua/net/uid/utils/db/orm/EntityObject.java new file mode 100644 index 0000000..3fecabd --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/orm/EntityObject.java @@ -0,0 +1,97 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.orm; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import ua.net.uid.utils.db.annotation.Column; +import ua.net.uid.utils.db.annotation.Columns; +import ua.net.uid.utils.db.annotation.PrimaryKey; + +public class EntityObject extends EntityField { + private final TypeElement typeElement; + private final List fields = new ArrayList<>(); + private final boolean isPrimary; + private final EntityField primaryField; + + public EntityObject(VariableElement variableElement, Map columns, EntityField parentField) { + super(variableElement, parentField); + typeElement = (TypeElement) ((DeclaredType) variableElement.asType()).asElement(); + isPrimary = variableElement.getAnnotation(PrimaryKey.class) != null; + EntityField primary = null; + for (Element element : typeElement.getEnclosedElements()) { + if (element.getKind() == ElementKind.FIELD/* || element.getKind() == ElementKind.ENUM_CONSTANT*/) { + final VariableElement variable = (VariableElement) element; + EntityField field = null; + if (variable.getAnnotation(Column.class) != null) { + field = new EntityColumn(variable, null); + columns.put(field.getName(), (EntityColumn) field); + } else if (variable.getAnnotation(PrimaryKey.class) != null || variable.getAnnotation(Columns.class) != null) { + field = new EntityObject(variable, columns, null); + } + if (field != null) { + fields.add(field); + if (field.hasPrimary()) primary = field.getPrimaryKey(); + //if (_auto == null && field.isAuto()) _auto = field.getAuto(); + } + } + } + + primaryField = isPrimary ? this : primary; + } + + @Override + public boolean hasPrimary() { return primaryField != null; } + + @Override + public boolean isPrimaryKey() { return isPrimary; } + + @Override + public EntityField getPrimaryKey() { return primaryField; } + + @Override + public void writeFetch(Appendable builder, String item) throws IOException { + final String varName = item + '_' + getVariableElement().getSimpleName(); + builder.append(" final ") + .append(typeElement.getQualifiedName()).append(' ').append(varName) + .append(" = new ").append(typeElement.getQualifiedName()).append("();\n"); + for (EntityField field : fields) { + field.writeFetch(builder, varName); + } + builder.append(" ").append(item).append('.'); + if (writeSetter(builder)) { + builder.append('(').append(varName).append(");\n"); + } else { + builder.append(" = ").append(varName).append(";\n"); + } + } + + @Override + public void writeColums(Appendable builder, String postfix, String div) throws IOException { + boolean comma = false; + for(EntityField field : fields) { + if (comma) builder.append(div); else comma = true; + field.writeColums(builder, postfix, div); + } + } +} diff --git a/src/main/java/ua/net/uid/utils/db/orm/Processor.java b/src/main/java/ua/net/uid/utils/db/orm/Processor.java new file mode 100644 index 0000000..f82b712 --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/orm/Processor.java @@ -0,0 +1,89 @@ +/* + * Copyright 2020 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.orm; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import ua.net.uid.utils.db.annotation.Table; + +@SupportedSourceVersion(SourceVersion.RELEASE_8) +@SupportedAnnotationTypes({ + "ua.net.uid.utils.db.annotation.Table", + "ua.net.uid.utils.db.annotation.Column", + "ua.net.uid.utils.db.annotation.Columns", + +/* + "ua.net.uid.utils.orm.annotations.Table", + "ua.net.uid.utils.orm.annotations.Select", + "ua.net.uid.utils.orm.annotations.Update", + */ +}) +public class Processor extends AbstractProcessor { + + private final Map entityMetas = new HashMap<>(); + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnvironment) { + if (annotations.isEmpty()) { + return false; + } + + message(Diagnostic.Kind.ERROR, "!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + + roundEnvironment + .getElementsAnnotatedWith(Table.class) + .stream() + .filter(element -> element instanceof TypeElement) + .forEachOrdered(element -> getEntityMeta((TypeElement) element)); + //createDAOMeta(Select.class, roundEnv); + //createDAOMeta(Update.class, roundEnv); + /*for (DAOMeta generator : generators.values()) { + try { + generator.generate(processingEnv.getFiler()); + } catch (Exception ex) { + message(Diagnostic.Kind.ERROR, generator.getTypeElement().getQualifiedName()+" : "+ex.getMessage()); + } + }*/ + return true; + } + + public EntityMeta getEntityMeta(TypeElement element) { + EntityMeta result = entityMetas.get(element); + message(Diagnostic.Kind.NOTE, element.getQualifiedName()); + if (result == null) { + try { + result = new EntityMeta(this, (TypeElement) element); + result.generateFetcher(processingEnv.getFiler()); + entityMetas.put((TypeElement) element, result); + } catch (Exception ex) { + message(Diagnostic.Kind.ERROR, ((TypeElement) element).getQualifiedName() + " : " + ex.getMessage()); + } + } + return result; + } + + public void message(Diagnostic.Kind kind, CharSequence message) { + processingEnv.getMessager().printMessage(kind, message); + } +} diff --git a/src/main/java/ua/net/uid/utils/db/orm/Utils.java b/src/main/java/ua/net/uid/utils/db/orm/Utils.java new file mode 100644 index 0000000..1f13538 --- /dev/null +++ b/src/main/java/ua/net/uid/utils/db/orm/Utils.java @@ -0,0 +1,103 @@ +/* + * Copyright 2021 nightfall. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ua.net.uid.utils.db.orm; + +import java.sql.ResultSet; +import java.sql.SQLException; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeKind; +import javax.lang.model.util.ElementFilter; +import ua.net.uid.utils.db.Fetcher; + +public class Utils { + public static final String FETCHER_SUFFIX = "$$Fetcher"; + public static final String FETCHER_CLASS = Fetcher.class.getCanonicalName(); + public static final String RESULTSET_CLASS = ResultSet.class.getCanonicalName(); + public static final String SQLEXCEPTION_CLASS = SQLException.class.getCanonicalName(); + + public static PackageElement getPackageElement(Element element) { + while(element != null && !(element instanceof PackageElement)) { + element = element.getEnclosingElement(); + } + return (PackageElement)element; + } + + public static boolean isAssignableFrom(TypeElement element, Class type) { + if (type.getName().equals(element.getQualifiedName().toString())) { + return true; + } + for (Class intf : type.getInterfaces()) { + if (isAssignableFrom(element, intf)) { + return true; + } + } + return (type.getSuperclass() != null && isAssignableFrom(element, type.getSuperclass())); + } + + public static ExecutableElement findSetter(String fieldName, TypeElement classElement) { + final String setter = buildName("set", fieldName); + Iterable methods = ElementFilter.methodsIn(classElement.getEnclosedElements()); + for (ExecutableElement method : methods) { + String methodName = method.getSimpleName().toString(); + if (setter.equals(methodName) && method.getParameters().size() == 1 && method.getReturnType().getKind().equals(TypeKind.VOID)) { + return method; + } + } + for (ExecutableElement method : methods) { + String methodName = method.getSimpleName().toString(); + if (fieldName.equals(methodName) && method.getParameters().size() == 1 && method.getReturnType().getKind().equals(TypeKind.VOID)) { + return method; + } + } + return null; + } + + public static ExecutableElement findGetter(String fieldName, TypeElement classElement) { + final String getter1 = buildName("get", fieldName); + final String getter2 = buildName("is", fieldName); + Iterable methods = ElementFilter.methodsIn(classElement.getEnclosedElements()); + for (ExecutableElement method : methods) { + String methodName = method.getSimpleName().toString(); + if ((getter1.equals(methodName) || getter2.equals(methodName)) && method.getParameters().isEmpty()) { + return method; + } + } + for (ExecutableElement method : methods) { + String methodName = method.getSimpleName().toString(); + if (fieldName.equals(methodName) && method.getParameters().isEmpty()) { + return method; + } + } + return null; + } + + private static String buildName(String prefix, String suffix) { + final StringBuilder builder = new StringBuilder(prefix.length() + suffix.length()); + builder.append(prefix); + if (suffix.length() > 0) { + builder.append(Character.toUpperCase(suffix.charAt(0))); + if (suffix.length() > 1) { + builder.append(suffix, 1, suffix.length()); + } + } + return builder.toString(); + } + + private Utils() {} +} diff --git a/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000..359e411 --- /dev/null +++ b/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +ua.net.uid.utils.db.orm.Processor diff --git a/src/test/java/ua/net/uid/utils/db/dao/DAOAbstractTest.java b/src/test/java/ua/net/uid/utils/db/dao/DAOAbstractTest.java index 9e64e47..8ad809e 100644 --- a/src/test/java/ua/net/uid/utils/db/dao/DAOAbstractTest.java +++ b/src/test/java/ua/net/uid/utils/db/dao/DAOAbstractTest.java @@ -255,7 +255,7 @@ @Override public boolean insert(Item item) throws SQLException { - Processor processor = new QueryBuilder().append("INSERT INTO ").append(getTableName()).append( + ua.net.uid.utils.db.Processor processor = new QueryBuilder().append("INSERT INTO ").append(getTableName()).append( " (title, value, disabled, modified) VALUES (?,?,?,?)" , item.getTitle(), item.getValue(), item.isDisabled(), item.getModified() ).on(getSession());