/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.apache.axis2.schema;

import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * This class is used as a holder to pass on the meta information to the bean writer.
 * This meta information is used by the writer to write the databinding conversion code.
 * Note - Metainfholders are not meant to be reused!!!. They are per-class basis and are strictly
 * not thread safe!!!!
 */
public class BeanWriterMetaInfoHolder {


    protected boolean ordered = false;
    protected boolean anonymous = false;
    protected boolean choice = false;
    protected boolean simple = false;

    protected boolean extension = false;
    protected boolean restriction = false;
    private String extensionClassName = "";
    private String restrictionClassName = "";
    private QName extensionBaseType = null;
    private QName restrictionBaseType = null;
    protected Map<QName,QName> elementToSchemaQNameMap = new LinkedHashMap<QName,QName>();
    protected Map<QName,String> elementToJavaClassMap = new LinkedHashMap<QName,String>();
    protected Map<QName,Integer> specialTypeFlagMap = new LinkedHashMap<QName,Integer>();
    protected Map<QName,Long> qNameMaxOccursCountMap = new LinkedHashMap<QName,Long>();
    protected Map<QName,Long> qNameMinOccursCountMap = new LinkedHashMap<QName,Long>();
    protected Map<Integer,QName> qNameOrderMap = new LinkedHashMap<Integer,QName>();
    protected QName ownQname = null;
    protected String ownClassName = null;

    protected long lengthFacet = -1;
    protected long maxLengthFacet = -1;
    protected long minLengthFacet = -1;
    protected ArrayList<String> enumFacet = new ArrayList<String>();
    protected String patternFacet = null;
    protected String totalDigitsFacet = null;
    protected String maxExclusiveFacet = null;
    protected String minExclusiveFacet = null;
    protected String maxInclusiveFacet = null;
    protected String minInclusiveFacet = null;

    protected Map<QName,String> memberTypes = new HashMap<QName,String>();
    protected Map<String,String> xmlNameJavaNameMap = new HashMap<String,String>();
    // TODO: why do we need this (instead of using memberTypes.keySet()?
    protected List<QName> memberTypesKeys = new ArrayList<QName>();

    protected Map<QName,String> elementQNameToDefulatValueMap = new HashMap<QName,String>();

    protected QName itemTypeQName;
    protected String itemTypeClassName;
    protected boolean isUnion;
    protected boolean isList;

    protected boolean isParticleClass;
    // keep whether this class has a partical class type variable
    protected boolean hasParticleType;

    protected List<QName> nillableQNameList = new ArrayList<QName>();
    protected List<QName> fixedQNameList = new ArrayList<QName>();

    //the parent metainfo holder, useful in handling extensions and
    //restrictions
    protected BeanWriterMetaInfoHolder parent = null;

    public boolean isChoice() {
        return choice;
    }

    public void setChoice(boolean choice) {
        this.choice = choice;
    }

    public boolean isSimple() {
        return simple;
    }

    public void setSimple(boolean simple) {
        this.simple = simple;
    }

    public String getOwnClassName() {
        return ownClassName;
    }

    public void setOwnClassName(String ownClassName) {
        this.ownClassName = ownClassName;
    }

    public QName getOwnQname() {
        return ownQname;
    }

    public void setOwnQname(QName ownQname) {
        this.ownQname = ownQname;
    }

    /**
     * Gets the parent
     */
    public BeanWriterMetaInfoHolder getParent() {
        return parent;
    }

    /**
     * Gets the anonymous status.
     *
     * @return Returns boolean.
     */
    public boolean isAnonymous() {
        return anonymous;
    }

    /**
     * Sets the anonymous flag.
     *
     * @param anonymous
     */
    public void setAnonymous(boolean anonymous) {
        this.anonymous = anonymous;
    }

    /**
     * Sets the extensions base class name. Valid only when the isExtension
     * returns true.
     *
     * @return Returns String.
     */
    public String getExtensionClassName() {
        return extensionClassName;
    }

