/*
 * Copyright (c) 2006-2008, Dennis M. Sosnoski All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 * disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 * following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of
 * JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.jibx.schema.codegen;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.InfixExpression.Operator;
import org.jibx.binding.model.BindingHolder;
import org.jibx.binding.model.CollectionElement;
import org.jibx.binding.model.ContainerElementBase;
import org.jibx.binding.model.ElementBase;
import org.jibx.binding.model.FormatDefaults;
import org.jibx.binding.model.FormatElement;
import org.jibx.binding.model.MappingElement;
import org.jibx.binding.model.NestingAttributes;
import org.jibx.binding.model.PropertyAttributes;
import org.jibx.binding.model.StructureElement;
import org.jibx.binding.model.StructureElementBase;
import org.jibx.binding.model.ValueElement;
import org.jibx.binding.util.NameUtilities;
import org.jibx.runtime.QName;
import org.jibx.runtime.Utility;
import org.jibx.schema.IArity;
import org.jibx.schema.INamed;
import org.jibx.schema.SchemaUtils;
import org.jibx.schema.codegen.ClassHolder.Value;
import org.jibx.schema.elements.AnnotatedBase;
import org.jibx.schema.elements.AttributeElement;
import org.jibx.schema.elements.CommonCompositorBase;
import org.jibx.schema.elements.ElementElement;
import org.jibx.schema.elements.SchemaBase;

/**
 * Information for a data class to be included in code generated from schema.
 * 
 * @author Dennis M. Sosnoski
 */
public class StructureClassHolder extends ClassHolder
{
    /** Logger for class. */
    private static final Logger s_logger = Logger.getLogger(StructureClassHolder.class.getName());
    
    /** Default format definitions map. */
    private static final Map s_formatMap;
    static {
        s_formatMap = new HashMap();
        for (int i = 0; i < FormatDefaults.s_defaultFormats.length; i++) {
            FormatElement format = FormatDefaults.s_defaultFormats[i];
            s_formatMap.put(format.getTypeName(), format);
        }
    }
    
    /** Flag for collection present in class. */
    private boolean m_collectionPresent;
    
    /** Parent wrapper for all items included in class. */
    private Wrapper m_classGroup;
    
    /** Binding definition element for this class. */
    private ContainerElementBase m_bindingElement;
    
    /**
     * Constructor.
     * 
     * @param name class name
     * @param base base class name
     * @param pack package information
     * @param nconv name converter
     * @param inner use inner classes for substructures
     */
    public StructureClassHolder(String name, String base, PackageHolder pack, NameConverter nconv, boolean inner) {
        super(name, base, pack, nconv, inner);
    }
    
    /**
     * Constructor for creating a child inner class definition.
     * 
     * @param name class name
     * @param context parent class
     */
    private StructureClassHolder(String name, StructureClassHolder context) {
        super(name, context);
    }
    
    /**
     * Derive group names from the containing group prefix and the simple name of the group.
     * 
     * @param group
     * @param container (<code>null</code> if none)
     * @return name
     */
    // static String deriveGroupName(GroupItem group, Group container) {
    // String prefix = null;
    // if (container != null) {
    // prefix = group.getClassName();
    // String prior = container.getPrefix();
    // if (prior == null) {
    // prefix = NameConverter.toNameLead(prefix);
    // } else {
    // prefix = prior + NameConverter.toNameWord(prefix);
    // }
    // prefix = container.uniqueChildPrefix(prefix);
    // }
    // return prefix;
    // }
    
    /**
     * Add the items in a structure to the class representation. This creates a grouping for the items, adding the
     * grouping to the containing grouping. It should only be used for structures with child items.
     * 
     * @param struct
     * @param container
     */
    private void addItems(GroupItem struct, Wrapper container) {
        for (Item item = struct.getFirstChild(); item != null; item = item.getNext()) {
            if ((USE_COLLECTIONS || USE_GENERIC_TYPES) && item.isCollection()) {
                m_collectionPresent = true;
            }
            if (item instanceof GroupItem) {
                GroupItem group = (GroupItem)item;
                if (group.isInline()) {
                    
                    // create a new group for an inlined compositor only if it's <choice> or nested
                    Wrapper into = container;
                    AnnotatedBase comp = item.getSchemaComponent();
                    if (comp instanceof CommonCompositorBase) {
                        if (comp.type() == SchemaBase.CHOICE_TYPE || comp.getParent() instanceof CommonCompositorBase) {
                            into = new Wrapper(group, container);
                        }
                    } else {
                        into = new Wrapper(group, container);
                    }
                    addItems(group, into);
                    into.adjustName();
                    
                } else {
                    
                    // create a new class and populate that
                    ClassHolder child;
                    String text = group.getEffectiveClassName();
                    if (m_useInnerClasses) {
                        if (m_nameSet.contains(text)) {
                            StructureClassHolder outer = this;
                            while (outer != null) {
                                if (outer.getName().equals(text)) {
                                    text += "Inner";
                                    break;
                                } else {
                                    outer = (StructureClassHolder)outer.m_outerClass;
                                }
                            }
                        }
                        text = m_nameSet.add(text);
                        child = group.isEnumeration() ? new EnumerationClassHolder(text, this)
                            : new StructureClassHolder(text, this);
                        m_inners.add(child);
                        if (s_logger.isDebugEnabled()) {
                            s_logger.debug("Added inner class " + child.getFullName());
                        }
                    } else {
                        String fullname = m_baseName + text;
                        child = m_package.addClass(fullname, m_baseName, m_nameConverter, group.isEnumeration());
                        addImport(child.getFullName(), true);
                        text = child.getName();
                        if (s_logger.isDebugEnabled()) {
                            s_logger.debug("Added derived class " + child.getFullName());
                        }
                    }
                    group.setClassName(text);
                    group.setGenerateClass(child);
                    Value value = new Value(item, container);
                    if (!group.isEnumeration()) {
                        group.convertExtensionReference();
                        importValueType(value);
                    }
                    child.createStructure(group);
                    
                }
                
            } else {
                importValueType(new Value(item, container));
            }
        }
    }
    
