Coverage Report - org.simpleframework.xml.core.Traverser
 
Classes in this File Line Coverage Branch Coverage Complexity
Traverser
92%
47/51
75%
12/16
2.083
 
 1  
 /*
 2  
  * Traverser.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 org.simpleframework.xml.strategy.Type;
 22  
 import org.simpleframework.xml.stream.InputNode;
 23  
 import org.simpleframework.xml.stream.OutputNode;
 24  
 import org.simpleframework.xml.stream.Style;
 25  
 
 26  
 /**
 27  
  * The <code>Traverser</code> object is used to traverse the XML class
 28  
  * schema and either serialize or deserialize an object. This is the
 29  
  * root of all serialization and deserialization operations. It uses
 30  
  * the <code>Root</code> annotation to ensure that the XML schema
 31  
  * matches the provided XML element. If no root element is defined the
 32  
  * serialization and deserialization cannot be performed.
 33  
  *
 34  
  * @author Niall Gallagher
 35  
  */ 
 36  
 class Traverser {
 37  
 
 38  
    /**
 39  
     * This is the context object used for the traversal performed.
 40  
     */
 41  
    private final Context context;
 42  
    
 43  
    /**
 44  
     * This is the style that is used to style the XML roots.
 45  
     */
 46  
    private final Style style;
 47  
         
 48  
    /**
 49  
     * Constructor for the <code>Traverser</code> object. This creates
 50  
     * a traverser that can be used to perform serialization or
 51  
     * or deserialization of an object. This requires a source object.
 52  
     * 
 53  
     * @param context the context object used for the traversal
 54  
     */
 55  193533
    public Traverser(Context context) {
 56  193533
       this.style = context.getStyle();
 57  193533
       this.context = context;           
 58  193533
    }
 59  
    
 60  
    /**
 61  
     * This will acquire the <code>Decorator</code> for the type.
 62  
     * A decorator is an object that adds various details to the
 63  
     * node without changing the overall structure of the node. For
 64  
     * example comments and namespaces can be added to the node with
 65  
     * a decorator as they do not affect the deserialization.
 66  
     * 
 67  
     * @param type this is the type to acquire the decorator for 
 68  
     *
 69  
     * @return this returns the decorator associated with this
 70  
     */
 71  
    private Decorator getDecorator(Class type) throws Exception {
 72  138955
       return context.getDecorator(type);
 73  
    }
 74  
    
 75  
    /**
 76  
     * This <code>read</code> method is used to deserialize an object 
 77  
     * from the provided XML element. The class provided acts as the
 78  
     * XML schema definition used to control the deserialization. If
 79  
     * the XML schema does not have a <code>Root</code> annotation 
 80  
     * this throws an exception. Also if the root annotation name is
 81  
     * not the same as the XML element name an exception is thrown.  
 82  
     * 
 83  
     * @param node this is the node that is to be deserialized
 84  
     * @param type this is the XML schema class to be used
 85  
     * 
 86  
     * @return an object deserialized from the XML element 
 87  
     * 
 88  
     * @throws Exception if the XML schema does not match the node
 89  
     */
 90  
    public Object read(InputNode node, Class type) throws Exception {
 91  434799
       Composite factory = getComposite(type);           
 92  434799
       Object value = factory.read(node);
 93  
       
 94  434762
       if(value != null) {
 95  434753
          Class real = value.getClass();
 96  
 
 97  434753
          return read(node, real, value);
 98  
       }
 99  9
       return null;
 100  
    }
 101  
    
 102  
    /**
 103  
     * This <code>read</code> method will read the contents of the XML
 104  
     * document from the provided source and populate the object with
 105  
     * the values deserialized. This is used as a means of injecting an
 106  
     * object with values deserialized from an XML document. If the
 107  
     * XML source cannot be deserialized or there is a problem building
 108  
     * the object graph an exception is thrown. 
 109  
     * 
 110  
     * @param node this is the node that is to be deserialized
 111  
     * @param value this is the value that is to be deserialized
 112  
     * 
 113  
     * @return an object deserialized from the XML element 
 114  
     * 
 115  
     * @throws Exception if the XML schema does not match the node
 116  
     */
 117  
    public Object read(InputNode node, Object value) throws Exception {
 118  2
       Class type = value.getClass();
 119  2
       Composite factory = getComposite(type);        
 120  2
       Object real = factory.read(node, value);
 121  
       
 122  2
       return read(node, type, real);
 123  
    }
 124  
    
 125  
    /**
 126  
     * This <code>read</code> method is used to deserialize an object 
 127  
     * from the provided XML element. The class provided acts as the
 128  
     * XML schema definition used to control the deserialization. If
 129  
     * the XML schema does not have a <code>Root</code> annotation 
 130  
     * this throws an exception. Also if the root annotation name is
 131  
     * not the same as the XML element name an exception is thrown.  
 132  
     * 
 133  
     * @param node this is the node that is to be deserialized
 134  
     * @param value this is the XML schema object to be used
 135  
     * 
 136  
     * @return an object deserialized from the XML element 
 137  
     * 
 138  
     * @throws Exception if the XML schema does not match the XML
 139  
     */ 
 140  
    private Object read(InputNode node, Class type, Object value) throws Exception {
 141  434755
       String root = getName(type);
 142  
      
 143  434755
       if(root == null) {
 144  0
          throw new RootException("Root annotation required for %s", type);
 145  
       }
 146  434755
       return value;
 147  
    }
 148  
    
 149  
    /**
 150  
     * This <code>validate</code> method will validate the contents of
 151  
     * the XML document against the specified XML class schema. This is
 152  
     * used to perform a read traversal of the class schema such that 
 153  
     * the document can be tested against it. This is preferred to
 154  
     * reading the document as it does not instantiate the objects or
 155  
     * invoke any callback methods, thus making it a safe validation.
 156  
     * 
 157  
     * @param type this is the class type to be validated against XML
 158  
     * @param node this provides the source of the XML document
 159  
     * 
 160  
     * @return true if the document matches the class XML schema 
 161  
     * 
 162  
     * @throws Exception if the class XML schema does not fully match
 163  
     */
 164  
    public boolean validate(InputNode node, Class type) throws Exception {
 165  1856
       Composite factory = getComposite(type);
 166  1856
       String root = getName(type);
 167  
       
 168  1856
       if(root == null) {
 169  0
          throw new RootException("Root annotation required for %s", type);
 170  
       }
 171  1856
       return factory.validate(node);
 172  
    }
 173  
    
 174  
    /**
 175  
     * This <code>write</code> method is used to convert the provided
 176  
     * object to an XML element. This creates a child node from the
 177  
     * given <code>OutputNode</code> object. Once this child element 
 178  
     * is created it is populated with the fields of the source object
 179  
     * in accordance with the XML schema class.  
 180  
     * 
 181  
     * @param source this is the object to be serialized to XML
 182  
     * 
 183  
     * @throws Exception thrown if there is a problem serializing
 184  
     */
 185  
    public void write(OutputNode node, Object source) throws Exception {
 186  24401
       write(node, source, source.getClass());
 187  24371
    }
 188  
 
 189  
    /**
 190  
     * This <code>write</code> method is used to convert the provided
 191  
     * object to an XML element. This creates a child node from the
 192  
     * given <code>OutputNode</code> object. Once this child element 
 193  
     * is created it is populated with the fields of the source object
 194  
     * in accordance with the XML schema class.  
 195  
     * 
 196  
     * @param source this is the object to be serialized to XML
 197  
     * @param expect this is the class that is expected to be written
 198  
     * 
 199  
     * @throws Exception thrown if there is a problem serializing
 200  
     */
 201  
    public void write(OutputNode node, Object source, Class expect) throws Exception {
 202  24401
       Class type = source.getClass();      
 203  24401
       String root = getName(type);
 204  
 
 205  24379
       if(root == null) {
 206  0
          throw new RootException("Root annotation required for %s", type);
 207  
       }
 208  24379
       write(node, source, expect, root);
 209  24371
    }
 210  
    
 211  
    /**
 212  
     * This <code>write</code> method is used to convert the provided
 213  
     * object to an XML element. This creates a child node from the
 214  
     * given <code>OutputNode</code> object. Once this child element 
 215  
     * is created it is populated with the fields of the source object
 216  
     * in accordance with the XML schema class.  
 217  
     * 
 218  
     * @param source this is the object to be serialized to XML
 219  
     * @param expect this is the class that is expected to be written
 220  
     * @param name this is the name of the root annotation used 
 221  
     * 
 222  
     * @throws Exception thrown if there is a problem serializing
 223  
     */
 224  
    public void write(OutputNode node, Object source, Class expect, String name) throws Exception {
 225  139056
       OutputNode child = node.getChild(name);
 226  139056
       Type type = getType(expect);
 227  
       
 228  139056
       if(source != null) {
 229  138955
          Class actual = source.getClass();
 230  138955
          Decorator decorator = getDecorator(actual);
 231  
          
 232  138955
          if(decorator != null) {
 233  138799
             decorator.decorate(child);
 234  
          }
 235  138955
          if(!context.setOverride(type, source, child)) {
 236  136818
             getComposite(actual).write(child, source);         
 237  
          }
 238  
       }         
 239  139048
       child.commit();      
 240  139048
    }
 241  
    
 242  
    /**
 243  
     * This will create a <code>Composite</code> object using the XML 
 244  
     * schema class provided. This makes use of the source object that
 245  
     * this traverser has been given to create a composite converter. 
 246  
     * 
 247  
     * @param expect this is the XML schema class to be used
 248  
     * 
 249  
     * @return a converter for the specified XML schema class
 250  
     */
 251  
    private Composite getComposite(Class expect) throws Exception {
 252  573475
       Type type = getType(expect);
 253  
       
 254  573475
       if(expect == null) {
 255  0
          throw new RootException("Can not instantiate null class");
 256  
       }
 257  573475
       return new Composite(context, type);
 258  
    }
 259  
    
 260  
    /**
 261  
     * This is used to acquire a type for the provided class. This will
 262  
     * wrap the class in a <code>Type</code> wrapper object. Wrapping
 263  
     * the class allows it to be used within the framework.
 264  
     * 
 265  
     * @param type this is the type that is to be wrapped for use
 266  
     * 
 267  
     * @return this returns the type that wraps the specified class
 268  
     */
 269  
    private Type getType(Class type) {
 270  712531
       return new ClassType(type);
 271  
    }
 272  
    
 273  
    /**
 274  
     * Extracts the <code>Root</code> annotation from the provided XML
 275  
     * schema class. If no annotation exists in the provided class the
 276  
     * super class is checked and so on until the <code>Object</code>
 277  
     * is encountered, if no annotation is found this returns null.
 278  
     *  
 279  
     * @param type this is the XML schema class to use
 280  
     * 
 281  
     * @return this returns the root annotation for the XML schema
 282  
     */   
 283  
    protected String getName(Class type) throws Exception {
 284  461012
       String root = context.getName(type);
 285  460990
       String name = style.getElement(root);
 286  
       
 287  460990
       return name;
 288  
    }
 289  
 }