All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.simpleframework.xml.load.Signature Maven / Gradle / Ivy

/*
 * Signature.java February 2005
 *
 * Copyright (C) 2005, Niall Gallagher 
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA
 */

package org.simpleframework.xml.load;

import org.simpleframework.xml.Root;
import java.lang.annotation.Annotation;
import java.beans.Introspector;

/**
 * The Signature object is used to determine the details
 * to use for an annotated field or method using both the field an
 * annotation details. This allows defaults to be picked up from the
 * method or field type if thay have not been explicitly overridden
 * in the annotation. 
 * 
 * @author Niall Gallagher
 */
class Signature {
   
   /**
    * This is the actual annotation from the specified contact.
    */
   private Annotation marker;
   
   /**
    * This is the field or method contact that has been annotated.
    */
   private Contact contact;
   
   /**
    * This is the label used to expose the annotation details.
    */
   private Label label;
   
   /**
    * This is the name as taken from the contact or annotation.
    */
   private String name;
   
   /**
    * Constructor for the Signature object. This is 
    * used to create an object that will use information available
    * within the field and annotation to determine exactly what 
    * the name of the XML element is to be and the type to use.
    * 
    * @param contact this is the method or field contact used
    * @param label this is the annotation on the contact object
    */
   public Signature(Contact contact, Label label) {
      this.marker = contact.getAnnotation();
      this.contact = contact;
      this.label = label;
   }
   
   /**
    * This is used to acquire the Contact for this. The
    * contact is the actual method or field that has been annotated
    * and is used to set or get information from the object instance.
    * 
    * @return the method or field that this signature represents
    */
   public Contact getContact() {
      return contact;
   }
   
   /**
    * This returns the dependant type for the annotation. This type
    * is the type other than the annotated field or method type that
    * the label depends on. For the ElementList this 
    * can be the generic parameter to an annotated collection type.
    * 
    * @return this is the type that the annotation depends on
    */
   public Class getDependant() throws Exception {
      return label.getDependant();
   }

   /**
    * This method is used to get the parent name of a label using 
    * the type of the label. This ensures that if there is no
    * parent XML element name declared by the annotation that a
    * suitable name can be calculated from the annotated type.
    * 
    * @return this returns a suitable XML parent element name
    */
   public String getParent() throws Exception {
      Class type = getDependant();

      if(!Factory.isPrimitive(type)) {
         type = Object.class;
      }
      return getParent(type);
   }
   
   /**
    * This method is used to get the parent name of a label using 
    * the type of the label. This ensures that if there is no
    * parent XML element name declared by the annotation that a
    * suitable name can be calculated from the annotated type.
    * 
    * @param type this is the type to get the 
    * 
    * @return this returns a suitable XML parent element name
    */
   private String getParent(Class type) throws Exception {          
      String name = type.getSimpleName();
      
      if(type.isPrimitive()) {
         return name;
      }
      return name.toLowerCase();      
   }
   
   /**
    * This is used to determine the name of the XML element that the
    * annotated field or method represents. This will determine based
    * on the annotation attributes and the dependant type required
    * what the name of the XML element this represents is. 
    * 
    * @return this returns the name of the XML element expected
    */
   public String getName() throws Exception {
      if(name == null) {
         Class type = getDependant();
         
         if(!label.isInline()) {
            name = getDefault();        
         } else {
            name = getName(type);
         }
      }
      return name;
   }
   
   /**
    * This is used to determine the name of the XML element that the
    * annotated field or method represents. This will determine based
    * on the annotation attributes and the dependant type required
    * what the name of the XML element this represents is.
    * 
    * @param type this is the dependant type to acquire the name for
    * 
    * @return this returns the name of the XML element expected
    */
   private String getName(Class type) throws Exception {      
      if(isPrimitive(type)) {
         name = label.getParent();         
      } else {
         name = getRoot();
      }
      return name;
   }
   
   /**
    * This is used to acquire the name of the XML element for a list
    * that has been declared inline. An inline list is a list that
    * has no containing element, thus a name cannot be used to find
    * the first element that belongs to the list. Instead the type
    * the list contains is required so the root name can be used.
    *
    * @return this will return the root name for the list type 
    */
   private String getRoot() throws Exception {
      String name = label.getOverride();
	  
      if(!isEmpty(name)) {
         throw new ElementException("Inline element %s can not have name", label);
      }
      Class type = getDependant();
      String root = getRoot(type);     
      
      if(root == null) {
         throw new RootException("Root required for %s in %s", type, label);        
      }   
      return root;
   }
   
   /**
    * This will acquire the name of the Root annotation
    * for the specified class. This will traverse the inheritance
    * heirarchy looking for the root annotation, when it is found it
    * is used to acquire a name for the XML element it represents.
    *  
    * @param type this is the type to acquire the root name with
    * 
    * @return the root name for the specified type if it exists
    */
   private String getRoot(Class type) { 
	   Class real = type;
	      
	   while(type != null) {
	      String name = getRoot(real, type);
	      
	      if(name != null) {
	    	  return name.intern();
	      }
         type = type.getSuperclass();
	   }
	   return null;     
   }
   
   /**
    * This will acquire the name of the Root annotation
    * for the specified class. This will traverse the inheritance
    * heirarchy looking for the root annotation, when it is found it
    * is used to acquire a name for the XML element it represents.
    *  
    * @param real the actual type of the object being searched
    * @param type this is the type to acquire the root name with    
    * 
    * @return the root name for the specified type if it exists
    */
   private String getRoot(Class real, Class type) {
      String name = type.getSimpleName();
	   
      if(type.isAnnotationPresent(Root.class)) {
          Root root = type.getAnnotation(Root.class);
          String text = root.name();
          
          if(!isEmpty(text)) {
             return text;
          }
          return Introspector.decapitalize(name);
      }
      return null;
   }
   
   /**
    * This is used to acquire the name for an element by firstly
    * checking for an override in the annotation. If one exists
    * then this is returned if not then the name of the field
    * or method contact is returned. 
    * 
    * @return this returns the XML element name to be used
    */
   private String getDefault() {
      String name = label.getOverride();
      
      if(!isEmpty(name)) {
         return name;
      }
      return contact.getName();
   }  
   
   /**
    * This method is used to determine whether the field type is a
    * primitive type. This check is required to ensure that primitive
    * elements do not consult the Strategy object for 
    * the field class. This improves the performance of serialization
    * and also ensures that the XML serialization is transparent.
    * 
    * @param type the type checked to determine if it is primitive
    * 
    * @return true if the type is primitive, false otherwise
    */   
   public boolean isPrimitive(Class type) {
      if(type != null) {
         return Factory.isPrimitive(type);      
      }
      return false;
   }
   
   /**
    * This method is used to determine if a root annotation value is
    * an empty value. Rather than determining if a string is empty
    * be comparing it to an empty string this method allows for the
    * value an empty string represents to be changed in future.
    * 
    * @param value this is the value to determine if it is empty
    * 
    * @return true if the string value specified is an empty value
    */
   public boolean isEmpty(String value) {
      return value.length() == 0;
   }
   
   /**
    * This method is used to construct a string that describes the
    * signature of an XML annotated field or method. This will use
    * the Contact object and the annotation used for
    * that contact to construct a string that has sufficient
    * information such that it can be used in error reporting.
    * 
    * @return returns a string used to represent this signature 
    */
   public String toString() {
      return String.format("%s on %s", marker, contact);
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy