/*
* $Id$
*
* Copyright 2006, The jCoderZ.org Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
* * Neither the name of the jCoderZ.org Project nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jcoderz.commons.util;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;
import org.jcoderz.commons.ArgumentMalformedException;
import org.jcoderz.commons.RteLogMessage;
import org.xml.sax.InputSource;
/**
* Utility class to simplify JAXB marshalling/unmarshalling.
* @author Albrecht Messner
*/
public final class JaxbUtil
{
private static final Map JAXB_CONTEXT_MAP = new HashMap();
private JaxbUtil ()
{
// avoid instantiation of utility class
}
/**
* Returns a JAXB context for the given context path. Contexts are
* cached in a hash map.
* @param contextPath the JAXB context path
* @return the jaxb context for the given context path
* @throws JAXBException if the context could not be retrieved
*/
public static synchronized JAXBContext getJaxbContext (String contextPath)
throws JAXBException
{
JAXBContext ctx = (JAXBContext) JAXB_CONTEXT_MAP.get(contextPath);
if (ctx == null)
{
ctx = JAXBContext.newInstance(contextPath);
JAXB_CONTEXT_MAP.put(contextPath, ctx);
}
return ctx;
}
/**
* Unmarshals the given InputSource and returns the unmarshalled object
* along with the validation event collector.
* @param data the data to unmarshal
* @param ctxPath the context path from which the JAXBContext is created
* to create an unmarshaller
* @return the unmarshalled object along with its validation events
* @throws JAXBException if unmarshalling or validation fails.
*/
public static UnmarshalResult unmarshal (InputSource data, String ctxPath)
throws JAXBException
{
final JAXBContext ctx = getJaxbContext(ctxPath);
return unmarshal(data, ctx);
}
/**
* Unmarshals the given InputSource and returns the unmarshalled object
* along with the validation event collector.
* @param data the data to unmarshal
* @param ctx the JAXBContext from which the unmarshaller should be
* retrieved
* @return the unmarshalled object along with its validation events
* @throws JAXBException if unmarshalling or validation fails.
*/
public static UnmarshalResult unmarshal (InputSource data, JAXBContext ctx)
throws JAXBException
{
final Unmarshaller unmarsh = ctx.createUnmarshaller();
unmarsh.setValidating(true);
final ValidationEventCollector evtHandler
= new ValidationEventCollector();
unmarsh.setEventHandler(evtHandler);
final Object parsedData = unmarsh.unmarshal(data);
return new UnmarshalResult(parsedData, evtHandler);
}
/**
* Serializes (marshals) a given JAXB object and returns the result as
* byte array, along with the validation events collected during
* marshalling.
* @param data the object to marshal
* @param contextPath the context path to retrieve the corresponding JAXB
* context for.
* @return the marshalled object and marshalling events
* @throws JAXBException if marshalling or validation fails.
*/
public static MarshalResult marshal (Object data, String contextPath)
throws JAXBException
{
final JAXBContext ctx = getJaxbContext(contextPath);
return marshal(data, ctx);
}
/**
* Serializes (marshals) a given JAXB object and returns the result as
* byte array, along with the validation events collected during
* marshalling.
* @param data the object to marshal
* @param ctx the JAXBContext from which the unmarshaller can be retrieved.
* @return the marshalled object and marshalling events
* @throws JAXBException if marshalling or validation fails.
*/
public static MarshalResult marshal (Object data, JAXBContext ctx)
throws JAXBException
{
final Marshaller marsh = ctx.createMarshaller();
final ValidationEventCollector evtHandler
= new ValidationEventCollector();
marsh.setEventHandler(evtHandler);
final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
marsh.marshal(data, outStream);
final MarshalResult result
= new MarshalResult(outStream.toByteArray(), evtHandler);
return result;
}
/**
* Holds the Result of an unmarshal operation.
* @author Albrecht Messner
*/
public static class UnmarshalResult
{
private final Object mParsedData;
private final ValidationEventCollector mValidationEvents;
UnmarshalResult (Object parsedData, ValidationEventCollector evtHandler)
{
mParsedData = parsedData;
mValidationEvents = evtHandler;
}
/**
* Returns the parsed (unmarshalled) object.
* @return the parsed (unmarshalled) object.
*/
public Object getParsedData ()
{
return mParsedData;
}
/**
* Returns the validation events of the unmarshal operation.
* @return the validation events of the unmarshal operation.
*/
public ValidationEventCollector getValidationEvents ()
{
return mValidationEvents;
}
}
/**
* Holds the Result of an Marshal operation.
* @author Albrecht Messner
*/
public static class MarshalResult
{
private final byte[] mMarshalledData;
private final ValidationEventCollector mValidationEvents;
MarshalResult (byte[] marshalledData, ValidationEventCollector evtHandler)
{
mMarshalledData = marshalledData;
mValidationEvents = evtHandler;
}
/**
* Returns the marshalled object.
* @return the marshalled object.
*/
public byte[] getMarshalledData ()
{
return mMarshalledData;
}
/**
* Returns the validation events of the marshal operation.
* @return the validation events of the marshal operation.
*/
public ValidationEventCollector getValidationEvents ()
{
return mValidationEvents;
}
}
/**
* Validation handler for JAXB.
*
* @author Michael Griffel
*/
public static class ValidationEventCollector
implements ValidationEventHandler
{
private static final String CLASSNAME
= ValidationEventCollector.class.getName();
private static final Logger logger = Logger.getLogger(CLASSNAME);
private final List mEvents = new ArrayList();
/**
* Returns all the collected errors and warnings or an empty list
* if there weren't any. The result is an unmodifiable list.
*
* @return all the collected errors and warnings or an empty list
* if there weren't any. The result is an unmodifiable list.
*/
public List getEvents ()
{
return Collections.unmodifiableList(mEvents);
}
/**
* Clear all collected errors and warnings.
*/
public void reset ()
{
mEvents.clear();
}
/**
* Returns true if this event collector contains at least one
* ValidationEvent.
*
* @return true if this event collector contains at least one
* ValidationEvent, false otherwise
*/
public boolean hasEvents ()
{
return mEvents.size() != 0;
}
/** {@inheritDoc} */
public boolean handleEvent (ValidationEvent event)
{
final String methodName = "handleEvent";
if (logger.isLoggable(Level.FINER))
{
logger.entering(CLASSNAME, methodName, event);
logger.finer("Event details: "
+ eventToString(new StringBuffer(), event));
}
mEvents.add(event);
final boolean doContinue
= event.getSeverity() != ValidationEvent.FATAL_ERROR;
if (logger.isLoggable(Level.FINER))
{
logger.exiting(CLASSNAME, methodName, String.valueOf(doContinue));
}
return doContinue;
}
/**
* Returns a summary of the validation events as String.
* @return a summary of the validation events as String.
*/
public String toString ()
{
final StringBuffer sb = new StringBuffer();
for (int i = 0; i < mEvents.size(); ++i)
{
sb.append('[');
sb.append(i + 1);
sb.append('/');
sb.append(mEvents.size());
sb.append("] ");
final ValidationEvent e = (ValidationEvent) mEvents.get(i);
eventToString(sb, e);
}
return sb.toString().trim();
}
private StringBuffer eventToString (
final StringBuffer sb, final ValidationEvent e)
{
appendLocator(sb, e.getLocator());
if (e.getLinkedException() != null)
{
appendLinkedException(sb, e);
}
else
{
sb.append(e.getMessage());
}
appendSpace(sb);
return sb;
}
private void appendLinkedException (final StringBuffer sb,
final ValidationEvent e)
{
final String causeMessage = e.getLinkedException().getMessage();
if (!e.getMessage().equals(causeMessage))
{
if (e.getLinkedException() instanceof ArgumentMalformedException)
{
final ArgumentMalformedException ame
= (ArgumentMalformedException) e.getLinkedException();
sb.append("The Argument ");
sb.append(getParameter(
ame, RteLogMessage.ArgumentMalformed.PARAM_ARGUMENT_NAME));
sb.append(" with the value '");
sb.append(getParameter(ame,
RteLogMessage.ArgumentMalformed.PARAM_ARGUMENT_VALUE));
sb.append("' is malformed. ");
sb.append(getParameter(
ame, RteLogMessage.ArgumentMalformed.PARAM_HINT));
}
else
{
sb.append(e.getMessage());
if (causeMessage != null)
{
sb.append(" Cause: ");
sb.append(causeMessage);
}
}
}
else
{
sb.append(e.getMessage());
}
}
private void appendLocator (final StringBuffer sb,
ValidationEventLocator locator)
{
if (locator != null)
{
if (locator.getObject() != null)
{
sb.append("Object: ");
sb.append(locator.getObject());
appendSpace(sb);
}
if (locator.getNode() != null)
{
sb.append("Node: ");
sb.append(locator.getObject());
appendSpace(sb);
}
if (locator.getOffset() >= 0)
{
sb.append("Offset: ");
sb.append(locator.getOffset());
appendSpace(sb);
}
}
}
/** Simply appends a spece to the given StringBuffer. */
private final StringBuffer appendSpace (final StringBuffer sb)
{
return sb.append(' ');
}
private String getParameter (ArgumentMalformedException ex, String name)
{
final List parameters = ex.getParameter(name);
final String result;
if (parameters != null)
{
result = (String) parameters.get(0);
}
else
{
result = "";
}
return result;
}
}
}