    /**
     * Get the binding component linked to this class.
     * 
     * @return binding definition element (&lt;mapping> or &lt;structure>)
     */
    public ContainerElementBase getBinding() {
        return m_bindingElement;
    }
    
    /**
     * Set the binding component linked to this class.
     * 
     * @param container binding definition element (&lt;mapping> or &lt;structure>)
     */
    public void setBinding(ContainerElementBase container) {
        m_bindingElement = container;
    }
    
    /**
     * Recursively add all inner enumeration classes as formats to a &lt;mapping> definition. This is used to create the
     * &lt;format> elements for all nested enumerations, which need to be direct children of the &lt;mapping> element
     * for the top-level class.
     * 
     * @param mapping
     */
    private void addInnerFormats(MappingElement mapping) {
        for (int i = 0; i < m_inners.size(); i++) {
            ClassHolder inner = (ClassHolder)m_inners.get(i);
            if (inner instanceof EnumerationClassHolder) {
                FormatElement format = new FormatElement();
                format.setTypeName(inner.getBindingName());
                ((EnumerationClassHolder)inner).setBinding(format);
                mapping.addTopChild(format);
            } else {
                ((StructureClassHolder)inner).addInnerFormats(mapping);
            }
        }
    }
    
    /**
     * Convert an item structure to a class representation. This may include creating child classes, where necessary.
     * 
     * @param group
     */
    public void createStructure(GroupItem group) {
        if (group.isEnumeration()) {
            throw new IllegalArgumentException("Internal error - group is an enumeration");
        } else {
            
            // set the basic configuration information
            setNamespace(group.getSchemaComponent().getSchema().getEffectiveNamespace());
            m_classGroup = new Wrapper(group, null);
            
            // populate the actual definition structure
            addItems(group, m_classGroup);
            
            // import the list type if needed
            if (m_collectionPresent) {
                addImport(COLLECTION_VARIABLE_TYPE, false);
            }
            
            // import the serializable interface if needed
            if (IMPLEMENT_SERIALIZABLE) {
                addImport("java.io.Serializable", false);
            }
        }
    }
    
    /**
     * Add all fixed names in a group to the set of names defined for this class. This calls itself recursively to
     * handle nested groups.
     * 
     * @param wrapper
     */
    private void addFixedNames(Wrapper wrapper) {
        ArrayList values = wrapper.getValues();
        for (int i = 0; i < values.size(); i++) {
            Value value = (Value)values.get(i);
            Item item = value.getItem();
            boolean addname = item.isFixedName();
            if (value instanceof Wrapper) {
                Wrapper childgrp = (Wrapper)value;
                addFixedNames(childgrp);
                addname = addname && (childgrp.isSelectorNeeded() || wrapper.isSelectorNeeded());
            }
            if (addname) {
                String name = item.getEffectiveName();
                if (!m_nameSet.add(name).equals(name)) {
                    // TODO: pass in the validation context, create an error
                    throw new IllegalStateException("Name '" + name + "' cannot be used twice in same context");
                }
            }
        }
    }
    
    /**
     * Handle value name assignments for a group within this class. This calls itself recursively to handle nested
     * groups. TODO: set up use-specific linked name sets, so that the same name can be used in different ways without
     * conflict
     * 
     * @param wrapper
     * @param innamed flag for parent group name already fixed
     */
    private void fixFlexibleNames(Wrapper wrapper, boolean innamed) {
        
        // check for group which uses a selector (choice or union)
        String suffix = null;
        ArrayList values = wrapper.getValues();
        if (wrapper.isSelectorNeeded()) {
            
            // add the actual variable name used to record current state
            Item item = wrapper.getItem();
            item.setName(m_nameSet.add(m_nameConverter.toBaseName(item.getEffectiveName()) + "Select"));
            
            // generate constant for each child value
            if (wrapper.getSchemaComponent().type() == SchemaBase.UNION_TYPE) {
                suffix = "_Form";
            } else {
                suffix = "_Choice";
            }
            for (int i = 0; i < values.size(); i++) {
                Value value = (Value)values.get(i);
                if (!(value instanceof Wrapper)) {
                    String name = m_nameConverter.toConstantName(value.getItem().getEffectiveName() + suffix);
                    value.setSelectValue(m_nameSet.add(name));
                }
            }
            
        }
        
        // handle name conversions and recording
        for (int i = 0; i < values.size(); i++) {
            Value value = (Value)values.get(i);
            Item item = value.getItem();
            if (value instanceof Wrapper) {
                
                // use recursive call to set child group names (adopting name for group if same as first child, for
                // group inside choice, in order to avoid adding the same name twice for non-conflicting usages)
                boolean adoptname = false;
                ArrayList childvals = ((Wrapper)value).getValues();
                if (childvals.size() > 0) {
                    Item firstchlditem = ((Value)childvals.get(0)).getItem();
                    String compname = firstchlditem.getEffectiveName();
                    String currname = item.getEffectiveName();
                    if (wrapper.isSelectorNeeded() && Utility.safeEquals(compname, currname)) {
                        adoptname = true;
                    }
                }
                boolean passname = item.isFixedName() || (innamed && item.getName() == null);
                fixFlexibleNames((Wrapper)value, passname);
                if (adoptname) {
                    item.setName(((Value)childvals.get(0)).getItem().getEffectiveName());
                }
                if (wrapper.isSelectorNeeded()) {
                    String name = m_nameConverter.toConstantName(item.getEffectiveName() + suffix);
                    value.setSelectValue(adoptname ? name : m_nameSet.add(name));
                }
                
            } else if (!item.isFixedName()) {
                
                // just use inherited value name directly if already added
                String name = item.getEffectiveName();
                if (item.getName() == null && innamed) {
                    item.setName(name);
                } else {
                    
                    // convert and add the value name
                    if (value.isCollection()) {
                        String singular = NameUtilities.depluralize(name);
                        if (!singular.equals(name)) {
                            s_logger.debug("Converted name " + name + " to " + singular);
                        }
                        name = singular;
                    }
                    if (NameConverter.isReserved(name)) {
                        name = name + '_';
                    }
                    item.setName(m_nameSet.add(name));
                }
                
            }
        }
    }
    
