Coverage Report - org.simpleframework.xml.core.Support
 
Classes in this File Line Coverage Branch Coverage Complexity
Support
87%
79/90
84%
42/50
2.88
 
 1  
 /*
 2  
  * Support.java May 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.lang.annotation.Annotation;
 22  
 import java.util.List;
 23  
 
 24  
 import org.simpleframework.xml.filter.Filter;
 25  
 import org.simpleframework.xml.filter.PlatformFilter;
 26  
 import org.simpleframework.xml.strategy.Value;
 27  
 import org.simpleframework.xml.stream.Format;
 28  
 import org.simpleframework.xml.stream.Style;
 29  
 import org.simpleframework.xml.transform.Matcher;
 30  
 import org.simpleframework.xml.transform.Transform;
 31  
 import org.simpleframework.xml.transform.Transformer;
 32  
 
 33  
 /**
 34  
  * The <code>Support</code> object is used to provide support to the
 35  
  * serialization engine for processing and transforming strings. This
 36  
  * contains a <code>Transformer</code> which will create objects from
 37  
  * strings and will also reverse this process converting an object
 38  
  * to a string. This is used in the conversion of primitive types.
 39  
  * 
 40  
  * @author Niall Gallagher
 41  
  * 
 42  
  * @see org.simpleframework.xml.transform.Transformer
 43  
  */
 44  
 class Support implements Filter {
 45  
    
 46  
    /**
 47  
     * This is the factory that is used to create the scanners.
 48  
     */
 49  
    private final InstanceFactory instances;
 50  
    
 51  
    /**
 52  
     * This will perform the scanning of types are provide scanners.
 53  
     */
 54  
    private final ScannerFactory scanners;
 55  
    
 56  
    /**
 57  
     * This is used to extract the details for a specific class.
 58  
     */
 59  
    private final DetailExtractor details;
 60  
    
 61  
    /**
 62  
     * This is used to extract the labels for a specific contact.
 63  
     */
 64  
    private final LabelExtractor labels;
 65  
    
 66  
    /**
 67  
     * This is the transformer used to transform objects to text.
 68  
     */
 69  
    private final Transformer transform;
 70  
    
 71  
    /**
 72  
     * This is the matcher used to acquire the transform objects.
 73  
     */
 74  
    private final Matcher matcher;
 75  
    
 76  
    /**
 77  
     * This is the filter used to transform the template variables.
 78  
     */
 79  
    private final Filter filter;
 80  
    
 81  
    /**
 82  
     * This is the format used by this persistence support object.
 83  
     */
 84  
    private final Format format;
 85  
    
 86  
    /**
 87  
     * Constructor for the <code>Support</code> object. This will
 88  
     * create a support object with a default matcher and default
 89  
     * platform filter. This ensures it contains enough information
 90  
     * to process a template and transform basic primitive types.
 91  
     */
 92  
    public Support() {
 93  68
       this(new PlatformFilter());
 94  68
    }
 95  
 
 96  
    /**
 97  
     * Constructor for the <code>Support</code> object. This will
 98  
     * create a support object with a default matcher and the filter
 99  
     * provided. This ensures it contains enough information to 
 100  
     * process a template and transform basic primitive types.
 101  
     * 
 102  
     * @param filter this is the filter to use with this support
 103  
     */
 104  
    public Support(Filter filter) {
 105  68
       this(filter, new EmptyMatcher());
 106  68
    }
 107  
    
 108  
    /**
 109  
     * Constructor for the <code>Support</code> object. This will
 110  
     * create a support object with the matcher and filter provided.
 111  
     * This allows the user to override the transformations that
 112  
     * are used to convert types to strings and back again.
 113  
     * 
 114  
     * @param filter this is the filter to use with this support
 115  
     * @param matcher this is the matcher used for transformations
 116  
     */
 117  
    public Support(Filter filter, Matcher matcher) {
 118  68
       this(filter, matcher, new Format());
 119  68
    }
 120  
    
 121  
    /**
 122  
     * Constructor for the <code>Support</code> object. This will
 123  
     * create a support object with the matcher and filter provided.
 124  
     * This allows the user to override the transformations that
 125  
     * are used to convert types to strings and back again.
 126  
     * 
 127  
     * @param filter this is the filter to use with this support
 128  
     * @param matcher this is the matcher used for transformations
 129  
     * @param format this contains all the formatting for the XML
 130  
     */
 131  950
    public Support(Filter filter, Matcher matcher, Format format) {
 132  950
       this.transform = new Transformer(matcher);
 133  950
       this.scanners = new ScannerFactory(this);
 134  950
       this.details = new DetailExtractor(this);
 135  950
       this.labels = new LabelExtractor(format);
 136  950
       this.instances = new InstanceFactory();
 137  950
       this.matcher = matcher;
 138  950
       this.filter = filter;
 139  950
       this.format = format;
 140  950
    }
 141  
    
 142  
    /**
 143  
     * Replaces the text provided with some property. This method 
 144  
     * acts much like a the get method of the <code>Map</code>
 145  
     * object, in that it uses the provided text as a key to some 
 146  
     * value. However it can also be used to evaluate expressions
 147  
     * and output the result for inclusion in the generated XML.
 148  
     *
 149  
     * @param text this is the text value that is to be replaced
 150  
     * 
 151  
     * @return returns a replacement for the provided text value
 152  
     */
 153  
    public String replace(String text) {
 154  200078
       return filter.replace(text);
 155  
    }
 156  
    
 157  
    /**
 158  
     * This is used to acquire the <code>Style</code> for the format.
 159  
     * This requires that the style is not null, if a null style is
 160  
     * returned from the format this will break serialization.
 161  
     * 
 162  
     * @return this returns the style used for this format object
 163  
     */
 164  
    public Style getStyle() {
 165  195966
       return format.getStyle();
 166  
    }
 167  
    
 168  
    /**
 169  
     * This is used to acquire the <code>Format</code> for this.
 170  
     * The format should never be null and contains information that
 171  
     * relates to the indentation that is to be used with XML elements.
 172  
     * 
 173  
     * @return this returns the format to be used for serialization
 174  
     */
 175  
    public Format getFormat() {
 176  8422
       return format;
 177  
    }
 178  
    
 179  
    /**
 180  
     * This will create an <code>Instance</code> that can be used
 181  
     * to instantiate objects of the specified class. This leverages
 182  
     * an internal constructor cache to ensure creation is quicker.
 183  
     * 
 184  
     * @param value this contains information on the object instance
 185  
     * 
 186  
     * @return this will return an object for instantiating objects
 187  
     */
 188  
    public Instance getInstance(Value value) {
 189  0
       return instances.getInstance(value);
 190  
    }
 191  
    
 192  
    /**
 193  
     * This will create an <code>Instance</code> that can be used
 194  
     * to instantiate objects of the specified class. This leverages
 195  
     * an internal constructor cache to ensure creation is quicker.
 196  
     * 
 197  
     * @param type this is the type that is to be instantiated
 198  
     * 
 199  
     * @return this will return an object for instantiating objects
 200  
     */
 201  
    public Instance getInstance(Class type) {
 202  1784132
       return instances.getInstance(type);
 203  
    }
 204  
    
 205  
    /**
 206  
     * This is used to match a <code>Transform</code> using the type
 207  
     * specified. If no transform can be acquired then this returns
 208  
     * a null value indicating that no transform could be found.
 209  
     * 
 210  
     * @param type this is the type to acquire the transform for
 211  
     * 
 212  
     * @return returns a transform for processing the type given
 213  
     */ 
 214  
    public Transform getTransform(Class type) throws Exception {
 215  0
       return matcher.match(type);
 216  
    }
 217  
 
 218  
    /**
 219  
     * Creates a <code>Label</code> using the provided contact and XML
 220  
     * annotation. The label produced contains all information related
 221  
     * to an object member. It knows the name of the XML entity, as
 222  
     * well as whether it is required. Once created the converter can
 223  
     * transform an XML node into Java object and vice versa.
 224  
     * 
 225  
     * @param contact this is contact that the label is produced for
 226  
     * @param label represents the XML annotation for the contact
 227  
     * 
 228  
     * @return returns the label instantiated for the contact
 229  
     */
 230  
    public Label getLabel(Contact contact, Annotation label) throws Exception {
 231  3472
       return labels.getLabel(contact, label);
 232  
    }
 233  
    
 234  
    /**
 235  
     * Creates a <code>List</code> using the provided contact and XML
 236  
     * annotation. The labels produced contain all information related
 237  
     * to an object member. It knows the name of the XML entity, as
 238  
     * well as whether it is required. Once created the converter can
 239  
     * transform an XML node into Java object and vice versa.
 240  
     * 
 241  
     * @param contact this is contact that the label is produced for
 242  
     * @param label represents the XML annotation for the contact
 243  
     * 
 244  
     * @return returns the list of labels associated with the contact
 245  
     */
 246  
    public List<Label> getLabels(Contact contact, Annotation label) throws Exception {
 247  165
       return labels.getList(contact, label);
 248  
    }
 249  
    
 250  
    /**
 251  
     * This is used to get a <code>Detail</code> object describing a
 252  
     * class and its annotations. Any detail retrieved from this will 
 253  
     * be cached to increase the performance of future accesses.
 254  
     * 
 255  
     * @param type this is the type to acquire the detail for
 256  
     * 
 257  
     * @return an object describing the type and its annotations
 258  
     */
 259  
    public Detail getDetail(Class type) {
 260  6771
       return details.getDetail(type);
 261  
    }
 262  
    
 263  
    /**
 264  
     * This is used to acquire a list of <code>Contact</code> objects
 265  
     * that represent the annotated fields in a type. The entire
 266  
     * class hierarchy is scanned for annotated fields. Caching of
 267  
     * the contact list is done to increase performance.
 268  
     * 
 269  
     * @param type this is the type to scan for annotated fields
 270  
     * 
 271  
     * @return this returns a list of the annotated fields
 272  
     */
 273  
    public ContactList getFields(Class type) throws Exception {
 274  3388
       return details.getFields(type);
 275  
    }
 276  
    
 277  
    /**
 278  
     * This is used to acquire a list of <code>Contact</code> objects
 279  
     * that represent the annotated methods in a type. The entire
 280  
     * class hierarchy is scanned for annotated methods. Caching of
 281  
     * the contact list is done to increase performance.
 282  
     * 
 283  
     * @param type this is the type to scan for annotated methods
 284  
     * 
 285  
     * @return this returns a list of the annotated methods
 286  
     */
 287  
    public ContactList getMethods(Class type) throws Exception {
 288  3393
       return details.getMethods(type);
 289  
    }
 290  
    
 291  
    /**
 292  
     * This creates a <code>Scanner</code> object that can be used to
 293  
     * examine the fields within the XML class schema. The scanner
 294  
     * maintains information when a field from within the scanner is
 295  
     * visited, this allows the serialization and deserialization
 296  
     * process to determine if all required XML annotations are used.
 297  
     * 
 298  
     * @param type the schema class the scanner is created for
 299  
     * 
 300  
     * @return a scanner that can maintains information on the type
 301  
     */ 
 302  
    public Scanner getScanner(Class type) throws Exception {
 303  2374169
       return scanners.getInstance(type);
 304  
    }
 305  
    
 306  
    /**
 307  
     * This method is used to convert the string value given to an
 308  
     * appropriate representation. This is used when an object is
 309  
     * being deserialized from the XML document and the value for
 310  
     * the string representation is required.
 311  
     * 
 312  
     * @param value this is the string representation of the value
 313  
     * @param type this is the type to convert the string value to
 314  
     * 
 315  
     * @return this returns an appropriate instanced to be used
 316  
     */
 317  
    public Object read(String value, Class type) throws Exception {
 318  1714125
       return transform.read(value, type);
 319  
    }
 320  
    
 321  
    /**
 322  
     * This method is used to convert the provided value into an XML
 323  
     * usable format. This is used in the serialization process when
 324  
     * there is a need to convert a field value in to a string so 
 325  
     * that that value can be written as a valid XML entity.
 326  
     * 
 327  
     * @param value this is the value to be converted to a string
 328  
     * @param type this is the type to convert to a string value
 329  
     * 
 330  
     * @return this is the string representation of the given value
 331  
     */
 332  
    public String write(Object value, Class type) throws Exception {
 333  526196
       return transform.write(value, type);
 334  
    }
 335  
    
 336  
    /**
 337  
     * This method is used to determine if the type specified can be
 338  
     * transformed. This will use the <code>Matcher</code> to find a
 339  
     * suitable transform, if one exists then this returns true, if
 340  
     * not then this returns false. This is used during serialization
 341  
     * to determine how to convert a field or method parameter. 
 342  
     *
 343  
     * @param type the type to determine whether its transformable
 344  
     * 
 345  
     * @return true if the type specified can be transformed by this
 346  
     */ 
 347  
    public boolean valid(Class type) throws Exception {  
 348  0
       return transform.valid(type);
 349  
    }
 350  
    
 351  
    /**
 352  
     * This is used to acquire the name of the specified type using
 353  
     * the <code>Root</code> annotation for the class. This will 
 354  
     * use either the name explicitly provided by the annotation or
 355  
     * it will use the name of the class that the annotation was
 356  
     * placed on if there is no explicit name for the root.
 357  
     * 
 358  
     * @param type this is the type to acquire the root name for
 359  
     * 
 360  
     * @return this returns the name of the type from the root
 361  
     * 
 362  
     * @throws Exception if the class contains an illegal schema
 363  
     */
 364  
    public String getName(Class type) throws Exception {
 365  463068
       Scanner schema = getScanner(type);
 366  463046
       String name = schema.getName();
 367  
       
 368  463046
       if(name != null) {
 369  456997
          return name;
 370  
       }
 371  6049
       return getClassName(type);
 372  
    }
 373  
    
 374  
    /**
 375  
     * This returns the name of the class specified. If there is a root
 376  
     * annotation on the type, then this is ignored in favour of the 
 377  
     * actual class name. This is typically used when the type is a
 378  
     * primitive or if there is no <code>Root</code> annotation present. 
 379  
     * 
 380  
     * @param type this is the type to acquire the root name for
 381  
     * 
 382  
     * @return this returns the name of the type from the root
 383  
     */
 384  
    private String getClassName(Class type) throws Exception {
 385  6049
       if(type.isArray()) {
 386  0
          type = type.getComponentType();
 387  
       }      
 388  6049
       String name = type.getSimpleName();
 389  
       
 390  6049
       if(type.isPrimitive()) {
 391  0
          return name;
 392  
       }
 393  6049
       return Reflector.getName(name);
 394  
    }
 395  
    
 396  
    /**
 397  
     * This is used to determine whether the scanned class represents
 398  
     * a primitive type. A primitive type is a type that contains no
 399  
     * XML annotations and so cannot be serialized with an XML form.
 400  
     * Instead primitives a serialized using transformations.
 401  
     *
 402  
     * @param type this is the type to determine if it is primitive
 403  
     * 
 404  
     * @return this returns true if no XML annotations were found
 405  
     */
 406  
    public boolean isPrimitive(Class type) throws Exception{
 407  2501512
       if(type == String.class) {
 408  1314759
          return true;
 409  
       }
 410  1186753
       if(type == Float.class) {
 411  49
          return true;
 412  
       }
 413  1186704
       if(type == Double.class) {
 414  246
          return true;
 415  
       }
 416  1186458
       if(type == Long.class) {
 417  51
          return true;
 418  
       }
 419  1186407
       if(type == Integer.class) {
 420  275
          return true;
 421  
       }
 422  1186132
       if(type == Boolean.class) {
 423  71
          return true;
 424  
       }
 425  1186061
       if(type.isEnum()) {
 426  101
          return true;
 427  
       }
 428  1185960
       if(type.isPrimitive()) {
 429  4867
          return true;
 430  
       }
 431  1181093
       return transform.valid(type);
 432  
    }
 433  
    
 434  
    /**
 435  
     * This is used to determine if the type specified is a floating
 436  
     * point type. Types that are floating point are the double and
 437  
     * float primitives as well as the java types for this primitives.
 438  
     * 
 439  
     * @param type this is the type to determine if it is a float
 440  
     * 
 441  
     * @return this returns true if the type is a floating point
 442  
     */
 443  
    public static boolean isFloat(Class type) throws Exception {
 444  7
       if(type == Double.class) {
 445  0
          return true;
 446  
       }
 447  7
       if(type == Float.class) {
 448  0
          return true;
 449  
       }
 450  7
       if(type == float.class) {
 451  0
          return true;
 452  
       }
 453  7
       if(type == double.class) {
 454  7
          return true;
 455  
       }
 456  0
       return false;
 457  
    }
 458  
    
 459  
    /**
 460  
     * This is used to determine if two objects are assignable to 
 461  
     * each other. To be sure that its is possible to inject primitive
 462  
     * values in to a constructor the primitives are wrapped in their
 463  
     * counterpart objects, this allows proper assignment checking.
 464  
     * 
 465  
     * @param expect this is the expected value of the object
 466  
     * @param actual this is the type in the declaration
 467  
     * 
 468  
     * @return this returns true if the types can be assigned
 469  
     */
 470  
    public static boolean isAssignable(Class expect, Class actual) {
 471  1577
       if(expect.isPrimitive()) {
 472  86
          expect = getPrimitive(expect);
 473  
       }
 474  1577
       if(actual.isPrimitive()) {
 475  147
          actual = getPrimitive(actual);
 476  
       }
 477  1577
       return actual.isAssignableFrom(expect);
 478  
    }
 479  
    
 480  
    /**
 481  
     * This method is used to convert a primitive type to its object
 482  
     * counterpart. Conversion to an object counterpart is useful when
 483  
     * there is a need to mask the difference between types.
 484  
     * 
 485  
     * @param type this is the primitive type to convert to an object
 486  
     * 
 487  
     * @return this returns the primitive type as its object type
 488  
     */
 489  
    public static Class getPrimitive(Class type) {
 490  2612
       if(type == double.class) {
 491  49
          return Double.class;
 492  
       }
 493  2563
       if(type == float.class) {
 494  40
          return Float.class;
 495  
       }
 496  2523
       if(type == int.class) {
 497  2292
          return Integer.class;
 498  
       }
 499  231
       if(type == long.class) {
 500  87
          return Long.class;
 501  
       }
 502  144
       if(type == boolean.class) {
 503  65
          return Boolean.class;
 504  
       }
 505  79
       if(type == char.class) {
 506  0
          return Character.class;
 507  
       }
 508  79
       if(type == short.class) {
 509  39
          return Short.class;
 510  
       }
 511  40
       if(type == byte.class) {
 512  40
          return Byte.class;
 513  
       }
 514  0
       return type;
 515  
    }
 516  
 }