blob: 88ddb0ce2fc7344d75c50a369d5d2153cf6bd4be [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.openjpa.persistence;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.util.Objects;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.DelegatingMetaDataFactory;
import org.apache.openjpa.meta.MetaDataFactory;
import org.apache.openjpa.meta.MetaDataRepository;
import org.apache.openjpa.meta.XMLMetaData;
/**
* JAXB xml annotation metadata parser.
*
* @author Catalina Wei
* @since 1.0.0
*/
public class AnnotationPersistenceXMLMetaDataParser {
private static final Localizer _loc = Localizer.forPackage
(AnnotationPersistenceXMLMetaDataParser.class);
private final OpenJPAConfiguration _conf;
private final Log _log;
private MetaDataRepository _repos = null;
// cache the JAXB Xml... classes if they are present so we do not
// have a hard-wired dependency on JAXB here
private Class xmlTypeClass = null;
private Class xmlRootElementClass = null;
private Class xmlAccessorTypeClass = null;
private Class xmlAttributeClass = null;
private Class xmlElementClass = null;
private Method xmlTypeName = null;
private Method xmlTypeNamespace = null;
private Method xmlRootName = null;
private Method xmlRootNamespace = null;
private Method xmlAttributeName = null;
private Method xmlAttributeNamespace = null;
private Method xmlElementName = null;
private Method xmlElementNamespace = null;
private Method xmlAccessorValue = null;
/**
* Constructor; supply configuration.
*/
public AnnotationPersistenceXMLMetaDataParser(OpenJPAConfiguration conf) {
_conf = conf;
_log = conf.getLog(OpenJPAConfiguration.LOG_METADATA);
try {
xmlTypeClass = Class.forName(
"javax.xml.bind.annotation.XmlType");
xmlTypeName = xmlTypeClass.getMethod("name", null);
xmlTypeNamespace = xmlTypeClass.getMethod("namespace", null);
xmlRootElementClass = Class.forName(
"javax.xml.bind.annotation.XmlRootElement");
xmlRootName = xmlRootElementClass.getMethod("name", null);
xmlRootNamespace = xmlRootElementClass.getMethod("namespace", null);
xmlAccessorTypeClass = Class.forName(
"javax.xml.bind.annotation.XmlAccessorType");
xmlAccessorValue = xmlAccessorTypeClass.getMethod("value", null);
xmlAttributeClass = Class.forName(
"javax.xml.bind.annotation.XmlAttribute");
xmlAttributeName = xmlAttributeClass.getMethod("name", null);
xmlAttributeNamespace = xmlAttributeClass.getMethod("namespace"
, null);
xmlElementClass = Class.forName(
"javax.xml.bind.annotation.XmlElement");
xmlElementName = xmlElementClass.getMethod("name", null);
xmlElementNamespace = xmlElementClass.getMethod("namespace", null);
} catch (Exception e) {
}
}
/**
* Configuration supplied on construction.
*/
public OpenJPAConfiguration getConfiguration() {
return _conf;
}
/**
* Metadata log.
*/
public Log getLog() {
return _log;
}
/**
* Returns the repository for this parser. If none has been set,
* create a new repository and sets it.
*/
public MetaDataRepository getRepository() {
if (_repos == null) {
MetaDataRepository repos = _conf.newMetaDataRepositoryInstance();
MetaDataFactory mdf = repos.getMetaDataFactory();
if (mdf instanceof DelegatingMetaDataFactory)
mdf = ((DelegatingMetaDataFactory) mdf).getInnermostDelegate();
if (mdf instanceof PersistenceMetaDataFactory)
((PersistenceMetaDataFactory) mdf).setXMLAnnotationParser(this);
_repos = repos;
}
return _repos;
}
/**
* Set the metadata repository for this parser.
*/
public void setRepository(MetaDataRepository repos) {
_repos = repos;
}
/**
* Clear caches.
*/
public void clear() {
}
/**
* Parse persistence metadata for the given field metadata. This parser/class is NOT threadsafe! The caller of
* this method needs to insure that the MetaData(/Mapping)Repository is locked prior to calling this method.
*/
public synchronized void parse(Class<?> cls) {
if (_log.isTraceEnabled())
_log.trace(_loc.get("parse-class", cls.getName()));
parseXMLClassAnnotations(cls);
}
/**
* Read annotations for the current type.
*/
private XMLMetaData parseXMLClassAnnotations(Class<?> cls) {
// check immediately whether the class has JAXB XML annotations
if (cls == null || xmlTypeClass == null
|| !(AccessController.doPrivileged(J2DoPrivHelper
.isAnnotationPresentAction(cls, (Class<? extends Annotation>) xmlTypeClass))
&& AccessController
.doPrivileged(J2DoPrivHelper.isAnnotationPresentAction(cls,
(Class<? extends Annotation>) xmlRootElementClass))))
return null;
// find / create metadata
XMLMetaData meta = getXMLMetaData(cls);
return meta;
}
/**
* Find or create xml metadata for the current type.
*/
private XMLMetaData getXMLMetaData(Class<?> cls) {
XMLMetaData meta = getRepository().getCachedXMLMetaData(cls);
if (meta == null) {
// if not in cache, create metadata
meta = getRepository().addXMLClassMetaData(cls);
parseXmlRootElement(cls, meta);
populateFromReflection(cls, meta);
}
return meta;
}
private void parseXmlRootElement(Class type, XMLMetaData meta) {
try {
if (type.getAnnotation(xmlRootElementClass) != null) {
meta.setXmlRootElement(true);
meta.setXmlname((String) xmlRootName.invoke(type.getAnnotation
(xmlRootElementClass), new Object[]{}));
meta.setXmlnamespace((String) xmlRootNamespace.invoke(type
.getAnnotation(xmlRootElementClass), new Object[]{}));
}
else {
meta.setXmlname((String) xmlTypeName.invoke(type.getAnnotation
(xmlTypeClass), new Object[]{}));
meta.setXmlnamespace((String) xmlTypeNamespace.invoke(type
.getAnnotation(xmlTypeClass), new Object[]{}));
}
} catch (Exception e) {
}
}
private void populateFromReflection(Class cls, XMLMetaData meta) {
Member[] members;
Class superclass = cls.getSuperclass();
// handle inheritance at sub-element level
if (AccessController.doPrivileged(J2DoPrivHelper
.isAnnotationPresentAction(superclass, (Class<? extends Annotation>) xmlTypeClass)))
populateFromReflection(superclass, meta);
try {
if (Objects.equals(xmlAccessorValue.invoke(cls.getAnnotation(
xmlAccessorTypeClass), new Object[]{}).toString(), "FIELD"))
members = cls.getDeclaredFields();
else
members = cls.getDeclaredMethods();
for (int i = 0; i < members.length; i++) {
Member member = members[i];
AnnotatedElement el = (AnnotatedElement) member;
XMLMetaData field = null;
if (el.getAnnotation(xmlElementClass) != null) {
String xmlname = (String) xmlElementName.invoke(el
.getAnnotation(xmlElementClass), new Object[]{});
// avoid JAXB XML bind default name
if (Objects.equals(XMLMetaData.defaultName, xmlname))
xmlname = member.getName();
if (AccessController.doPrivileged(J2DoPrivHelper
.isAnnotationPresentAction(((Field) member).getType(),
(Class<? extends Annotation>) xmlTypeClass))) {
field = _repos.addXMLClassMetaData(((Field) member).getType());
parseXmlRootElement(((Field) member).getType(), field);
populateFromReflection(((Field) member).getType()
, field);
field.setXmltype(XMLMetaData.XMLTYPE);
field.setXmlname(xmlname);
}
else {
field = _repos.newXMLFieldMetaData(((Field) member)
.getType(), member.getName());
field.setXmltype(XMLMetaData.ELEMENT);
field.setXmlname(xmlname);
field.setXmlnamespace((String) xmlElementNamespace
.invoke(el.getAnnotation(xmlElementClass)
, new Object[]{}));
}
}
else if (el.getAnnotation(xmlAttributeClass) != null) {
field = _repos.newXMLFieldMetaData(((Field) member)
.getType(), member.getName());
field.setXmltype(XMLMetaData.ATTRIBUTE);
String xmlname = (String) xmlAttributeName.invoke(
el.getAnnotation(xmlAttributeClass), new Object[]{});
// avoid JAXB XML bind default name
if (Objects.equals(XMLMetaData.defaultName, xmlname))
xmlname = member.getName();
field.setXmlname("@"+xmlname);
field.setXmlnamespace((String) xmlAttributeNamespace.invoke(
el.getAnnotation(xmlAttributeClass), new Object[]{}));
}
if (field != null)
meta.addField(member.getName(), field);
}
} catch(Exception e) {
}
}
}