package org.codehaus.modello.model;

/*
 * Copyright (c) 2004, Codehaus.org
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

import org.codehaus.modello.metadata.FieldMetadata;

/**
 * @author <a href="mailto:jason@modello.org">Jason van Zyl </a>
 * @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse </a>
 */
public class ModelField
    extends BaseElement
{
    private String type;

    private String defaultValue;

    private String typeValidator;

    private boolean required;

    private boolean identifier;

    private String alias;

    private transient ModelClass modelClass;

    private static final String[] PRIMITIVE_TYPES =
        { "boolean", "Boolean", "char", "Character", "byte", "Byte", "short", "Short", "int", "Integer", "long",
            "Long", "float", "Float", "double", "Double", "String", "Date", "DOM" };

    public ModelField()
    {
        super( true );
    }

    public ModelField( ModelClass modelClass, String name )
    {
        super( true, name );

        this.modelClass = modelClass;
    }

    // ----------------------------------------------------------------------
    // Property accessors
    // ----------------------------------------------------------------------

    public String getType()
    {
        return type;
    }

    public void setType( String type )
    {
        this.type = type;
    }

    public String getDefaultValue()
    {
        return defaultValue;
    }

    public void setDefaultValue( String defaultValue )
    {
        this.defaultValue = defaultValue;
    }

    public String getTypeValidator()
    {
        return typeValidator;
    }

    public void setTypeValidator( String typeValidator )
    {
        this.typeValidator = typeValidator;
    }

    public boolean isRequired()
    {
        return required;
    }

    public void setRequired( boolean required )
    {
        this.required = required;
    }

    public boolean isIdentifier()
    {
        return identifier;
    }

    public void setIdentifier( boolean identifier )
    {
        this.identifier = identifier;
    }

    public String getAlias()
    {
        return alias;
    }

    public void setAlias( String alias )
    {
        this.alias = alias;
    }
    // ----------------------------------------------------------------------
    // Misc
    // ----------------------------------------------------------------------

    public ModelClass getModelClass()
    {
        return modelClass;
    }

    public FieldMetadata getMetadata( String key )
    {
        return getMetadata( FieldMetadata.class, key );
    }

    public boolean isPrimitive()
    {
        String type = getType();

        // TODO: This should not happen
        if ( type == null )
        {
            return false;
        }

        for ( String validType : PRIMITIVE_TYPES )
        {
            if ( type.equals( validType ) )
            {
                return true;
            }
        }

        return false;
    }

    public boolean isArray()
    {
        return getType().endsWith( "[]" );
    }

    public boolean isPrimitiveArray()
    {
        String type = getType();

        for ( String PRIMITIVE_TYPE : PRIMITIVE_TYPES )
        {
            String validType = PRIMITIVE_TYPE + "[]";

            if ( validType.equals( type ) )
            {
                return true;
            }
        }

        return false;
    }

    // ----------------------------------------------------------------------
    // BaseElement Overrides
    // ----------------------------------------------------------------------

    public void initialize( ModelClass modelClass )
    {
        this.modelClass = modelClass;

        if ( defaultValue == null )
        {
            if ( "boolean".equals( type ) )
            {
                defaultValue = "false";
            }
            else if ( "float".equals( type ) || "double".equals( type ) )
            {
                defaultValue = "0.0";
            }
            else if ( "int".equals( type ) || "long".equals( type ) || "short".equals( type ) || "byte".equals( type ) )
            {
                defaultValue = "0";
            }
            else if ( "char".equals( type ) )
            {
                defaultValue = "\0";
            }
        }
    }

    public void validateElement()
        throws ModelValidationException
    {
        validateFieldNotEmpty( "field", "name", getName() );

        validateFieldNotEmpty( "field '" + getName() + "'", "type", type );

        // TODO: these definitions are duplicated throughout. Defined centrally, and loop through in the various uses

        if ( !isPrimitive() && !isPrimitiveArray() )
        {
            throw new ModelValidationException( "Field '" + getName() + "': Illegal type: '" + type + "'." );
        }
    }

    // ----------------------------------------------------------------------
    // Object Overrides
    // ----------------------------------------------------------------------

    public String toString()
    {
        return "[Field: name=" + getName() + ", alias: " + alias + ", type: " + type + ", " + "version: "
            + getVersionRange() + "]";
    }

    public boolean isModelVersionField()
    {
        Model model = modelClass.getModel();
        VersionDefinition versionDefinition = model.getVersionDefinition();

        return ( versionDefinition != null ) && versionDefinition.isFieldType()
            && ( versionDefinition.getValue().equals( getName() ) || versionDefinition.getValue().equals( alias ) );
    }
}
