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

org.simpleframework.xml.core.MethodScanner Maven / Gradle / Ivy

Go to download

Simple is a high performance XML serialization and configuration framework for Java

There is a newer version: 2.7.1
Show newest version
/*
 * MethodScanner.java April 2007
 *
 * Copyright (C) 2007, 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.core;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.LinkedHashMap;

import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementArray;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.ElementMap;
import org.simpleframework.xml.Text;
import org.simpleframework.xml.Version;

/**
 * The MethodScanner object is used to scan an object 
 * for matching get and set methods for an XML annotation. This will
 * scan for annotated methods starting with the most specialized
 * class up the class hierarchy. Thus, annotated methods can be 
 * overridden in a type specialization.
 * 

* The annotated methods must be either a getter or setter method * following the Java Beans naming conventions. This convention is * such that a method must begin with "get", "set", or "is". A pair * of set and get methods for an annotation must make use of the * same type. For instance if the return type for the get method * was String then the set method must have a single * argument parameter that takes a String type. *

* For a method to be considered there must be both the get and set * methods. If either method is missing then the scanner fails with * an exception. Also, if an annotation marks a method which does * not follow Java Bean naming conventions an exception is thrown. * * @author Niall Gallagher */ class MethodScanner extends ContactList { /** * This is used to acquire the hierarchy for the class scanned. */ private final Hierarchy hierarchy; /** * This is used to collect all the set methods from the object. */ private final PartMap write; /** * This is used to collect all the get methods from the object. */ private final PartMap read; /** * This is the type of the object that is being scanned. */ private final Class type; /** * Constructor for the MethodScanner object. This is * used to create an object that will scan the specified class * such that all bean property methods can be paired under the * XML annotation specified within the class. * * @param type this is the type that is to be scanned for methods * * @throws Exception thrown if there was a problem scanning */ public MethodScanner(Class type) throws Exception { this.hierarchy = new Hierarchy(type); this.write = new PartMap(); this.read = new PartMap(); this.type = type; this.scan(type); } /** * This method is used to scan the class hierarchy for each class * in order to extract methods that contain XML annotations. If * a method is annotated it is converted to a contact so that * it can be used during serialization and deserialization. * * @param type this is the type to be scanned for methods * * @throws Exception thrown if the object schema is invalid */ private void scan(Class type) throws Exception { for(Class next : hierarchy) { scan(type, next); } build(); validate(); } /** * This is used to scan the declared methods within the specified * class. Each method will be checked to determine if it contains * an XML element and can be used as a Contact for * an entity within the object. * * @param real this is the actual type of the object scanned * @param type this is one of the super classes for the object * * @throws Exception thrown if the class schema is invalid */ private void scan(Class real, Class type) throws Exception { Method[] method = type.getDeclaredMethods(); for(int i = 0; i < method.length; i++) { scan(method[i]); } } /** * This is used to scan all annotations within the given method. * Each annotation is checked against the set of supported XML * annotations. If the annotation is one of the XML annotations * then the method is considered for acceptance as either a * get method or a set method for the annotated property. * * @param method the method to be scanned for XML annotations * * @throws Exception if the method is not a Java Bean method */ private void scan(Method method) throws Exception { Annotation[] list = method.getDeclaredAnnotations(); for(int i = 0; i < list.length; i++) { scan(method, list[i]); } } /** * This reflectively checks the annotation to determine the type * of annotation it represents. If it represents an XML schema * annotation it is used to create a Contact which * can be used to represent the method within the source object. * * @param method the method that the annotation comes from * @param label the annotation used to model the XML schema * * @throws Exception if there is more than one text annotation */ private void scan(Method method, Annotation label) throws Exception { if(label instanceof Attribute) { process(method, label); } if(label instanceof ElementList) { process(method, label); } if(label instanceof ElementArray) { process(method, label); } if(label instanceof ElementMap) { process(method, label); } if(label instanceof Element) { process(method, label); } if(label instanceof Version) { process(method, label); } if(label instanceof Text) { process(method, label); } } /** * This is used to classify the specified method into either a get * or set method. If the method is neither then an exception is * thrown to indicate that the XML annotations can only be used * with methods following the Java Bean naming conventions. Once * the method is classified is is added to either the read or * write map so that it can be paired after scanning is complete. * * @param method this is the method that is to be classified * @param label this is the annotation applied to the method */ private void process(Method method, Annotation label) throws Exception { MethodPart part = MethodPartFactory.getInstance(method, label); MethodType type = part.getMethodType(); if(type == MethodType.GET) { process(part, read); } if(type == MethodType.IS) { process(part, read); } if(type == MethodType.SET) { process(part, write); } } /** * This is used to determine whether the specified method can be * inserted into the given PartMap. This ensures * that only the most specialized method is considered, which * enables annotated methods to be overridden in subclasses. * * @param method this is the method part that is to be inserted * @param map this is the part map used to contain the method */ private void process(MethodPart method, PartMap map) { String name = method.getName(); if(name != null) { map.put(name, method); } } /** * This method is used to pair the get methods with a matching set * method. This pairs methods using the Java Bean method name, the * names must match exactly, meaning that the case and value of * the strings must be identical. Also in order for this to succeed * the types for the methods and the annotation must also match. * * @throws Exception thrown if there is a problem matching methods */ private void build() throws Exception { for(String name : read) { MethodPart part = read.get(name); if(part != null) { build(part, name); } } } /** * This method is used to pair the get methods with a matching set * method. This pairs methods using the Java Bean method name, the * names must match exactly, meaning that the case and value of * the strings must be identical. Also in order for this to succeed * the types for the methods and the annotation must also match. * * @param read this is a get method that has been extracted * @param name this is the Java Bean methods name to be matched * * @throws Exception thrown if there is a problem matching methods */ private void build(MethodPart read, String name) throws Exception { MethodPart match = write.take(name); if(match != null) { build(read, match); } else { build(read); // read only } } /** * This method is used to create a read only contact. A read only * contact object is used when there is constructor injection used * by the class schema. So, read only methods can be used in a * fully serializable and deserializable object. * * @param read this is the part to add as a read only contact * * @throws Exception thrown if there is a problem matching methods */ private void build(MethodPart read) throws Exception { add(new MethodContact(read)); } /** * This method is used to pair the get methods with a matching set * method. This pairs methods using the Java Bean method name, the * names must match exactly, meaning that the case and value of * the strings must be identical. Also in order for this to succeed * the types for the methods and the annotation must also match. * * @param read this is a get method that has been extracted * @param write this is the write method to compare details with * * @throws Exception thrown if there is a problem matching methods */ private void build(MethodPart read, MethodPart write) throws Exception { Annotation label = read.getAnnotation(); String name = read.getName(); if(!write.getAnnotation().equals(label)) { throw new MethodException("Annotations do not match for '%s' in %s", name, type); } Class type = read.getType(); if(type != write.getType()) { throw new MethodException("Method types do not match for %s in %s", name, type); } add(new MethodContact(read, write)); } /** * This is used to validate the object once all the get methods * have been matched with a set method. This ensures that there * is not a set method within the object that does not have a * match, therefore violating the contract of a property. * * @throws Exception thrown if there is a unmatched set method */ private void validate() throws Exception { for(String name : write) { MethodPart part = write.get(name); if(part != null) { validate(part, name); } } } /** * This is used to validate the object once all the get methods * have been matched with a set method. This ensures that there * is not a set method within the object that does not have a * match, therefore violating the contract of a property. * * @param write this is a get method that has been extracted * @param name this is the Java Bean methods name to be matched * * @throws Exception thrown if there is a unmatched set method */ private void validate(MethodPart write, String name) throws Exception { MethodPart match = read.take(name); Method method = write.getMethod(); if(match == null) { throw new MethodException("No matching get method for %s in %s", method, type); } } /** * The PartMap is used to contain method parts using * the Java Bean method name for the part. This ensures that the * scanned and extracted methods can be acquired using a common * name, which should be the parsed Java Bean method name. * * @see org.simpleframework.xml.core.MethodPart */ private class PartMap extends LinkedHashMap implements Iterable{ /** * This returns an iterator for the Java Bean method names for * the MethodPart objects that are stored in the * map. This allows names to be iterated easily in a for loop. * * @return this returns an iterator for the method name keys */ public Iterator iterator() { return keySet().iterator(); } /** * This is used to acquire the method part for the specified * method name. This will remove the method part from this map * so that it can be checked later to ensure what remains. * * @param name this is the method name to get the method with * * @return this returns the method part for the given key */ public MethodPart take(String name) { return remove(name); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy