/*
* XML 2 Java Binding (X2JB) - the excellent Java tool.
* Copyright 2009, by Richard Opalka.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not see the FSF site:
* http://www.fsf.org/ and search for the LGPL License document there.
*/
package org.x2jb.bind;
import java.io.IOException;
import java.io.InputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.x2jb.bind.Messages.BundleKey;
import org.x2jb.bind.spi.handler.AttributeHandler;
import org.x2jb.bind.spi.handler.ElementHandler;
/**
* <code>HandlersRepository</code> holds default and user defined primitive type handlers.
* Each handler must implement either {@link ElementHandler} or {@link AttributeHandler} interface.
*
* @author <a href="mailto:richard_opalka@yahoo.com">Richard Opalka</a>
*/
final class HandlersRepository
{
/**
* Default handles config file location.
*/
private static final String DEFAULT_HANDLERS = "META-INF/x2jb.handlers";
/**
* Default element handlers cache.
*/
private static final Map< String, Object > DEFAULT_ELEMENT_HANDLERS = new HashMap< String, Object >();
/**
* Default attribute handlers cache.
*/
private static final Map< String, Object > DEFAULT_ATTRIBUTE_HANDLERS = new HashMap< String, Object >();
/**
* User element handlers cache.
*/
private static final Map< String, Object > USER_ELEMENT_HANDLERS = new HashMap< String, Object >();
/**
* User attribute handlers cache.
*/
private static final Map< String, Object > USER_ATTRIBUTE_HANDLERS = new HashMap< String, Object >();
/**
* Repository lock.
*/
private static Object repositoryLock = new Object();
/**
* Constructor.
*/
private HandlersRepository()
{
// no instances
}
static
{
HandlersRepository.instantiateAllDefaultHandlers();
}
/**
* Instantiates all default handlers registered in property file.
* @throws RuntimeException if there was some problem during handler instantiation
*/
private static void instantiateAllDefaultHandlers()
{
// not initialized yet
final InputStream repositoryProps = AccessController.doPrivileged(
new PrivilegedAction< InputStream >()
{
public InputStream run()
{
return HandlersRepository.class.
getClassLoader().
getResourceAsStream( HandlersRepository.DEFAULT_HANDLERS );
}
}
);
if ( repositoryProps == null )
{
throw new BindingException
(
Messages.get( BundleKey.UNABLE_TO_READ_PROPERTY_FILE, HandlersRepository.DEFAULT_HANDLERS )
);
}
// load properties
final Properties props = new Properties();
try
{
props.load( repositoryProps );
}
catch ( IOException ioe )
{
throw new BindingException
(
Messages.get( BundleKey.UNABLE_TO_READ_PROPERTY_FILE, HandlersRepository.DEFAULT_HANDLERS ), ioe
);
}
// instantiate handlers
for ( final Iterator<Object> i = props.keySet().iterator(); i.hasNext(); )
{
final String handlerOutputClass = ( String ) i.next();
final String handlerClassName = props.getProperty( handlerOutputClass );
final Object handler = HandlersRepository.instantiateHandler( handlerClassName );
if ( handler instanceof ElementHandler )
{
// register element binding handler
HandlersRepository.DEFAULT_ELEMENT_HANDLERS.put( handlerOutputClass, handler );
}
if ( handler instanceof AttributeHandler )
{
// register attribute binding handler
HandlersRepository.DEFAULT_ATTRIBUTE_HANDLERS.put( handlerOutputClass, handler );
}
}
}
/**
* Returns true if handler specified in binding exists, false otherwise.
*
* @param bean meta data
* @param failIfNotFound if set to true and handler doesn't exist, <code>BindingException</code> will be thrown
* @return true if handler exists, false otherwise
*/
static boolean handlerExists( final BeanImpl bean, final boolean failIfNotFound )
{
return HandlersRepository.getHandler( bean, failIfNotFound ) != null;
}
/**
* Binds XML element to object.
*
* @param value element which content will be used for object creation
* @param bean meta data
* @return instance of <code>clazz</code> class
*/
static Object bindElementToObject( final Element value, final BeanImpl bean )
{
final Class< ? > handlerClass = bean.getTypeHandler();
final Class< ? > clazz = bean.getType();
ElementHandler handler;
if ( handlerClass == null )
{
handler = ( ElementHandler ) HandlersRepository.getDefaultHandler( bean, true );
}
else
{
handler = ( ElementHandler ) HandlersRepository.getUserHandler( bean, true );
}
return handler.bind( value, clazz );
}
/**
* Binds XML element array to object array.
*
* @param values XML elements which content will be used for objects creation
* @param bean meta data
* @return one dimensional array of instances of <code>clazz</code> class
*/
static Object bindElementsToObjectArray( final List< Element > values, final BeanImpl bean )
{
final Class< ? > clazz = bean.getType();
final Object array = ReflectionUtils.newArrayInstance( clazz, values.size() );
final Class< ? > handlerClass = bean.getTypeHandler();
ElementHandler handler;
if ( handlerClass == null )
{
handler = ( ElementHandler ) HandlersRepository.getDefaultHandler( bean, true );
}
else
{
handler = ( ElementHandler ) HandlersRepository.getUserHandler( bean, true );
}
for ( int index = 0; index < values.size(); index++ )
{
final Object boundValue = handler.bind( values.get( index ), clazz );
ReflectionUtils.setArrayValue( array, index, boundValue );
}
return array;
}
/**
* Binds XML attribute value to object.
*
* @param value attribute which value will be used for object creation
* @param bean meta data
* @return instance of <code>clazz</code> class
*/
static Object bindAttributeToObject( final Attr value, final BeanImpl bean )
{
final Class< ? > handlerClass = bean.getTypeHandler();
final Class< ? > clazz = bean.getType();
AttributeHandler handler;
if ( handlerClass == null )
{
handler = ( AttributeHandler ) HandlersRepository.getDefaultHandler( bean, true );
}
else
{
handler = ( AttributeHandler ) HandlersRepository.getUserHandler( bean, true );
}
return handler.bind( value, clazz );
}
/**
* Returns default value associated with the missing XML node.
*
* @param defaultValue default value
* @param bean meta data
* @return instance of <code>clazz</code> class
*/
static Object getDefaultValue( final String defaultValue, final BeanImpl bean )
{
final Object handler;
if ( bean.getTypeHandler() == null )
{
handler = HandlersRepository.getDefaultHandler( bean, true );
}
else
{
handler = HandlersRepository.getUserHandler( bean, true );
}
if ( handler instanceof ElementHandler )
{
return ( ( ElementHandler ) handler ).getDefault( defaultValue, bean.getType() );
}
else
{
return ( ( AttributeHandler ) handler ).getDefault( defaultValue, bean.getType() );
}
}
/**
* Returns handler instance or <code>BindingException</code> if there was a problem during handler instantiation.
*
* @param handlerName name of the handler to instantiate. Handler must contain default public constructor
* @return BindingHandler created instance
*/
private static Object instantiateHandler( final String handlerName )
{
final ClassLoader classLoader = HandlersRepository.class.getClassLoader();
final Class< ? > handlerClass = ReflectionUtils.getClass( handlerName, classLoader );
return HandlersRepository.instantiateHandler( handlerClass );
}
/**
* Returns handler instance or <code>BindingException</code> if there was a problem during handler instantiation.
*
* @param handlerClass handler class to instantiate. Handler must contain default public constructor
* @return BindingHandler created instance
*/
private static Object instantiateHandler( final Class< ? > handlerClass )
{
try
{
return handlerClass.newInstance();
}
catch ( IllegalAccessException iae )
{
throw new BindingException
(
Messages.get( BundleKey.UNABLE_TO_INSTANTIATE_HANDLER, handlerClass.getName() ), iae
);
}
catch ( InstantiationException ie )
{
throw new BindingException
(
Messages.get( BundleKey.UNABLE_TO_INSTANTIATE_HANDLER, handlerClass.getName() ), ie
);
}
}
/**
* Returns default handler instance or null if handler doesn't exist.
*
* @param bean meta data
* @param failIfNotFound forces this method to throw BindingException if required handler will not be found
* @return default element or attribute binding handler
*/
private static Object getDefaultHandler( final BeanImpl bean, final boolean failIfNotFound )
{
final boolean elementHandler = bean.isElementNode();
final Map< String, Object > handlers;
if ( elementHandler )
{
handlers = HandlersRepository.DEFAULT_ELEMENT_HANDLERS;
}
else
{
handlers = HandlersRepository.DEFAULT_ATTRIBUTE_HANDLERS;
}
final Class< ? > clazz = bean.getType();
final Object handler = handlers.get( clazz.getName() );
if ( handler != null )
{
return handler;
}
// handler not found
if ( failIfNotFound )
{
throw new BindingException
(
Messages.get
(
BundleKey.DEFAULT_HANDLER_NOT_FOUND,
elementHandler ? "element" : "attribute", clazz.getName()
)
);
}
else
{
return null;
}
}
/**
* Returns user handler creating <b>clazz</b> instances or null if such handler doesn't exist.
*
* @param bean meta data
* @param failIfNotFound forces this method to throw BindingException if required handler will not be found
* @return element or attribute binding handler
*/
private static Object getUserHandler( final BeanImpl bean, final boolean failIfNotFound )
{
final Class< ? > clazz = bean.getType();
final Class< ? > handlerClass = bean.getTypeHandler();
final boolean elementHandler = bean.isElementNode();
Object handler = null;
synchronized ( HandlersRepository.repositoryLock )
{
handler = HandlersRepository.getCachedUserHandler( elementHandler, handlerClass );
if ( handler == null )
{
handler = HandlersRepository.createAndCacheHandler( elementHandler, handlerClass );
}
}
final boolean isElementOrAttributeHandler = ( handler instanceof ElementHandler ) ||
( handler instanceof AttributeHandler );
if ( ( handler != null ) && isElementOrAttributeHandler )
{
return handler;
}
// handler not found
if ( failIfNotFound )
{
throw new BindingException
(
Messages.get
(
BundleKey.CUSTOM_HANDLER_NOT_FOUND,
elementHandler ? "Element" : "Attribute", handlerClass.getName(), clazz.getName()
)
);
}
return null;
}
/**
* Returns cached user handler or null.
*
* @param elementHandler indicates whether element or attribute handler should be returned
* @param handlerClass handler to return
* @return cached user handler or null
*/
private static Object getCachedUserHandler( final boolean elementHandler, final Class< ? > handlerClass )
{
if ( elementHandler )
{
return HandlersRepository.USER_ELEMENT_HANDLERS.get( handlerClass.getName() );
}
else
{
return HandlersRepository.USER_ATTRIBUTE_HANDLERS.get( handlerClass.getName() );
}
}
/**
* Instantiates handler and puts it to user caches.
*
* @param handlerClass handler
*/
private static void loadAndCacheUserHandler( final Class< ? > handlerClass )
{
final Object handler = HandlersRepository.instantiateHandler( handlerClass );
if ( handler instanceof ElementHandler )
{
HandlersRepository.USER_ELEMENT_HANDLERS.put( handlerClass.getName(), handler );
}
if ( handler instanceof AttributeHandler )
{
HandlersRepository.USER_ATTRIBUTE_HANDLERS.put( handlerClass.getName(), handler );
}
}
/**
* Creates and caches user handler.
*
* @param elementHandler indicates whether attribute or element handler
* @param handlerClass to return
* @return created handler or null if not exists
*/
private static Object createAndCacheHandler( final boolean elementHandler, final Class< ? > handlerClass )
{
HandlersRepository.loadAndCacheUserHandler( handlerClass );
if ( elementHandler )
{
return HandlersRepository.USER_ELEMENT_HANDLERS.get( handlerClass.getName() );
}
else
{
return HandlersRepository.USER_ATTRIBUTE_HANDLERS.get( handlerClass.getName() );
}
}
/**
* Returns handler instance if handler specified in binding exists, null otherwise.
*
* @param bean meta data
* @param failIfNotFound if set to true and handler doesn't exist, <code>BindingException</code> will be thrown
* @return handler instance if handler specified in binding exists, null otherwise
*/
static Object getHandler( final BeanImpl bean, final boolean failIfNotFound )
{
final Object handler;
if ( bean.getTypeHandler() == null )
{
handler = HandlersRepository.getDefaultHandler( bean, failIfNotFound );
}
else
{
handler = HandlersRepository.getUserHandler( bean, failIfNotFound );
}
return handler;
}
}