    /**
     * Generate the code to check and set the selection on any containing selector group. This should be used when
     * setting any value, including inside selector methods (if used), since selector groups may be nested.
     * 
     * @param selectcall
     * @param value
     * @param block
     * @param builder
     */
    private void generateSelectorSet(boolean selectcall, Value value, BlockBuilder block, ClassBuilder builder) {
        Wrapper group;
        while ((group = value.getWrapper()) != null) {
            if (group.isSelectorNeeded()) {
                if (selectcall) {
                    
                    // when using select method call, just call that method (it will call containing group method, if
                    // any)
                    InvocationBuilder call = builder.createMemberMethodCall(group.getSelectMethod());
                    call.addVariableOperand(value.getSelectValue());
                    block.addCall(call);
                    break;
                    
                } else {
                    
                    // if setting directly, set this one and continue up to next containing group
                    block.addAssignVariableToField(value.getSelectValue(), group.getSelectField());
                    
                }
            }
            value = group;
        }
    }
    
    /**
     * Generate a test method for a value, if it's part of a group with a selector.
     * 
     * @param value
     * @param propname
     * @param builder
     */
    private void checkIfMethod(Value value, String propname, ClassBuilder builder) {
        if (value.getWrapper().isSelectorNeeded()) {
            
            // add the if method definition for selector group
            MethodBuilder ifmeth = builder.addMethod("if" + propname, "boolean");
            ifmeth.setPublic();
            InfixExpressionBuilder testexpr = builder.buildNameOp(value.getWrapper().getSelectField(), Operator.EQUALS);
            testexpr.addVariableOperand(value.getSelectValue());
            ifmeth.createBlock().addReturnExpression(testexpr);
        }
    }
    
    /**
     * Set the attributes for a &lt;structure> or &lt;collection> element to represent a wrapped group of values in the
     * binding.
     * 
     * @param value
     * @param struct
     */
    private void setStructureAttributes(Value value, StructureElementBase struct) {
        struct.setFieldName(value.getFieldName());
        if (value.isOptional()) {
            struct.setUsage(PropertyAttributes.OPTIONAL_USAGE);
        }
    }

    /**
     * Set the name for a &lt;structure> or &lt;collection> element based on the schema component associated with a
     * value.
     *
     * @param value
     * @param struct
     */
    private void setStructureName(Value value, StructureElementBase struct) {
        AnnotatedBase comp = findNamedComponent(value);
        if (comp != null) {
            struct.setName(((INamed)comp).getName());
        }
    }
    
    /**
     * Find the component supplying the name to be used for a value. If the value doesn't define an element or attribute
     * name directly, this searches up through any containing wrappers with no other children until a named component is
     * found. If that name has not already been represented in the binding this returns that name. Whether obtained
     * directly or indirectly, the returned name is flagged as used so that it won't be used again.
     * 
     * @param value
     * @return component supply name, or <code>null</code> if none
     */
    private AnnotatedBase findNamedComponent(Value value) {
        
        // loop to find wrapper with name and no other children
        while (!value.isNamed()) {
            Wrapper wrapper = value.getWrapper();
            if (wrapper != null && wrapper.getValues().size() == 1) {
                value = wrapper;
            } else {
                return null;
            }
        }
        
        // check for name used, either directly or at ancestor level with same component
        AnnotatedBase comp = value.getSchemaComponent();
        Value match = value;
        while (match != null && match.getSchemaComponent() == comp) {
            if (match.isNameUsed()) {
                return null;
            } else {
                match.setNameUsed(true);
                match = match.getWrapper();
            }
        }
        return comp;
    }
    
    /**
     * Build a &lt;value> binding component for a field.
     * 
     * @param value
     * @param propname
     * @return constructed binding component
     */
    private ValueElement buildValueBinding(Value value, String propname) {
        
        // add <value> element to binding structure for simple (primitive or text) value
        ValueElement element = new ValueElement();
        element.setFieldName(value.getFieldName());
        
        // set test method if needed to pick between alternatives
        Wrapper wrapper = value.getWrapper();
        if (wrapper.isSelectorNeeded()) {
            element.setTestName("if" + propname);
            element.setUsage(PropertyAttributes.OPTIONAL_USAGE);
        } else if (value.isOptional()) {
            element.setUsage(PropertyAttributes.OPTIONAL_USAGE);
        }
        
        // get the schema component supplying an element or attribute name
        AnnotatedBase comp = findNamedComponent(value);
        if (comp == null) {
            element.setEffectiveStyle(ValueElement.TEXT_STYLE);
        } else {
            if (comp.type() == SchemaBase.ELEMENT_TYPE) {
                
                // value is an element, set the name directly
                element.setEffectiveStyle(NestingAttributes.ELEMENT_STYLE);
                ElementElement elem = (ElementElement)comp;
                element.setName(elem.getName());
                if (SchemaUtils.isOptionalElement(elem)) {
                    // TODO: this is needed because the optional status doesn't inherit downward for embedded items, as when
                    //  a simpleType is nested inside an optional attribute. should the code be changed to inherit instead?
                    element.setUsage(PropertyAttributes.OPTIONAL_USAGE);
                }
                
                // check for embedded inside a <choice>
                if (wrapper.isSelectorNeeded()) {
                    element.setUsage(PropertyAttributes.OPTIONAL_USAGE);
                    element.setTestName("if" + propname);
                }
                
            } else if (comp.type() == SchemaBase.ATTRIBUTE_TYPE) {
                
                // value is an attribute, set the name directly
                element.setEffectiveStyle(NestingAttributes.ATTRIBUTE_STYLE);
                AttributeElement attr = (AttributeElement)comp;
                element.setName(attr.getName());
                if (SchemaUtils.isOptionalAttribute(attr)) {
                    // TODO: this is needed because the optional status doesn't inherit downward for embedded items, as when
                    //  a simpleType is nested inside an optional attribute. should the code be changed to inherit instead?
                    element.setUsage(PropertyAttributes.OPTIONAL_USAGE);
                }
                
            } else {
                throw new IllegalStateException("Internal error - expected wrapper element or attribute not found");
            }
        }
        return element;
    }
    
