blob: 8e4729a970d8bc2e3483724c4a83b8b96f0d71b5 [file] [log] [blame]
/*
* 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&lt;X&gt;, 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;
}
}