    /**
     * Sets the extensions base class name. Valid only when the isExtension
     * returns true.
     *
     * @param extensionClassName
     */
    public void setExtensionClassName(String extensionClassName) {
        this.extensionClassName = extensionClassName;
    }

    /**
     * Gets the extension status.
     *
     * @return Returns boolean.
     */
    public boolean isExtension() {
        return extension;
    }


    /**
     * Sets the extension status.
     *
     * @param extension
     */
    public void setExtension(boolean extension) {
        this.extension = extension;
    }

    public String getRestrictionClassName() {
        return restrictionClassName;
    }

    /**
     * Sets the restriction base class name. Valid only when the isRestriction
     * returns true.
     *
     * @param restrictionClassName
     */
    public void setRestrictionClassName(String restrictionClassName) {
        this.restrictionClassName = restrictionClassName;
    }

    /**
     * Gets the restriction status.
     *
     * @return Returns boolean.
     */
    public boolean isRestriction() {
        return restriction;
    }

    /**
     * Sets the restriction status.
     *
     * @param restriction
     */
    public void setRestriction(boolean restriction) {
        this.restriction = restriction;
    }

    /**
     * Sets the extension basetype.
     *
     * @param extensionBaseType
     */
    public void setExtensionBaseType(QName extensionBaseType) {
        this.extensionBaseType = extensionBaseType;
    }

    /**
     * Checks if it is a extension base type.
     *
     * @param extensionBaseType
     */
    public boolean isExtensionBaseType(QName extensionBaseType) {
        return (this.extensionBaseType == extensionBaseType);
    }

    /**
     * Sets the restriction basetype.
     *
     * @param restrictionBaseType
     */
    public void setRestrictionBaseType(QName restrictionBaseType) {
        this.restrictionBaseType = restrictionBaseType;
    }

    /**
     * Checks if it is a restriction base type.
     *
     * @param restrictionBaseType
     */
    public boolean isRestrictionBaseType(QName restrictionBaseType) {
        QName baseTypeQName = this.elementToSchemaQNameMap.get(restrictionBaseType);
        if (restriction && simple && baseTypeQName != null && restrictionBaseType != null) {
            return true;
        } else if (this.restrictionBaseType != null && baseTypeQName != null
                && this.restrictionBaseType.equals(baseTypeQName)) {
            return true;

        }
        return false;
    }

    /**
     * Gets the ordered status.
     *
     * @return Returns boolean.
     */
    public boolean isOrdered() {
        return ordered;
    }

    /**
     * Sets the ordered flag.
     *
     * @param ordered
     */
    public void setOrdered(boolean ordered) {
        this.ordered = ordered;
    }

    /**
     * Registers a mapping.
     *
     * @param qName
     * @param schemaName
     * @param javaClassName
     */
    public void registerMapping(QName qName, QName schemaName, String javaClassName) {
        registerMapping(qName, schemaName, javaClassName, SchemaConstants.ELEMENT_TYPE);
    }


    /**
     * Registers a Qname as nillable
     * The qName better be of an element
     *
     * @param qName
     * @param schemaName
     * @param javaClassName
     */
    public void registerNillableQName(QName eltQName) {
        nillableQNameList.add(eltQName);
    }

    /**
     * Returns whether a QName is nillable or not
     *
     * @param eltQName
     */
    public boolean isNillable(QName eltQName) {
        return nillableQNameList.contains(eltQName);
    }

    /**
     * Registers a Qname as fixed
     * The qName better be of an element
     *
     * @param eltQName
     */
    public void registerFixedQName(QName eltQName) {
        fixedQNameList.add(eltQName);
    }

    /**
     * Returns whether a QName is fixed or not
     *
     * @param eltQName
     */
    public boolean isFixed(QName eltQName) {
        return fixedQNameList.contains(eltQName);
    }

    /**
     * Registers a mapping.
     *
     * @param qName
     * @param schemaName
     * @param javaClassName
     * @param type
     */
    public void registerMapping(QName qName, QName schemaName, String javaClassName, int type) {
        this.elementToJavaClassMap.put(qName, javaClassName);
        this.elementToSchemaQNameMap.put(qName, schemaName);
        addtStatus(qName, type);

    }

