Coverage Report - org.simpleframework.xml.core.CompositeInlineMap
 
Classes in this File Line Coverage Branch Coverage Complexity
CompositeInlineMap
92%
48/52
72%
13/18
2.857
 
 1  
 /*
 2  
  * CompositeInlineMap.java July 2007
 3  
  *
 4  
  * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License");
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
 15  
  * implied. See the License for the specific language governing 
 16  
  * permissions and limitations under the License.
 17  
  */
 18  
 
 19  
 package org.simpleframework.xml.core;
 20  
 
 21  
 import java.util.Map;
 22  
 
 23  
 import org.simpleframework.xml.strategy.Type;
 24  
 import org.simpleframework.xml.stream.InputNode;
 25  
 import org.simpleframework.xml.stream.Mode;
 26  
 import org.simpleframework.xml.stream.OutputNode;
 27  
 import org.simpleframework.xml.stream.Style;
 28  
 
 29  
 /**
 30  
  * The <code>CompositeMap</code> is used to serialize and deserialize
 31  
  * maps to and from a source XML document. The structure of the map in
 32  
  * the XML format is determined by the annotation. Keys can be either
 33  
  * attributes or elements, and values can be inline. This can perform
 34  
  * serialization and deserialization of the key and value objects 
 35  
  * whether the object types are primitive or composite.
 36  
  * <pre>
 37  
  * 
 38  
  *    &lt;map&gt;
 39  
  *       &lt;entry key='1'&gt;           
 40  
  *          &lt;value&gt;one&lt;/value&gt;
 41  
  *       &lt;/entry&gt;
 42  
  *       &lt;entry key='2'&gt;
 43  
  *          &lt;value&gt;two&lt;/value&gt;
 44  
  *       &lt;/entry&gt;      
 45  
  *    &lt;/map&gt;
 46  
  *    
 47  
  * </pre>
 48  
  * For the above XML element map the element <code>entry</code> is 
 49  
  * used to wrap the key and value such that they can be grouped. This
 50  
  * element does not represent any real object. The names of each of
 51  
  * the XML elements serialized and deserialized can be configured.
 52  
  *  
 53  
  * @author Niall Gallagher
 54  
  * 
 55  
  * @see org.simpleframework.xml.core.Entry
 56  
  */
 57  
 class CompositeInlineMap implements Repeater {
 58  
       
 59  
    /**
 60  
     * The factory used to create suitable map object instances.
 61  
     */
 62  
    private final MapFactory factory;
 63  
    
 64  
    /**
 65  
     * This is the type that the value objects are instances of. 
 66  
     */
 67  
    private final Converter value;
 68  
    
 69  
    /**
 70  
     * This is the name of the entry wrapping the key and value.
 71  
     */
 72  
    private final Converter key;  
 73  
    
 74  
    /**
 75  
     * This is the style used to style the names used for the XML.
 76  
     */
 77  
    private final Style style;
 78  
    
 79  
    /**
 80  
     * The entry object contains the details on how to write the map.
 81  
     */
 82  
    private final Entry entry;
 83  
     
 84  
    /**
 85  
     * Constructor for the <code>CompositeMap</code> object. This will
 86  
     * create a converter that is capable of writing map objects to 
 87  
     * and from XML. The resulting XML is configured by an annotation
 88  
     * such that key values can attributes and values can be inline. 
 89  
     * 
 90  
     * @param context this is the root context for the serialization
 91  
     * @param entry this provides configuration for the resulting XML
 92  
     * @param type this is the map type that is to be converted
 93  
     */
 94  266
    public CompositeInlineMap(Context context, Entry entry, Type type) throws Exception {
 95  266
       this.factory = new MapFactory(context, type);
 96  266
       this.value = entry.getValue(context);
 97  266
       this.key = entry.getKey(context);
 98  266
       this.style = context.getStyle();
 99  266
       this.entry = entry;
 100  266
    }
 101  
 
 102  
    /**
 103  
     * This <code>read</code> method will read the XML element map from
 104  
     * the provided node and deserialize its children as entry types.
 105  
     * Each entry type must contain a key and value so that the entry 
 106  
     * can be inserted in to the map as a pair. If either the key or 
 107  
     * value is composite it is read as a root object, which means its
 108  
     * <code>Root</code> annotation must be present and the name of the
 109  
     * object element must match that root element name.
 110  
     * 
 111  
     * @param node this is the XML element that is to be deserialized
 112  
     * 
 113  
     * @return this returns the item to attach to the object contact
 114  
     */
 115  
    public Object read(InputNode node) throws Exception{
 116  32
       Object value = factory.getInstance();
 117  32
       Map table = (Map) value;
 118  
       
 119  32
       if(table != null) {
 120  32
          return read(node, table);
 121  
       }
 122  0
       return null;
 123  
    }
 124  
    
 125  
    /**
 126  
     * This <code>read</code> method will read the XML element map from
 127  
     * the provided node and deserialize its children as entry types.
 128  
     * Each entry type must contain a key and value so that the entry 
 129  
     * can be inserted in to the map as a pair. If either the key or 
 130  
     * value is composite it is read as a root object, which means its
 131  
     * <code>Root</code> annotation must be present and the name of the
 132  
     * object element must match that root element name.
 133  
     * 
 134  
     * @param node this is the XML element that is to be deserialized
 135  
     * 
 136  
     * @return this returns the item to attach to the object contact
 137  
     */
 138  
    public Object read(InputNode node, Object value) throws Exception {
 139  36
       Map map = (Map) value;
 140  
       
 141  36
       if(map != null) {
 142  36
          return read(node, map);
 143  
       }
 144  0
       return read(node);
 145  
    }
 146  
    
 147  
    /**
 148  
     * This <code>read</code> method will read the XML element map from
 149  
     * the provided node and deserialize its children as entry types.
 150  
     * Each entry type must contain a key and value so that the entry 
 151  
     * can be inserted in to the map as a pair. If either the key or 
 152  
     * value is composite it is read as a root object, which means its
 153  
     * <code>Root</code> annotation must be present and the name of the
 154  
     * object element must match that root element name.
 155  
     * 
 156  
     * @param node this is the XML element that is to be deserialized
 157  
     * @param map this is the map object that is to be populated
 158  
     * 
 159  
     * @return this returns the item to attach to the object contact
 160  
     */
 161  
    private Object read(InputNode node, Map map) throws Exception {
 162  68
       InputNode from = node.getParent();
 163  68
       String name = node.getName();                
 164  
 
 165  230
       while(node != null) {         
 166  162
          Object index = key.read(node);
 167  162
          Object item = value.read(node);
 168  
             
 169  162
          if(map != null) {
 170  162
             map.put(index, item);
 171  
          }
 172  162
          node = from.getNext(name);
 173  162
       }
 174  68
       return map;
 175  
    }
 176  
    
 177  
    /**
 178  
     * This <code>read</code> method will read the XML element map from
 179  
     * the provided node and deserialize its children as entry types.
 180  
     * Each entry type must contain a key and value so that the entry 
 181  
     * can be inserted in to the map as a pair. If either the key or 
 182  
     * value is composite it is read as a root object, which means its
 183  
     * <code>Root</code> annotation must be present and the name of the
 184  
     * object element must match that root element name.
 185  
     * 
 186  
     * @param node this is the XML element that is to be deserialized
 187  
     * 
 188  
     * @return this returns the item to attach to the object contact
 189  
     */
 190  
    public boolean validate(InputNode node) throws Exception{
 191  25
       InputNode from = node.getParent();
 192  25
       String name = node.getName();                
 193  
       
 194  85
       while(node != null) {
 195  60
          if(!key.validate(node)) {
 196  0
             return false;
 197  
          }
 198  60
          if(!value.validate(node)) {
 199  0
             return false;
 200  
          }
 201  60
          node = from.getNext(name);
 202  
       }
 203  25
       return true;
 204  
    }
 205  
    
 206  
    /**
 207  
     * This <code>write</code> method will write the key value pairs
 208  
     * within the provided map to the specified XML node. This will 
 209  
     * write each entry type must contain a key and value so that
 210  
     * the entry can be deserialized in to the map as a pair. If the
 211  
     * key or value object is composite it is read as a root object 
 212  
     * so its <code>Root</code> annotation must be present.
 213  
     * 
 214  
     * @param node this is the node the map is to be written to
 215  
     * @param source this is the source map that is to be written 
 216  
     */
 217  
    public void write(OutputNode node, Object source) throws Exception {               
 218  173
       OutputNode parent = node.getParent();  
 219  173
       Mode mode = node.getMode();
 220  173
       Map map = (Map) source;
 221  
 
 222  173
       if(!node.isCommitted()) {
 223  101
          node.remove();
 224  
       }
 225  173
       write(parent, map, mode);
 226  173
    }
 227  
    
 228  
    /**
 229  
     * This <code>write</code> method will write the key value pairs
 230  
     * within the provided map to the specified XML node. This will 
 231  
     * write each entry type must contain a key and value so that
 232  
     * the entry can be deserialized in to the map as a pair. If the
 233  
     * key or value object is composite it is read as a root object 
 234  
     * so its <code>Root</code> annotation must be present.
 235  
     * 
 236  
     * @param node this is the node the map is to be written to
 237  
     * @param map this is the source map that is to be written 
 238  
     * @param mode this is the mode that has been inherited
 239  
     */
 240  
    private void write(OutputNode node, Map map, Mode mode) throws Exception {   
 241  173
       String root = entry.getEntry();
 242  173
       String name = style.getElement(root);
 243  
       
 244  173
       for(Object index : map.keySet()) {
 245  346
          OutputNode next = node.getChild(name);
 246  346
          Object item = map.get(index);            
 247  
          
 248  346
          next.setMode(mode); 
 249  346
          key.write(next, index);            
 250  346
          value.write(next, item);                  
 251  346
       }
 252  173
    }
 253  
 }