001: /*
002: * XML 2 Java Binding (X2JB) - the excellent Java tool.
003: * Copyright 2007, by Richard Opalka.
004: *
005: * This is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU Lesser General Public License as
007: * published by the Free Software Foundation; either version 2.1 of
008: * the License, or (at your option) any later version.
009: *
010: * This software is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this software; if not see the FSF site:
017: * http://www.fsf.org/ and search for the LGPL License document there.
018: */
019: package org.x2jb.bind;
020:
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.lang.reflect.Array;
024: import java.text.MessageFormat;
025: import java.util.Iterator;
026: import java.util.Map;
027: import java.util.Properties;
028: import java.util.HashMap;
029: import org.x2jb.bind.Messages;
030: import org.x2jb.bind.Messages.BundleKey;
031: import org.x2jb.bind.provider.BindingDefinition;
032: import org.x2jb.bind.handler.AttributeHandler;
033: import org.x2jb.bind.handler.ElementHandler;
034: import org.w3c.dom.Attr;
035: import org.w3c.dom.Element;
036:
037: /**
038: * <code>HandlersRepository</code> holds default and user defined primitive type handlers.
039: * Each handler must implement either {@link ElementHandler} or {@link AttributeHandler} interface.
040: *
041: * @author <a href="mailto:richard_opalka@yahoo.com">Richard Opalka</a>
042: * @version 1.0
043: */
044: final class HandlersRepository {
045:
046: private static final String DEFAULT_HANDLER_NAME = "";
047: private static final String DEFAULT_HANDLERS = "META-INF/x2jb.handlers";
048: // default handlers
049: private static final Map DEFAULT_ELEMENT_HANDLERS = new HashMap();
050: private static final Map DEFAULT_ATTRIBUTE_HANDLERS = new HashMap();
051: // user defined handlers
052: private static final Map USER_ELEMENT_HANDLERS = new HashMap();
053: private static final Map USER_ATTRIBUTE_HANDLERS = new HashMap();
054: // lock
055: private static final Object LOCK = new Object();
056:
057: /**
058: * Constructor
059: */
060: private HandlersRepository() {
061: // no instances
062: }
063:
064: static {
065: instantiateAllDefaultHandlers();
066: }
067:
068: /**
069: * Instantiates all default handlers registered in property file
070: * @throws RuntimeException if there was some problem during handler instantiation
071: */
072: private static void instantiateAllDefaultHandlers() {
073: // not initialized yet
074: InputStream repositoryProps = HandlersRepository.class
075: .getClassLoader().getResourceAsStream(DEFAULT_HANDLERS);
076:
077: if (repositoryProps == null) {
078: throw new RuntimeException(MessageFormat.format(Messages
079: .get(BundleKey.UNABLE_TO_READ_PROPERTY_FILE),
080: new Object[] { DEFAULT_HANDLERS }));
081: }
082:
083: // load properties
084: Properties props = new Properties();
085: try {
086: props.load(repositoryProps);
087: } catch (IOException ioe) {
088: throw new RuntimeException(MessageFormat.format(Messages
089: .get(BundleKey.UNABLE_TO_READ_PROPERTY_FILE),
090: new Object[] { DEFAULT_HANDLERS }), ioe);
091: }
092:
093: // instantiate handlers
094: for (Iterator i = props.keySet().iterator(); i.hasNext();) {
095: String handlerOutputClass = (String) i.next();
096: String handlerClassName = props
097: .getProperty(handlerOutputClass);
098: Object handler = instantiateHandler(handlerClassName);
099: if (handler instanceof ElementHandler) {
100: // register element binding handler
101: DEFAULT_ELEMENT_HANDLERS.put(handlerOutputClass,
102: handler);
103: }
104: if (handler instanceof AttributeHandler) {
105: // register attribute binding handler
106: DEFAULT_ATTRIBUTE_HANDLERS.put(handlerOutputClass,
107: handler);
108: }
109: }
110: }
111:
112: /**
113: * Returns true if handler specified in binding exists, false otherwise
114: * @param clazz class which instance is created by returned handler
115: * @param binding used for obtaining informations about handler to be returned
116: * @param failIfNotFound if set to true and handler doesn't exist, <code>BindingException</code> will be thrown
117: * @return true if handler specified in binding exists, false otherwise
118: * @throws BindingException if handler specified in binding wasn't find and <code>failIfNotFound</code> is set to true
119: */
120: static boolean handlerExists(Class clazz,
121: BindingDefinition binding, boolean failIfNotFound)
122: throws BindingException {
123: return getHandler(clazz, binding, failIfNotFound) != null;
124: }
125:
126: /**
127: * Binds XML element to object
128: * @param clazz class which instance will be created
129: * @param value element which content will be used for object creation
130: * @param handlerName name of the handler that will be used for binding process
131: * @return instance of <code>clazz</code> class
132: * @throws BindingException if there was a problem during binding process
133: */
134: static Object bindElementToObject(Class clazz, Element value,
135: Class handlerClass) throws BindingException {
136: ElementHandler handler;
137:
138: if ((handlerClass == null)
139: || DEFAULT_HANDLER_NAME.equals(handlerClass.getName())) {
140: handler = (ElementHandler) getDefaultHandler(clazz, true,
141: true);
142: } else {
143: handler = (ElementHandler) getUserHandler(clazz,
144: handlerClass, true, true);
145: }
146:
147: return handler.bind(value, clazz);
148: }
149:
150: /**
151: * Binds XML element array to object array
152: * @param clazz class which instances will be created and stored in returned array
153: * @param values XML elements which content will be used for objects creation
154: * @param handlerName name of the handler that will be used for binding process
155: * @return one dimensional array of instances of <code>clazz</code> class
156: * @throws BindingException if there was a problem during binding process
157: */
158: static Object bindElementsToObjectArray(Class clazz,
159: Element[] values, Class handlerClass)
160: throws BindingException {
161: Object retVal = Array.newInstance(clazz, values.length);
162: ElementHandler handler;
163:
164: if ((handlerClass == null)
165: || DEFAULT_HANDLER_NAME.equals(handlerClass.getName())) {
166: handler = (ElementHandler) getDefaultHandler(clazz, true,
167: true);
168: } else {
169: handler = (ElementHandler) getUserHandler(clazz,
170: handlerClass, true, true);
171: }
172:
173: for (int i = 0; i < values.length; i++) {
174: // bind each element to object instance
175: Array.set(retVal, i, handler.bind(values[i], clazz));
176: }
177:
178: return retVal;
179: }
180:
181: /**
182: * Binds XML attribute value to object
183: * @param clazz class which instance will be created
184: * @param value attribute which value will be used for object creation
185: * @param handlerName name of the handler that will be used for binding process
186: * @return instance of <code>clazz</code> class
187: * @throws BindingException if there was a problem during binding process
188: */
189: static Object bindAttributeToObject(Class clazz, Attr value,
190: Class handlerClass) throws BindingException {
191: AttributeHandler handler;
192:
193: if ((handlerClass == null)
194: || DEFAULT_HANDLER_NAME.equals(handlerClass.getName())) {
195: handler = (AttributeHandler) getDefaultHandler(clazz,
196: false, true);
197: } else {
198: handler = (AttributeHandler) getUserHandler(clazz,
199: handlerClass, false, true);
200: }
201:
202: return handler.bind(value, clazz);
203: }
204:
205: /**
206: * Returns default value associated with the missing XML node
207: * @param clazz class which instance will be created
208: * @param BindingDefinition binding that will be used for binding process of default value
209: * @return instance of <code>clazz</code> class
210: * @throws BindingException if there was a problem during binding process
211: */
212: static Object getDefaultValue(Class clazz, BindingDefinition binding)
213: throws BindingException {
214: Class handlerClass = binding.getTypeHandler();
215: String handlerName = handlerClass == null ? null : handlerClass
216: .getName();
217: boolean isElement = binding.isElementNode();
218: Object handler;
219:
220: if ((handlerClass == null)
221: || DEFAULT_HANDLER_NAME.equals(handlerName)) {
222: handler = getDefaultHandler(clazz, isElement, true);
223: } else {
224: handler = getUserHandler(clazz, handlerClass, isElement,
225: true);
226: }
227:
228: if (handler instanceof ElementHandler) {
229: return ((ElementHandler) handler).getDefault(clazz);
230: } else {
231: return ((AttributeHandler) handler).getDefault(clazz);
232: }
233: }
234:
235: /**
236: * Returns handler instance or <code>BindingException</code> if there was a problem during handler instantiation
237: * @param handlerName name of the handler to instantiate. Handler must contain default public constructor
238: * @return BindingHandler created instance
239: * @throws RuntimeException if there was a problem during handler instantiation
240: */
241: private static Object instantiateHandler(String handlerName) {
242: try {
243: return instantiateHandler(Class.forName(handlerName));
244: } catch (ClassNotFoundException e) {
245: throw new RuntimeException(MessageFormat.format(Messages
246: .get(BundleKey.UNABLE_TO_INSTANTIATE_HANDLER),
247: new Object[] { handlerName }), e);
248: }
249: }
250:
251: /**
252: * Returns handler instance or <code>BindingException</code> if there was a problem during handler instantiation
253: * @param handlerName name of the handler to instantiate. Handler must contain default public constructor
254: * @return BindingHandler created instance
255: * @throws RuntimeException if there was a problem during handler instantiation
256: */
257: private static Object instantiateHandler(Class handlerClass) {
258: try {
259: return handlerClass.newInstance();
260: } catch (IllegalAccessException e) {
261: throw new RuntimeException(MessageFormat.format(Messages
262: .get(BundleKey.UNABLE_TO_INSTANTIATE_HANDLER),
263: new Object[] { handlerClass.getName() }), e);
264: } catch (InstantiationException e) {
265: throw new RuntimeException(MessageFormat.format(Messages
266: .get(BundleKey.UNABLE_TO_INSTANTIATE_HANDLER),
267: new Object[] { handlerClass.getName() }), e);
268: }
269: }
270:
271: /**
272: * Returns default handler creating <b>clazz</b> instances or null if such handler doesn't exist
273: * @param clazz class types the found handler will create
274: * @param elementHandler indicates whether handler have to be searched in element or attribute binding handlers
275: * @param failIfNotFound forces this method to throw BindingException if required handler will not be found
276: * @return default element or attribute binding handler
277: */
278: private static Object getDefaultHandler(Class clazz,
279: boolean elementHandler, boolean failIfNotFound)
280: throws BindingException {
281: // find handler
282: Map handlers = elementHandler ? DEFAULT_ELEMENT_HANDLERS
283: : DEFAULT_ATTRIBUTE_HANDLERS;
284: Object handler = handlers.get(clazz.getName());
285: if (handler != null)
286: return handler;
287:
288: // handler not found
289: if (failIfNotFound) {
290: throw new BindingException(MessageFormat.format(Messages
291: .get(BundleKey.DEFAULT_HANDLER_NOT_FOUND),
292: new Object[] {
293: elementHandler ? "element" : "attribute",
294: clazz.getName() }));
295: } else {
296: return null;
297: }
298: }
299:
300: /**
301: * Returns user handler creating <b>clazz</b> instances or null if such handler doesn't exist
302: * @param clazz class types the found handler will create
303: * @param handlerName the name of handler class to load from classloader if not found in cache map
304: * @param elementHandler indicates whether handler will be element or attribute binding handler
305: * @param failIfNotFound forces this method to throw BindingException if required handler will not be found
306: * @return element or attribute binding handler
307: */
308: private static Object getUserHandler(Class clazz,
309: Class handlerClass, boolean elementHandler,
310: boolean failIfNotFound) throws BindingException {
311: Object handler = null;
312:
313: synchronized (LOCK) {
314: // obtaining handler from cache first
315: if (elementHandler) {
316: handler = USER_ELEMENT_HANDLERS.get(handlerClass
317: .getName());
318: } else {
319: handler = USER_ATTRIBUTE_HANDLERS.get(handlerClass
320: .getName());
321: }
322:
323: if (handler == null) {
324: // handler not found - load and cache it
325: Object loadedHandler = instantiateHandler(handlerClass);
326: if (elementHandler) {
327: if (loadedHandler instanceof ElementHandler) {
328: handler = loadedHandler;
329: USER_ELEMENT_HANDLERS.put(handlerClass
330: .getName(), handler);
331: if (loadedHandler instanceof AttributeHandler) {
332: // the same handler can implement attribute handler interface too
333: USER_ATTRIBUTE_HANDLERS.put(handlerClass
334: .getName(), handler);
335: }
336: }
337: } else {
338: if (loadedHandler instanceof AttributeHandler) {
339: handler = loadedHandler;
340: USER_ATTRIBUTE_HANDLERS.put(handlerClass
341: .getName(), handler);
342: if (loadedHandler instanceof ElementHandler) {
343: // the same handler can implement element handler interface too
344: USER_ELEMENT_HANDLERS.put(handlerClass
345: .getName(), handler);
346: }
347: }
348: }
349: }
350: }
351:
352: boolean isElementOrAttributeHandler = (handler instanceof ElementHandler)
353: || (handler instanceof AttributeHandler);
354:
355: if ((handler != null) && isElementOrAttributeHandler)
356: return handler;
357:
358: // handler not found
359: if (failIfNotFound) {
360: throw new BindingException(MessageFormat.format(Messages
361: .get(BundleKey.CUSTOM_HANDLER_NOT_FOUND),
362: new Object[] {
363: elementHandler ? "Element" : "Attribute",
364: handlerClass.getName(), clazz.getName() }));
365: } else {
366: return null;
367: }
368: }
369:
370: /**
371: * Returns handler instance if handler specified in binding exists, null otherwise
372: * @param clazz class which instance is created by returned handler
373: * @param binding used for obtaining informations about handler to be returned
374: * @param failIfNotFound if set to true and handler doesn't exist, <code>BindingException</code> will be thrown
375: * @return handler instance if handler specified in binding exists, null otherwise
376: * @throws BindingException if handler specified in binding wasn't found and <code>failIfNotFound</code> is set to true
377: */
378: static Object getHandler(Class clazz, BindingDefinition binding,
379: boolean failIfNotFound) throws BindingException {
380: Class handlerClass = binding.getTypeHandler();
381: String handlerName = handlerClass == null ? null : handlerClass
382: .getName();
383: boolean elementHandler = binding.isElementNode();
384: Object handler;
385:
386: if ((handlerClass == null)
387: || DEFAULT_HANDLER_NAME.equals(handlerName)) {
388: handler = getDefaultHandler(clazz, elementHandler,
389: failIfNotFound);
390: } else {
391: handler = getUserHandler(clazz, handlerClass,
392: elementHandler, failIfNotFound);
393: }
394:
395: return handler;
396: }
397:
398: }
|