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

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

Go to download

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

The newest version!
/*
 * GroupExtractor.java March 2011
 *
 * Copyright (C) 2011, Niall Gallagher 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
 * implied. See the License for the specific language governing 
 * permissions and limitations under the License.
 */

package org.simpleframework.xml.core;

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

import org.simpleframework.xml.Text;
import org.simpleframework.xml.stream.Format;

/**
 * The GroupExtractor represents an extractor for labels
 * associated with a particular union annotation. This extractor 
 * registers Label by name and by type. Acquiring
 * the label by type allows the serialization process to dynamically
 * select a label, and thus converter, based on the instance type.
 * On deserialization a label is dynamically selected based on name.
 * 
 * @author Niall Gallagher
 * 
 * @see org.simpleframework.xml.core.Group
 */
class GroupExtractor implements Group {

   /**
    * This represents a factory for creating union extractors.
    */
   private final ExtractorFactory factory;
   
   /**
    * This represents the union label to be used for this group.
    */
   private final Annotation label;
   
   /**
    * This contains each label registered by name and by type.
    */
   private final Registry registry;
   
   /**
    * This contains each label registered by label name.
    */
   private final LabelMap elements;
   
   /**
    * Constructor for the GroupExtractor object. This
    * will create an extractor for the provided union annotation.
    * Each individual declaration within the union is extracted
    * and made available within the group.
    * 
    * @param contact this is the annotated field or method
    * @param label this is the label associated with the contact
    * @param format this is the format used by this extractor
    */
   public GroupExtractor(Contact contact, Annotation label, Format format) throws Exception{
      this.factory = new ExtractorFactory(contact, label, format);
      this.elements = new LabelMap();
      this.registry = new Registry(elements);
      this.label = label;
      this.extract();
   } 
   
   /**
    * This is used to acquire the names for each label associated
    * with this Group instance. The names provided
    * here are not styled according to a serialization context.
    * 
    * @return this returns the names of each union extracted
    */
   public String[] getNames() throws Exception {
      return elements.getKeys();
   }
   
   /**
    * This is used to acquire the paths for each label associated
    * with this Group instance. The paths provided
    * here are not styled according to a serialization context.
    * 
    * @return this returns the paths of each union extracted
    */
   public String[] getPaths() throws Exception {
      return elements.getPaths();
   }
   
   /**
    * This is used to acquire a LabelMap containing the
    * labels available to the group. Providing a context object 
    * ensures that each of the labels is mapped to a name that is
    * styled according to its internal style.
    * 
    * @return this returns a label map containing the labels 
    */
   public LabelMap getElements() throws Exception {
      return elements.getLabels();
   }

   /**
    * This is used to acquire a Label based on the type
    * of an object. Selecting a label based on the type ensures that
    * the serialization process can dynamically convert an object
    * to XML. If the type is not supported, this returns null.
    * 
    * @param type this is the type to select the label from
    * 
    * @return this returns the label based on the type
    */
   public Label getLabel(Class type) {
      return registry.resolve(type);
   }
   
   /**
    * This is used to get a Label that represents the
    * text between elements on an element union. Providing a label
    * here ensures that the free text found between elements can
    * be converted in to strings and added to the list.
    * 
    * @return a label if a text annotation has been declared
    */
   public Label getText() {
      return registry.resolveText();
   }
   
   /**
    * This is used to determine if the associated type represents a
    * label defined within the union group. If the label exists
    * this returns true, if not then this returns false.
    * 
    * @param type this is the type to check for
    * 
    * @return this returns true if a label for the type exists
    */
   public boolean isValid(Class type) {
      return registry.resolve(type) != null;
   }
   
   /**
    * This is used to determine if a type has been declared by the
    * annotation associated with the group. Unlike determining if
    * the type is valid this will not consider super types.
    * 
    * @param type this is the type to determine if it is declared
    * 
    * @return this returns true if the type has been declared
    */
   public boolean isDeclared(Class type) {
      return registry.containsKey(type);
   }
   
   /**
    * This is used to determine if the group is inline. A group is
    * inline if all of the elements in the group is inline. If any of
    * the Label objects in the group is not inline then
    * the entire group is not inline, although this is unlikely.
    * 
    * @return this returns true if each label in the group is inline
    */
   public boolean isInline() {
      for(Label label : registry) {
         if(!label.isInline()) {
            return false;
         }
      }
      return !registry.isEmpty();
   }
   
   /**
    * This is used to determine if an annotated list is a text 
    * list. A text list is a list of elements that also accepts
    * free text. Typically this will be an element list union that
    * will allow unstructured XML such as XHTML to be parsed.
    * 
    * @return returns true if the label represents a text list
    */
   public boolean isTextList() {
      return registry.isText();
   }
   
   /**
    * This is used to extract the labels associated with the group.
    * Extraction will instantiate a Label object for
    * an individual annotation declared within the union. Each of
    * the label instances is then registered by both name and type.
    */
   private void extract() throws Exception {
      Extractor extractor = factory.getInstance();

      if(extractor != null) {
         extract(extractor);
      }
   }
   
   /**
    * This is used to extract the labels associated with the group.
    * Extraction will instantiate a Label object for
    * an individual annotation declared within the union. Each of
    * the label instances is then registered by both name and type.
    * 
    * @param extractor this is the extractor to get labels for
    */
   private void extract(Extractor extractor) throws Exception {
      Annotation[] list = extractor.getAnnotations();
      
      for(Annotation label : list) {
         extract(extractor, label);
      }
   }
   
   /**
    * This is used to extract the labels associated with the group.
    * Extraction will instantiate a Label object for
    * an individual annotation declared within the union. Each of
    * the label instances is then registered by both name and type.
    * 
    * @param extractor this is the extractor to get labels for
    * @param value this is an individual annotation declared
    */
   private void extract(Extractor extractor, Annotation value) throws Exception {
      Label label = extractor.getLabel(value);
      Class type = extractor.getType(value);
      
      if(registry != null) {
         registry.register(type, label);
      }
   }
   
   /**
    * This returns a string representation of the union group.
    * Providing a string representation in this way ensures that the
    * group can be used in exception messages and for any debugging.
    * 
    * @return this returns a string representation of the group
    */
   public String toString() {
      return label.toString();
   }
   
   /**
    * The Registry object is used to maintain mappings
    * from types to labels. Each of the mappings can be used to 
    * dynamically select a label based on the instance type that is
    * to be serialized. This also registers based on the label name.
    */
   private static class Registry extends LinkedHashMap implements Iterable