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.axis2.jaxws.utility;
020:
021: import org.apache.axis2.java.security.AccessController;
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024:
025: import javax.xml.bind.JAXBElement;
026: import javax.xml.bind.annotation.XmlElement;
027: import javax.xml.bind.annotation.XmlEnumValue;
028: import javax.xml.bind.annotation.XmlRootElement;
029: import javax.xml.bind.annotation.XmlSchema;
030: import javax.xml.namespace.QName;
031: import java.beans.IntrospectionException;
032: import java.beans.Introspector;
033: import java.beans.PropertyDescriptor;
034: import java.lang.reflect.Field;
035: import java.security.PrivilegedAction;
036: import java.util.ArrayList;
037: import java.util.HashMap;
038: import java.util.List;
039: import java.util.Map;
040: import java.util.StringTokenizer;
041:
042: /**
043: *
044: */
045: public class XMLRootElementUtil {
046:
047: private static final Log log = LogFactory
048: .getLog(XMLRootElementUtil.class);
049:
050: /** Constructor is intentionally private. This class only provides static utility methods */
051: private XMLRootElementUtil() {
052:
053: }
054:
055: /**
056: * @param clazz
057: * @return namespace of root element qname or null if this is not object does not represent a
058: * root element
059: */
060: public static QName getXmlRootElementQNameFromObject(Object obj) {
061:
062: // A JAXBElement stores its name
063: if (obj instanceof JAXBElement) {
064: return ((JAXBElement) obj).getName();
065: }
066:
067: Class clazz = (obj instanceof java.lang.Class) ? (Class) obj
068: : obj.getClass();
069: return getXmlRootElementQName(clazz);
070: }
071:
072: /**
073: * @param clazz
074: * @return namespace of root element qname or null if this is not object does not represent a
075: * root element
076: */
077: public static QName getXmlRootElementQName(Class clazz) {
078:
079: // See if the object represents a root element
080: XmlRootElement root = (XmlRootElement) clazz
081: .getAnnotation(XmlRootElement.class);
082: if (root == null) {
083: return null;
084: }
085:
086: String name = root.name();
087: String namespace = root.namespace();
088:
089: // The name may need to be defaulted
090: if (name == null || name.length() == 0
091: || name.equals("##default")) {
092: name = getSimpleName(clazz.getCanonicalName());
093: }
094:
095: // The namespace may need to be defaulted
096: if (namespace == null || namespace.length() == 0
097: || namespace.equals("##default")) {
098: Package pkg = clazz.getPackage();
099: XmlSchema schema = (XmlSchema) pkg
100: .getAnnotation(XmlSchema.class);
101: if (schema != null) {
102: namespace = schema.namespace();
103: } else {
104: namespace = "";
105: }
106: }
107:
108: return new QName(namespace, name);
109: }
110:
111: /**
112: * @param clazz
113: * @return namespace of root element qname or null if this is not object does not represent a root element
114: */
115: public static String getEnumValue(Enum myEnum) {
116: Field f;
117: String value;
118: try {
119: f = myEnum.getClass().getField(myEnum.name());
120:
121: f.setAccessible(true);
122:
123: XmlEnumValue xev = f.getAnnotation(XmlEnumValue.class);
124: if (xev == null) {
125: value = f.getName();
126: } else {
127: value = xev.value();
128: }
129: } catch (SecurityException e) {
130: value = null;
131: } catch (NoSuchFieldException e) {
132: value = null;
133: }
134:
135: return value;
136: }
137:
138: /**
139: * utility method to get the last token in a "."-delimited package+classname string
140: *
141: * @return
142: */
143: private static String getSimpleName(String in) {
144: if (in == null || in.length() == 0) {
145: return in;
146: }
147: String out = null;
148: StringTokenizer tokenizer = new StringTokenizer(in, ".");
149: if (tokenizer.countTokens() == 0)
150: out = in;
151: else {
152: while (tokenizer.hasMoreTokens()) {
153: out = tokenizer.nextToken();
154: }
155: }
156: return out;
157: }
158:
159: /**
160: * The JAXBClass has a set of bean properties each represented by a PropertyDescriptor Each of
161: * the fields of the class has an associated xml name. The method returns a map where the key is
162: * the xml name and value is the PropertyDescriptor
163: *
164: * @param jaxbClass
165: * @return map
166: */
167: public static Map<String, PropertyDescriptorPlus> createPropertyDescriptorMap(
168: Class jaxbClass) throws NoSuchFieldException,
169: IntrospectionException {
170:
171: if (log.isDebugEnabled()) {
172: log.debug("Get the PropertyDescriptor[] for " + jaxbClass);
173: }
174:
175: PropertyDescriptor[] pds = Introspector.getBeanInfo(jaxbClass)
176: .getPropertyDescriptors();
177: Map<String, PropertyDescriptorPlus> map = new HashMap<String, PropertyDescriptorPlus>();
178:
179: // Unfortunately the element names are stored on the fields.
180: // Get all of the fields in the class and super classes
181:
182: List<Field> fields = getFields(jaxbClass);
183:
184: // Now match up the fields with the property descriptors...Sigh why didn't JAXB put the @XMLElement annotations on the
185: // property methods!
186: for (PropertyDescriptor pd : pds) {
187:
188: // Skip over the class property..it is never represented as an xml element
189: if (pd.getName().equals("class")) {
190: continue;
191: }
192:
193: // For the current property, find a matching field...so that we can get the xml name
194: boolean found = false;
195: if (log.isDebugEnabled()) {
196: log.debug(" Start: Find xmlname for property:"
197: + pd.getName());
198: }
199: for (Field field : fields) {
200: String fieldName = field.getName();
201:
202: // Use the name of the field and property to find the match
203: if (fieldName.equalsIgnoreCase(pd.getDisplayName())
204: || fieldName.equalsIgnoreCase(pd.getName())) {
205: // Get the xmlElement name for this field
206: String xmlName = getXmlElementName(field
207: .getDeclaringClass(), field);
208: found = true;
209: if (log.isDebugEnabled()) {
210: log.debug(" Found field " + field.getName()
211: + " which has xmlname=" + xmlName);
212: }
213: if (map.get(xmlName) != null) {
214: if (log.isDebugEnabled()) {
215: log
216: .debug(" ALERT: property "
217: + map.get(xmlName)
218: .getPropertyName()
219: + " already has this same xmlName..this may cause problems.");
220: }
221: }
222: map.put(xmlName, new PropertyDescriptorPlus(pd,
223: xmlName));
224: break;
225: }
226:
227: // Unfortunately, sometimes the field name is preceeded by an underscore
228: if (fieldName.startsWith("_")) {
229: fieldName = fieldName.substring(1);
230: if (fieldName.equalsIgnoreCase(pd.getDisplayName())
231: || fieldName.equalsIgnoreCase(pd.getName())) {
232: // Get the xmlElement name for this field
233: String xmlName = getXmlElementName(field
234: .getDeclaringClass(), field);
235: found = true;
236: if (log.isDebugEnabled()) {
237: log.debug(" Found field "
238: + field.getName()
239: + " which has xmlname=" + xmlName);
240: }
241: if (map.get(xmlName) != null) {
242: if (log.isDebugEnabled()) {
243: log
244: .debug(" ALERT: property "
245: + map
246: .get(xmlName)
247: .getPropertyName()
248: + " already has this same xmlName..this may cause problems.");
249: }
250: }
251: map.put(xmlName, new PropertyDescriptorPlus(pd,
252: xmlName));
253: break;
254: }
255: }
256: }
257:
258: // We didn't find a field. Default the xmlname to the property name
259: if (!found) {
260: String xmlName = pd.getName();
261: if (log.isDebugEnabled()) {
262: log
263: .debug(" A matching field was not found. Defaulting xmlname to "
264: + xmlName);
265: }
266: if (map.get(xmlName) != null) {
267: if (log.isDebugEnabled()) {
268: log
269: .debug(" ALERT: property "
270: + map.get(xmlName)
271: .getPropertyName()
272: + " already has this same xmlName..this may cause problems.");
273: }
274: }
275: map.put(xmlName,
276: new PropertyDescriptorPlus(pd, xmlName));
277: }
278: if (log.isDebugEnabled()) {
279: log.debug(" End: Find xmlname for property:"
280: + pd.getName());
281: }
282: }
283: return map;
284: }
285:
286: /**
287: * Gets all of the fields in this class and the super classes
288: *
289: * @param beanClass
290: * @return
291: */
292: static private List<Field> getFields(final Class beanClass) {
293: // This class must remain private due to Java 2 Security concerns
294: List<Field> fields;
295: fields = (List<Field>) AccessController
296: .doPrivileged(new PrivilegedAction() {
297: public Object run() {
298: List<Field> fields = new ArrayList<Field>();
299: Class cls = beanClass;
300: while (cls != null) {
301: Field[] fieldArray = cls
302: .getDeclaredFields();
303: for (Field field : fieldArray) {
304: fields.add(field);
305: }
306: cls = cls.getSuperclass();
307: }
308: return fields;
309: }
310: });
311:
312: return fields;
313: }
314:
315: /**
316: * Get the name of the field by looking at the XmlElement annotation.
317: *
318: * @param jaxbClass
319: * @param fieldName
320: * @return
321: * @throws NoSuchFieldException
322: */
323: private static String getXmlElementName(Class jaxbClass, Field field)
324: throws NoSuchFieldException {
325: XmlElement xmlElement = field.getAnnotation(XmlElement.class);
326:
327: // If XmlElement does not exist, default to using the field name
328: if (xmlElement == null || xmlElement.name().equals("##default")) {
329: return field.getName();
330: }
331: return xmlElement.name();
332:
333: }
334:
335: }
|