    /**
     * this method registers the defult value agaist the element qname.
     * @param qname
     * @param value
     */
    public void registerDefaultValue(QName qname,String value){
        this.elementQNameToDefulatValueMap.put(qname,value);
    }

    /**
     *
     * @param qname
     * @return is a default value available for this qname
     */
    public boolean isDefaultValueAvailable(QName qname){
        return this.elementQNameToDefulatValueMap.containsKey(qname);
    }

    /**
     * gets the default value for qname
     * @param qname
     * @return default value for this qname
     */

    public String getDefaultValueForQName(QName qname){
        return this.elementQNameToDefulatValueMap.get(qname);
    }

    /**
     * Gets the schema name for the given QName.
     *
     * @param eltQName
     * @return Returns QName.
     */
    public QName getSchemaQNameForQName(QName eltQName) {
        return this.elementToSchemaQNameMap.get(eltQName);
    }

    /**
     * Gets the class name for the QName.
     *
     * @param eltQName
     * @return Returns String.
     */
    public String getClassNameForQName(QName eltQName) {
        return this.elementToJavaClassMap.get(eltQName);
    }

    /**
     * Gets whether a given QName is an attribute
     *
     * @param qName
     * @return Returns boolean.
     */
    public boolean getAttributeStatusForQName(QName qName) {

        Integer state = specialTypeFlagMap.get(qName);
        return state != null && getStatus(state.intValue(), SchemaConstants.ATTRIBUTE_TYPE);
    }

    /**
     * checks the element corresponds to the qName type is xsd:anyType
     *
     * @param qName
     * @return is element corresponds to qName has xsd:anyType
     */

    public boolean getDefaultStatusForQName(QName qName) {
        boolean isDefault = false;
        QName schemaTypeQName = this.elementToSchemaQNameMap.get(qName);
        if (schemaTypeQName != null) {
            isDefault = schemaTypeQName.equals(SchemaConstants.XSD_ANYTYPE);
        }
        return isDefault;
    }


    /**
     * Gets whether a given QName represents a anyType
     *
     * @param qName
     * @return Returns boolean.
     */
    public boolean getAnyStatusForQName(QName qName) {
        Integer state = specialTypeFlagMap.get(qName);
        return state != null && getStatus(state.intValue(), SchemaConstants.ANY_TYPE);
    }

    /**
     * Gets whether a given QName refers to an array.
     *
     * @param qName
     * @return Returns boolean.
     */
    public boolean getArrayStatusForQName(QName qName) {
        Integer state = specialTypeFlagMap.get(qName);
        return state != null && getStatus(state.intValue(),
                SchemaConstants.ARRAY_TYPE);
    }

    /**
     * Gets whether a given QName refers to binary.
     *
     * @param qName
     * @return Returns boolean.
     */
    public boolean getBinaryStatusForQName(QName qName) {
        Integer state = specialTypeFlagMap.get(qName);
        return state != null && getStatus(state.intValue(),
                SchemaConstants.BINARY_TYPE);
    }

    /**
     *
     * @param qName
     * @return is this a inner choice
     */

    public boolean getInnerChoiceStatusForQName(QName qName){
        Integer state = specialTypeFlagMap.get(qName);
        return state != null && getStatus(state.intValue(),
                SchemaConstants.INNER_CHOICE_ELEMENT);
    }

    /**
     * Gets whether a given QName refers to Simple Type.
     *
     * @param qName
     * @return Returns boolean.
     */
    public boolean getSimpleStatusForQName(QName qName) {
        Integer state = specialTypeFlagMap.get(qName);
        return state != null && getStatus(state.intValue(),
                SchemaConstants.SIMPLE_TYPE_OR_CONTENT);
    }

    /**
     *
     * @param qName
     * @return whether the attribute is a partical class or not
     */

    public boolean getParticleTypeStatusForQName(QName qName){
        Integer state = specialTypeFlagMap.get(qName);
        return state != null && getStatus(state.intValue(),
                SchemaConstants.PARTICLE_TYPE_ELEMENT);
    }

