Coverage Report - org.simpleframework.xml.core.CompositeInlineList
 
Classes in this File Line Coverage Branch Coverage Complexity
CompositeInlineList
91%
52/57
68%
15/22
3
 
 1  
 /*
 2  
  * CompositeInlineList.java July 2006
 3  
  *
 4  
  * Copyright (C) 2006, 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.Collection;
 22  
 
 23  
 import org.simpleframework.xml.strategy.Type;
 24  
 import org.simpleframework.xml.stream.InputNode;
 25  
 import org.simpleframework.xml.stream.OutputNode;
 26  
 
 27  
 /**
 28  
  * The <code>CompositeInlineList</code> object is used to convert an 
 29  
  * group of elements in to a collection of element entries. This is
 30  
  * used when a containing element for a list is not required. It 
 31  
  * extracts the elements by matching elements to name of the type
 32  
  * that the annotated field or method requires. This enables these
 33  
  * element entries to exist as siblings to other objects within the
 34  
  * object.  One restriction is that the <code>Root</code> annotation
 35  
  * for each of the types within the list must be the same.
 36  
  * <pre> 
 37  
  *    
 38  
  *    &lt;entry attribute="one"&gt;
 39  
  *       &lt;text&gt;example text value&lt;/text&gt;
 40  
  *    &lt;/entry&gt;
 41  
  *    &lt;entry attribute="two"&gt;
 42  
  *       &lt;text&gt;some other example&lt;/text&gt;
 43  
  *    &lt;/entry&gt;  
 44  
  *    &lt;entry attribute="three"&gt;
 45  
  *       &lt;text&gt;yet another example&lt;/text&gt;
 46  
  *    &lt;/entry&gt;      
 47  
  * 
 48  
  * </pre>
 49  
  * For the above XML element list the element <code>entry</code> is
 50  
  * contained within the list. Each entry element is thus deserialized
 51  
  * as a root element and then inserted into the list. This enables
 52  
  * lists to be composed from XML documents. For serialization the
 53  
  * reverse is done, each element taken from the collection is written
 54  
  * as a root element to the owning element to create the list. 
 55  
  * Entry objects do not need to be of the same type.
 56  
  * 
 57  
  * @author Niall Gallagher
 58  
  *
 59  
  * @see org.simpleframework.xml.core.Traverser
 60  
  * @see org.simpleframework.xml.ElementList
 61  
  */ 
 62  
 class CompositeInlineList implements Repeater {
 63  
 
 64  
    /**
 65  
     * This factory is used to create a suitable collection list.
 66  
     */         
 67  
    private final CollectionFactory factory;
 68  
 
 69  
    /**
 70  
     * This performs the traversal used for object serialization.
 71  
     */ 
 72  
    private final Traverser root;
 73  
 
 74  
    /**
 75  
     * This represents the name of the entry elements to write.
 76  
     */
 77  
    private final String name;
 78  
    
 79  
    /**
 80  
     * This is the entry type for elements within the list.
 81  
     */   
 82  
    private final Type entry;
 83  
    
 84  
    /**
 85  
     * This represents the actual method or field for the list.
 86  
     */
 87  
    private final Type type;
 88  
 
 89  
    /**
 90  
     * Constructor for the <code>CompositeInlineList</code> object. 
 91  
     * This is given the list type and entry type to be used. The list
 92  
     * type is the <code>Collection</code> implementation that is used 
 93  
     * to collect the deserialized entry objects from the XML source. 
 94  
     *
 95  
     * @param context this is the context object used for serialization
 96  
     * @param type this is the collection type for the list used
 97  
     * @param entry the entry type to be stored within the list
 98  
     * @param name this is the name of the entries used for this list
 99  
     */    
 100  1720
    public CompositeInlineList(Context context, Type type, Type entry, String name) {
 101  1720
       this.factory = new CollectionFactory(context, type);
 102  1720
       this.root = new Traverser(context);
 103  1720
       this.entry = entry;
 104  1720
       this.type = type;
 105  1720
       this.name = name;
 106  1720
    }
 107  
 
 108  
    /**
 109  
     * This <code>read</code> method wll read the XML element list from
 110  
     * the provided node and deserialize its children as entry types.
 111  
     * This will each entry type is deserialized as a root type, that 
 112  
     * is, its <code>Root</code> annotation must be present and the
 113  
     * name of the entry element must match that root element name.
 114  
     * 
 115  
     * @param node this is the XML element that is to be deserialized
 116  
     * 
 117  
     * @return this returns the item to attach to the object contact
 118  
     */ 
 119  
    public Object read(InputNode node) throws Exception{     
 120  87
       Object value = factory.getInstance();
 121  87
       Collection list = (Collection) value;
 122  
       
 123  87
       if(list != null) {
 124  87
          return read(node, list);
 125  
       }
 126  0
       return null;
 127  
    }
 128  
    
 129  
    /**
 130  
     * This <code>read</code> method will read the XML element list from
 131  
     * the provided node and deserialize its children as entry types.
 132  
     * This will each entry type is deserialized as a root type, that 
 133  
     * is, its <code>Root</code> annotation must be present and the
 134  
     * name of the entry element must match that root element name.
 135  
     * 
 136  
     * @param node this is the XML element that is to be deserialized
 137  
     * 
 138  
     * @return this returns the item to attach to the object contact
 139  
     */ 
 140  
    public Object read(InputNode node, Object value) throws Exception {
 141  1125
       Collection list = (Collection) value;
 142  
       
 143  1125
       if(list != null) {
 144  1125
          return read(node, list);
 145  
       }
 146  0
       return read(node);
 147  
    }
 148  
    
 149  
    /**
 150  
     * This <code>read</code> method wll read the XML element list from
 151  
     * the provided node and deserialize its children as entry types.
 152  
     * This will each entry type is deserialized as a root type, that 
 153  
     * is, its <code>Root</code> annotation must be present and the
 154  
     * name of the entry element must match that root element name.
 155  
     * 
 156  
     * @param node this is the XML element that is to be deserialized
 157  
     * @param list this is the collection 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, Collection list) throws Exception {              
 162  1212
       InputNode from = node.getParent();
 163  1212
       String name = node.getName();
 164  
       
 165  4624
       while(node != null) {
 166  3412
          Class type = entry.getType();
 167  3412
          Object item = read(node, type);
 168  
          
 169  3412
          if(item != null) {
 170  3412
             list.add(item);
 171  
          }      
 172  3412
          node = from.getNext(name);
 173  3412
       }
 174  1212
       return list;
 175  
    }     
 176  
    
 177  
    /**
 178  
     * This <code>read</code> method will read the XML element from the     
 179  
     * provided node. This checks to ensure that the deserialized type
 180  
     * is the same as the entry type provided. If the types are not 
 181  
     * the same then an exception is thrown. This is done to ensure
 182  
     * each node in the collection contain the same root annotation. 
 183  
     * 
 184  
     * @param node this is the XML element that is to be deserialized
 185  
     * @param expect this is the type expected of the deserialized type
 186  
     *      
 187  
     * @return this returns the item to attach to the object contact
 188  
     */ 
 189  
    private Object read(InputNode node, Class expect) throws Exception {
 190  3412
       Object item = root.read(node, expect);
 191  3412
       Class result = item.getClass();
 192  3412
       Class actual = entry.getType();
 193  
       
 194  3412
       if(!actual.isAssignableFrom(result)) {
 195  0
          throw new PersistenceException("Entry %s does not match %s for %s", result, entry, type);
 196  
       }
 197  3412
       return item;      
 198  
    }
 199  
    
 200  
    /**
 201  
     * This <code>read</code> method will read the XML element list from
 202  
     * the provided node and deserialize its children as entry types.
 203  
     * This will each entry type is deserialized as a root type, that 
 204  
     * is, its <code>Root</code> annotation must be present and the
 205  
     * name of the entry element must match that root element name.
 206  
     * 
 207  
     * @param node this is the XML element that is to be deserialized
 208  
     * 
 209  
     * @return this returns the item to attach to the object contact
 210  
     */ 
 211  
    public boolean validate(InputNode node) throws Exception{
 212  56
       InputNode from = node.getParent();
 213  56
       Class type = entry.getType();
 214  56
       String name = node.getName();
 215  
       
 216  1171
       while(node != null) {
 217  1115
          boolean valid = root.validate(node, type);
 218  
      
 219  1115
          if(valid == false) {
 220  0
             return false;
 221  
          }
 222  1115
          node = from.getNext(name);
 223  1115
       }  
 224  56
       return true;
 225  
    } 
 226  
    
 227  
    /**
 228  
     * This <code>write</code> method will write the specified object
 229  
     * to the given XML element as as list entries. Each entry within
 230  
     * the given collection must be assignable from the annotated 
 231  
     * type specified within the <code>ElementList</code> annotation.
 232  
     * Each entry is serialized as a root element, that is, its
 233  
     * <code>Root</code> annotation is used to extract the name. 
 234  
     * 
 235  
     * @param source this is the source collection to be serialized 
 236  
     * @param node this is the XML element container to be populated
 237  
     */ 
 238  
    public void write(OutputNode node, Object source) throws Exception {
 239  452
       Collection list = (Collection) source;              
 240  452
       OutputNode parent = node.getParent();      
 241  
       
 242  452
       if(!node.isCommitted()) {
 243  210
          node.remove();
 244  
       }
 245  452
       write(parent, list);
 246  452
    }
 247  
    
 248  
    /**
 249  
     * This <code>write</code> method will write the specified object
 250  
     * to the given XML element as as list entries. Each entry within
 251  
     * the given collection must be assignable from the annotated 
 252  
     * type specified within the <code>ElementList</code> annotation.
 253  
     * Each entry is serialized as a root element, that is, its
 254  
     * <code>Root</code> annotation is used to extract the name. 
 255  
     * 
 256  
     * @param list this is the source collection to be serialized 
 257  
     * @param node this is the XML element container to be populated
 258  
     */ 
 259  
    public void write(OutputNode node, Collection list) throws Exception {  
 260  452
       for(Object item : list) {
 261  5760
          if(item != null) {
 262  5760
             Class expect = entry.getType();
 263  5760
             Class actual = item.getClass();
 264  
 
 265  5760
             if(!expect.isAssignableFrom(actual)) {
 266  0
                throw new PersistenceException("Entry %s does not match %s for %s", actual, expect, type);
 267  
             }
 268  5760
             root.write(node, item, expect, name);
 269  5760
          }
 270  
       }
 271  452
    }
 272  
 }