Java tutorial
/** * Copyright 2013-2018 the original author or authors from the Jeddict project (https://jeddict.github.io/). * * 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 io.github.jeddict.jpa.spec.sync; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.ImportDeclaration; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.PackageDeclaration; import com.github.javaparser.ast.body.BodyDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.EnumDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; import com.github.javaparser.ast.body.InitializerDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.body.TypeDeclaration; import com.github.javaparser.ast.comments.Comment; import com.github.javaparser.ast.expr.AnnotationExpr; import com.github.javaparser.ast.type.ClassOrInterfaceType; import com.github.javaparser.ast.type.TypeParameter; import static io.github.jeddict.jcode.BeanVaildationConstants.BV_ANNOTATIONS; import static io.github.jeddict.jcode.BeanVaildationConstants.BV_CONSTRAINTS_PACKAGE; import static io.github.jeddict.jcode.JAXBConstants.JAXB_ANNOTATIONS; import static io.github.jeddict.jcode.JAXBConstants.JAXB_PACKAGE; import static io.github.jeddict.jcode.JPAConstants.JPA_ANNOTATIONS; import static io.github.jeddict.jcode.JPAConstants.PERSISTENCE_PACKAGE; import static io.github.jeddict.jcode.JSONBConstants.JSONB_ANNOTATIONS; import static io.github.jeddict.jcode.JSONBConstants.JSONB_PACKAGE; import io.github.jeddict.jcode.util.Inflector; import static io.github.jeddict.jcode.util.JavaIdentifiers.isFQN; import static io.github.jeddict.jcode.util.JavaIdentifiers.unqualify; import static io.github.jeddict.jcode.util.JavaUtil.getFieldName; import static io.github.jeddict.jcode.util.JavaUtil.getFieldNameFromDelegatorMethod; import static io.github.jeddict.jcode.util.JavaUtil.isBeanMethod; import io.github.jeddict.jpa.spec.extend.Attribute; import io.github.jeddict.jpa.spec.extend.ClassAnnotation; import io.github.jeddict.jpa.spec.extend.ClassAnnotationLocationType; import static io.github.jeddict.jpa.spec.extend.ClassAnnotationLocationType.TYPE; import io.github.jeddict.jpa.spec.extend.CollectionTypeHandler; import io.github.jeddict.jpa.spec.extend.Constructor; import io.github.jeddict.jpa.spec.extend.IAttributes; import io.github.jeddict.jpa.spec.extend.JavaClass; import io.github.jeddict.jpa.spec.extend.ReferenceClass; import static io.github.jeddict.settings.generate.GenerateSettings.isGenerateFluentAPI; import io.github.jeddict.snippet.ClassSnippet; import io.github.jeddict.snippet.ClassSnippetLocationType; import static io.github.jeddict.snippet.ClassSnippetLocationType.AFTER_CLASS; import static io.github.jeddict.snippet.ClassSnippetLocationType.AFTER_FIELD; import static io.github.jeddict.snippet.ClassSnippetLocationType.AFTER_METHOD; import static io.github.jeddict.snippet.ClassSnippetLocationType.BEFORE_FIELD; import static io.github.jeddict.snippet.ClassSnippetLocationType.BEFORE_PACKAGE; import static io.github.jeddict.snippet.ClassSnippetLocationType.IMPORT; import static io.github.jeddict.snippet.ClassSnippetLocationType.TYPE_JAVADOC; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeMap; import static java.util.function.Function.identity; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toMap; import static org.apache.commons.lang.StringUtils.isNotBlank; /** * * @author jGauravGupta */ public class JavaClassSyncHandler { private final JavaClass<IAttributes> javaClass; private JavaClassSyncHandler(JavaClass<IAttributes> javaClass) { this.javaClass = javaClass; } public static JavaClassSyncHandler getInstance(JavaClass<IAttributes> javaClass) { return new JavaClassSyncHandler(javaClass); } public void syncExistingSnippet(CompilationUnit existingSource) { Map<String, Attribute> attributes = javaClass.getAttributes().getAllAttributeMap(); Map<String, Attribute> previousAttributes = javaClass.getAttributes().getAllAttribute().stream() .filter(attr -> Objects.nonNull(attr.getPreviousName())) .collect(toMap(Attribute::getPreviousName, identity())); Map<String, ImportDeclaration> imports = existingSource.getImports().stream() .collect(toMap(importDec -> unqualify(importDec.getNameAsString()), identity())); NodeList<TypeDeclaration<?>> types = existingSource.getTypes(); for (TypeDeclaration<?> type : types) { if (type.getNameAsString().equals(javaClass.getPreviousClass()) || type.getNameAsString().equals(javaClass.getClazz())) { ClassOrInterfaceDeclaration rootClass = (ClassOrInterfaceDeclaration) type; syncHeaderJavaDoc(type); syncTypeParameters(rootClass.getTypeParameters(), imports); syncExtendedTypes(rootClass.getExtendedTypes(), imports); syncImplementedTypes(rootClass.getImplementedTypes(), imports); syncAnnotations(rootClass.getAnnotations(), TYPE, imports); NodeList<BodyDeclaration<?>> members = rootClass.getMembers(); for (BodyDeclaration<?> member : members) { if (member instanceof MethodDeclaration) { MethodDeclaration method = (MethodDeclaration) member; String methodName = method.getNameAsString(); if (isBeanMethod(methodName)) { String attributeName = getFieldName(methodName); if (javaClass.getRemovedAttributes().contains(attributeName)) { continue; //ignore deleted attribute } Attribute previousAttribute = previousAttributes.get(attributeName); Attribute attribute = attributes.get(attributeName); if (previousAttribute != null) { //renamed AttributeSyncHandler.getInstance(previousAttribute) .syncExistingSnippet(attributeName, method, imports); } else if (attribute != null) { // new or non-modified AttributeSyncHandler.getInstance(attribute).syncExistingSnippet(attributeName, method, imports); } else { syncMethodSnippet(method, imports); } } else if (methodName.equals("toString") && method.getParameters().isEmpty()) { if (javaClass.getToStringMethod().getAttributes().isEmpty()) { syncMethodSnippet(method, imports); } } else if (methodName.equals("hashCode") && method.getParameters().isEmpty()) { if (javaClass.getHashCodeMethod().getAttributes().isEmpty()) { syncMethodSnippet(method, imports); } } else if (methodName.equals("equals") && method.getParameters().size() == 1 && method.getParameters().get(0).getTypeAsString().equals("Object")) { if (javaClass.getEqualsMethod().getAttributes().isEmpty()) { syncMethodSnippet(method, imports); } } else if ((methodName.startsWith("add") || methodName.startsWith("remove")) && (method.getParameters().size() == 1 || method.getParameters().size() == 2)) { // delegator/helper method String attributeName = getFieldNameFromDelegatorMethod(methodName); String attributePluralName = Inflector.getInstance().pluralize(attributeName); if (javaClass.getRemovedAttributes().contains(attributePluralName)) { continue; } Attribute previousAttribute = previousAttributes.get(attributePluralName); Attribute attribute = attributes.get(attributePluralName); if (previousAttribute == null && attribute == null) { // if helper method field name is not plural if (javaClass.getRemovedAttributes().contains(attributeName)) { continue; } previousAttribute = previousAttributes.get(attributeName); attribute = attributes.get(attributeName); } if (previousAttribute != null && previousAttribute instanceof CollectionTypeHandler && isNotBlank( ((CollectionTypeHandler) previousAttribute).getCollectionImplType())) { //renamed // skip } else if (attribute != null && attribute instanceof CollectionTypeHandler && isNotBlank(((CollectionTypeHandler) attribute).getCollectionImplType())) { // new or non-modified // skip } else { syncMethodSnippet(method, imports); } } else if (isFluentMethod(method)) { String attributeName = method.getParameter(0).getNameAsString(); Attribute previousAttribute = previousAttributes.get(attributeName); Attribute attribute = attributes.get(attributeName); if (previousAttribute == null && attribute == null) { syncMethodSnippet(method, imports); } } else { syncMethodSnippet(method, imports); } } else if (member instanceof FieldDeclaration) { FieldDeclaration field = (FieldDeclaration) member; String attributeName = field.getVariable(0).getNameAsString(); if (javaClass.getRemovedAttributes().contains(attributeName)) { continue; //ignore deleted attribute } Attribute previousAttribute = previousAttributes.get(attributeName); Attribute attribute = attributes.get(attributeName); if (previousAttribute != null) { //renamed AttributeSyncHandler.getInstance(previousAttribute).syncExistingSnippet(attributeName, field, imports); } else if (attribute != null) { // new or non-modified AttributeSyncHandler.getInstance(attribute).syncExistingSnippet(attributeName, field, imports); } else { syncFieldSnippet((FieldDeclaration) member, imports); } } else if (member instanceof ClassOrInterfaceDeclaration || member instanceof EnumDeclaration) { syncInnerClassOrInterfaceOrEnumSnippet(member, imports); } else if (member instanceof InitializerDeclaration) { syncInitializationBlockSnippet((InitializerDeclaration) member, imports); } else if (member instanceof ConstructorDeclaration) { syncConstructorSnippet((ConstructorDeclaration) member, imports); } else { System.out.println("member not supported"); } } } else if (type instanceof ClassOrInterfaceDeclaration || type instanceof EnumDeclaration) { syncClassOrInterfaceOrEnumSnippet(type, imports); } else { System.out.println("member not supported"); } } } public boolean isFluentMethod(MethodDeclaration method) { return isGenerateFluentAPI() && method.getParameters().size() == 1 && (method.getTypeAsString().equals(javaClass.getPreviousClass()) || method.getTypeAsString().equals(javaClass.getClazz())) && method.getNameAsString().toLowerCase() .endsWith(method.getParameter(0).getNameAsString().toLowerCase()); } private void syncHeader(Comment comment) { String value = comment.toString(); javaClass.addRuntimeSnippet(new ClassSnippet(value, BEFORE_PACKAGE)); } private void syncJavadoc(Comment comment) { String value = comment.toString(); if (javaClass.getDescription() == null || !value.contains(javaClass.getDescription())) { javaClass.addRuntimeSnippet(new ClassSnippet(value, TYPE_JAVADOC)); } } private void syncHeaderJavaDoc(TypeDeclaration<?> type) { TreeMap<Integer, Comment> comments = new TreeMap<>(); int packagePosition = 1; if (type.getParentNode().isPresent()) { Node parentNode = type.getParentNode().get(); parentNode.getComment().ifPresent(comment -> comments.put(comment.getBegin().get().line, comment)); for (Node node : parentNode.getChildNodes()) { if (node instanceof PackageDeclaration) { PackageDeclaration packageDeclaration = (PackageDeclaration) node; if (packageDeclaration.getBegin().isPresent()) { packagePosition = packageDeclaration.getBegin().get().line; } if (packageDeclaration.getComment().isPresent()) { Comment comment = packageDeclaration.getComment().get(); comments.put(comment.getBegin().get().line, comment); } } else if (node instanceof Comment) { Comment comment = (Comment) node; comments.put(comment.getBegin().get().line, comment); } } } type.getComment().ifPresent(comment -> comments.put(comment.getBegin().get().line, comment)); comments.headMap(packagePosition).values().forEach(this::syncHeader); comments.tailMap(packagePosition).values().forEach(this::syncJavadoc); } private void syncTypeParameters(List<TypeParameter> typeParameters, Map<String, ImportDeclaration> imports) { for (TypeParameter typeParameter : typeParameters) { String value = typeParameter.toString(); javaClass.addRuntimeTypeParameter(value); syncImportSnippet(value, imports); ; } } private void syncExtendedTypes(List<ClassOrInterfaceType> extendedTypes, Map<String, ImportDeclaration> imports) { if (extendedTypes.size() != 1) { return; // single extends is valid for entity } ClassOrInterfaceType extendedType = extendedTypes.get(0); String value = extendedType.toString(); if (javaClass.getSuperclassRef() == null && javaClass.getSuperclass() == null) { javaClass.setRuntimeSuperclassRef(new ReferenceClass(value)); syncImportSnippet(value, imports); ; } } private void syncImplementedTypes(List<ClassOrInterfaceType> implementedTypes, Map<String, ImportDeclaration> imports) { Set<ReferenceClass> allInterfaces = new LinkedHashSet<>(javaClass.getRootElement().getInterfaces()); allInterfaces.addAll(javaClass.getInterfaces()); for (ClassOrInterfaceType implementedType : implementedTypes) { String implementedExprName = implementedType.getNameAsString(); String implementedName; if (isFQN(implementedExprName)) { implementedName = unqualify(implementedExprName); } else { implementedName = implementedExprName; } String value = implementedType.toString(); if (!allInterfaces.stream().filter(inter -> inter.isEnable()) .filter(inter -> inter.getName().contains(implementedName)).findAny().isPresent()) { javaClass.addRuntimeInterface(new ReferenceClass(value)); syncImportSnippet(value, imports); ; } } } private void syncAnnotations(List<AnnotationExpr> annotationExprs, ClassAnnotationLocationType locationType, Map<String, ImportDeclaration> imports) { for (AnnotationExpr annotationExpr : annotationExprs) { String annotationExprName = annotationExpr.getNameAsString(); String annotationName; String annotationFQN; //TODO calculate using resolve type or find solution for static import ?? if (isFQN(annotationExprName)) { annotationFQN = annotationExprName; annotationName = unqualify(annotationExprName); } else { annotationFQN = imports.containsKey(annotationExprName) ? imports.get(annotationExprName).getNameAsString() : annotationExprName; annotationName = annotationExprName; } if (!annotationFQN.startsWith(PERSISTENCE_PACKAGE) && !annotationFQN.startsWith(BV_CONSTRAINTS_PACKAGE) && !annotationFQN.startsWith(JSONB_PACKAGE) && !annotationFQN.startsWith(JAXB_PACKAGE) && !JPA_ANNOTATIONS.contains(annotationFQN) && !BV_ANNOTATIONS.contains(annotationFQN) && !JSONB_ANNOTATIONS.contains(annotationFQN) && !JAXB_ANNOTATIONS.contains(annotationFQN)) { String value = annotationExpr.toString(); if (!javaClass.getAnnotation().stream().filter(anot -> anot.getLocationType() == locationType) .filter(anot -> anot.getName().contains(annotationName)).findAny().isPresent()) { javaClass.addRuntimeAnnotation(new ClassAnnotation(value, locationType)); syncImportSnippet(value, imports); ; } } } } private void syncFieldSnippet(FieldDeclaration field, Map<String, ImportDeclaration> imports) { syncClassSnippet(field.isStatic() ? BEFORE_FIELD : AFTER_FIELD, field.toString(), imports); } private void syncInitializationBlockSnippet(InitializerDeclaration initializationBlock, Map<String, ImportDeclaration> imports) { syncClassSnippet(AFTER_FIELD, initializationBlock.toString(), imports); } private void syncConstructorSnippet(ConstructorDeclaration constructor, Map<String, ImportDeclaration> imports) { String signature = constructor.getParameters().stream().map(Parameter::getTypeAsString) .collect(joining(", ")); if (!javaClass.getConstructors().stream().filter(Constructor::isEnable) .filter(cot -> cot.getSignature().equals(signature)).findAny().isPresent()) { syncClassSnippet(AFTER_FIELD, constructor.toString(), imports); } } private void syncMethodSnippet(MethodDeclaration method, Map<String, ImportDeclaration> imports) { syncClassSnippet(AFTER_METHOD, method.toString(), imports); } private void syncInnerClassOrInterfaceOrEnumSnippet(BodyDeclaration<?> member, Map<String, ImportDeclaration> imports) { syncClassSnippet(AFTER_METHOD, member.toString(), imports); } private void syncClassOrInterfaceOrEnumSnippet(TypeDeclaration<?> type, Map<String, ImportDeclaration> imports) { syncClassSnippet(AFTER_CLASS, type.toString(), imports); } private void syncClassSnippet(ClassSnippetLocationType locationType, String snippet, Map<String, ImportDeclaration> imports) { syncImportSnippet(snippet, imports); javaClass.addRuntimeSnippet(new ClassSnippet(snippet, locationType)); } private void syncImportSnippet(String snippet, Map<String, ImportDeclaration> imports) { imports.keySet().stream().filter(snippet::contains).map(imports::get) .map(importClass -> new ClassSnippet(importClass.getNameAsString(), IMPORT)) .forEach(javaClass::addRuntimeSnippet); } }