    /**
     * Gets whether a given QName has the any attribute status.
     *
     * @param qName
     * @return Returns boolean.
     */
    public boolean getAnyAttributeStatusForQName(QName qName) {
        return getArrayStatusForQName(qName) &&
                getAnyStatusForQName(qName);
    }

    /**
     * Gets whether a given QName has the optional attribute status.
     *
     * @param qName QName of attribute
     * @return Returns <code>true</code> if attribute has optional status
     */
    public boolean getOptionalAttributeStatusForQName(QName qName) {
        Integer state = specialTypeFlagMap.get(qName);
        return state != null && getStatus(state.intValue(),
                SchemaConstants.OPTIONAL_TYPE);
    }

    /**
     * Clears the whole set of tables.
     */
    public void clearTables() {
        this.elementToJavaClassMap.clear();
        this.elementToSchemaQNameMap.clear();
        this.elementToSchemaQNameMap.clear();
        this.elementToJavaClassMap.clear();
        this.specialTypeFlagMap.clear();
        this.qNameMaxOccursCountMap.clear();
        this.qNameMinOccursCountMap.clear();
        this.qNameOrderMap.clear();
        this.elementQNameToDefulatValueMap.clear();
    }

    /**
     * Adds the minOccurs associated with a QName.
     *
     * @param qName
     * @param minOccurs
     */
    public void addMinOccurs(QName qName, long minOccurs) {
        this.qNameMinOccursCountMap.put(qName, minOccurs);
    }

    /**
     * Registers a QName for the order.
     *
     * @param qName
     * @param index
     */
    public void registerQNameIndex(QName qName, int index) {
        this.qNameOrderMap.put(index, qName);
    }

    /**
     * Adds the minOccurs associated with a QName.
     *
     * @param qName
     * @return Returns long.
     */
    public long getMinOccurs(QName qName) {
        Long l = this.qNameMinOccursCountMap.get(qName);
        return l != null ? l.longValue() : 1; //default for min is 1
    }

    /**
     * Gets the maxOccurs associated with a QName.
     *
     * @param qName
     * @return Returns long.
     */
    public long getMaxOccurs(QName qName) {
        Long l = this.qNameMaxOccursCountMap.get(qName);
        return l != null ? l.longValue() : 1; //default for max is 1
    }

    /**
     * Adds the maxOccurs associated with a QName.
     *
     * @param qName
     * @param maxOccurs
     */
    public void addMaxOccurs(QName qName, long maxOccurs) {
        this.qNameMaxOccursCountMap.put(qName, maxOccurs);
    }

    /**
     * @return Returns Iterator.
     * @deprecated Use #getQNameArray
     */
    public Iterator<QName> getElementQNameIterator() {
        return elementToJavaClassMap.keySet().iterator();
    }

    /**
     * Gets the QName array - may not be ordered.
     *
     * @return Returns QName[].
     */
    public QName[] getQNameArray() {
        Set<QName> keySet = elementToJavaClassMap.keySet();
        return keySet.toArray(new QName[keySet.size()]);
    }

    /**
     * Gets the ordered QName array - useful in sequences where the order needs to be preserved
     * Note - #registerQNameIndex needs to be called if this is to work properly!
     *
     * @return Returns QName[].
     */
    public QName[] getOrderedQNameArray() {
        //get the keys of the order map
        Set<Integer> set = qNameOrderMap.keySet();
        int count = set.size();
        Integer[] keys = set.toArray(new Integer[count]);
        Arrays.sort(keys);

        //Now refill the Ordered QName Array
        List<QName> returnQNames = new ArrayList<QName>();
        for (int i = 0; i < keys.length; i++) {
            returnQNames.add(qNameOrderMap.get(keys[i]));
        }

        //we've missed the attributes, so if there are attributes
        //add them explicitly to the end of this list
        QName[] allNames = getQNameArray();
        for (int i = 0; i < allNames.length; i++) {
            if (getAttributeStatusForQName(allNames[i])) {
                returnQNames.add(allNames[i]);
            }
        }

        return returnQNames.toArray(new QName[returnQNames.size()]);
    }

