/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2004-2006, GeoTools Project Managment Committee (PMC)
 *    
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.xml.handlers.xsi;

import org.geotools.xml.XSIElementHandler;
import org.geotools.xml.schema.Attribute;
import org.geotools.xml.schema.SimpleType;
import org.geotools.xml.schema.impl.AttributeGT;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;


/**
 * AttributeHandler purpose.
 * 
 * <p>
 * Represents an 'attribute' element
 * </p>
 * 
 * <p>
 * Example Use:
 * <pre><code>
 * 
 *  AttributeHandler x = new AttributeHandler(...);
 *  
 * </code></pre>
 * </p>
 *
 * @author dzwiers, Refractions Research, Inc. http://www.refractions.net
 * @author $Author:$ (last modification)
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/xml/src/main/java/org/geotools/xml/handlers/xsi/AttributeHandler.java $
 * @version $Id: AttributeHandler.java 22266 2006-10-19 11:30:55Z acuster $
 */
public class AttributeHandler extends XSIElementHandler {
    /** 'attribute' */
    public final static String LOCALNAME = "attribute";

    /** OPTIONAL */
    public static final int OPTIONAL = 0;

    /** PROHIBITED */
    public static final int PROHIBITED = 1;

    /** REQUIRED */
    public static final int REQUIRED = 2;
    private static int offset = 0;
    private String id;
    private String name;
    private String type;
    private String ref;
    private String def;
    private String fixed;
    private int use;
    private SimpleTypeHandler simpleType;
    private int hashCodeOffset = getOffset();
    private Attribute cache = null;

    /*
     * hashCode() helper
     */
    private static int getOffset() {
        return offset++;
    }

    /**
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
        return (LOCALNAME.hashCode() * ((id == null) ? 1 : id.hashCode()) * ((type == null)
        ? 1 : type.hashCode()) * ((name == null) ? 1 : name.hashCode()))
        + hashCodeOffset;
    }

    /**
     * @see org.geotools.xml.XSIElementHandler#getHandler(java.lang.String,
     *      java.lang.String)
     */
    public XSIElementHandler getHandler(String namespaceURI, String localName)
        throws SAXException {
        if (SchemaHandler.namespaceURI.equalsIgnoreCase(namespaceURI)) {
            // child types
            //
            // simpleType
            if (SimpleTypeHandler.LOCALNAME.equalsIgnoreCase(localName)) {
                SimpleTypeHandler sth = new SimpleTypeHandler();

                if (simpleType == null) {
                    simpleType = sth;
                } else {
                    throw new SAXNotRecognizedException(
                        "Extension may only have one 'simpleType' or 'complexType' declaration.");
                }

                return sth;
            }
        }

        return null;
    }

    /**
     * @see org.geotools.xml.XSIElementHandler#startElement(java.lang.String,
     *      java.lang.String, org.xml.sax.Attributes)
     */
    public void startElement(String namespaceURI, String localName,
        Attributes atts){
        id = atts.getValue("", "id");

        if (id == null) {
            id = atts.getValue(namespaceURI, "id");
        }

        String name1 = atts.getValue("", "name");

        if (name1 == null) {
            name1 = atts.getValue(namespaceURI, "name");
        }

        String ref1 = atts.getValue("", "ref");

        if (ref1 == null) {
            ref1 = atts.getValue(namespaceURI, "ref");
        }

        String type1 = atts.getValue("", "type");

        if (type1 == null) {
            type1 = atts.getValue(namespaceURI, "type");
        }

        this.name = name1;
        this.type = type1;
        this.ref = ref1;

        def = atts.getValue("", "default");

        if (def == null) {
            def = atts.getValue(namespaceURI, "default");
        }

        fixed = atts.getValue("", "fixed");

        if (fixed == null) {
            fixed = atts.getValue(namespaceURI, "fixed");
        }

        // form -- Ignore
        String use1 = atts.getValue("", "use");

        if (use1 == null) {
            use1 = atts.getValue(namespaceURI, "use");
        }

        this.use = findUse(use1);
    }

    /**
     * @see org.geotools.xml.XSIElementHandler#getLocalName()
     */
    public String getLocalName() {
        return LOCALNAME;
    }

    /**
     * <p>
     * Convert the 'use' attribute to an int mask
     * </p>
     *
     * @param use
     *
     */
    public static int findUse(String use) {
        if ("optional".equalsIgnoreCase(use)) {
            return OPTIONAL;
        }

        if ("prohibited".equalsIgnoreCase(use)) {
            return PROHIBITED;
        }

        if ("required".equalsIgnoreCase(use)) {
            return REQUIRED;
        }

        return -1;
    }

    /**
     * <p>
     * converts an int mask representing use to the string representation
     * </p>
     *
     * @param use
     *
     */
    public static String writeUse(int use) {
        switch (use) {
        case OPTIONAL:
            return "optional";

        case PROHIBITED:
            return "prohibited";

        case REQUIRED:
            return "required";

        default:
            return null;
        }
    }

    /**
     * <p>
     * Returns the attribute name
     * </p>
     *
     */
    public String getName() {
        return name;
    }

    /**
     * <p>
     * creates a smaller simpler version
     * </p>
     *
     * @param parent
     *
     *
     * @throws SAXException
     */
    protected Attribute compress(SchemaHandler parent)
        throws SAXException {
        if (cache != null) {
            return cache;
        }

        // a.form = form; TODO add form support?
        SimpleType st = null;
        String name1 = this.name;
        String def1 = this.def;
        String fixed1 = this.fixed;
        int use1 = this.use;

        if (simpleType != null) {
            st = simpleType.compress(parent);
        } else {
            if ((ref != null) && !"".equalsIgnoreCase(ref)) {
                Attribute refA = parent.lookUpAttribute(ref);

                if (refA == null) {
                    throw new SAXException("Attribute '" + ref
                        + "' was refered and not found");
                }

                st = refA.getSimpleType();
                name1 = refA.getName();
                use1 = use1 | refA.getUse();

                if ((def1 == null) || "".equalsIgnoreCase(def1)) {
                    def1 = refA.getDefault();
                }

                if ((fixed1 == null) || "".equalsIgnoreCase(fixed1)) {
                    fixed1 = refA.getFixed();
                }
            } else if ((type != null) && (!"".equalsIgnoreCase(type))) {
                // 	look it up --- find it
                st = parent.lookUpSimpleType(type);
            }
        }

        cache = new AttributeGT(id, name1, parent.getTargetNamespace(), st,
                use1, def1, fixed1, false);

        id = type = ref = null;

        return cache;
    }

    /**
     * @see org.geotools.xml.XSIElementHandler#getHandlerType()
     */
    public int getHandlerType() {
        return DEFAULT;
    }

    /**
     * @see org.geotools.xml.XSIElementHandler#endElement(java.lang.String,
     *      java.lang.String)
     */
    public void endElement(String namespaceURI, String localName){
        // do nothing
    }
}
