blob: f27acfd5827bc5458d5cbc01abd3d44e1565ea08 [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:
// Oracle - initial API and implementation from Oracle TopLink
// 05/16/2008-1.0M8 Guy Pelletier
// - 218084: Implement metadata merging functionality between mapping files
// 09/23/2008-1.1 Guy Pelletier
// - 241651: JPA 2.0 Access Type support
// 10/01/2008-1.1 Guy Pelletier
// - 249329: To remain JPA 1.0 compliant, any new JPA 2.0 annotations should be referenced by name
// 03/29/2010-2.1 Guy Pelletier
// - 267217: Add Named Access Type to EclipseLink-ORM
// 04/09/2010-2.1 Guy Pelletier
// - 307050: Add defaults for access methods of a VIRTUAL access type
// 06/22/2010-2.2 Guy Pelletier
// - 308729: Persistent Unit deployment exception when mappedsuperclass has no annotations but has lifecycle callbacks
// 12/01/2010-2.2 Guy Pelletier
// - 331234: xml-mapping-metadata-complete overriden by metadata-complete specification
package org.eclipse.persistence.internal.jpa.metadata.accessors.objects;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ClassAccessor;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ACCESS_PROPERTY;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_POST_LOAD;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_POST_PERSIST;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_POST_REMOVE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_POST_UPDATE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PRE_PERSIST;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PRE_REMOVE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PRE_UPDATE;
/**
* INTERNAL:
* An object to hold onto a valid JPA decorated method.
*
* @author Guy Pelletier
* @since TopLink 10.1.3/EJB 3.0 Preview
*/
public class MetadataMethod extends MetadataAnnotatedElement {
/** Class that the method is defined in. */
protected MetadataClass m_metadataClass;
/** Class name of method return type. */
protected String m_returnType;
/** List of class names of method parameters. */
protected List<String> m_parameters;
/** Corresponding set method, if the method is an accessor get method. */
protected MetadataMethod m_setMethod;
/** Used to store multiple methods with the same name in a class. */
protected MetadataMethod m_next;
/**
* Create the method from the class metadata.
*/
public MetadataMethod(MetadataFactory factory, MetadataClass metadataClass) {
super(factory);
m_metadataClass = metadataClass;
m_parameters = new ArrayList<String>();
}
/**
* INTERNAL:
*/
public void addParameter(String parameter) {
m_parameters.add(parameter);
}
/**
* INTERNAL:
*/
public MetadataClass getMetadataClass() {
return m_metadataClass;
}
/**
* INTERNAL:
*/
public MetadataMethod getNext() {
return m_next;
}
/**
* INTERNAL:
*/
public List<String> getParameters() {
return m_parameters;
}
/**
* INTERNAL:
*/
public String getReturnType() {
return m_returnType;
}
/**
* INTERNAL:
* Method to convert a getMethod into a setMethod. This method could return
* null if the corresponding set method is not found.
*/
public MetadataMethod getSetMethod() {
if (m_setMethod == null) {
m_setMethod = getSetMethod(getMetadataClass());
}
return m_setMethod;
}
/**
* INTERNAL:
* Method to convert a getMethod into a setMethod.
*/
public void setSetMethod(MetadataMethod method) {
m_setMethod = method;
}
/**
* INTERNAL:
* Method to convert a getMethod into a setMethod. This method could return
* null if the corresponding set method is not found.
*/
public MetadataMethod getSetMethod(MetadataClass cls) {
String getMethodName = getName();
List<String> params = Arrays.asList(getReturnType());
MetadataMethod setMethod;
if (getMethodName.startsWith(Helper.GET_PROPERTY_METHOD_PREFIX)) {
// Replace 'get' with 'set'.
setMethod = cls.getMethod(Helper.SET_PROPERTY_METHOD_PREFIX + getMethodName.substring(3), params);
} else {
// methodName.startsWith(IS_PROPERTY_METHOD_PREFIX)
// Check for a setXyz method first, if it exists use it.
setMethod = cls.getMethod(Helper.SET_PROPERTY_METHOD_PREFIX + getMethodName.substring(2), params);
if (setMethod == null) {
// setXyz method was not found try setIsXyz
setMethod = cls.getMethod(Helper.SET_IS_PROPERTY_METHOD_PREFIX + getMethodName.substring(2), params);
}
}
return setMethod;
}
/**
* INTERNAL:
*/
public String getSetMethodName() {
return getSetMethod().getName();
}
/**
* INTERNAL:
*/
public boolean hasAttributeName() {
return ! getAttributeName().equals("");
}
/**
* INTERNAL:
*/
public boolean hasParameters() {
return getParameters().size() > 0;
}
/**
* INTERNAL:
*/
public boolean hasSetMethod() {
return getSetMethod() != null;
}
/**
* INTERNAL:
* Return true if it has a valid name (starts with get or is) and has a
* property name (getXyz or isXyz) and does not have parameters and has
* an associated set method.
*/
public boolean isALifeCycleCallbackMethod() {
return isAnnotationPresent(JPA_POST_LOAD) ||
isAnnotationPresent(JPA_POST_PERSIST) ||
isAnnotationPresent(JPA_POST_REMOVE) ||
isAnnotationPresent(JPA_POST_UPDATE) ||
isAnnotationPresent(JPA_PRE_PERSIST) ||
isAnnotationPresent(JPA_PRE_REMOVE) ||
isAnnotationPresent(JPA_PRE_UPDATE);
}
/**
* INTERNAL:
* Return true if it has a valid name (starts with 'get' or 'is') and has a
* property name (get'Xyz' or is'Xyz') and does not have parameters and has
* an associated set method.
*/
protected boolean isValidPersistenceMethod() {
return isValidPersistenceMethodName() && ! hasParameters() && hasSetMethod();
}
/**
* INTERNAL:
* Return true is this method is a valid persistence method. This method
* will validate against any declared annotations on the method. If the
* mustBeExplicit flag is true, then we are processing the inverse of an
* explicit access setting and the property must have an Access(PROPERTY)
* setting to be processed. Otherwise, it is ignored.
*/
public boolean isValidPersistenceMethod(boolean mustBeExplicit, ClassAccessor classAccessor) {
if (isValidPersistenceElement(mustBeExplicit, JPA_ACCESS_PROPERTY, classAccessor)) {
return ! isALifeCycleCallbackMethod() && isValidPersistenceMethod(classAccessor, hasDeclaredAnnotations(classAccessor));
}
return false;
}
/**
* INTERNAL:
* Return true is this method is a valid persistence method. User decorated
* is used to indicate that the method either had persistence annotations
* defined on it or that it was specified in XML.
*/
public boolean isValidPersistenceMethod(ClassAccessor classAccessor, boolean userDecorated) {
if (! isValidPersistenceElement(getModifiers()) || ! isValidPersistenceMethod()) {
// So it's not a valid method, did the user decorate it with
// annotations or specify it in XML?
if (userDecorated) {
// Let's figure out which exception we should throw then ...
if (hasParameters()) {
throw ValidationException.mappingMetadataAppliedToMethodWithArguments(this, classAccessor.getDescriptorJavaClass());
} else if (!hasSetMethod()) {
throw ValidationException.noCorrespondingSetterMethodDefined(classAccessor.getDescriptorJavaClass(), this);
} else {
// General, catch all remaining cases and log a warning message.
getLogger().logConfigMessage(MetadataLogger.IGNORE_MAPPING_METADATA, this, classAccessor.getDescriptorJavaClass());
}
}
return false;
}
return true;
}
/**
* INTERNAL:
*/
public boolean isValidPersistenceMethodName() {
return (getName().startsWith(Helper.GET_PROPERTY_METHOD_PREFIX) || getName().startsWith(Helper.IS_PROPERTY_METHOD_PREFIX)) && hasAttributeName();
}
/**
* INTERNAL:
*/
public void setMetadataClass(MetadataClass metadataClass) {
m_metadataClass = metadataClass;
}
/**
* INTERNAL:
*/
public void setNext(MetadataMethod next) {
m_next = next;
}
/**
* INTERNAL:
*/
public void setParameters(List<String> parameters) {
m_parameters = parameters;
}
/**
* INTERNAL:
*/
public void setReturnType(String returnType) {
m_returnType = returnType;
setType(returnType);
}
}