/*
 * 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.HashMap;

/**
 * Java types corresponding to schema types. The schema type list here should always match that in
 * {@link org.jibx.schema.support.SchemaTypes}. As a special case, an instance of this class is also used to represent
 * the special &lt;any> schema component.
 * 
 * @author Dennis M. Sosnoski
 */
public class JavaType
{
    /** Predefined schema simple type correspondences (note not all are defined yet). */
    private static final HashMap s_schemaTypesMap;
    static {
        // TODO define correspondences and add handling for current nulls
        s_schemaTypesMap = new HashMap();
        addType("anySimpleType", null);
        addType("anyURI", null);
        addType("base64Binary", "byte[]");
        addType("boolean", "boolean", "java.lang.Boolean", "org.jibx.runtime.Utility.ifBoolean");
        addType("byte", "byte", "java.lang.Byte", "org.jibx.runtime.Utility.ifByte");
        addType("date", null, "java.sql.Date", "org.jibx.runtime.Utility.ifDate");
        addType("dateTime", null, "java.util.Date", "org.jibx.runtime.Utility.ifDateTime");
        addType("decimal", "java.math.BigDecimal");
        addType("double", "double", "java.lang.Double");
        addType("duration", null);
        addType("ENTITY", null);
        addType("ENTITIES", null);
        addType("float", "float", "java.lang.Float");
        addType("gDay", null);
        addType("gMonth", null);
        addType("gMonthDay", null);
        addType("gYear", null);
        addType("gYearMonth", null);
        addType("hexBinary", "byte[]");
        addType("ID", "java.lang.String");
        addType("IDREF", "java.lang.String");
        addType("IDREFS", null);
        addType("int", "int", "java.lang.Integer", "org.jibx.runtime.Utility.ifInt");
        addType("integer", null, "java.math.BigInteger", "org.jibx.runtime.Utility.ifInteger");
        addType("language", null);
        addType("long", "long", "java.lang.Long", "org.jibx.runtime.Utility.ifLong");
        addType("Name", null);
        addType("negativeInteger", null);
        addType("nonNegativeInteger", null);
        addType("nonPositiveInteger", null);
        addType("normalizedString", null);
        addType("NCName", null);
        addType("NMTOKEN", null);
        addType("NMTOKENS", null);
        addType("NOTATION", null);
        addType("positiveInteger", null);
        addType("QName", "org.jibx.runtime.QName");
        addType("short", "short", "java.lang.Short", "org.jibx.runtime.Utility.ifShort");
        addType("string", "java.lang.String");
        addType("time", null, "java.sql.Time", "org.jibx.runtime.Utility.ifTime");
        addType("token", null);
        addType("unsignedByte", null);
        addType("unsignedInt", null);
        addType("unsignedLong", null);
        addType("unsignedShort", null);
    }
    
    /** &lt;any> schema component type. */
    public static final JavaType s_anyType = new JavaType(null, null, "org.w3c.dom.Element");
    
    /** &lt;anyAttribute> schema component type. */
    public static final JavaType s_anyAttributeType = new JavaType(null, null, "org.w3c.dom.Attribute");
    
    /** Schema type local name (may be needed for special handling in binding - ID and IDREF, in particular). */
    private final String m_schemaName;
    
    /** Fully qualified primitive type name (<code>null</code> if none). */
    private final String m_primitiveName;
    
    /** Fully qualified object type name (non-<code>null</code>). */
    private final String m_fqName;
    
    /** Object type an implicit import flag (from <code>java.lang</code> package). */
    private final boolean m_isImplicit;
    
    /** JiBX format name (for types requiring special handling, <code>null</code> otherwise). */
    private final String m_format;
    
    /** Method to check if a text string matches the format for this type (<code>null</code> if unused). */
    private final String m_checkMethod;
    
    /**
     * Constructor supporting special handling. This uses a string value for any types without specific Java equivalents
     * defined.
     * 
     * @param slname schema type local name
     * @param pname primitive type name (<code>null</code> if none)
     * @param fqname object type fully-qualified name (<code>null</code> if none)
     * @param format JiBX format name (<code>null</code> if none)
     * @param check check method name (<code>null</code> if none)
     */
    private JavaType(String slname, String pname, String fqname, String format, String check) {
        m_schemaName = slname;
        m_primitiveName = pname;
        if (fqname == null) {
            fqname = "java.lang.String";
        }
        m_fqName = fqname;
        m_isImplicit = fqname.startsWith("java.lang.");
        m_format = format;
        m_checkMethod = check;
    }
    
    /**
     * Basic constructor.
     * 
     * @param slname schema type local name
     * @param pname primitive type name (<code>null</code> if none)
     * @param fqname object type fully-qualified name
     */
    private JavaType(String slname, String pname, String fqname) {
        this(slname, pname, fqname, null, null);
    }
    
    /**
     * Helper method for adding object-only types to map.
     * 
     * @param lname schema type local name
     * @param fqname fully-qualified java object type name
     */
    private static void addType(String lname, String fqname) {
        addType(lname, null, fqname, null);
    }
    
    /**
     * Helper method for adding types without check methods to map.
     * 
     * @param lname schema type local name
     * @param pname primitive type name (<code>null</code> if object type)
     * @param fqname fully-qualified java object type name
     */
    private static void addType(String lname, String pname, String fqname) {
        addType(lname, null, fqname, null);
    }
    
    /**
     * Helper method for creating instances and adding them to map.
     * 
     * @param lname schema type local name
     * @param pname primitive type name (<code>null</code> if object type)
     * @param fqname fully-qualified java object type name (<code>null</code> if primitive type)
     * @param check check method name (<code>null</code> if none)
     */
    private static void addType(String lname, String pname, String fqname, String check) {
        s_schemaTypesMap.put(lname, new JavaType(lname, pname, fqname, null, check));
    }
    
    //
    // Lookup method
    
    /**
     * Get type instance.
     * 
     * @param name schema type local name
     * @return type information (non-<code>null</code>)
     */
    public static JavaType getType(String name) {
        JavaType type = (JavaType)s_schemaTypesMap.get(name);
        if (type == null) {
            throw new IllegalArgumentException("Internal error: " + name + " is not a recognized schema type name");
        } else {
            return type;
        }
    }
    
    //
    // Access methods
    
    /**
     * Get schema type local name. This is only required because the binding generation needs to implement special
     * handling for ID and IDREF values.
     * 
     * @return schema type local name
     */
    public String getSchemaName() {
        return m_schemaName;
    }
    
    /**
     * Get fully-qualified object type name.
     * 
     * @return fully-qualified name
     */
    public String getClassName() {
        return m_fqName;
    }
    
    /**
     * Check if object type is implicit import.
     * 
     * @return implicit
     */
    public boolean isImplicit() {
        return m_isImplicit;
    }
    
    /**
     * Get primitive type name.
     * 
     * @return primitive type, <code>null</code> if none
     */
    public String getPrimitiveName() {
        return m_primitiveName;
    }
    
    /**
     * Get format.
     * 
     * @return format
     */
    public String getFormat() {
        return m_format;
    }
}