001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.persistence;
020:
021: import java.lang.reflect.AnnotatedElement;
022: import java.lang.reflect.Field;
023: import java.lang.reflect.Member;
024: import java.lang.reflect.Method;
025: import java.security.AccessController;
026:
027: import org.apache.commons.lang.StringUtils;
028: import org.apache.openjpa.conf.OpenJPAConfiguration;
029: import org.apache.openjpa.lib.log.Log;
030: import org.apache.openjpa.lib.util.J2DoPriv5Helper;
031: import org.apache.openjpa.lib.util.Localizer;
032: import org.apache.openjpa.meta.DelegatingMetaDataFactory;
033: import org.apache.openjpa.meta.FieldMetaData;
034: import org.apache.openjpa.meta.MetaDataFactory;
035: import org.apache.openjpa.meta.MetaDataRepository;
036: import org.apache.openjpa.meta.XMLFieldMetaData;
037: import org.apache.openjpa.meta.XMLMetaData;
038:
039: /**
040: * JAXB xml annotation metadata parser.
041: *
042: * @author Catalina Wei
043: * @since 1.0.0
044: * @nojavadoc
045: */
046: public class AnnotationPersistenceXMLMetaDataParser {
047:
048: private static final Localizer _loc = Localizer
049: .forPackage(AnnotationPersistenceXMLMetaDataParser.class);
050:
051: private final OpenJPAConfiguration _conf;
052: private final Log _log;
053: private MetaDataRepository _repos = null;
054:
055: // the class we were invoked to parse
056: private Class _cls = null;
057: private FieldMetaData _fmd = null;
058:
059: // cache the JAXB Xml... classes if they are present so we do not
060: // have a hard-wired dependency on JAXB here
061: private Class xmlTypeClass = null;
062: private Class xmlRootElementClass = null;
063: private Class xmlAccessorTypeClass = null;
064: private Class xmlAttributeClass = null;
065: private Class xmlElementClass = null;
066: private Method xmlTypeName = null;
067: private Method xmlTypeNamespace = null;
068: private Method xmlRootName = null;
069: private Method xmlRootNamespace = null;
070: private Method xmlAttributeName = null;
071: private Method xmlAttributeNamespace = null;
072: private Method xmlElementName = null;
073: private Method xmlElementNamespace = null;
074: private Method xmlAccessorValue = null;
075:
076: /**
077: * Constructor; supply configuration.
078: */
079: public AnnotationPersistenceXMLMetaDataParser(
080: OpenJPAConfiguration conf) {
081: _conf = conf;
082: _log = conf.getLog(OpenJPAConfiguration.LOG_METADATA);
083: try {
084: xmlTypeClass = Class
085: .forName("javax.xml.bind.annotation.XmlType");
086: xmlTypeName = xmlTypeClass.getMethod("name", null);
087: xmlTypeNamespace = xmlTypeClass
088: .getMethod("namespace", null);
089: xmlRootElementClass = Class
090: .forName("javax.xml.bind.annotation.XmlRootElement");
091: xmlRootName = xmlRootElementClass.getMethod("name", null);
092: xmlRootNamespace = xmlRootElementClass.getMethod(
093: "namespace", null);
094: xmlAccessorTypeClass = Class
095: .forName("javax.xml.bind.annotation.XmlAccessorType");
096: xmlAccessorValue = xmlAccessorTypeClass.getMethod("value",
097: null);
098: xmlAttributeClass = Class
099: .forName("javax.xml.bind.annotation.XmlAttribute");
100: xmlAttributeName = xmlAttributeClass
101: .getMethod("name", null);
102: xmlAttributeNamespace = xmlAttributeClass.getMethod(
103: "namespace", null);
104: xmlElementClass = Class
105: .forName("javax.xml.bind.annotation.XmlElement");
106: xmlElementName = xmlElementClass.getMethod("name", null);
107: xmlElementNamespace = xmlElementClass.getMethod(
108: "namespace", null);
109: } catch (Exception e) {
110: }
111: }
112:
113: /**
114: * Configuration supplied on construction.
115: */
116: public OpenJPAConfiguration getConfiguration() {
117: return _conf;
118: }
119:
120: /**
121: * Metadata log.
122: */
123: public Log getLog() {
124: return _log;
125: }
126:
127: /**
128: * Returns the repository for this parser. If none has been set,
129: * create a new repository and sets it.
130: */
131: public MetaDataRepository getRepository() {
132: if (_repos == null) {
133: MetaDataRepository repos = _conf
134: .newMetaDataRepositoryInstance();
135: MetaDataFactory mdf = repos.getMetaDataFactory();
136: if (mdf instanceof DelegatingMetaDataFactory)
137: mdf = ((DelegatingMetaDataFactory) mdf)
138: .getInnermostDelegate();
139: if (mdf instanceof PersistenceMetaDataFactory)
140: ((PersistenceMetaDataFactory) mdf)
141: .setXMLAnnotationParser(this );
142: _repos = repos;
143: }
144: return _repos;
145: }
146:
147: /**
148: * Set the metadata repository for this parser.
149: */
150: public void setRepository(MetaDataRepository repos) {
151: _repos = repos;
152: }
153:
154: /**
155: * Clear caches.
156: */
157: public void clear() {
158: _cls = null;
159: _fmd = null;
160: }
161:
162: /**
163: * Parse persistence metadata for the given field metadata.
164: */
165: public void parse(FieldMetaData fmd) {
166: _fmd = fmd;
167: _cls = fmd.getDeclaredType();
168: if (_log.isTraceEnabled())
169: _log.trace(_loc.get("parse-class", _cls.getName()));
170:
171: try {
172: parseXMLClassAnnotations();
173: } finally {
174: _cls = null;
175: _fmd = null;
176: }
177: }
178:
179: /**
180: * Read annotations for the current type.
181: */
182: private XMLMetaData parseXMLClassAnnotations() {
183: // check immediately whether the class has JAXB XML annotations
184: if (_cls == null
185: || xmlTypeClass == null
186: || !(((Boolean) AccessController
187: .doPrivileged(J2DoPriv5Helper
188: .isAnnotationPresentAction(_cls,
189: xmlTypeClass))).booleanValue() && ((Boolean) AccessController
190: .doPrivileged(J2DoPriv5Helper
191: .isAnnotationPresentAction(_cls,
192: xmlRootElementClass)))
193: .booleanValue()))
194: return null;
195:
196: // find / create metadata
197: XMLMetaData meta = getXMLMetaData();
198:
199: return meta;
200: }
201:
202: /**
203: * Find or create xml metadata for the current type.
204: */
205: private synchronized XMLMetaData getXMLMetaData() {
206: XMLMetaData meta = getRepository().getCachedXMLMetaData(_cls);
207: if (meta == null) {
208: // if not in cache, create metadata
209: meta = getRepository().addXMLMetaData(_cls, _fmd.getName());
210: parseXmlRootElement(_cls, meta);
211: populateFromReflection(_cls, meta);
212: }
213: return meta;
214: }
215:
216: private void parseXmlRootElement(Class type, XMLMetaData meta) {
217: try {
218: if (type.getAnnotation(xmlRootElementClass) != null) {
219: meta.setXmlRootElement(true);
220: meta.setXmlname((String) xmlRootName.invoke(type
221: .getAnnotation(xmlRootElementClass),
222: new Object[] {}));
223: meta.setXmlnamespace((String) xmlRootNamespace.invoke(
224: type.getAnnotation(xmlRootElementClass),
225: new Object[] {}));
226: } else {
227: meta.setXmlname((String) xmlTypeName.invoke(type
228: .getAnnotation(xmlTypeClass), new Object[] {}));
229: meta.setXmlnamespace((String) xmlTypeNamespace.invoke(
230: type.getAnnotation(xmlTypeClass),
231: new Object[] {}));
232: }
233: } catch (Exception e) {
234: }
235: }
236:
237: private void populateFromReflection(Class cls, XMLMetaData meta) {
238: Member[] members;
239:
240: Class super class = cls.getSuperclass();
241:
242: // handle inheritance at sub-element level
243: if (((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
244: .isAnnotationPresentAction(super class, xmlTypeClass)))
245: .booleanValue())
246: populateFromReflection(super class, meta);
247:
248: try {
249: if (StringUtils.equals(xmlAccessorValue.invoke(
250: cls.getAnnotation(xmlAccessorTypeClass),
251: new Object[] {}).toString(), "FIELD"))
252: members = cls.getDeclaredFields();
253: else
254: members = cls.getDeclaredMethods();
255:
256: for (int i = 0; i < members.length; i++) {
257: Member member = members[i];
258: AnnotatedElement el = (AnnotatedElement) member;
259: XMLMetaData field = null;
260: if (el.getAnnotation(xmlElementClass) != null) {
261: String xmlname = (String) xmlElementName.invoke(el
262: .getAnnotation(xmlElementClass),
263: new Object[] {});
264: // avoid JAXB XML bind default name
265: if (StringUtils.equals(XMLMetaData.defaultName,
266: xmlname))
267: xmlname = member.getName();
268: if (((Boolean) AccessController
269: .doPrivileged(J2DoPriv5Helper
270: .isAnnotationPresentAction(
271: ((Field) member).getType(),
272: xmlTypeClass)))
273: .booleanValue()) {
274: field = _repos.addXMLMetaData(((Field) member)
275: .getType(), member.getName());
276: parseXmlRootElement(((Field) member).getType(),
277: field);
278: populateFromReflection(((Field) member)
279: .getType(), field);
280: field.setXmltype(XMLMetaData.XMLTYPE);
281: field.setXmlname(xmlname);
282: } else {
283: field = _repos.newXMLFieldMetaData(
284: ((Field) member).getType(), member
285: .getName());
286: field.setXmltype(XMLMetaData.ELEMENT);
287: field.setXmlname(xmlname);
288: field
289: .setXmlnamespace((String) xmlElementNamespace
290: .invoke(
291: el
292: .getAnnotation(xmlElementClass),
293: new Object[] {}));
294: }
295: } else if (el.getAnnotation(xmlAttributeClass) != null) {
296: field = _repos.newXMLFieldMetaData(((Field) member)
297: .getType(), member.getName());
298: field.setXmltype(XMLFieldMetaData.ATTRIBUTE);
299: String xmlname = (String) xmlAttributeName.invoke(
300: el.getAnnotation(xmlAttributeClass),
301: new Object[] {});
302: // avoid JAXB XML bind default name
303: if (StringUtils.equals(XMLMetaData.defaultName,
304: xmlname))
305: xmlname = member.getName();
306: field.setXmlname("@" + xmlname);
307: field
308: .setXmlnamespace((String) xmlAttributeNamespace
309: .invoke(
310: el
311: .getAnnotation(xmlAttributeClass),
312: new Object[] {}));
313: }
314: if (field != null)
315: meta.addField(member.getName(), field);
316: }
317: } catch (Exception e) {
318: }
319: }
320: }
|