    /**
     * Finds the starting count for the addition of new items to the order
     *
     * @return the starting number for the sequence
     */
    public int getOrderStartPoint() {
        return qNameOrderMap.size();
    }


    /**
     * Creates link to th
     *
     * @param metaInfo
     */
    public void setAsParent(BeanWriterMetaInfoHolder metaInfo) {
        parent = metaInfo;
    }

    /**
     * Adds a another status to a particular Qname.
     * A Qname can be associated with multiple status flags
     * and they all will be preserved
     *
     * @param type
     * @param mask
     */

    public void addtStatus(QName type, int mask) {
        Integer preValue = this.specialTypeFlagMap.get(type);
        if (preValue != null) {
            this.specialTypeFlagMap.put(type, preValue | mask);
        } else {
            this.specialTypeFlagMap.put(type, mask);
        }

    }


    private boolean getStatus(int storedStatus, int mask) {
        //when the mask is anded with the status then we should get
        //the mask it self!
        return (mask == (mask & storedStatus));
    }

    /**
     * Sets the length facet.
     *
     * @param lengthFacet
     */
    public void setLengthFacet(long lengthFacet) {
        this.lengthFacet = lengthFacet;
    }

    /**
     * Gets the length facet.
     *
     * @return Returns length facet.
     */
    public long getLengthFacet() {
        return this.lengthFacet;
    }

    /**
     * Sets the totalDigits
     *
     * @param totalDigitsFacet
     */
    public void setTotalDigitsFacet(String totalDigitsFacet) {
        this.totalDigitsFacet = totalDigitsFacet;
    }

    /**
     *
     * @return Returns the totalDigits
     */
    public String getTotalDigitsFacet() {
        return this.totalDigitsFacet;
    }
    /**
     * Sets the maxExclusive.
     *
     * @param maxExclusiveFacet
     */
    public void setMaxExclusiveFacet(String maxExclusiveFacet) {
        this.maxExclusiveFacet = maxExclusiveFacet;
    }

    /**
     * Gets the maxExclusive.
     *
     * @return Returns the maxExclusive.
     */
    public String getMaxExclusiveFacet() {
        return this.maxExclusiveFacet;
    }

    /**
     * Sets the minExclusive.
     *
     * @param minExclusiveFacet
     */
    public void setMinExclusiveFacet(String minExclusiveFacet) {
        this.minExclusiveFacet = minExclusiveFacet;
    }

    /**
     * Gets the minExclusive.
     *
     * @return Returns the minExclusive.
     */
    public String getMinExclusiveFacet() {
        return this.minExclusiveFacet;
    }

    /**
     * Sets the maxInclusive.
     *
     * @param maxInclusiveFacet
     */
    public void setMaxInclusiveFacet(String maxInclusiveFacet) {
        this.maxInclusiveFacet = maxInclusiveFacet;
    }

    /**
     * Gets the maxInclusive.
     *
     * @return Returns the maxInclusive.
     */
    public String getMaxInclusiveFacet() {
        return this.maxInclusiveFacet;
    }

    /**
     * Sets the minInclusive.
     *
     * @param minInclusiveFacet
     */
    public void setMinInclusiveFacet(String minInclusiveFacet) {
        this.minInclusiveFacet = minInclusiveFacet;
    }

    /**
     * Gets the minInclusive.
     *
     * @return Returns the minInclusive.
     */
    public String getMinInclusiveFacet() {
        return this.minInclusiveFacet;
    }

    /**
     * Sets the maxLength.
     *
     * @param maxLengthFacet
     */
    public void setMaxLengthFacet(long maxLengthFacet) {
        this.maxLengthFacet = maxLengthFacet;
    }

    /**
     * Gets the maxLength.
     *
     * @return Returns maxLength.
     */
    public long getMaxLengthFacet() {
        return this.maxLengthFacet;
    }

    /**
     * Sets the minLength.
     *
     * @param minLengthFacet
     */
    public void setMinLengthFacet(long minLengthFacet) {
        this.minLengthFacet = minLengthFacet;
    }

