/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.tools.internal.xml.businessprocess; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.ConverterLookup; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider; import com.thoughtworks.xstream.converters.reflection.ReflectionConverter; import com.thoughtworks.xstream.converters.reflection.ReflectionProvider; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.StreamException; import com.thoughtworks.xstream.mapper.Mapper; /** * XStream doesn't support XML tag attributes so far. As the client * configuration file contains attributes, this converter adds attribute support * to the deserialization mechanism of XStream. * * @author BREDEX GmbH * @created 25.07.2005 * */ public class XStreamXmlAttributeConverter extends ReflectionConverter { /** * The reflection provider used to write the attribute into * the object field. */ private ReflectionProvider m_reflectionProvider; /** * The XStream facade object. */ private XStream m_stream; /** * The mapper is used to find the alias definitions of the object fields. */ private Mapper m_mapper; /** * The type of the object which is deserialized. */ private Class m_type; /** * A list of field names which are deserialized from XML attributes. */ private List m_fieldNames; /** * The constructor. * * @param stream * The XStream * @param mapper * The alias mapper * @param provider * The reflection provider to write the object field * @param type * The type of the object that is deserialized * @param fieldNames * The array of field names that will be deserialized */ private XStreamXmlAttributeConverter(XStream stream, Mapper mapper, ReflectionProvider provider, Class type, String[] fieldNames) { super(mapper, provider); m_stream = stream; m_reflectionProvider = provider; m_mapper = mapper; m_type = type; m_fieldNames = Arrays.asList(fieldNames); } /** * Creates a new attribute converter. * * @param stream * The XStream * @param type * The type of the object that is deserialized * @param fieldName * The field name that will be deserialized * @return A new converter instance */ public static XStreamXmlAttributeConverter create(XStream stream, Class type, String fieldName) { return create(stream, type, new String[] {fieldName}); } /** * Creates a new attribute converter. * * @param stream * The XStream * @param type * The type of the object that is deserialized * @param fieldNames * The array of field names that will be deserialized * @return A new converter instance */ public static XStreamXmlAttributeConverter create(XStream stream, Class type, String[] fieldNames) { ReflectionProvider provider = new PureJavaReflectionProvider(); XStreamXmlAttributeConverter converter = new XStreamXmlAttributeConverter( stream, stream.getMapper(), provider, type, fieldNames); return converter; } /** * Calls the protected declared method <code>fromString</code> of the * passed converter instance with the value as parameter. * * @param converter * The converter * @param value * The value to convert * @return The converted value */ private Object fromString(Converter converter, String value) { try { Method method = converter.getClass().getDeclaredMethod("fromString", //$NON-NLS-1$ new Class[] { String.class }); method.setAccessible(true); return method.invoke(converter, new Object[] { value }); } catch (SecurityException e) { throw new StreamException(e); } catch (IllegalArgumentException e) { throw new StreamException(e); } catch (NoSuchMethodException e) { throw new StreamException(e); } catch (IllegalAccessException e) { throw new StreamException(e); } catch (InvocationTargetException e) { throw new StreamException(e); } } /** * {@inheritDoc} * @param type The type to check * @return <code>true</code> if the type passed to the constructor is * assignable from the parameter <code>type</code>. */ public boolean canConvert(Class type) { return m_type.isAssignableFrom(type); } /** * Writes the <code>source</code> into the XML writer with the fields as * XML attributes. * * {@inheritDoc} * com.thoughtworks.xstream.io.HierarchicalStreamWriter, * com.thoughtworks.xstream.converters.MarshallingContext) * @param source * @param writer * @param context */ public void marshal(Object source, final HierarchicalStreamWriter writer, MarshallingContext context) { m_reflectionProvider.visitSerializableFields(source, new ReflectionProvider.Visitor() { public void visit(String field, Class fieldType, Class type, Object value) { if (m_fieldNames.contains(field)) { writer.addAttribute(m_mapper.serializedMember(m_type, field), value.toString()); } } }); super.marshal(source, writer, context); } /** * Reads object from the XML source and writes the XML attributes into the * object fields. * * {@inheritDoc} * com.thoughtworks.xstream.converters.UnmarshallingContext) * @param reader * @param context * @return */ public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String[] attributes = new String[m_fieldNames.size()]; for (int i = 0; i < m_fieldNames.size(); i++) { String fieldName = (String)m_fieldNames.get(i); attributes[i] = reader.getAttribute(m_mapper.serializedMember( m_type, fieldName)); } Object target = super.unmarshal(reader, context); ConverterLookup lookup = m_stream.getConverterLookup(); for (int i = 0; i < attributes.length; i++) { String attribute = attributes[i]; if (attribute != null) { String fieldName = (String)m_fieldNames.get(i); Class fieldType = m_reflectionProvider.getFieldType(target, fieldName, null); Converter converter = lookup.lookupConverterForType(fieldType); if (converter.canConvert(fieldType)) { m_reflectionProvider.writeField(target, fieldName, fromString(converter, attribute), null); } } } return target; } }