| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // 08/10/2009-2.0 Guy Pelletier |
| // - 267391: JPA 2.0 implement/extend/use an APT tooling library for MetaModel API canonical classes |
| // 08/25/2010-2.2 Guy Pelletier |
| // - 309445: CannonicalModelProcessor process all files |
| // 11/23/2010-2.2 Guy Pelletier |
| // - 330660: Canonical model generator throws ClassCastException when using package-info.java |
| package org.eclipse.persistence.internal.jpa.modelgen.visitors; |
| |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.ECLIPSELINK_PERSISTENCE_PACKAGE_PREFIX; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PERSISTENCE_PACKAGE_PREFIX; |
| |
| import java.util.List; |
| import java.util.Set; |
| |
| import javax.annotation.processing.ProcessingEnvironment; |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.Modifier; |
| import javax.lang.model.element.PackageElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.TypeParameterElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.util.AbstractElementVisitor8; |
| |
| import org.eclipse.persistence.internal.helper.Helper; |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotatedElement; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotation; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataClass; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataField; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataMethod; |
| import org.eclipse.persistence.internal.jpa.modelgen.MetadataMirrorFactory; |
| import org.eclipse.persistence.logging.SessionLog; |
| |
| /** |
| * An element visitor. |
| * |
| * @author Guy Pelletier |
| * @since EclipseLink 1.2 |
| */ |
| public class ElementVisitor<R, P> extends AbstractElementVisitor8<MetadataAnnotatedElement, MetadataClass> { |
| private ProcessingEnvironment processingEnv; |
| private TypeVisitor<MetadataAnnotatedElement, MetadataAnnotatedElement> typeVisitor; |
| |
| /** |
| * INTERNAL: |
| */ |
| public ElementVisitor(ProcessingEnvironment processingEnv) { |
| this.processingEnv = processingEnv; |
| |
| typeVisitor = new TypeVisitor<>(); |
| } |
| |
| /** |
| * INTERNAL: |
| * The pre-processing stages requires some knowledge of the annotation |
| * values (e.g. targetEntity) so we will visit the annotation values |
| * and build complete MetadataAnnotation from the mirrors. |
| */ |
| protected void buildMetadataAnnotations(MetadataAnnotatedElement annotatedElement, List<? extends AnnotationMirror> annotationMirrors) { |
| AnnotationValueVisitor<Object, Object> visitor = new AnnotationValueVisitor<>(); |
| |
| for (AnnotationMirror annotationMirror : annotationMirrors) { |
| String annotation = annotationMirror.getAnnotationType().toString() ; |
| |
| // Only add annotations that we care about. Be nice to have API |
| // that we could call into with the annotation mirror to the |
| // processor to see if it is a supported annotation. That is, it |
| // would tie into the @SupportedAnnotationTypes({"jakarta.persistence.*", "org.eclipse.persistence.annotations.*"}) |
| // declaration from CanonicalModelProcessor, but I couldn't find a |
| // way to do this. For now we'll check the strings (similar to what |
| // is done with our ASM factory). |
| if (annotation.contains(JPA_PERSISTENCE_PACKAGE_PREFIX) || annotation.contains(ECLIPSELINK_PERSISTENCE_PACKAGE_PREFIX)) { |
| annotatedElement.addAnnotation((MetadataAnnotation) visitor.visitAnnotation(annotationMirror, null)); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected int getModifiers(Set<Modifier> modifiers) { |
| int mods = 0; |
| |
| for (Modifier modifier : modifiers) { |
| if (modifier.equals(Modifier.ABSTRACT)) { |
| mods += java.lang.reflect.Modifier.ABSTRACT; |
| } |
| |
| if (modifier.equals(Modifier.FINAL)) { |
| mods += java.lang.reflect.Modifier.FINAL; |
| } |
| |
| if (modifier.equals(Modifier.NATIVE)) { |
| mods += java.lang.reflect.Modifier.NATIVE; |
| } |
| |
| if (modifier.equals(Modifier.PRIVATE)) { |
| mods += java.lang.reflect.Modifier.PRIVATE; |
| } |
| |
| if (modifier.equals(Modifier.PROTECTED)) { |
| mods += java.lang.reflect.Modifier.PROTECTED; |
| } |
| |
| if (modifier.equals(Modifier.PUBLIC)) { |
| mods += java.lang.reflect.Modifier.PUBLIC; |
| } |
| |
| if (modifier.equals(Modifier.STATIC)) { |
| mods += java.lang.reflect.Modifier.STATIC; |
| } |
| |
| if (modifier.equals(Modifier.STRICTFP)) { |
| mods += java.lang.reflect.Modifier.STRICT; |
| } |
| |
| if (modifier.equals(Modifier.SYNCHRONIZED)) { |
| mods += java.lang.reflect.Modifier.SYNCHRONIZED; |
| } |
| |
| if (modifier.equals(Modifier.TRANSIENT)) { |
| mods += java.lang.reflect.Modifier.TRANSIENT; |
| } |
| |
| if (modifier.equals(Modifier.VOLATILE)) { |
| mods += java.lang.reflect.Modifier.VOLATILE; |
| } |
| } |
| |
| return mods; |
| } |
| |
| /** |
| * INTERNAL: |
| * Visit an executable and create a MetadataMethod object. |
| */ |
| @Override |
| public MetadataMethod visitExecutable(ExecutableElement executableElement, MetadataClass metadataClass) { |
| MetadataMethod method = new MetadataMethod(metadataClass.getMetadataFactory(), metadataClass); |
| |
| // Set the name. |
| method.setName(executableElement.getSimpleName().toString()); |
| |
| // Set the attribute name. |
| method.setAttributeName(Helper.getAttributeNameFromMethodName(method.getName())); |
| |
| // Set the modifiers. |
| method.setModifiers(getModifiers(executableElement.getModifiers())); |
| |
| // Visit executable element for the parameters, return type and generic type. |
| executableElement.asType().accept(typeVisitor, method); |
| |
| // Set the annotations. |
| buildMetadataAnnotations(method, executableElement.getAnnotationMirrors()); |
| |
| // Handle multiple methods with the same name. |
| MetadataMethod existing = metadataClass.getMethods().get(method.getName()); |
| if (existing == null) { |
| metadataClass.addMethod(method); |
| } else { |
| while (existing.getNext() != null) { |
| existing = existing.getNext(); |
| } |
| existing.setNext(method); |
| } |
| |
| return method; |
| } |
| |
| /** |
| * INTERNAL: |
| * Visit a packing-info.java file. We currently don't support package level |
| * annotations, but if we did and they impacted canonical model generation |
| * we would pick them up here. We should never hit this visit since we |
| * filter out package elements, and package elements can not be referenced |
| * from classes. |
| */ |
| @Override |
| public MetadataClass visitPackage(PackageElement packageElement, MetadataClass metadataClass) { |
| MetadataLogger logger = metadataClass.getMetadataFactory().getLogger(); |
| logger.getSession().getSessionLog().log(SessionLog.FINE, SessionLog.PROCESSOR, |
| "ElementVisitor Package NOT IMPLEMENTED : {0}", |
| new Object[] {packageElement}, false); |
| return null; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public MetadataClass visitType(TypeElement typeElement, MetadataClass metadataClass) { |
| MetadataMirrorFactory factory = ((MetadataMirrorFactory) metadataClass.getMetadataFactory()); |
| factory.getLogger().getSession().getSessionLog().log(SessionLog.FINEST, SessionLog.PROCESSOR, |
| "Visiting class: {0}", |
| new Object[] {typeElement}, false); |
| |
| // Set the qualified name. |
| metadataClass.setName(typeElement.getQualifiedName().toString()); |
| |
| // By default, set the type to be the same as the name, which in most |
| // cases is correct. For non JDK elements we'll visit the typeElement |
| // further (see below). This will further process any generic types, |
| // e.g. Employee<Integer>. For the most part I don't think we need to |
| // care, certainly not with JDK classes but for round elements we'll |
| // set them anyway. |
| metadataClass.setType(metadataClass.getName()); |
| |
| // Add the interfaces. |
| for (TypeMirror interfaceCls : typeElement.getInterfaces()) { |
| metadataClass.addInterface(factory.getMetadataClass(interfaceCls).getName()); |
| } |
| |
| // Set the superclass name (if there is one) |
| TypeMirror superclass = typeElement.getSuperclass(); |
| if (superclass != null) { |
| metadataClass.setSuperclassName(factory.getMetadataClass(superclass).getName()); |
| } |
| |
| // As a performance gain, limit what is visited by JDK elements. |
| if (! metadataClass.isJDK()) { |
| // Set the modifiers. |
| metadataClass.setModifiers(getModifiers(typeElement.getModifiers())); |
| |
| // Visit the type element for type and generic type. |
| typeElement.asType().accept(typeVisitor, metadataClass); |
| |
| // Visit the enclosed elements. |
| for (Element enclosedElement : typeElement.getEnclosedElements()) { |
| ElementKind kind = enclosedElement.getKind(); |
| if (kind.isClass() || kind.isInterface()) { |
| metadataClass.addEnclosedClass(factory.getMetadataClass(enclosedElement)); |
| } else { |
| enclosedElement.accept(this, metadataClass); |
| } |
| } |
| |
| // Visit the annotations only if it is a round element. |
| buildMetadataAnnotations(metadataClass, typeElement.getAnnotationMirrors()); |
| } |
| |
| return metadataClass; |
| } |
| |
| /** |
| * INTERNAL: |
| * Visit a generic type parameter (either to a field or method) |
| * e.g Collection<X>, the type parameter being X. |
| */ |
| @Override |
| public MetadataClass visitTypeParameter(TypeParameterElement typeParameterElement, MetadataClass metadataClass) { |
| metadataClass.setName(typeParameterElement.getSimpleName().toString()); |
| metadataClass.setType(TypeVisitor.GENERIC_TYPE); |
| return metadataClass; |
| } |
| |
| /** |
| * INTERNAL: |
| * Visit a variable and create a MetadataField object. |
| */ |
| @Override |
| public MetadataField visitVariable(VariableElement variableElement, MetadataClass metadataClass) { |
| MetadataField field = new MetadataField(metadataClass); |
| |
| // Set the name. |
| field.setName(variableElement.getSimpleName().toString()); |
| |
| // Set the attribute name (same as name in this case) |
| field.setAttributeName(field.getName()); |
| |
| // Visit the variable element for type and generic type. |
| variableElement.asType().accept(typeVisitor, field); |
| |
| // Set the modifiers. |
| field.setModifiers(getModifiers(variableElement.getModifiers())); |
| |
| // Set the annotations. |
| buildMetadataAnnotations(field, variableElement.getAnnotationMirrors()); |
| |
| // Add the field to the class and return the field. |
| metadataClass.addField(field); |
| |
| return field; |
| } |
| } |