content: /*
   * CompositeListUnion.java March 2011
   *
   * Copyright (C) 2011, Niall Gallagher <niallg@users.sf.net>
   *
   * 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 com.aliyun.odps.simpleframework.xml.core;
  
  import com.aliyun.odps.simpleframework.xml.strategy.Type;
  import com.aliyun.odps.simpleframework.xml.stream.InputNode;
  import com.aliyun.odps.simpleframework.xml.stream.OutputNode;
  import com.aliyun.odps.simpleframework.xml.stream.Style;
  import java.util.Collection;
  import java.util.Collections;
  
  /**
   * The <code>CompositeListUnion</code> object is used to act as a 
   * mediator for multiple converters associated with a particular union 
   * group. This will basically determine which <code>Converter</code> 
   * should be delegated to based on either the XML element name being read 
   * or the type of the instance object being written. Selection of the 
   * converter is done by consulting the <code>Group</code> of labels 
   * representing the union declaration.
   * 
   * @author Niall Gallagher
   */
  class CompositeListUnion implements Repeater {
     
     /**
      * This contains the labels in the union group keyed by name.
      */
     private final LabelMap elements;
     
     /**
      * This is the path expression used to represent this union.
      */
     private final Expression path;
     
     /**
      * This is the current context used for the serialization.
      */
     private final Context context;
     
     /**
      * This contains the group of labels associated with the union.
      */
     private final Group group;
     
     /**
      * This is this style associated with the serialization context.
      */
     private final Style style;
     
     /**
      * This is the type field or method annotated as a union.
      */
     private final Type type;
  
     /**
      * Constructor for the <code>CompositeListUnion</code> object. This
      * is used to create a converter that delegates to other associated
      * converters within the union group depending on the XML element
      * name being read or the instance type that is being written.
      * 
      * @param context this is the context used for the serialization
      * @param group this is the union group used for delegation
      * @param path this is the path expression representing this union
      * @param type this is the annotated field or method to be used
      */
     public CompositeListUnion(Context context, Group group, Expression path, Type type) throws Exception {
        this.elements = group.getElements();
        this.style = context.getStyle();
        this.context = context;
        this.group = group;
        this.type = type;
        this.path = path;
     }
  
     /**
      * The <code>read</code> method uses the name of the XML element to
      * select a converter to be used to read the instance. Selection of
      * the converter is done by looking up the associated label from
      * the union group using the element name. Once the converter has
      * been selected it is used to read the instance.
      * 
      * @param node this is the XML element used to read the instance
      * 
      * @return this is the instance that has been read by this
      */
     public Object read(InputNode node) throws Exception {
        Label text = group.getText();
        
        if(text == null) {
           return readElement(node);
        } 
        return readText(node);
     }
     
     /**
      * The <code>readElement</code> method uses the name of the element 
      * to select a converter to be used to read the instance. Selection 
      * of the converter is done by looking up the associated label from
      * the union group using the element name. Once the converter has
      * been selected it is used to read the instance.
      * 
      * @param node this is the XML element used to read the instance
      * 
      * @return this is the instance that has been read by this
      */
     private Object readElement(InputNode node) throws Exception {
        String name = node.getName();
        String element = path.getElement(name);
        Label label = elements.get(element);
        Converter converter = label.getConverter(context);
     
        return converter.read(node);
     }
     
     /**
      * The <code>readText</code> method is used to read free text from
      * between the declared elements and add them to a list. Consuming
      * free text in this manner enables an element list union to parse
      * unstructured XML such as XHTML.
      * 
      * @param node this is the node to consume the free text from
      * 
      * @return this returns the list with the text added to it
      */
     private Object readText(InputNode node) throws Exception {
        Label text = group.getText();
        Converter converter = text.getConverter(context);
        
        return converter.read(node);
     }
  
     /**
      * The <code>read</code> method uses the name of the XML element to
      * select a converter to be used to read the instance. Selection 
      * of the converter is done by looking up the associated label from
      * the union group using the element name. Once the converter has
      * been selected it is used to read the instance.
      * 
      * @param node this is the XML element used to read the instance
      * @param value this is the value that is to be repeated
      * 
      * @return this is the instance that has been read by this
      */
     public Object read(InputNode node, Object value) throws Exception {
        Object result = readElement(node, value);
        Label text = group.getText();
       
        if(text != null) {
           return readText(node, value);
        }
        return result;
     }
     
     /**
      * The <code>readElement</code> method uses the name of the element 
      * to select a converter to be used to read the instance. Selection 
      * of the converter is done by looking up the associated label from
      * the union group using the element name. Once the converter has
      * been selected it is used to read the instance.
      * 
      * @param node this is the XML element used to read the instance
      * @param value this is the value that is to be repeated
      * 
      * @return this is the instance that has been read by this
      */
     private Object readElement(InputNode node, Object value) throws Exception {
        String name = node.getName();
        String element = path.getElement(name);
        Label label = elements.get(element);
        Converter converter = label.getConverter(context);
        
        return converter.read(node, value);
     }
     
     /**
      * The <code>readText</code> method is used to read free text from
      * between the declared elements and add them to a list. Consuming
      * free text in this manner enables an element list union to parse
      * unstructured XML such as XHTML.
      * 
      * @param node this is the node to consume the free text from
      * @param value this is the value that is to be repeated
      * 
      * @return this returns the list with the text added to it
      */
     private Object readText(InputNode node, Object value) throws Exception {
        Label label = group.getText();
        Converter converter = label.getConverter(context);
        InputNode parent = node.getParent();
        
        return converter.read(parent, value);
     }
     
     /**
      * The <code>validate</code> method is used to validate the XML
      * element provided using an associated class schema. The schema
      * is selected using the name of the XML element to acquire
      * the associated converter. Once the converter has been acquired
      * it is delegated to and validated against it.
      * 
      * @param node this is the input XML element to be validated
      * 
      * @return this returns true if the node validates 
      */
     public boolean validate(InputNode node) throws Exception {
        String name = node.getName();
        String element = path.getElement(name);
        Label label = elements.get(element);
        Converter converter = label.getConverter(context);
        
        return converter.validate(node);
     }
     
     /**
      * The <code>write</code> method uses the name of the XML element to
      * select a converter to be used to write the instance. Selection of
      * the converter is done by looking up the associated label from
      * the union group using the instance type. Once the converter has
      * been selected it is used to write the instance.
      * 
      * @param source this is the source collection to be serialized 
      * @param node this is the XML element container to be populated
      */ 
     public void write(OutputNode node, Object source) throws Exception {
        Collection list = (Collection) source;                  
        
        if(group.isInline()) {
           if(!list.isEmpty()) {
              write(node, list);
           } else if(!node.isCommitted()){
              node.remove();
           }
        } else {
           write(node, list);
        }
     }
  
     /**
      * The <code>write</code> method uses the name of the XML element to
      * select a converter to be used to write the instance. Selection of
      * the converter is done by looking up the associated label from
      * the union group using the instance type. Once the converter has
      * been selected it is used to write the instance.
      * 
      * @param node this is the XML element used to write the instance
      * @param list this is the value that is to be written
      */
     private void write(OutputNode node, Collection list) throws Exception {     
        for(Object item : list) {
           if(item != null) {
              Class real = item.getClass();
              Label label = group.getLabel(real);
              
              if(label == null) {          
                 throw new UnionException("Entry of %s not declared in %s with annotation %s", real, type, group);
              }
              write(node, item, label);
           }
        }
     }
        
     /**
      * The <code>write</code> method uses the name of the XML element to
      * select a converter to be used to write the instance. Selection of
      * the converter is done by looking up the associated label from
      * the union group using the instance type. Once the converter has
      * been selected it is used to write the instance.
      * 
      * @param node this is the XML element used to write the instance
      * @param item this is the individual list entry to be serialized
      * @param label this is the label to used to acquire the converter     
      */
     private void write(OutputNode node, Object item, Label label) throws Exception {
        Converter converter = label.getConverter(context);
        Collection list = Collections.singleton(item);
  
        if(!label.isInline()) {
           String name = label.getName();
           String root = style.getElement(name);
          
           if(!node.isCommitted()) {
              node.setName(root);
           }
        }
        converter.write(node, list);    
     }
  }
  