/* * Javolution - Java(TM) Solution for Real-Time and Embedded Systems * Copyright (C) 2012 - Javolution (http://javolution.org/) * All rights reserved. * * Permission to use, copy, modify, and distribute this software is * freely granted, provided that this notice is preserved. */ package javolution.xml.internal; import java.util.Collection; import java.util.Iterator; import java.util.Map; import javolution.context.LogContext; import javolution.text.CharArray; import javolution.text.TextBuilder; import javolution.text.TextContext; import javolution.text.TextFormat; import javolution.util.FastMap; import javolution.xml.DefaultXMLFormat; import javolution.xml.XMLContext; import javolution.xml.XMLFormat; import javolution.xml.stream.XMLStreamException; /** * Holds the default implementation of XMLContext. * * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> * @version 6.0, July 21, 2013 */ @SuppressWarnings("rawtypes") public final class XMLContextImpl extends XMLContext { // Holds class->format mapping. private final FastMap<Class<?>, XMLFormat<?>> classToFormat = new FastMap<Class<?>, XMLFormat<?>>() .shared(); // Holds parent (null if root). private final XMLContextImpl parent; /** Default constructor for root */ public XMLContextImpl() { parent = null; } /** Inner constructor */ public XMLContextImpl(XMLContextImpl parent) { this.parent = parent; } @Override protected XMLContext inner() { return new XMLContextImpl(this); } @SuppressWarnings("unchecked") @Override protected <T> XMLFormat<T> searchFormat(Class<? extends T> type) { XMLFormat<T> format = (XMLFormat<T>) classToFormat.get(type); if (format != null) return format; if (parent != null) { // Searches parent. format = parent.searchFormat(type); classToFormat.put(type, format); return format; } // Root context (search inheritable annotations). DefaultXMLFormat annotation = type .getAnnotation(DefaultXMLFormat.class); if (annotation != null) { // Found it. try { format = (XMLFormat<T>) annotation.value().newInstance(); classToFormat.put(type, format); return format; } catch (Throwable error) { LogContext.warning(error); } } if (Collection.class.isAssignableFrom(type)) { classToFormat.put(type, COLLECTION_XML); return (XMLFormat<T>) COLLECTION_XML; } else if (Map.class.isAssignableFrom(type)) { classToFormat.put(type, MAP_XML); return (XMLFormat<T>) MAP_XML; } else { classToFormat.put(type, OBJECT_XML); return (XMLFormat<T>) OBJECT_XML; } } @Override public <T> void setFormat(Class<? extends T> type, XMLFormat<T> format) { classToFormat.put(type, format); } ///////////////////////// // PREDEFINED FORMATS // ///////////////////////// /** * THe default Object XML representation consists of the text representation * of the object as a "value" attribute. */ private static final XMLFormat<Object> OBJECT_XML = new XMLFormat<Object>() { @Override public boolean isReferenceable() { return false; // Always by value. } @Override public Object newInstance(Class<?> cls, XMLFormat.InputElement xml) throws XMLStreamException { TextFormat<?> format = TextContext.getFormat(cls); CharArray value = xml.getAttribute("value"); if (value == null) throw new XMLStreamException( "Missing value attribute to parse " + cls + " instances."); return format.parse(value); } @Override public void read(XMLFormat.InputElement xml, Object obj) throws XMLStreamException { // Do nothing. } @Override public void write(Object obj, XMLFormat.OutputElement xml) throws XMLStreamException { TextBuilder tmp = new TextBuilder(); TextFormat<Object> tf = TextContext.getFormat(obj.getClass()); tf.format(obj, tmp); xml.setAttribute("value", tmp); } }; /** * The default XML representation for {@link java.util.Collection} * consists of nested XML elements one for each element of the collection. * The elements' order is defined by the collection iterator order. * Collections are deserialized using their default constructor. */ private static final XMLFormat<Collection> COLLECTION_XML = new XMLFormat<Collection>() { @SuppressWarnings("unchecked") @Override public void read(XMLFormat.InputElement xml, Collection collection) throws XMLStreamException { while (xml.hasNext()) { collection.add(xml.getNext()); } } @Override public void write(Collection collection, XMLFormat.OutputElement xml) throws XMLStreamException { for (Iterator i = collection.iterator(); i.hasNext();) { xml.add(i.next()); } } }; /** * The default XML representation for {@link java.util.Map} consists of * key/value pair as nested XML elements. For example:[code] * <javolution.util.FastMap> * <Key class="java.lang.String" value="ONE"/> * <Value class="java.lang.Integer" value="1"/> * <Key class="java.lang.String" value="TWO"/> * <Value class="java.lang.Integer" value="2"/> * <Key class="java.lang.String" value="THREE"/> * <Value class="java.lang.Integer" value="3"/> * </javolution.util.FastMap>[/code] * * The elements' order is defined by the map's entries iterator order. * Maps are deserialized using their default constructor. */ private static final XMLFormat<Map> MAP_XML = new XMLFormat<Map>() { @SuppressWarnings("unchecked") @Override public void read(XMLFormat.InputElement xml, Map map) throws XMLStreamException { while (xml.hasNext()) { Object key = xml.get("Key"); Object value = xml.get("Value"); map.put(key, value); } } @Override public void write(Map map, XMLFormat.OutputElement xml) throws XMLStreamException { for (Iterator it = map.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); xml.add(entry.getKey(), "Key"); xml.add(entry.getValue(), "Value"); } } }; }