Coverage Report - org.simpleframework.xml.core.SignatureBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
SignatureBuilder
100%
59/59
90%
20/22
1.889
SignatureBuilder$ParameterList
100%
4/4
N/A
1.889
SignatureBuilder$ParameterTable
94%
17/18
66%
4/6
1.889
 
 1  
 /*
 2  
  * SignatureBuilder.java July 2009
 3  
  *
 4  
  * Copyright (C) 2009, 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.lang.reflect.Constructor;
 22  
 import java.util.ArrayList;
 23  
 import java.util.List;
 24  
 
 25  
 /**
 26  
  * The <code>SignatureBuilder</code> is used to build all permutations
 27  
  * of signatures a constructor contains. Permutations are calculated
 28  
  * by determining the number of annotations a parameter contains and
 29  
  * ensuring a signature is created with one of each. This is useful 
 30  
  * when a constructor is annotated with a union annotation.  
 31  
  * 
 32  
  * @author Niall Gallagher
 33  
  * 
 34  
  * @see org.simpleframework.xml.core.Signature
 35  
  */
 36  
 class SignatureBuilder {
 37  
    
 38  
    /**
 39  
     * This contains each parameter and annotation pair found.
 40  
     */
 41  
    private final ParameterTable table;
 42  
    
 43  
    /**
 44  
     * this is the constructor that this signature builder is for.
 45  
     */
 46  
    private final Constructor factory;
 47  
    
 48  
    /**
 49  
     * Constructor for the <code>SignatureBuilder</code> object. This
 50  
     * requires the constructor that the signatures will be built for.
 51  
     * If the constructor contains no annotations then no signatures
 52  
     * will be built, unless this is the default no-arg constructor.
 53  
     * 
 54  
     * @param factory this is the constructor to build for
 55  
     */
 56  3664
    public SignatureBuilder(Constructor factory) {
 57  3664
       this.table = new ParameterTable();
 58  3664
       this.factory = factory;
 59  3664
    }
 60  
    
 61  
    /**
 62  
     * This validates the builder by checking that the width of the
 63  
     * table is the same as the count of parameters in the constructor.
 64  
     * If there table width and parameter count does not match then 
 65  
     * this means the constructor is not fully annotated.
 66  
     * 
 67  
     * @return true if the constructor has been properly annotated
 68  
     */
 69  
    public boolean isValid() {
 70  5479
       Class[] types = factory.getParameterTypes();
 71  5479
       int width = table.width();
 72  
       
 73  5479
       return types.length == width;
 74  
    }
 75  
 
 76  
    /**
 77  
     * This will add a a parameter at the specified column in the
 78  
     * table. The parameter is typically added to the table at an
 79  
     * index mirroring the index it appears within the constructor.
 80  
     * 
 81  
     * @param value this is the parameter to be added in the table
 82  
     * @param index this is the index to added the parameter to
 83  
     */
 84  
    public void insert(Parameter value, int index) {
 85  591
       table.insert(value, index);
 86  591
    }
 87  
    
 88  
    /**
 89  
     * This method will build all the signatures for the constructor.
 90  
     * If a union annotation was used this may result in several 
 91  
     * signatures being created. Also if this builder represents
 92  
     * the default constructor then this returns a single value.
 93  
     * 
 94  
     * @return this returns the list of signatures to be built
 95  
     */
 96  
    public List<Signature> build() throws Exception {
 97  2102
       return build(new ParameterTable());
 98  
    }
 99  
    
 100  
    /**
 101  
     * This method will build all the signatures for the constructor.
 102  
     * If a union annotation was used this may result in several 
 103  
     * signatures being created. Also if this builder represents
 104  
     * the default constructor then this returns a single value.
 105  
     * 
 106  
     * @param matrix this is the matrix of parameters to collect
 107  
     * 
 108  
     * @return this returns the list of signatures to be built
 109  
     */
 110  
    private List<Signature> build(ParameterTable matrix) throws Exception {
 111  2102
       if(table.isEmpty()) {
 112  1818
          return create();
 113  
       } 
 114  284
       build(matrix, 0);
 115  284
       return create(matrix);
 116  
    }
 117  
    
 118  
    /**
 119  
     * This is used to create a list of signatures that represent 
 120  
     * the permutations of parameter and annotation pairs that
 121  
     * exist for a single constructor. The list may be empty.
 122  
     * 
 123  
     * @return this returns the list of signatures that exist
 124  
     */
 125  
    private List<Signature> create() throws Exception {
 126  1818
       List<Signature> list = new ArrayList<Signature>();
 127  1818
       Signature signature = new Signature(factory);
 128  
       
 129  1818
       if(isValid()) {
 130  1818
          list.add(signature);
 131  
       }
 132  1818
       return list;
 133  
    }
 134  
    
 135  
    /**
 136  
     * This is used to create a list of signatures that represent the
 137  
     * permutations of parameter and annotation pairs that exist. All
 138  
     * permutations are taken from the provided matrix. When building
 139  
     * the list of signature the parameter paths are validated.
 140  
     * 
 141  
     * @param matrix this contains the permutations of parameters
 142  
     * 
 143  
     * @return this returns the list of signatures for a constructor
 144  
     */
 145  
    private List<Signature> create(ParameterTable matrix) throws Exception {
 146  284
       List<Signature> list = new ArrayList<Signature>();
 147  284
       int height = matrix.height();
 148  284
       int width = matrix.width();
 149  
 
 150  592
       for(int i = 0; i < height; i++) {
 151  311
          Signature signature = new Signature(factory);
 152  
          
 153  923
          for(int j = 0; j < width; j++) {
 154  615
             Parameter parameter = matrix.get(j, i);
 155  615
             String path = parameter.getPath();
 156  615
             Object key = parameter.getKey();
 157  
             
 158  615
             if(signature.contains(key)) {
 159  3
                throw new ConstructorException("Parameter '%s' is a duplicate in %s", path, factory);
 160  
             }
 161  612
             signature.add(parameter);
 162  
          }
 163  308
          list.add(signature);
 164  
       }
 165  281
       return list;
 166  
    }
 167  
    
 168  
    /**
 169  
     * This is used to build all permutations of parameters that exist
 170  
     * within the constructor. By building a matrix of the permutations
 171  
     * all possible signatures can be created allowing for a better 
 172  
     * way to perform dependency injection for the objects.
 173  
     * 
 174  
     * @param matrix this is the matrix to hold the permutations
 175  
     * @param index this is the particular index to evaluate
 176  
     */
 177  
    private void build(ParameterTable matrix, int index) {
 178  284
       build(matrix, new ParameterList(), index);
 179  284
    }
 180  
    
 181  
    /**
 182  
     * This is used to build all permutations of parameters that exist
 183  
     * within the constructor. By building a matrix of the permutations
 184  
     * all possible signatures can be created allowing for a better 
 185  
     * way to perform dependency injection for the objects.
 186  
     * 
 187  
     * @param matrix this is the matrix to hold the permutations
 188  
     * @param signature the row to perform a permutation with
 189  
     * @param index this is the particular index to evaluate
 190  
     */
 191  
    private void build(ParameterTable matrix, ParameterList signature, int index) {
 192  574
       ParameterList column = table.get(index);
 193  574
       int height = column.size();
 194  574
       int width = table.width();
 195  
       
 196  574
       if(width - 1 > index) {
 197  576
          for(int i = 0; i < height; i++) {
 198  290
             ParameterList extended = new ParameterList(signature);
 199  
             
 200  290
             if(signature != null) {
 201  290
                Parameter parameter = column.get(i);
 202  
                
 203  290
                extended.add(parameter);
 204  290
                build(matrix, extended, index + 1);
 205  
             }
 206  
          }
 207  
       } else {
 208  288
          populate(matrix, signature, index);
 209  
       }
 210  574
    }
 211  
    
 212  
    /**
 213  
     * This is the final leg of building a permutation where a signature
 214  
     * is given to permutate on the last column. Once finished the
 215  
     * matrix will have a new row of parameters added which represents 
 216  
     * a new set of permutations to create signatures from.
 217  
     * 
 218  
     * @param matrix this is the matrix to hold the permutations
 219  
     * @param signature the row to perform a permutation with
 220  
     * @param index this is the particular index to evaluate
 221  
     */
 222  
    private void populate(ParameterTable matrix, ParameterList signature, int index) {
 223  288
       ParameterList column = table.get(index);
 224  288
       int width = signature.size();
 225  288
       int height = column.size();
 226  
 
 227  599
       for(int i = 0; i < height; i++) {
 228  615
          for(int j = 0; j < width; j++) {
 229  304
             ParameterList list = matrix.get(j);
 230  304
             Parameter parameter = signature.get(j);
 231  
             
 232  304
             list.add(parameter);
 233  
          }
 234  311
          ParameterList list = matrix.get(index); 
 235  311
          Parameter parameter = column.get(i);
 236  
          
 237  311
          list.add(parameter);
 238  
       }
 239  288
    }
 240  
    
 241  
    /**
 242  
     * The <code>ParameterTable</code> is used to build a table of 
 243  
     * parameters to represent a constructor. Each column of parameters
 244  
     * mirrors the index of the parameter in the constructor with each
 245  
     * parameter containing its type and the annotation it uses.
 246  
     * 
 247  
     * @author Niall Gallagher
 248  
     */
 249  6621
    private static class ParameterTable extends ArrayList<ParameterList> {
 250  
       
 251  
       /**
 252  
        * Constructor for the <code>ParameterTable</code> object. This
 253  
        * creates a table of parameters that can be used to represent
 254  
        * each permutation of parameter and annotation pairs.
 255  
        */
 256  
       public ParameterTable() {
 257  5766
          super();
 258  5766
       }
 259  
       
 260  
       /**
 261  
        * This represents the number of parameters for each index in
 262  
        * the table. This is determined from the first column within
 263  
        * the table, if the table is empty this returns zero.
 264  
        * 
 265  
        * @return the height of the table using the first column
 266  
        */
 267  
       private int height() {
 268  284
          int width = width();
 269  
          
 270  284
          if(width > 0) {
 271  284
             return get(0).size();
 272  
          }
 273  0
          return 0;
 274  
       }
 275  
       
 276  
       /**
 277  
        * This is used to determine the width of this table. The width
 278  
        * is the number of columns the table contains. Each column in
 279  
        *  represents a parameter at its index in the constructor.
 280  
        * 
 281  
        * @return this returns the width of the table 
 282  
        */
 283  
       private int width() {
 284  6621
          return size();
 285  
       }
 286  
       
 287  
       /**
 288  
        * This will add a a parameter at the specified column in the
 289  
        * table. The parameter is typically added to the table at an
 290  
        * index mirroring the index it appears within the constructor.
 291  
        * 
 292  
        * @param value this is the parameter to be added in the table
 293  
        * @param column this is the index to added the parameter to
 294  
        */
 295  
       public void insert(Parameter value, int column) {
 296  591
          ParameterList list = get(column);
 297  
          
 298  591
          if(list != null) {
 299  591
             list.add(value);
 300  
          }
 301  591
       }
 302  
       
 303  
       /**
 304  
        * This is used to acquire the column of parameters from the
 305  
        * table. If no column exists at the specified index then one
 306  
        * is created and added to the table at the column index.
 307  
        * 
 308  
        * @param column this is the column to acquire from the table
 309  
        * 
 310  
        * @return the column of parameters from the table
 311  
        */
 312  
       public ParameterList get(int column) {
 313  2967
          int size = size();
 314  
          
 315  4108
          for(int i = size; i <= column; i++) {
 316  1141
             ParameterList list = new ParameterList();
 317  1141
             add(list);
 318  
          }
 319  2967
          return super.get(column);
 320  
       }
 321  
       
 322  
       /**
 323  
        * This is used to get a <code>Parameter</code> at the row and
 324  
        * column specified. This if the parameter does not exist the
 325  
        * an index out of bounds exception is thrown.
 326  
        * 
 327  
        * @param column the column to acquire the parameter for
 328  
        * @param row the row to acquire the parameter for
 329  
        * 
 330  
        * @return this returns the parameter at the specified cell
 331  
        */
 332  
       public Parameter get(int column, int row) {
 333  615
          return get(column).get(row);
 334  
       }
 335  
    }
 336  
    
 337  
    /**
 338  
     * The <code>ParameterList</code> object is used to represent a 
 339  
     * column of parameters within a table. A column will contain each
 340  
     * parameter and annotation pair extracted from an index in the
 341  
     * constructor, a permutation can come from a union annotation,
 342  
     * 
 343  
     * @author Niall Gallagher
 344  
     */
 345  
    private static class ParameterList extends ArrayList<Parameter> {
 346  
       
 347  
       /**
 348  
        * Constructor for the <code>ParameterList</code> object. 
 349  
        * This creates a default list for building a column for
 350  
        * the parameter table it is added to.
 351  
        */
 352  
       public ParameterList() {
 353  1425
          super();
 354  1425
       }
 355  
       
 356  
       /**
 357  
        * Constructor for the <code>ParameterList</code> object. 
 358  
        * This creates a list of parameters by using the provided
 359  
        * list of parameters, each parameter will be added in order.
 360  
        */
 361  
       public ParameterList(ParameterList list) {
 362  290
          super(list);
 363  290
       }
 364  
    }
 365  
 }