    /**
     * Gets the minLength.
     *
     * @return Returns minLength.
     */
    public long getMinLengthFacet() {
        return this.minLengthFacet;
    }

    /**
     * Sets the enumeration.
     *
     * @param enumFacet
     */
    public void setEnumFacet(ArrayList<String> enumFacet) {
        this.enumFacet = enumFacet;
    }

    /**
     * Adds the enumeration.
     *
     * @param enumFacet
     */
    public void addEnumFacet(String enumFacet) {
        this.enumFacet.add(enumFacet);
    }

    /**
     * Gets the enumeration.
     *
     * @return Returns enumeration.
     */
    public List<String> getEnumFacet() {
        return this.enumFacet;
    }

    /**
     * Sets the pattern.
     *
     * @param patternFacet
     */
    public void setPatternFacet(String patternFacet) {
        this.patternFacet = patternFacet;
    }

    /**
     * Gets the pattern.
     *
     * @return Returns pattern.
     */
    public String getPatternFacet() {
        return this.patternFacet;
    }

    /**
     *
     * @return Returns is union
     */

    public boolean isUnion() {
        return isUnion;
    }

    public void setUnion(boolean union) {
        isUnion = union;
    }

    /**
     *
     * @return Returns memeber type in a union
     */

    public Map<QName,String> getMemberTypes() {
        return memberTypes;
    }

    public void setMemberTypes(Map<QName,String> memberTypes) {
        this.memberTypes = memberTypes;
    }

    public List<QName> getMemberTypesKeys() {
        return memberTypesKeys;
    }

    public void setMemberTypesKeys(List<QName> memberTypesKeys) {
        this.memberTypesKeys = memberTypesKeys;
    }

    public void addMemberType(QName qname,String className){
        this.memberTypes.put(qname,className);
        this.memberTypesKeys.add(qname);
    }

    public boolean isList() {
        return isList;
    }

    public void setList(boolean list) {
        isList = list;
    }

    public QName getItemTypeQName() {
        return itemTypeQName;
    }

    public void setItemTypeQName(QName itemTypeQName) {
        this.itemTypeQName = itemTypeQName;
    }

    public String getItemTypeClassName() {
        return itemTypeClassName;
    }

    public void setItemTypeClassName(String itemTypeClassName) {
        this.itemTypeClassName = itemTypeClassName;
    }

    public boolean isParticleClass() {
        return isParticleClass;
    }

    public void setParticleClass(boolean particleClass) {
        isParticleClass = particleClass;
    }

    public boolean isHasParticleType() {
        return hasParticleType;
    }

    public void setHasParticleType(boolean hasParticleType) {
        this.hasParticleType = hasParticleType;
    }

    public void addXmlNameJavaNameMapping(String xmlName,String javaName){
        this.xmlNameJavaNameMap.put(xmlName,javaName);
    }

    public boolean isJavaNameMappingAvailable(String xmlName){
        return this.xmlNameJavaNameMap.containsKey(xmlName);
    }

    public String getJavaName(String xmlName){
        return this.xmlNameJavaNameMap.get(xmlName);
    }

    public QName getRestrictionBaseType() {
        return restrictionBaseType;
    }