    /**
     * Generate the fields and methods for a group, and add to binding. This calls itself recursively to handle nested
     * groups.
     * 
     * @param wrapper
     * @param builder
     * @param binding
     * @param holder
     */
    private void buildDataModel(Wrapper wrapper, ClassBuilder builder, ContainerElementBase binding,
        BindingHolder holder) {
        
        // first check if group requires a selector field
        ArrayList values = wrapper.getValues();
        Item grpitem = wrapper.getItem();
        if (wrapper.isSelectorNeeded()) {
            
            // build the selector field
            String basename = grpitem.getEffectiveName();
            String fieldname = m_nameConverter.toFieldName(basename);
            wrapper.setSelectField(fieldname);
            builder.addIntField(fieldname, "-1").setPrivate();
            
            // create constants for each alternative value
            String descript;
            if (wrapper.getSchemaComponent().type() == SchemaBase.UNION_TYPE) {
                descript = "form";
            } else {
                descript = "choice";
            }
            for (int i = 0; i < values.size(); i++) {
                Value value = (Value)values.get(i);
                builder.addIntField(value.getSelectValue(), Integer.toString(i)).setPrivateFinal();
            }
            
            // add selector set method
            String namesuffix = NameConverter.toNameWord(basename);
            String resetname = "clear" + namesuffix;
            String selectname = "set" + namesuffix;
            wrapper.setSelectMethod(selectname);
            MethodBuilder setmeth = builder.addMethod(selectname, "void");
            setmeth.setPrivate();
            BlockBuilder block = setmeth.createBlock();
            setmeth.addParameter(descript, "int");
            
            // start by setting any containing selectors
            generateSelectorSet(USE_SELECTION_SET_METHODS, wrapper, block, builder);
            
            // create the set block for when there's no current choice
            BlockBuilder assignblock = builder.newBlock();
            assignblock.addAssignVariableToField(descript, fieldname);
            
            // create the exception thrown when choice does not match current setting
            BlockBuilder throwblock = builder.newBlock();
            throwblock.addThrowException("IllegalStateException", "Need to call " + resetname
                + "() before changing existing " + descript);
            
            // finish with the if statement that decides which to execute
            InfixExpressionBuilder iftest = builder.buildNameOp(fieldname, Operator.EQUALS);
            iftest.addNumberLiteralOperand("-1");
            InfixExpressionBuilder elsetest = builder.buildNameOp(fieldname, Operator.NOT_EQUALS);
            elsetest.addVariableOperand(descript);
            block.addIfElseIfStatement(iftest, elsetest, assignblock, throwblock);
            
            // add selector clear method
            MethodBuilder resetmeth = builder.addMethod(resetname, "void");
            resetmeth.setPublic();
            block = resetmeth.createBlock();
            block.addAssignToName(block.numberLiteral("-1"), fieldname);
            if (s_logger.isDebugEnabled()) {
                s_logger.debug("Created selector for grouping component "
                    + SchemaUtils.describeComponent(grpitem.getSchemaComponent()) + " in class " + getFullName());
            }
        }
        
        // generate all values in group
        for (int i = 0; i < values.size(); i++) {
            
            // get the base name to be used for item
            Value value = (Value)values.get(i);
            Item item = value.getItem();
            String basename = item.getEffectiveName();
            if (value.isCollection()) {
                basename += "List";
            }
            
            // generate test method, if inside selector group
            String propname = NameConverter.toNameWord(basename);
            checkIfMethod(value, propname, builder);
            if (value instanceof Wrapper) {
                
                // check if <structure> wrapper needed for name
                Wrapper childwrap = (Wrapper)value;
                if (childwrap.isNamed() && (item.isElementPresent() || item.isAttributePresent())) {
                    
                    // create named <structure> or <collection> wrapper and add wrapped values to that
                    StructureElementBase struct;
                    StructureElementBase inner;
                    if (childwrap.isCollectionWrapper()) {
                        if (binding.type() == ElementBase.COLLECTION_ELEMENT
                            && ((CollectionElement)binding).getFieldName() == null) {
                            
                            // reuse the already-created collection element in binding
                            struct = inner = (StructureElementBase)binding;
                            
                        } else {
                            
                            // create a new collection element for the binding
                            struct = inner = new CollectionElement();
                            binding.addChild(struct);
                            String name = ((INamed)childwrap.getSchemaComponent()).getName();
                            if (name != null) {
                                
                                // use name as wrapper on collection or as embedded element name
                                // TODO: will this work for nested repeating elements?
                                if (item.isCollection()) {
                                    
                                    // name represents repeated element, use it that way
                                    inner = new StructureElement();
                                    struct.addChild(inner);
                                    setStructureName(value, struct);
                                    
                                } else {
                                    
                                    // name is a wrapper element for collection, set it that way
                                    setStructureName(value, struct);
                                    
                                }
                                
                            }
                            
                        }
                    } else {
                        struct = inner = new StructureElement();
                        setStructureName(value, struct);
                        binding.addChild(struct);
                    }
                    setStructureAttributes(value, struct);
                    if (wrapper.isSelectorNeeded()) {
                        struct.setTestName("if" + propname);
                        struct.setUsage(PropertyAttributes.OPTIONAL_USAGE);
                    }
                    if (childwrap.isSelectorNeeded()) {
                        struct.setChoice(true);
                        struct.setOrdered(false);
                    }
                    buildDataModel(childwrap, builder, inner, holder);
                    if (struct.type() == ElementBase.COLLECTION_ELEMENT && struct.getFieldName() == null) {
                        System.out.println("<collection> with no field");
                    }
                    
                } else if (childwrap.isSelectorNeeded() || wrapper.isSelectorNeeded()) {
                    
                    // create unnamed <structure> wrapper and add wrapped values to that
                    StructureElement struct = new StructureElement();
                    setStructureName(value, struct);
                    setStructureAttributes(value, struct);
                    if (childwrap.isSelectorNeeded()) {
                        struct.setChoice(true);
                        struct.setOrdered(false);
                    }
                    binding.addChild(struct);
                    buildDataModel(childwrap, builder, struct, holder);
                    
                } else {
                    
                    // just ignore this wrapper and add wrapped values directly
                    buildDataModel(childwrap, builder, binding, holder);
                }
                
            } else {
                
                // remaining code generation depends on simple or collection item
                s_logger.debug("Adding value item " + basename);
                String type = value.getType();
                if (type != null) {
                    
                    // set the names to be used for value
                    String fname = m_nameConverter.toFieldName(basename);
                    value.setFieldName(fname);
                    String getname = "get" + propname;
                    value.setGetMethodName(getname);
                    String setname = "set" + propname;
                    value.setSetMethodName(setname);
                    if (value.isCollection() || value.isList()) {
                        
                        // find the types to be used for field and actual instance
                        Type fieldtype;
                        Type gettype;
                        Type settype;
                        Type insttype;
                        if (USE_GENERIC_TYPES) {
                            fieldtype = builder.createParameterizedType(COLLECTION_VARIABLE_TYPE, type);
                            gettype = builder.createParameterizedType(COLLECTION_VARIABLE_TYPE, type);
                            settype = builder.createParameterizedType(COLLECTION_VARIABLE_TYPE, type);
                            insttype = builder.createParameterizedType(COLLECTION_INSTANCE_TYPE, type);
                        } else if (USE_COLLECTIONS) {
                            fieldtype = builder.createType(COLLECTION_VARIABLE_TYPE);
                            gettype = builder.createType(COLLECTION_VARIABLE_TYPE);
                            settype = builder.createType(COLLECTION_VARIABLE_TYPE);
                            insttype = builder.createType(COLLECTION_INSTANCE_TYPE);
                        } else {
                            fieldtype = builder.createType(type + "[]");
                            gettype = builder.createType(type + "[]");
                            settype = builder.createType(type + "[]");
                            insttype = null;
                        }
                        
                        // generate the field as a collection (uninitialized if optional)
                        FieldBuilder field = builder.addField(fname, fieldtype);
                        // if (!value.isOptional()) {
                        if (insttype != null) {
                            field.setInitializer(builder.newInstance(insttype));
                        }
                        // }
                        field.setPrivate();
                        
                        // add get method definition (unchecked, but result meaningless if not the selected group item)
                        MethodBuilder getmeth = builder.addMethod(getname, gettype);
                        getmeth.setPublic();
                        getmeth.createBlock().addReturnNamed(fname);
                        
                        // add the set method definition
                        MethodBuilder setmeth = builder.addMethod(setname, "void");
                        setmeth.setPublic();
                        setmeth.addParameter(COLLECTION_VARIABLE_NAME, settype);
                        BlockBuilder block = setmeth.createBlock();
                        generateSelectorSet(USE_SELECTION_SET_METHODS, value, block, builder);
                        block.addAssignVariableToField(COLLECTION_VARIABLE_NAME, fname);
                        
                        // process list and collection differently for binding
                        CollectionElement collect = null;
                        if (value.isCollection()) {
                            
                            // create a new <collection> unless inside a wrapper element
                            if (binding.type() == ElementBase.COLLECTION_ELEMENT) {
                                if (((CollectionElement)binding).getField() == null) {
                                    collect = (CollectionElement)binding;
                                }
                            }
                            if (collect == null) {
                                collect = new CollectionElement();
                                binding.addChild(collect);
                            }
                            
                            // fill in the collection details
                            collect.setFieldName(fname);
                            if (wrapper.isSelectorNeeded()) {
                                collect.setUsage(PropertyAttributes.OPTIONAL_USAGE);
                                collect.setTestName("if" + propname);
                            }
                            if (USE_COLLECTIONS) {
                                collect.setCreateType(COLLECTION_INSTANCE_TYPE);
                            }
                            
                            // check the content (if any) for <collection>
                            boolean usevalue = true;
                            String usetype = type;
                            if (item instanceof ReferenceItem) {
                                DefinitionItem definition = ((ReferenceItem)item).getDefinition();
                                ClassHolder defclas = definition.getGenerateClass();
                                if (defclas instanceof StructureClassHolder) {
                                    
                                    // reference to mapped class, configure <collection> to handle it properly
                                    usevalue = false;
                                    if (definition.getSchemaComponent().type() == SchemaBase.ELEMENT_TYPE) {
                                        
                                        // must be a non-abstract <mapping>, so use it directly
                                        collect.setItemTypeName(defclas.getBindingName());
                                        
                                    } else {
                                        
                                        // abstract mapping reference, create child <structure> with map-as type
                                        StructureElement struct = new StructureElement();
                                        INamed named = (INamed)definition.getSchemaComponent();
                                        QName qname = named.getQName();
                                        holder.addDependency(qname.getUri());
                                        struct.setMapAsQName(qname);
                                        AnnotatedBase comp = findNamedComponent(value);
                                        if (comp != null) {
                                            struct.setName(((INamed)comp).getName());
                                        }
                                        collect.addChild(struct);
                                        
                                    }
                                    
                                } else {
                                    usetype = defclas.getBindingName();
                                }
                                
                            } else if (item instanceof GroupItem) {
                                
                                // handle group directly if a structure class, else just as <value>
                                ClassHolder groupclas = ((GroupItem)item).getGenerateClass();
                                if (groupclas instanceof StructureClassHolder) {
                                    
                                    // add <structure> element to be filled in by inner class generation
                                    usevalue = false;
                                    StructureClassHolder classholder = ((StructureClassHolder)groupclas);
                                    StructureElement struct = new StructureElement();
                                    struct.setDeclaredType(classholder.getBindingName());
                                    
                                    // set attributes without field name (since that will be for collection)
                                    setStructureName(value, struct);
                                    setStructureAttributes(value, struct);
                                    struct.setFieldName(null);
                                    
                                    // set component for dependent class generation
                                    classholder.setBinding(struct);
                                    collect.addChild(struct);
                                    
                                } else {
                                    usetype = groupclas.getBindingName();
                                }
                                
                            }
                            if (usevalue) {
                                
                                // add <value> element to collection for simple (primitive or text) value
                                ValueElement element = new ValueElement();
                                element.setEffectiveStyle(NestingAttributes.ELEMENT_STYLE);
                                AnnotatedBase comp = findNamedComponent(value);
                                if (comp == null) {
                                    throw new IllegalStateException("Internal error - no name for value in collection");
                                } else {
                                    element.setName(((INamed)comp).getName());
                                }
                                element.setDeclaredType(usetype);
                                collect.addChild(element);
                                
                            }
                            
                        } else {
                            
                            // determine format conversion handling for type
                            String valsername = null;
                            String valdesername = null;
                            String valuename = null;
                            FormatElement format = (FormatElement)s_formatMap.get(type);
                            if (format != null) {
                                valsername = format.getSerializerName();
                                valdesername = format.getDeserializerName();
                                if (valsername == null && !"java.lang.String".equals(type)) {
                                    valuename = "toString";
                                }
                            } else if (item instanceof ReferenceItem) {
                                DefinitionItem def = ((ReferenceItem)item).getDefinition();
                                if (def.isEnumeration()) {
                                    EnumerationClassHolder genclas = (EnumerationClassHolder)def.getGenerateClass();
                                    valsername = EnumerationClassHolder.CONVERTFORCE_METHOD;
                                    valuename = genclas.getName() + '.' + EnumerationClassHolder.INSTANCEVALUE_METHOD;
                                }
                            }
                            
                            // add list serializer method to class
                            String sername = "serialize" + propname;
                            MethodBuilder sermeth = builder.addMethod(sername, "java.lang.String");
                            sermeth.addParameter("values", (Type)builder.clone(settype));
                            sermeth.setPublicStatic();
                            
                            // create a simple null return for null parameter string
                            BlockBuilder nullblock = builder.newBlock();
                            nullblock.addReturnNull();
                            
                            // create block for actual serialization when parameter non-null
                            BlockBuilder serblock = builder.newBlock();
                            NewInstanceBuilder newbuff = builder.newInstance("java.lang.StringBuffer");
                            serblock.addLocalVariableDeclaration("java.lang.StringBuffer", "buff", newbuff);
                            
                            // create body of loop to handle the conversion
                            BlockBuilder forblock = builder.newBlock();
                            if (USE_GENERIC_TYPES) {
                                forblock.addLocalVariableDeclaration(type, "value", builder.createNormalMethodCall(
                                    "iter", "next"));
                            } else if (USE_COLLECTIONS) {
                                CastBuilder castexpr = builder.buildCast(type);
                                castexpr.addOperand(builder.createNormalMethodCall("iter", "next"));
                                forblock.addLocalVariableDeclaration(type, "value", castexpr);
                            } else {
                                forblock.addLocalVariableDeclaration(type, "value", builder.buildArrayIndexAccess(
                                    "values", "index"));
                            }
                            
                            // append space to buffer unless empty
                            InfixExpressionBuilder lengthexpr = builder.buildInfix(Operator.GREATER);
                            lengthexpr.addOperand(builder.createNormalMethodCall("buff", "length"));
                            lengthexpr.addNumberLiteralOperand("0");
                            InvocationBuilder appendcall = builder.createNormalMethodCall("buff", "append");
                            appendcall.addCharacterLiteralOperand(' ');
                            BlockBuilder spaceblock = builder.newBlock();
                            spaceblock.addExpressionStatement(appendcall);
                            forblock.addIfStatement(lengthexpr, spaceblock);
                            
                            // append the current value to the buffer
                            appendcall = builder.createNormalMethodCall("buff", "append");
                            if (valuename != null) {
                                appendcall.addOperand(builder.createNormalMethodCall("value", valuename));
                            } else if (valdesername != null) {
                                InvocationBuilder desercall = builder.createStaticMethodCall(valdesername);
                                desercall.addVariableOperand("value");
                                appendcall.addOperand(desercall);
                            } else {
                                appendcall.addVariableOperand("value");
                            }
                            forblock.addExpressionStatement(appendcall);
                            
                            // build the for loop around the conversion
                            if (USE_GENERIC_TYPES) {
                                Type itertype = builder.createParameterizedType("java.util.Iterator", type);
                                serblock.addIteratedForStatement("iter", itertype, builder.createNormalMethodCall(
                                    "values", "iterator"), forblock);
                            } else if (USE_COLLECTIONS) {
                                serblock.addIteratedForStatement("iter", builder.createType("java.util.Iterator"),
                                    builder.createNormalMethodCall("values", "iterator"), forblock);
                            } else {
                                serblock.addIndexedForStatement("iter", builder.createNormalMethodCall("values",
                                    "iterator"), forblock);
                            }
                            
                            // finish non-null serialization block with buffer conversion
                            serblock.addReturnExpression(builder.createNormalMethodCall("buff", "toString"));
                            
                            // finish with the if statement that decides which to execute
                            InfixExpressionBuilder iftest = builder.buildNameOp("values", Operator.EQUALS);
                            iftest.addNullOperand();
                            sermeth.createBlock().addIfElseStatement(iftest, nullblock, serblock);
                            
                            // add list deserializer method to class
                            String desername = "deserialize" + propname;
                            MethodBuilder desermeth = builder.addMethod(desername, (Type)builder.clone(gettype));
                            desermeth.addParameter("text", "java.lang.String");
                            desermeth.setPublicStatic();
                            desermeth.addThrows("org.jibx.runtime.JiBXException");
                            block = desermeth.createBlock();
                            
                            // build instance creation for anonymous inner class to handle deserialization
                            NewInstanceBuilder newinst = builder.newInstance("org.jibx.runtime.IListItemDeserializer");
                            ClassBuilder anonclas = newinst.addAnonymousInnerClass();
                            MethodBuilder innermeth = anonclas.addMethod("deserialize", "java.lang.Object");
                            innermeth.addParameter("text", "java.lang.String");
                            innermeth.setPublic();
                            BlockBuilder innerblock = innermeth.createBlock();
                            if (valdesername == null) {
                                innerblock.addReturnNamed("text");
                            } else {
                                InvocationBuilder desercall = builder.createLocalStaticMethodCall(valdesername);
                                desercall.addVariableOperand("text");
                                innerblock.addReturnExpression(desercall);
                            }
                            block.addLocalVariableDeclaration("org.jibx.runtime.IListItemDeserializer", "ldser",
                                newinst);
                            
                            // build call using anonymous inner class to deserialize to untyped collection
                            InvocationBuilder desercall = builder
                                .createStaticMethodCall("org.jibx.runtime.Utility.deserializeList");
                            desercall.addVariableOperand("text");
                            desercall.addVariableOperand("ldser");
                            
                            // handle the return as appropriate
                            if (USE_GENERIC_TYPES) {
                                CastBuilder castexpr = builder.buildCast((Type)builder.clone(gettype));
                                castexpr.addOperand(desercall);
                                block.addReturnExpression(castexpr);
                            } else if (USE_COLLECTIONS) {
                                block.addReturnExpression(desercall);
                            } else {
                                
                                // save deserialization result list to local variable
                                block.addLocalVariableDeclaration("java.util.List", "list", desercall);
                                
                                // create null return block
                                BlockBuilder ifnull = builder.newBlock();
                                ifnull.addReturnNull();
                                
                                // create non-null return with conversion to array
                                BlockBuilder ifnonnull = builder.newBlock();
                                InvocationBuilder toarraycall = builder.createNormalMethodCall("list", "toArray");
                                NewArrayBuilder newarray = builder.newArrayBuilder(type);
                                newarray.addOperand(builder.createNormalMethodCall("list", "size"));
                                toarraycall.addOperand(newarray);
                                CastBuilder castexpr = builder.buildCast((Type)builder.clone(fieldtype));
                                castexpr.addOperand(toarraycall);
                                ifnonnull.addReturnExpression(castexpr);
                                
                                // finish with the if statement that decides which to execute
                                iftest = builder.buildNameOp("list", Operator.EQUALS);
                                iftest.addNullOperand();
                                block.addIfElseStatement(iftest, ifnull, ifnonnull);
                            }
                            
                            // handle list serialization and deserialization directly
                            ValueElement valbind = buildValueBinding(value, propname);
                            valbind.setSerializerName(getBindingName() + '.' + sername);
                            valbind.setDeserializerName(getBindingName() + '.' + desername);
                            binding.addChild(valbind);
                            
                        }
                        if (EXTRA_COLLECTION_METHODS && (USE_GENERIC_TYPES || USE_COLLECTIONS)) {
                            
                            // add size method
                            String sizename = "size" + propname;
                            MethodBuilder sizemeth = builder.addMethod(sizename, "int");
                            sizemeth.setPublic();
                            InvocationBuilder expr = builder.createNormalMethodCall(fname, "size");
                            sizemeth.createBlock().addReturnExpression(expr);
                            
                            // add typed indexed get method (AKA binding load-method)
                            String itemname = item.getEffectiveName();
                            String itemprop = NameConverter.toNameWord(itemname);
                            String loadname = "get" + itemprop;
                            MethodBuilder getitem = builder.addMethod(loadname, type);
                            getitem.setPublic();
                            getitem.addParameter("index", "int");
                            expr = builder.createNormalMethodCall(fname, "get");
                            expr.addVariableOperand("index");
                            if (USE_GENERIC_TYPES) {
                                getitem.createBlock().addReturnExpression(expr);
                            } else {
                                CastBuilder cast = builder.buildCast(type);
                                cast.addOperand(expr);
                                getitem.createBlock().addReturnExpression(cast);
                            }
                            
                            // add typed add method
                            String addname = "add" + itemprop;
                            MethodBuilder additem = builder.addMethod(addname, "void");
                            additem.setPublic();
                            additem.addParameter("item", type);
                            block = additem.createBlock();
                            expr = builder.createNormalMethodCall(fname, "add");
                            expr.addVariableOperand("item");
                            block.addExpressionStatement(expr);
                            
                            // add methods to binding description, if used (only if not using fields directly)
                            // if (collect != null) {
                            // collect.setSizeMethodName(sizename);
                            // collect.setLoadMethodName(loadname);
                            // collect.setAddMethodName(addname);
                            // }
                        }
                        
                    } else {
                        
                        // generate the field as a simple value
                        FieldBuilder field = builder.addField(fname, type);
                        field.setPrivate();
                        
                        // add get method definition (unchecked, but result meaningless if not the selected group item)
                        MethodBuilder getmeth = builder.addMethod(getname, type);
                        getmeth.setPublic();
                        getmeth.createBlock().addReturnNamed(fname);
                        
                        // add the set method definition
                        MethodBuilder setmeth = builder.addMethod(setname, "void");
                        setmeth.setPublic();
                        setmeth.addParameter(basename, type);
                        BlockBuilder block = setmeth.createBlock();
                        generateSelectorSet(USE_SELECTION_SET_METHODS, value, block, builder);
                        block.addAssignVariableToField(basename, fname);
                        
                        // build the appropriate binding representation
                        StructureElement struct = null;
                        if (item instanceof ReferenceItem) {
                            
                            // handle reference directly if a structure class, else just as value
                            DefinitionItem def = ((ReferenceItem)item).getDefinition();
                            ClassHolder defclas = def.getGenerateClass();
                            if (defclas instanceof StructureClassHolder) {
                                
                                // add <structure> element for field, with type name if needed
                                struct = new StructureElement();
                                if (def.getSchemaComponent().type() != SchemaBase.ELEMENT_TYPE) {
                                    QName qname = ((INamed)def.getSchemaComponent()).getQName();
                                    holder.addDependency(qname.getUri());
                                    struct.setMapAsQName(qname);
                                }
                                
                                // fill in structure attributes
                                setStructureName(value, struct);
                                setStructureAttributes(value, struct);
                                
/*                                // special case: delete the name if already set on containing binding component
                                if (struct.getName() != null) {
                                    boolean duplname = false;
                                    if (binding instanceof StructureElement) {
                                        duplname = struct.getName().equals(((StructureElement)binding).getName());
                                    } else if (binding instanceof MappingElement) {
                                        duplname = struct.getName().equals(((MappingElement)binding).getName());
                                    }
                                    if (duplname) {
                                        
                                        // same name reused, check if named wrapper relate to same schema component
                                        Wrapper ancestor = wrapper;
                                        while (!ancestor.isNamed() && ancestor.getValues().size() == 1) {
                                            ancestor = ancestor.getWrapper();
                                        }
                                        if (ancestor.getSchemaComponent() == item.getSchemaComponent()) {
                                            
                                            // same name for same schema component, wipe it from this <structure>
                                            struct.setName(null);
                                            
                                        }
                                    }
                                }
*/                                
                            }
                            
                        } else if (item instanceof GroupItem) {
                            
                            // handle group directly if a structure class, else just as value
                            ClassHolder groupclas = ((GroupItem)item).getGenerateClass();
                            if (groupclas instanceof StructureClassHolder) {
                                
                                // add <structure> element to be filled in by inner class generation
                                struct = new StructureElement();
                                setStructureName(value, struct);
                                setStructureAttributes(value, struct);
                                ((StructureClassHolder)groupclas).setBinding(struct);
                                
                            }
                            
                        }
                        if (struct == null) {
                            
                            // add simple <value> binding for field
                            binding.addChild(buildValueBinding(value, propname));
                            
                        } else {
                            
                            // common <choice> handling for structure
                            if (wrapper.isSelectorNeeded()) {
                                struct.setUsage(PropertyAttributes.OPTIONAL_USAGE);
                                struct.setTestName("if" + propname);
                            }
                            
                            // add to containing binding component
                            binding.addChild(struct);
                            
                        }
                    }
                }
            }
        }
        
        // finish with check that names have been used
        AnnotatedBase comp = findNamedComponent(wrapper);
        if (comp != null) {
            throw new IllegalStateException("Internal error - name '" + ((INamed)comp).getName() + "' not used in binding");
        }
    }
    
