001: /*
002: * Project: Gulden Utilies
003: * Class: de.gulden.util.xml.serializer.smart.SmartReflectionXMLSerializer
004: * Version: snapshot-beautyj-1.1
005: *
006: * Date: 2004-09-29
007: *
008: * This is a snapshot version of the Gulden Utilities,
009: * it is not released as a seperate version.
010: *
011: * Note: Contains auto-generated Javadoc comments created by BeautyJ.
012: *
013: * This is licensed under the GNU Lesser General Public License (LGPL)
014: * and comes with NO WARRANTY.
015: *
016: * Author: Jens Gulden
017: * Email: amoda@jensgulden.de
018: */
019:
020: package de.gulden.util.xml.serializer.smart;
021:
022: import de.gulden.util.Toolbox;
023: import de.gulden.util.xml.*;
024: import de.gulden.util.xml.serializer.*;
025: import java.lang.reflect.*;
026: import java.lang.reflect.Field;
027: import java.lang.reflect.InvocationTargetException;
028: import java.util.*;
029: import org.w3c.dom.Document;
030: import org.w3c.dom.Element;
031:
032: /**
033: * Class SmartReflectionXMLSerializer.
034: *
035: * @author Jens Gulden
036: * @version snapshot-beautyj-1.1
037: */
038: public class SmartReflectionXMLSerializer implements XMLSerializer {
039:
040: // ------------------------------------------------------------------------
041: // --- final static field ---
042: // ------------------------------------------------------------------------
043:
044: /**
045: * Constant DEFAULT_COLLECTION_CLASS.
046: */
047: public static final Class DEFAULT_COLLECTION_CLASS = ArrayList.class;
048:
049: // ------------------------------------------------------------------------
050: // --- static field ---
051: // ------------------------------------------------------------------------
052:
053: /**
054: * The d e f a u l t_ m a p_ c l a s s.
055: */
056: public static Class DEFAULT_MAP_CLASS = de.gulden.util.OrderedHashMap.class;
057:
058: // ------------------------------------------------------------------------
059: // --- field ---
060: // ------------------------------------------------------------------------
061:
062: /**
063: * <p>
064: * </p>
065: */
066: protected SmartReflectionXMLSerializerFactory factory;
067:
068: // ------------------------------------------------------------------------
069: // --- constructor ---
070: // ------------------------------------------------------------------------
071:
072: /**
073: * Creates a new instance of SmartReflectionXMLSerializer.
074: */
075: SmartReflectionXMLSerializer(
076: SmartReflectionXMLSerializerFactory factory) {
077: super ();
078: setFactory(factory);
079: }
080:
081: // ------------------------------------------------------------------------
082: // --- methods ---
083: // ------------------------------------------------------------------------
084:
085: /**
086: * Returns the factory.
087: */
088: public SmartReflectionXMLSerializerFactory getFactory() {
089: return factory;
090: }
091:
092: /**
093: * Sets the factory.
094: */
095: public void setFactory(
096: SmartReflectionXMLSerializerFactory smartReflectionXMLSerializerFactory) {
097: this .factory = smartReflectionXMLSerializerFactory;
098: }
099:
100: public Element xmlSerialize(Object object, Document document)
101: throws XMLException {
102: // TODO
103: throw new Error("NOT IMPLEMENTED YET");
104: }
105:
106: public Object xmlDeserialize(Element element) throws XMLException {
107: Class clazz = getFactory().mapToClass(element.getTagName(),
108: element.getAttribute("class"));
109: Object o;
110: try {
111: o = clazz.newInstance();
112: } catch (InstantiationException ie) {
113: throw new XMLException(ie);
114: } catch (IllegalAccessException iae) {
115: throw new XMLException(iae);
116: }
117: xmlDeserialize(o, element);
118: return o;
119: }
120:
121: public void xmlDeserialize(Object object, Element element)
122: throws XMLException {
123: xmlDeserialize(object, element, true);
124: }
125:
126: public void xmlDeserialize(Object object, Element element,
127: boolean enableSerializableActive) throws XMLException {
128: // object does its own serialization
129: if ((enableSerializableActive)
130: && (object instanceof XMLSerializableActive)) {
131: ((XMLSerializableActive) object).xmlDeserialize(element,
132: this );
133: // no own serialization by object, auto-reflection
134: } else if (object instanceof XMLSerializable) {
135: Class clazz = object.getClass();
136: try {
137: // iterate over fields
138: java.lang.reflect.Field[] fields = clazz.getFields();
139: for (int i = 0; i < fields.length; i++) {
140: int fieldModifiers = fields[i].getModifiers();
141: if (!(java.lang.reflect.Modifier
142: .isTransient(fieldModifiers)
143: || java.lang.reflect.Modifier
144: .isStatic(fieldModifiers)
145: || java.lang.reflect.Modifier
146: .isFinal(fieldModifiers) || java.lang.reflect.Modifier
147: .isVolatile(fieldModifiers))) {
148: String fieldName = fields[i].getName();
149: Class fieldType = fields[i].getType();
150: Object fieldObject = fields[i].get(object);
151: String xmlName = XMLToolbox
152: .translateJavaNameToXMLName(fieldName);
153:
154: // primitive type or String
155: if (fieldType.isPrimitive()
156: || (fieldType == String.class)) {
157: String attr = element.getAttribute(xmlName);
158: String valueStr = null;
159: // attribute with corresponding name?
160: if ((attr != null) && (!attr.equals(""))) {
161: valueStr = attr;
162: // sub-element with corresponding name?
163: } else {
164: Element e = XMLToolbox.getChild(
165: element, xmlName);
166: // yes...
167: if (e != null) {
168: if (fieldType == String.class) {
169: // might be langstring...
170: valueStr = XMLToolbox
171: .getLangstring(e); // might stay null if no langstring
172: }
173: if (valueStr == null) {
174: valueStr = XMLToolbox
175: .getText(e);
176: }
177: // special field name "text" allows direct element content
178: } else if ((fieldType == String.class)
179: && (fieldName.equals("text"))) {
180: valueStr = XMLToolbox
181: .getLangstring(element); // might stay null if no langstring
182: if (valueStr == null) {
183: valueStr = XMLToolbox
184: .getText(element);
185: }
186: // no element found
187: } else {
188: valueStr = null; // ignore, leave initial value of attribute (if any)
189: }
190: }
191: if (valueStr != null) {
192: // set field
193: fieldType = convertPrimitiveClassToWrapperClass(fieldType);
194: Object fieldValue;
195: if (fieldType == String.class) {
196: fieldValue = valueStr;
197: } else if (Boolean.class
198: .isAssignableFrom(fieldType)) {
199: fieldValue = Boolean
200: .valueOf(de.gulden.util.Toolbox
201: .parseBoolean(valueStr));
202: } else if (Number.class
203: .isAssignableFrom(fieldType)) {
204: Class[] valueOfParamType = { String.class };
205: java.lang.reflect.Method valueOfMethod = fieldType
206: .getMethod("valueOf",
207: valueOfParamType);
208: Object[] valueOfParamValue = { valueStr };
209: fieldValue = valueOfMethod.invoke(
210: null, valueOfParamValue);
211: } else {
212: throw new XMLException(
213: "can't deserialize attribute of type "
214: + fieldType
215: .getName());
216: }
217: setFieldValue(fields[i], object,
218: fieldValue);
219: }
220: // Collection type
221: } else if (Collection.class
222: .isAssignableFrom(fieldType)) {
223: Collection c = (Collection) fieldObject;
224: if (c == null) {
225: // create new instance to hold collection
226: Class t;
227: if (fieldType.isInterface()
228: || java.lang.reflect.Modifier
229: .isAbstract(fieldModifiers)) {
230: t = DEFAULT_COLLECTION_CLASS; // use this if cannot create instance if type
231: // **** MIGHT NOT WORK ALL THE TIME, if fieldType is a subclass of Collection but not impl. by DEFAULT_COLLECTION_CLASS
232: } else {
233: t = fieldType;
234: }
235: c = (Collection) t.newInstance(); // create the collection instance to hold the elements
236: }
237: String[] childrenTagNames = getChildrenTagNames(
238: clazz, fieldName, xmlName);
239: org.w3c.dom.NodeList nl = XMLToolbox
240: .getChildren(element,
241: childrenTagNames);
242: for (int j = 0; j < nl.getLength(); j++) {
243: Element childTag = (Element) nl.item(j);
244: //xmlDeserialize(child,childTag); // recursion
245: Object child = xmlDeserialize(childTag); // recursion
246: c.add(child);
247: }
248: if (fieldObject == null) {
249: setFieldValue(fields[i], object, c);
250: }
251: // Map type
252: } else if (Map.class
253: .isAssignableFrom(fieldType)) {
254: Map m = (Map) fieldObject;
255: if (m == null) {
256: // create new instance to hold map (keep original Map with values if already there)
257: Class t;
258: if (fieldType.isInterface()
259: || java.lang.reflect.Modifier
260: .isAbstract(fieldModifiers)) {
261: t = DEFAULT_MAP_CLASS; // use this if cannot create instance if type
262: // **** MIGHT NOT WORK ALL THE TIME, see above
263: } else {
264: t = fieldType;
265: }
266: m = (Map) t.newInstance(); // create the map instance to hold the elements
267: }
268: String[] childrenTagNames = getChildrenTagNames(
269: clazz, fieldName, xmlName);
270: org.w3c.dom.NodeList nl = XMLToolbox
271: .getChildren(element,
272: childrenTagNames);
273: for (int j = 0; j < nl.getLength(); j++) {
274: Element childTag = (Element) nl.item(j);
275: //xmlDeserialize(child,childTag); // recursion
276: Object child = xmlDeserialize(childTag); // recursion
277: // obtain map key by reflectively calling "getId():String"
278: // **** THIS IS HARD-WIRED and inflexible
279: Class[] getIdParamType = {};
280: java.lang.reflect.Method getIdMethod;
281: try {
282: getIdMethod = child.getClass()
283: .getMethod("getId",
284: getIdParamType);
285: } catch (NoSuchMethodException nsme0) {
286: throw new XMLException(
287: "mapped objects must contain a getId():String method to be return a key ("
288: + child.getClass()
289: .getName()
290: + ")");
291: }
292: Object[] getIdParamValue = {};
293: String key = (String) getIdMethod
294: .invoke(child, getIdParamValue);
295: m.put(key, child);
296: }
297: if (fieldObject == null) {
298: setFieldValue(fields[i], object, m);
299: }
300: // Array type
301: } else if (fieldType.isArray()) {
302: throw new XMLException(
303: "NOT IMPLEMENTED YET: array types cannot be XML-deserialized");
304: // complex type: use smart reflection recursively
305: } else {
306: Element e = XMLToolbox.getChild(element,
307: xmlName);
308: if (e == null) {
309: // ignore, leave initial value
310: } else {
311: //xmlDeserialize(o,e); // recursion
312: Object o;
313: if (fieldObject == null) {
314: o = xmlDeserialize(e); // recursion
315: } else {
316: o = fieldObject; // keep original value if exists
317: xmlDeserialize(o, e, true);
318: }
319: setFieldValue(fields[i], object, o);
320: }
321: }
322: }
323: }
324: } catch (NoSuchMethodException nsme) {
325: throw new XMLException(nsme);
326: } catch (IllegalAccessException iae) {
327: throw new XMLException(iae);
328: } catch (InstantiationException ie) {
329: throw new XMLException(ie);
330: } catch (java.lang.reflect.InvocationTargetException ite) {
331: throw new XMLException(ite.getTargetException());
332: }
333: } else {
334: throw new XMLException(
335: "object of type '"
336: + object.getClass().getName()
337: + "' cannot be deserialized from XML - it does not implement XMLSerializable");
338: }
339: }
340:
341: // ------------------------------------------------------------------------
342: // --- static methods ---
343: // ------------------------------------------------------------------------
344:
345: /**
346: * Sets the field value.
347: */
348: protected static void setFieldValue(Field field, Object object,
349: Object value) throws InvocationTargetException,
350: IllegalAccessException {
351: String fieldName = field.getName();
352: String setterName = "set" + Toolbox.capitalize(fieldName);
353: Class[] setterParams = { field.getType() };
354: try {
355: java.lang.reflect.Method m = object.getClass().getMethod(
356: setterName, setterParams);
357: // if no exception thrown, there is a setter method for this field
358: Object[] setterValues = { value };
359: m.invoke(object, setterValues);
360: } catch (NoSuchMethodException nsme) {
361: // set field directly, no setter method
362: field.set(object, value);
363: }
364: }
365:
366: /**
367: * Returns the children tag names.
368: */
369: protected static String[] getChildrenTagNames(Class clazz,
370: String fieldName, String xmlName) {
371: // fieldName might be a plural form which gets converted to singular
372: String[] result = null;
373: try {
374: Field xmlMapAssignField = clazz
375: .getField("XML_COLLECTION_ELEMENTS");
376: Object[][] xmlMapAssign = (Object[][]) xmlMapAssignField
377: .get(null);
378: if (xmlMapAssign != null) {
379: for (int i = 0; (result == null)
380: && (i < xmlMapAssign.length); i++) {
381: String key = (String) xmlMapAssign[i][0];
382: if (key.equals(fieldName)) { // found explicit map assignment
383: String[] assignedFields = (String[]) xmlMapAssign[i][1];
384: return assignedFields;
385: }
386: }
387: }
388: } catch (NoSuchFieldException nsfe) {
389: //fallthrough
390: } catch (IllegalAccessException iae) {
391: //fallthrough
392: }
393: if (result == null) { // not found yet
394: // get singular form of xmlName
395: if (xmlName.endsWith("ies")) {
396: xmlName = xmlName.substring(0, xmlName.length() - 3)
397: + "y";
398: } else if (xmlName.endsWith("s")) {
399: xmlName = xmlName.substring(0, xmlName.length() - 1);
400: }
401: result = new String[1];
402: result[0] = xmlName;
403: }
404: return result;
405: }
406:
407: protected static Class convertPrimitiveClassToWrapperClass(Class c) {
408: // returns same class if not primitive
409: if (c.isPrimitive()) {
410: try {
411: return Class.forName("java.lang."
412: + Toolbox.capitalize(c.getName()));
413: } catch (ClassNotFoundException cnfe) { // (should never happen)
414: return null;
415: }
416: } else {
417: return c;
418: }
419: }
420:
421: } // end SmartReflectionXMLSerializer
|