gt: 			throw new MappingException(e);
context: /* 
   * Model Tools.
   * Copyright (C) 2013 Pal Hargitai (pal@lunarray.org)
   * 
   * This program 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, either version 3 of the
   * License, or (at your option) any later version.
   * 
   * This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
   */
  package org.lunarray.model.descriptor.scanner;
  
  import java.io.Serializable;
  import java.lang.annotation.Annotation;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.util.Collections;
  import java.util.LinkedHashSet;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import org.apache.commons.lang.Validate;
  import org.lunarray.common.check.CheckUtil;
  import org.lunarray.model.descriptor.accessor.entity.DescribedEntity;
  import org.lunarray.model.descriptor.accessor.exceptions.ValueAccessException;
  import org.lunarray.model.descriptor.accessor.operation.DescribedOperation;
  import org.lunarray.model.descriptor.accessor.property.DescribedProperty;
  import org.lunarray.model.descriptor.builder.annotation.simple.SimpleBuilder;
  import org.lunarray.model.descriptor.converter.ConverterTool;
  import org.lunarray.model.descriptor.converter.def.DefaultConverterTool;
  import org.lunarray.model.descriptor.converter.def.DelegatingEnumConverterTool;
  import org.lunarray.model.descriptor.converter.exceptions.ConverterException;
  import org.lunarray.model.descriptor.creational.CreationException;
  import org.lunarray.model.descriptor.model.Model;
  import org.lunarray.model.descriptor.model.ModelProcessor;
  import org.lunarray.model.descriptor.model.entity.EntityDescriptor;
  import org.lunarray.model.descriptor.model.entity.EntityExtension;
  import org.lunarray.model.descriptor.model.extension.Extension;
  import org.lunarray.model.descriptor.model.extension.ExtensionContainer;
  import org.lunarray.model.descriptor.model.member.Cardinality;
  import org.lunarray.model.descriptor.model.operation.OperationExtension;
  import org.lunarray.model.descriptor.model.property.CollectionPropertyDescriptor;
  import org.lunarray.model.descriptor.model.property.PropertyDescriptor;
  import org.lunarray.model.descriptor.model.property.PropertyExtension;
  import org.lunarray.model.descriptor.objectfactory.simple.SimpleObjectFactory;
  import org.lunarray.model.descriptor.resource.ResourceException;
  import org.lunarray.model.descriptor.resource.simpleresource.SimpleClazzResource;
  import org.lunarray.model.descriptor.scanner.impl.exception.MappingException;
  import org.lunarray.model.descriptor.scanner.impl.extensions.AnnotationMetaModelImpl;
  import org.lunarray.model.descriptor.scanner.impl.inner.AnnotationDescriptor;
  import org.lunarray.model.descriptor.scanner.impl.inner.AnnotationMetaValues;
  import org.lunarray.model.descriptor.scanner.impl.inner.DescriptorProcessor;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * A processor that processes annotations of a model to an annotation meta model
   * for easier processing.
   * 
   * @author Pal Hargitai (pal@lunarray.org)
   * @param <S>
   *            The model super type.
   */
  public final class AnnotationMetaModelProcessor<S>
  		implements ModelProcessor<S> {
  
  	/** The logger. */
  	private static final Logger LOGGER = LoggerFactory.getLogger(AnnotationMetaModelProcessor.class);
  	/**
  	 * The converter, by default supports most Java platform types usable within
  	 * annotations.
  	 */
  	private ConverterTool converterTool;
  	/** The inner model. */
  	private Model<Serializable> model;
  	/** The inner model builder. */
  	private SimpleBuilder<Serializable> modelBuilder;
  	/** The inner model descriptor processor. */
  	private DescriptorProcessor processor;
  	/** Model resources. */
  	private SimpleClazzResource<Serializable> resource;
  
  	/**
  	 * Default constructor.
  	 */
  	@SuppressWarnings("unchecked")
  	public AnnotationMetaModelProcessor() {
  		this.processor = new DescriptorProcessor();
  		this.modelBuilder = SimpleBuilder.createBuilder();
  		this.modelBuilder.postProcessors(this.processor);
  		this.resource = new SimpleClazzResource<Serializable>();
  		this.converterTool = new DelegatingEnumConverterTool(new DefaultConverterTool());
  	}
  
  	/**
  	 * Gets the annotation meta values.
  	 * 
  	 * @return The meta values.
  	 */
  	public AnnotationMetaValues createMetaValues() {
  		return this.createAnnotationMetaValues();
  	}
  
  	/**
  	 * Gets the value for the converterTool field.
  	 * 
  	 * @return The value for the converterTool field.
  	 */
  	public ConverterTool getConverterTool() {
  		return this.converterTool;
  	}
  
  	/**
  	 * Gets the value for the model field.
  	 * 
  	 * @return The value for the model field.
  	 */
  	public Model<Serializable> getModel() {
  		return this.model;
  	}
  
  	/**
  	 * Gets the value for the modelBuilder field.
  	 * 
  	 * @return The value for the modelBuilder field.
  	 */
  	public SimpleBuilder<Serializable> getModelBuilder() {
  		return this.modelBuilder;
  	}
  
  	/**
  	 * Gets the value for the processor field.
  	 * 
  	 * @return The value for the processor field.
  	 */
  	public DescriptorProcessor getProcessor() {
  		return this.processor;
  	}
  
  	/**
  	 * Gets the value for the resource field.
  	 * 
  	 * @return The value for the resource field.
  	 */
  	public SimpleClazzResource<Serializable> getResource() {
  		return this.resource;
  	}
  
  	/** {@inheritDoc} */
  	@Override
  	public <E extends S> EntityExtension<E> process(final DescribedEntity<E> entityType, final ExtensionContainer extensionContainer) {
  		Validate.notNull(entityType, "Entity may not be null.");
  		EntityExtension<E> extension = null;
  		try {
  			extension = this.processNode(entityType.getAnnotations());
  		} catch (final MappingException e) {
  			AnnotationMetaModelProcessor.LOGGER.warn("Could not process entity.", e);
  		}
  		return extension;
  	}
  
  	/** {@inheritDoc} */
  	@Override
  	public <E extends S> OperationExtension<E> process(final DescribedOperation entityType, final ExtensionContainer extensionContainer) {
  		return null;
  	}
  
  	/** {@inheritDoc} */
  	@Override
  	public <E extends S, P> PropertyExtension<P, E> process(final DescribedProperty<P> property, final ExtensionContainer extensionContainer) {
  		Validate.notNull(property, "Property may not be null.");
  		PropertyExtension<P, E> extension = null;
  		try {
  			extension = this.processNode(property.getAnnotations());
  		} catch (final MappingException e) {
  			AnnotationMetaModelProcessor.LOGGER.warn("Could not process property.", e);
  		}
  		return extension;
  	}
  
  	/** {@inheritDoc} */
  	@Override
  	public Extension process(final ExtensionContainer extensionContainer) {
  		if (CheckUtil.isNull(this.model)) {
  			try {
  				this.model = this.modelBuilder.extensions(new SimpleObjectFactory(), this.converterTool).resources(this.resource).build();
  			} catch (final ResourceException e) {
  				AnnotationMetaModelProcessor.LOGGER.warn("Could not process model.", e);
  			}
  		}
  		return null;
  	}
  
  	/**
  	 * Register an annotation entity.
  	 * 
  	 * @param domainType
  	 *            The domain type.
  	 */
  	public void registerAnnotationEntity(final Class<? extends Serializable> domainType) {
  		this.resource.addClazz(domainType);
  	}
  
  	/**
  	 * Sets a new value for the converterTool field.
  	 * 
  	 * @param converterTool
  	 *            The new value for the converterTool field.
  	 */
  	public void setConverterTool(final ConverterTool converterTool) {
  		this.converterTool = converterTool;
  	}
  
  	/**
  	 * Sets a new value for the model field.
  	 * 
  	 * @param model
  	 *            The new value for the model field.
  	 */
  	public void setModel(final Model<Serializable> model) {
  		this.model = model;
  	}
  
  	/**
  	 * Sets a new value for the modelBuilder field.
  	 * 
  	 * @param modelBuilder
  	 *            The new value for the modelBuilder field.
  	 */
  	public void setModelBuilder(final SimpleBuilder<Serializable> modelBuilder) {
  		this.modelBuilder = modelBuilder;
  	}
  
  	/**
  	 * Sets a new value for the processor field.
  	 * 
  	 * @param processor
  	 *            The new value for the processor field.
  	 */
  	public void setProcessor(final DescriptorProcessor processor) {
  		this.processor = processor;
  	}
  
  	/**
  	 * Sets a new value for the resource field.
  	 * 
  	 * @param resource
  	 *            The new value for the resource field.
  	 */
  	public void setResource(final SimpleClazzResource<Serializable> resource) {
  		this.resource = resource;
  	}
  
  	/**
  	 * Copy array values.
  	 * 
  	 * @param values
  	 *            The array values.
  	 * @param name
  	 *            The property name.
  	 * @param obj
  	 *            The object value.
  	 * @param type
  	 *            The array type.
  	 * @throws MappingException
  	 *             Thrown if the mapping was unsuccessful.
  	 */
  	private void arrayCopy(final AnnotationMetaValues values, final String name, final Object obj, final Class<?> type)
  			throws MappingException {
  		try {
  			final Class<?> compType = type.getComponentType();
  			if (Integer.TYPE.equals(compType)) {
  				final int[] array = int[].class.cast(obj);
  				for (final int a : array) {
  					values.getMetaValueList(name).add(this.converterTool.convertToString(Integer.TYPE, a));
  				}
  			} else if (Long.TYPE.equals(compType)) {
  				final long[] array = long[].class.cast(obj);
  				for (final long a : array) {
  					values.getMetaValueList(name).add(this.converterTool.convertToString(Long.TYPE, a));
  				}
  			} else {
  				this.copyShortNumbers(values, name, obj, type, compType);
  			}
  		} catch (final ConverterException e) {
  			throw new MappingException(e);
  		}
  	}
  
  	/**
  	 * Convert an array to string types.
  	 * 
  	 * @param array
  	 *            The array to convert.
  	 * @param type
  	 *            The array type.
  	 * @param tail
  	 *            The result tail.
  	 * @throws ConverterException
  	 *             Thrown if a value could not be converted.
  	 * @param <T>
  	 *            The array type.
  	 */
  	private <T> void convert(final T[] array, final Class<T> type, final List<String> tail) throws ConverterException {
  		for (final T t : array) {
  			if (!CheckUtil.isNull(t)) {
  				tail.add(this.converterTool.convertToString(type, t));
  			}
  		}
  	}
  
  	/**
  	 * Copy array values.
  	 * 
  	 * @param values
  	 *            The array values.
  	 * @param name
  	 *            The property name.
  	 * @param obj
  	 *            The object value.
  	 * @param type
  	 *            The array type.
  	 * @param compType
  	 *            The component type.
  	 * @throws MappingException
  	 *             Thrown if the mapping was unsuccessful.
  	 */
  	private void copyNonDiscreteNumbers(final AnnotationMetaValues values, final String name, final Object obj, final Class<?> type,
  			final Class<?> compType) throws MappingException {
  		try {
  			if (Double.TYPE.equals(compType)) {
  				final double[] array = double[].class.cast(obj);
  				for (final double a : array) {
  					values.getMetaValueList(name).add(this.converterTool.convertToString(Double.TYPE, a));
  				}
  			} else if (Float.TYPE.equals(compType)) {
  				final float[] array = float[].class.cast(obj);
  				for (final float a : array) {
  					values.getMetaValueList(name).add(this.converterTool.convertToString(Float.TYPE, a));
  				}
  			} else {
  				this.extractNonFloats(values, name, obj, type, compType);
  			}
  		} catch (final ConverterException e) {
  			throw new MappingException(e);
  		}
  	}
  
  	/**
  	 * Copy array values.
  	 * 
  	 * @param values
  	 *            The array values.
  	 * @param name
  	 *            The property name.
  	 * @param obj
  	 *            The object value.
  	 * @param type
  	 *            The array type.
  	 * @param compType
  	 *            The component type.
  	 * @throws MappingException
  	 *             Thrown if the mapping was unsuccessful.
  	 */
  	private void copyObjectTypes(final AnnotationMetaValues values, final String name, final Object obj, final Class<?> type,
  			final Class<?> compType) throws MappingException {
  		if (Annotation.class.isAssignableFrom(compType)) {
  			final Annotation[] array = Annotation[].class.cast(obj);
  			for (final Annotation element : array) {
  				final AnnotationMetaValues innerValues = this.createMetaValues();
  				values.getMetaAnnotationList(name).add(innerValues);
  				this.processAnnotation(innerValues, element);
  			}
  		} else {
  			final Object[] array = Object[].class.cast(obj);
  			@SuppressWarnings("unchecked")
  			final Class<Object> aType = (Class<Object>) type.getComponentType();
  			try {
  				this.convert(array, aType, values.getMetaValueList(name));
  			} catch (final ConverterException e) {
  				throw new MappingException(e);
  			}
  		}
  	}
  
  	/**
  	 * Copy array values.
  	 * 
  	 * @param values
  	 *            The array values.
  	 * @param name
  	 *            The property name.
  	 * @param obj
  	 *            The object value.
  	 * @param type
  	 *            The array type.
  	 * @param compType
  	 *            The component type.
  	 * @throws MappingException
  	 *             Thrown if the mapping was unsuccessful.
  	 */
  	private void copyShortNumbers(final AnnotationMetaValues values, final String name, final Object obj, final Class<?> type,
  			final Class<?> compType) throws MappingException {
  		try {
  			if (Short.TYPE.equals(compType)) {
  				final short[] array = short[].class.cast(obj);
  				for (final short a : array) {
  					values.getMetaValueList(name).add(this.converterTool.convertToString(Short.TYPE, a));
  				}
  			} else if (Byte.TYPE.equals(compType)) {
  				final byte[] array = byte[].class.cast(obj);
  				for (final byte a : array) {
  					values.getMetaValueList(name).add(this.converterTool.convertToString(Byte.TYPE, a));
  				}
  			} else {
  				this.copyNonDiscreteNumbers(values, name, obj, type, compType);
  			}
  		} catch (final ConverterException e) {
  			throw new MappingException(e);
  		}
  	}
  
  	/**
  	 * Copy a value.
  	 * 
  	 * @param values
  	 *            The values to copy to.
  	 * @param annotation
  	 *            The annotation to copy for.
  	 * @param method
  	 *            The method of the annotation to copy.
  	 * @throws MappingException
  	 *             Thrown if the value could not be related.
  	 */
  	private void copyValue(final AnnotationMetaValues values, final Annotation annotation, final Method method) throws MappingException {
  		final String name = method.getName();
  		final Object defaultValue = method.getDefaultValue();
  		try {
  			final Object obj = method.invoke(annotation);
  			if (!CheckUtil.isNull(obj) && !obj.equals(defaultValue)) {
  				values.getMetaAnnotationList(name);
  				final Class<?> type = obj.getClass();
  				this.processType(values, name, obj, type);
  			}
  		} catch (final IllegalArgumentException e) {
  			throw new MappingException(e);
  		} catch (final IllegalAccessException e) {
  