    /**
     * Generate this class.
     * 
     * @param builder class source file builder
     * @param holder binding holder
     */
    public void generate(SourceBuilder builder, BindingHolder holder) {
        
        // setup the class builder
        String basename = getSuperClass() == null ? null : getSuperClass().getFullName();
        ClassBuilder clasbuilder;
        String name = getName();
        if (m_outerClass == null) {
            clasbuilder = builder.newMainClass(name, basename, false);
        } else {
            clasbuilder = builder.newInnerClass(name, basename, m_outerClass.getBuilder(), false);
        }
        
        // handle the common initialization
        setBuilder(clasbuilder);
        initClass(m_classGroup, holder);
        m_classGroup.setNameUsed(true);
        
        // add nested <format> definitions to <mapping>
        if (m_bindingElement instanceof MappingElement) {
            addInnerFormats((MappingElement)m_bindingElement);
        }
        
        // fix all the value names
        String fullname = getFullName();
        if (s_logger.isInfoEnabled()) {
            s_logger.info("Generating class " + fullname + ":\n" + m_classGroup.describe(0));
        }
        addFixedNames(m_classGroup);
        fixFlexibleNames(m_classGroup, false);
        if (s_logger.isInfoEnabled()) {
            s_logger.info("Class " + fullname + " after names fixed:\n" + m_classGroup.describe(0));
        }
        
        // check for superclass handling needed
        if (basename != null) {
            StructureElement struct = new StructureElement();
            AnnotatedBase comp = ((StructureClassHolder)getSuperClass()).m_classGroup.getSchemaComponent();
            QName qname = ((INamed)comp).getQName();
            holder.addDependency(qname.getUri());
            struct.setMapAsQName(qname);
            m_bindingElement.addChild(struct);
        }
        buildDataModel(m_classGroup, clasbuilder, m_bindingElement, holder);
        
        // generate the binding code
        // m_classBuilder.addInterface("org.jibx.v2.MappedStructure");
        
        // finish with subclass generation (which might be needed, if enumeration uses inlined type)
        generateInner(builder, holder);
        
        /*
         * // add the binding code for top-level classes if (m_generateCategory == TYPE_CLASS) {
         * m_classBuilder.addInterface(TYPE_INTERFACE); FieldBuilder field = m_classBuilder.addField(TYPE_NAME_VARIABLE,
         * QNAME_TYPE); field.setPrivateStaticFinal(); QName qname = ((INamed)comp).getQName();
         * field.setInitializer(m_classBuilder.newInstanceFromStrings(QNAME_TYPE, qname.getUri(), qname.getName()));
         * MethodBuilder namemeth = m_classBuilder.addMethod(TYPE_NAME_METHOD, QNAME_TYPE); namemeth.setPublic();
         * BlockBuilder block = namemeth.createBlock(); block.addReturnNamed(TYPE_NAME_VARIABLE); } else if
         * (m_generateCategory == ELEMENT_CLASS) { m_classBuilder.addInterface(ELEMENT_INTERFACE); FieldBuilder field =
         * m_classBuilder.addField(ELEMENT_NAME_VARIABLE, QNAME_TYPE); field.setPrivateStaticFinal(); QName qname =
         * ((INamed)comp).getQName(); field.setInitializer(m_classBuilder.newInstanceFromStrings(QNAME_TYPE,
         * qname.getUri(), qname.getName())); MethodBuilder namemeth = m_classBuilder.addMethod(ELEMENT_NAME_METHOD,
         * QNAME_TYPE); namemeth.setPublic(); BlockBuilder block = namemeth.createBlock();
         * block.addReturnNamed(ELEMENT_NAME_VARIABLE); } else if (m_generateCategory == STRUCTURE_CLASS) {
         * m_classBuilder.addInterface(STRUCTURE_INTERFACE); } if (m_generateCategory != NONBOUND_CLASS) {
         * InfixExpressionBuilder ifexpr = m_generateCategory == TYPE_CLASS ? null :
         * m_classBuilder.buildInfix(Operator.OR); if (m_generateCategory != TYPE_CLASS) { // how to handle if present
         * unmarshalling method? the logic will look like: // if (rdr.isAt("a") || rdr.isAt("b") || ...) { // if (inst ==
         * null || inst.getClass().getName() != "...") { // inst = new ...(); // } // inst._unmarshal(rdr); // } else { //
         * inst = null; // } // return inst; // so just pass an expression builder? } MethodBuilder marmeth =
         * m_classBuilder.addMethod(MARSHAL_METHOD, "void"); marmeth.setPublic(); marmeth.addParameter(WRITER_VARNAME,
         * WRITER_TYPE); MethodBuilder umarmeth = m_classBuilder.addMethod(UNMARSHAL_METHOD, "void");
         * umarmeth.setPublic(); umarmeth.addParameter(READER_VARNAME, READER_TYPE); // bindGroup(m_classGroup,
         * marmeth.createBlock(), ifexpr, umarmeth.createBlock()); }
         */
    }
}