Coverage Report - org.simpleframework.xml.core.ModelAssembler
 
Classes in this File Line Coverage Branch Coverage Complexity
ModelAssembler
98%
59/60
88%
23/26
3.125
 
 1  
 /*
 2  
  * ModelAssembler.java November 2010
 3  
  *
 4  
  * Copyright (C) 2010, 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.Order;
 22  
 import org.simpleframework.xml.stream.Format;
 23  
 import org.simpleframework.xml.stream.Style;
 24  
 
 25  
 /**
 26  
  * The <code>ModelAssembler</code> is used to assemble the model
 27  
  * using registrations based on the specified order. The order of
 28  
  * elements and attributes is specified by an <code>Order</code>
 29  
  * annotation. For order, all attributes within an XPath expression
 30  
  * must be valid attribute references, for example
 31  
  * <pre>
 32  
  * 
 33  
  *    some[1]/path/@attribute
 34  
  *    path/to/@attribute
 35  
  *    attribute    
 36  
  * 
 37  
  * </pre>
 38  
  * The above expressions are all legal references. The final 
 39  
  * reference specifies an attribute that is not within an XPath
 40  
  * expression. If the '@' character is missing from attribute
 41  
  * orderings an exception is thrown to indicate this.
 42  
  * 
 43  
  * @author Niall Gallagher
 44  
  * 
 45  
  * @see org.simpleframework.xml.Order
 46  
  */
 47  
 class ModelAssembler {
 48  
   
 49  
    /**
 50  
     * This is used to parse the XPath expressions in the order
 51  
     */
 52  
    private final ExpressionBuilder builder;  
 53  
    
 54  
    /**
 55  
     * This is the format that is used to style the order values. 
 56  
     */
 57  
    private final Format format;
 58  
    
 59  
    /**
 60  
     * This is the type this this is assembling the model for.
 61  
     */
 62  
    private final Detail detail;
 63  
 
 64  
    /**
 65  
     * Constructor for the <code>ModelAssembler</code> object. If
 66  
     * no order has been specified for the schema class then this
 67  
     * will perform no registrations on the specified model.   
 68  
     * 
 69  
     * @param builder this is the builder for XPath expressions
 70  
     * @param detail this contains the details for the assembler
 71  
     * @param support this contains various support functions
 72  
     */
 73  2379
    public ModelAssembler(ExpressionBuilder builder, Detail detail, Support support) throws Exception {
 74  2379
       this.format = support.getFormat();
 75  2379
       this.builder = builder;     
 76  2379
       this.detail = detail;
 77  2379
    }
 78  
    
 79  
    /**
 80  
     * This is used to assemble the model by perform registrations
 81  
     * based on the <code>Order</code> annotation. The initial
 82  
     * registrations performed by this establish the element and
 83  
     * attribute order for serialization of the schema class.
 84  
     * 
 85  
     * @param model the model to perform registrations on
 86  
     * @param order this is the order specified by the class   
 87  
     */
 88  
    public void assemble(Model model, Order order) throws Exception { 
 89  26
       assembleElements(model, order);
 90  24
       assembleAttributes(model, order);
 91  23
    }
 92  
    
 93  
    /**
 94  
     * This is used to assemble the model by perform registrations
 95  
     * based on the <code>Order</code> annotation. The initial
 96  
     * registrations performed by this establish the element and
 97  
     * attribute order for serialization of the schema class.
 98  
     * 
 99  
     * @param model the model to perform registrations on
 100  
     * @param order this is the order specified by the class   
 101  
     */
 102  
    private void assembleElements(Model model, Order order) throws Exception {
 103  114
       for(String value : order.elements()) {
 104  90
          Expression path = builder.build(value);
 105  
          
 106  90
          if(path.isAttribute()) {
 107  1
             throw new PathException("Ordered element '%s' references an attribute in %s", path, detail);
 108  
          }         
 109  89
          registerElements(model, path);         
 110  
       }
 111  24
    }
 112  
    
 113  
    /**
 114  
     * This is used to assemble the model by perform registrations
 115  
     * based on the <code>Order</code> annotation. The initial
 116  
     * registrations performed by this establish the element and
 117  
     * attribute order for serialization of the schema class.
 118  
     * 
 119  
     * @param model the model to perform registrations on
 120  
     * @param order this is the order specified by the class   
 121  
     */
 122  
    private void assembleAttributes(Model model, Order order) throws Exception {
 123  91
       for(String value : order.attributes()) {
 124  68
          Expression path = builder.build(value);
 125  
          
 126  68
          if(!path.isAttribute() && path.isPath()) {
 127  1
             throw new PathException("Ordered attribute '%s' references an element in %s", path, detail);
 128  
          }
 129  67
          if(!path.isPath()) {
 130  28
             Style style = format.getStyle();
 131  28
             String name = style.getAttribute(value);
 132  
             
 133  28
             model.registerAttribute(name);
 134  28
          } else {
 135  39
          registerAttributes(model, path);         
 136  
          }
 137  
       }
 138  23
    }
 139  
    
 140  
    /**
 141  
     * This is used to perform registrations using an expression.
 142  
     * Each segment in the expression will create a new model and
 143  
     * the final segment of the expression is the attribute.
 144  
     * 
 145  
     * @param model the model to register the attribute with
 146  
     * @param path this is the expression to be evaluated
 147  
     */
 148  
    private void registerAttributes(Model model, Expression path) throws Exception {
 149  117
       String prefix = path.getPrefix();
 150  117
       String name = path.getFirst();   
 151  117
       int index = path.getIndex();
 152  
   
 153  117
       if(path.isPath()) {
 154  78
          Model next = model.register(name, prefix, index);
 155  78
          Expression child = path.getPath(1);
 156  
          
 157  78
          if(next == null) {
 158  0
             throw new PathException("Element '%s' does not exist in %s", name, detail);
 159  
          }
 160  78
          registerAttributes(next, child);
 161  78
       } else {         
 162  39
          registerAttribute(model, path);
 163  
       }
 164  117
    }
 165  
    
 166  
    /**
 167  
     * This will register the attribute specified in the path within
 168  
     * the provided model. Registration here will ensure that the
 169  
     * attribute is ordered so that it is placed within the document
 170  
     * in a required position.
 171  
     * 
 172  
     * @param model this is the model to register the attribute in
 173  
     * @param path this is the path referencing the attribute
 174  
     */
 175  
    private void registerAttribute(Model model, Expression path) throws Exception {
 176  39
       String name = path.getFirst(); 
 177  
       
 178  39
       if(name != null) {
 179  39
          model.registerAttribute(name);
 180  
       }
 181  39
    }
 182  
    
 183  
    /**
 184  
     * This is used to perform registrations using an expression.
 185  
     * Each segment in the expression will create a new model and
 186  
     * the final segment of the expression is the element.
 187  
     * 
 188  
     * @param model the model to register the element with
 189  
     * @param path this is the expression to be evaluated
 190  
     */
 191  
    private void registerElements(Model model, Expression path) throws Exception {
 192  185
       String prefix = path.getPrefix();
 193  185
       String name = path.getFirst();  
 194  185
       int index = path.getIndex();
 195  
       
 196  185
       if(name != null) {
 197  185
          Model next = model.register(name, prefix, index);
 198  185
          Expression child = path.getPath(1);
 199  
       
 200  185
          if(path.isPath()) {            
 201  96
             registerElements(next, child);
 202  
          }
 203  
       }
 204  185
       registerElement(model, path);      
 205  184
    }   
 206  
    
 207  
    /**
 208  
     * This is used to register the element within the specified
 209  
     * model. To ensure the order does not conflict with expressions
 210  
     * the index of the ordered path is checked. If the order comes
 211  
     * before an expected order then an exception is thrown. 
 212  
     * For example, take the following expressions.
 213  
     * <pre>
 214  
     *    
 215  
     *    path[1]/element
 216  
     *    path[3]/element
 217  
     *    path[2]/element
 218  
     *    
 219  
     * </pre>
 220  
     * In the above the order of appearance of the expressions does
 221  
     * not match the indexes of the paths. This causes a conflict.
 222  
     * To ensure such a situation does not arise this is checked.
 223  
     * 
 224  
     * @param model this is the model to register the element in
 225  
     * @param path this is the expression referencing the element
 226  
     */
 227  
    private void registerElement(Model model, Expression path) throws Exception {
 228  185
       String prefix = path.getPrefix();
 229  185
       String name = path.getFirst();  
 230  185
       int index = path.getIndex();
 231  
       
 232  185
       if(index > 1) {
 233  4
          Model previous = model.lookup(name, index -1);
 234  
          
 235  4
          if(previous == null) {
 236  1
             throw new PathException("Ordered element '%s' in path '%s' is out of sequence for %s", name, path, detail);
 237  
          }
 238  
       }
 239  184
       model.register(name, prefix, index);
 240  184
    }
 241  
 }