Coverage Report - org.simpleframework.xml.core.CompositeListUnion
 
Classes in this File Line Coverage Branch Coverage Complexity
CompositeListUnion
100%
65/65
90%
18/20
2.182
 
 1  
 /*
 2  
  * CompositeListUnion.java March 2011
 3  
  *
 4  
  * Copyright (C) 2011, 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  
 import java.util.Collections;
 23  
 
 24  
 import org.simpleframework.xml.strategy.Type;
 25  
 import org.simpleframework.xml.stream.InputNode;
 26  
 import org.simpleframework.xml.stream.OutputNode;
 27  
 import org.simpleframework.xml.stream.Style;
 28  
 
 29  
 /**
 30  
  * The <code>CompositeListUnion</code> object is used to act as a 
 31  
  * mediator for multiple converters associated with a particular union 
 32  
  * group. This will basically determine which <code>Converter</code> 
 33  
  * should be delegated to based on either the XML element name being read 
 34  
  * or the type of the instance object being written. Selection of the 
 35  
  * converter is done by consulting the <code>Group</code> of labels 
 36  
  * representing the union declaration.
 37  
  * 
 38  
  * @author Niall Gallagher
 39  
  */
 40  
 class CompositeListUnion implements Repeater {
 41  
    
 42  
    /**
 43  
     * This contains the labels in the union group keyed by name.
 44  
     */
 45  
    private final LabelMap elements;
 46  
    
 47  
    /**
 48  
     * This is the path expression used to represent this union.
 49  
     */
 50  
    private final Expression path;
 51  
    
 52  
    /**
 53  
     * This is the current context used for the serialization.
 54  
     */
 55  
    private final Context context;
 56  
    
 57  
    /**
 58  
     * This contains the group of labels associated with the union.
 59  
     */
 60  
    private final Group group;
 61  
    
 62  
    /**
 63  
     * This is this style associated with the serialization context.
 64  
     */
 65  
    private final Style style;
 66  
    
 67  
    /**
 68  
     * This is the type field or method annotated as a union.
 69  
     */
 70  
    private final Type type;
 71  
 
 72  
    /**
 73  
     * Constructor for the <code>CompositeListUnion</code> object. This
 74  
     * is used to create a converter that delegates to other associated
 75  
     * converters within the union group depending on the XML element
 76  
     * name being read or the instance type that is being written.
 77  
     * 
 78  
     * @param context this is the context used for the serialization
 79  
     * @param group this is the union group used for delegation
 80  
     * @param path this is the path expression representing this union
 81  
     * @param type this is the annotated field or method to be used
 82  
     */
 83  332
    public CompositeListUnion(Context context, Group group, Expression path, Type type) throws Exception {
 84  332
       this.elements = group.getElements();
 85  332
       this.style = context.getStyle();
 86  332
       this.context = context;
 87  332
       this.group = group;
 88  332
       this.type = type;
 89  332
       this.path = path;
 90  332
    }
 91  
 
 92  
    /**
 93  
     * The <code>read</code> method uses the name of the XML element to
 94  
     * select a converter to be used to read the instance. Selection of
 95  
     * the converter is done by looking up the associated label from
 96  
     * the union group using the element name. Once the converter has
 97  
     * been selected it is used to read the instance.
 98  
     * 
 99  
     * @param node this is the XML element used to read the instance
 100  
     * 
 101  
     * @return this is the instance that has been read by this
 102  
     */
 103  
    public Object read(InputNode node) throws Exception {
 104  54
       Label text = group.getText();
 105  
       
 106  54
       if(text == null) {
 107  39
          return readElement(node);
 108  
       } 
 109  15
       return readText(node);
 110  
    }
 111  
    
 112  
    /**
 113  
     * The <code>readElement</code> method uses the name of the element 
 114  
     * to select a converter to be used to read the instance. Selection 
 115  
     * of the converter is done by looking up the associated label from
 116  
     * the union group using the element name. Once the converter has
 117  
     * been selected it is used to read the instance.
 118  
     * 
 119  
     * @param node this is the XML element used to read the instance
 120  
     * 
 121  
     * @return this is the instance that has been read by this
 122  
     */
 123  
    private Object readElement(InputNode node) throws Exception {
 124  39
       String name = node.getName();
 125  39
       String element = path.getElement(name);
 126  39
       Label label = elements.get(element);
 127  39
       Converter converter = label.getConverter(context);
 128  
    
 129  39
       return converter.read(node);
 130  
    }
 131  
    
 132  
    /**
 133  
     * The <code>readText</code> method is used to read free text from
 134  
     * between the declared elements and add them to a list. Consuming
 135  
     * free text in this manner enables an element list union to parse
 136  
     * unstructured XML such as XHTML.
 137  
     * 
 138  
     * @param node this is the node to consume the free text from
 139  
     * 
 140  
     * @return this returns the list with the text added to it
 141  
     */
 142  
    private Object readText(InputNode node) throws Exception {
 143  15
       Label text = group.getText();
 144  15
       Converter converter = text.getConverter(context);
 145  
       
 146  15
       return converter.read(node);
 147  
    }
 148  
 
 149  
    /**
 150  
     * The <code>read</code> method uses the name of the XML element to
 151  
     * select a converter to be used to read the instance. Selection 
 152  
     * of the converter is done by looking up the associated label from
 153  
     * the union group using the element name. Once the converter has
 154  
     * been selected it is used to read the instance.
 155  
     * 
 156  
     * @param node this is the XML element used to read the instance
 157  
     * @param value this is the value that is to be repeated
 158  
     * 
 159  
     * @return this is the instance that has been read by this
 160  
     */
 161  
    public Object read(InputNode node, Object value) throws Exception {
 162  129
       Object result = readElement(node, value);
 163  129
       Label text = group.getText();
 164  
      
 165  129
       if(text != null) {
 166  30
          return readText(node, value);
 167  
       }
 168  99
       return result;
 169  
    }
 170  
    
 171  
    /**
 172  
     * The <code>readElement</code> method uses the name of the element 
 173  
     * to select a converter to be used to read the instance. Selection 
 174  
     * of the converter is done by looking up the associated label from
 175  
     * the union group using the element name. Once the converter has
 176  
     * been selected it is used to read the instance.
 177  
     * 
 178  
     * @param node this is the XML element used to read the instance
 179  
     * @param value this is the value that is to be repeated
 180  
     * 
 181  
     * @return this is the instance that has been read by this
 182  
     */
 183  
    private Object readElement(InputNode node, Object value) throws Exception {
 184  129
       String name = node.getName();
 185  129
       String element = path.getElement(name);
 186  129
       Label label = elements.get(element);
 187  129
       Converter converter = label.getConverter(context);
 188  
       
 189  129
       return converter.read(node, value);
 190  
    }
 191  
    
 192  
    /**
 193  
     * The <code>readText</code> method is used to read free text from
 194  
     * between the declared elements and add them to a list. Consuming
 195  
     * free text in this manner enables an element list union to parse
 196  
     * unstructured XML such as XHTML.
 197  
     * 
 198  
     * @param node this is the node to consume the free text from
 199  
     * @param value this is the value that is to be repeated
 200  
     * 
 201  
     * @return this returns the list with the text added to it
 202  
     */
 203  
    private Object readText(InputNode node, Object value) throws Exception {
 204  30
       Label label = group.getText();
 205  30
       Converter converter = label.getConverter(context);
 206  30
       InputNode parent = node.getParent();
 207  
       
 208  30
       return converter.read(parent, value);
 209  
    }
 210  
    
 211  
    /**
 212  
     * The <code>validate</code> method is used to validate the XML
 213  
     * element provided using an associated class schema. The schema
 214  
     * is selected using the name of the XML element to acquire
 215  
     * the associated converter. Once the converter has been acquired
 216  
     * it is delegated to and validated against it.
 217  
     * 
 218  
     * @param node this is the input XML element to be validated
 219  
     * 
 220  
     * @return this returns true if the node validates 
 221  
     */
 222  
    public boolean validate(InputNode node) throws Exception {
 223  47
       String name = node.getName();
 224  47
       String element = path.getElement(name);
 225  47
       Label label = elements.get(element);
 226  47
       Converter converter = label.getConverter(context);
 227  
       
 228  47
       return converter.validate(node);
 229  
    }
 230  
    
 231  
    /**
 232  
     * The <code>write</code> method uses the name of the XML element to
 233  
     * select a converter to be used to write the instance. Selection of
 234  
     * the converter is done by looking up the associated label from
 235  
     * the union group using the instance type. Once the converter has
 236  
     * been selected it is used to write the instance.
 237  
     * 
 238  
     * @param source this is the source collection to be serialized 
 239  
     * @param node this is the XML element container to be populated
 240  
     */ 
 241  
    public void write(OutputNode node, Object source) throws Exception {
 242  123
       Collection list = (Collection) source;                  
 243  
       
 244  123
       if(group.isInline()) {
 245  113
          if(!list.isEmpty()) {
 246  112
             write(node, list);
 247  1
          } else if(!node.isCommitted()){
 248  1
             node.remove();
 249  
          }
 250  
       } else {
 251  10
          write(node, list);
 252  
       }
 253  122
    }
 254  
 
 255  
    /**
 256  
     * The <code>write</code> method uses the name of the XML element to
 257  
     * select a converter to be used to write the instance. Selection of
 258  
     * the converter is done by looking up the associated label from
 259  
     * the union group using the instance type. Once the converter has
 260  
     * been selected it is used to write the instance.
 261  
     * 
 262  
     * @param node this is the XML element used to write the instance
 263  
     * @param list this is the value that is to be written
 264  
     */
 265  
    private void write(OutputNode node, Collection list) throws Exception {     
 266  122
       for(Object item : list) {
 267  469
          if(item != null) {
 268  469
             Class real = item.getClass();
 269  469
             Label label = group.getLabel(real);
 270  
             
 271  469
             if(label == null) {          
 272  1
                throw new UnionException("Entry of %s not declared in %s with annotation %s", real, type, group);
 273  
             }
 274  468
             write(node, item, label);
 275  468
          }
 276  
       }
 277  121
    }
 278  
       
 279  
    /**
 280  
     * The <code>write</code> method uses the name of the XML element to
 281  
     * select a converter to be used to write the instance. Selection of
 282  
     * the converter is done by looking up the associated label from
 283  
     * the union group using the instance type. Once the converter has
 284  
     * been selected it is used to write the instance.
 285  
     * 
 286  
     * @param node this is the XML element used to write the instance
 287  
     * @param item this is the individual list entry to be serialized
 288  
     * @param label this is the label to used to acquire the converter     
 289  
     */
 290  
    private void write(OutputNode node, Object item, Label label) throws Exception {
 291  468
       Converter converter = label.getConverter(context);
 292  468
       Collection list = Collections.singleton(item);
 293  
 
 294  468
       if(!label.isInline()) {
 295  20
          String name = label.getName();
 296  20
          String root = style.getElement(name);
 297  
         
 298  20
          if(!node.isCommitted()) {
 299  10
             node.setName(root);
 300  
          }
 301  
       }
 302  468
       converter.write(node, list);    
 303  468
    }
 304  
 }