    /**
     * Re-keys all map entries from oldKey to newKey across all internal maps.
     * This is used when an attribute ref= resolves to a global attribute whose
     * wireName has lost the original namespace, so we need to re-register
     * the mapping under the correct QName (AXIS2-5972).
     *
     * @param oldKey the QName currently used as key (e.g. with empty namespace)
     * @param newKey the correct QName to use (e.g. with the ref namespace)
     */
    public void rekeyMapping(QName oldKey, QName newKey) {
        if (oldKey.equals(newKey)) {
            return;
        }
        // Re-key elementToJavaClassMap
        if (elementToJavaClassMap.containsKey(oldKey)) {
            elementToJavaClassMap.put(newKey, elementToJavaClassMap.remove(oldKey));
        }
        // Re-key elementToSchemaQNameMap
        if (elementToSchemaQNameMap.containsKey(oldKey)) {
            elementToSchemaQNameMap.put(newKey, elementToSchemaQNameMap.remove(oldKey));
        }
        // Re-key specialTypeFlagMap
        if (specialTypeFlagMap.containsKey(oldKey)) {
            specialTypeFlagMap.put(newKey, specialTypeFlagMap.remove(oldKey));
        }
        // Re-key qNameMaxOccursCountMap
        if (qNameMaxOccursCountMap.containsKey(oldKey)) {
            qNameMaxOccursCountMap.put(newKey, qNameMaxOccursCountMap.remove(oldKey));
        }
        // Re-key qNameMinOccursCountMap
        if (qNameMinOccursCountMap.containsKey(oldKey)) {
            qNameMinOccursCountMap.put(newKey, qNameMinOccursCountMap.remove(oldKey));
        }
        // Re-key elementQNameToDefulatValueMap
        if (elementQNameToDefulatValueMap.containsKey(oldKey)) {
            elementQNameToDefulatValueMap.put(newKey, elementQNameToDefulatValueMap.remove(oldKey));
        }
        // Re-key nillableQNameList
        int nillIdx = nillableQNameList.indexOf(oldKey);
        if (nillIdx >= 0) {
            nillableQNameList.set(nillIdx, newKey);
        }
        // Re-key fixedQNameList
        int fixedIdx = fixedQNameList.indexOf(oldKey);
        if (fixedIdx >= 0) {
            fixedQNameList.set(fixedIdx, newKey);
        }
        // Re-key qNameOrderMap (maps Integer -> QName, so replace values)
        for (Map.Entry<Integer, QName> entry : qNameOrderMap.entrySet()) {
            if (oldKey.equals(entry.getValue())) {
                entry.setValue(newKey);
            }
        }
    }

    @Override
    public String toString() {
        return "BeanWriterMetaInfoHolder [anonymous=" + anonymous + ", choice=" + choice
                + ", elementQNameToDefulatValueMap=" + elementQNameToDefulatValueMap
                + ", elementToJavaClassMap=" + elementToJavaClassMap + ", elementToSchemaQNameMap="
                + elementToSchemaQNameMap + ", enumFacet=" + enumFacet + ", extension=" + extension
                + ", extensionBaseType=" + extensionBaseType + ", extensionClassName="
                + extensionClassName + ", hasParticleType=" + hasParticleType + ", isList="
                + isList + ", isParticleClass=" + isParticleClass + ", isUnion=" + isUnion
                + ", itemTypeClassName=" + itemTypeClassName + ", itemTypeQName=" + itemTypeQName
                + ", lengthFacet=" + lengthFacet + ", maxExclusiveFacet=" + maxExclusiveFacet
                + ", maxInclusiveFacet=" + maxInclusiveFacet + ", maxLengthFacet=" + maxLengthFacet
                + ", memberTypes=" + memberTypes + ", memberTypesKeys=" + memberTypesKeys
                + ", minExclusiveFacet=" + minExclusiveFacet + ", minInclusiveFacet="
                + minInclusiveFacet + ", minLengthFacet=" + minLengthFacet + ", nillableQNameList="
                + nillableQNameList + ", ordered=" + ordered + ", ownClassName=" + ownClassName
                + ", ownQname=" + ownQname + ", parent=" + parent + ", patternFacet="
                + patternFacet + ", qNameMaxOccursCountMap=" + qNameMaxOccursCountMap
                + ", qNameMinOccursCountMap=" + qNameMinOccursCountMap + ", qNameOrderMap="
                + qNameOrderMap + ", restriction=" + restriction + ", restrictionBaseType="
                + restrictionBaseType + ", restrictionClassName=" + restrictionClassName
                + ", simple=" + simple + ", specialTypeFlagMap=" + specialTypeFlagMap
                + ", totalDigitsFacet=" + totalDigitsFacet + ", xmlNameJavaNameMap="
                + xmlNameJavaNameMap + ", fixedQNameList=" + fixedQNameList + "]";
    }
    
}
