/*******************************************************************************
 * Copyright (c) 2004 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.report.model.parser;

import java.util.ArrayList;

import org.eclipse.birt.report.model.api.core.IStructure;
import org.eclipse.birt.report.model.api.elements.structures.Action;
import org.eclipse.birt.report.model.api.elements.structures.IncludeScript;
import org.eclipse.birt.report.model.api.util.StringUtil;
import org.eclipse.birt.report.model.core.DesignElement;
import org.eclipse.birt.report.model.core.Module;
import org.eclipse.birt.report.model.elements.GroupElement;
import org.eclipse.birt.report.model.elements.OdaDataSet;
import org.eclipse.birt.report.model.elements.OdaDataSource;
import org.eclipse.birt.report.model.elements.ScriptDataSet;
import org.eclipse.birt.report.model.elements.interfaces.IReportItemModel;
import org.eclipse.birt.report.model.metadata.PropertyDefn;
import org.eclipse.birt.report.model.metadata.PropertyType;
import org.eclipse.birt.report.model.util.AbstractParseState;
import org.eclipse.birt.report.model.util.VersionUtil;
import org.eclipse.birt.report.model.util.XMLParserException;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

/**
 * Parses the "property-list" tag. We use the "property-list" tag if the element
 * property or structure member is defined as structure list type, the
 * user-defined properties, user-defined property values and the value choices
 * of user-defined property.
 */

public class ListPropertyState extends AbstractPropertyState
{

	/**
	 * The temporary list which holds the structures in one structure list.
	 */

	ArrayList list = new ArrayList( );

	/**
	 * The definition of the property of this list property.
	 */

	PropertyDefn propDefn = null;

	/**
	 * Constructs the design parse state with the design file parser handler.
	 * This constructor is used when this list property to parse is a property
	 * of one element.
	 * 
	 * @param theHandler
	 *            the design file parser handler
	 * @param element
	 *            the element which holds this property
	 */

	ListPropertyState( ModuleParserHandler theHandler, DesignElement element )
	{
		super( theHandler, element );
	}

	/**
	 * Constructs the design parse state with the design file parser handler.
	 * This constructor is used when this list property to parse is a member of
	 * one structure.
	 * 
	 * @param theHandler
	 *            the design parser handler
	 * @param element
	 *            the element holding this list property
	 * @param propDefn
	 *            the definition of the property which is structure list
	 * @param struct
	 *            the structure which holds this list property
	 */

	ListPropertyState( ModuleParserHandler theHandler, DesignElement element,
			PropertyDefn propDefn, IStructure struct )
	{
		super( theHandler, element );

		this.propDefn = propDefn;
		this.struct = struct;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.model.parser.AbstractPropertyState#setName(java.lang.String)
	 */

	protected void setName( String name )
	{
		super.setName( name );

		if ( struct != null )
		{
			propDefn = (PropertyDefn) struct.getDefn( ).getMember( name );
		}
		else
		{
			propDefn = element.getPropertyDefn( name );
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.model.util.AbstractParseState#parseAttrs(org.xml.sax.Attributes)
	 */

	public void parseAttrs( Attributes attrs ) throws XMLParserException
	{

		super.parseAttrs( attrs );

		if ( StringUtil.isBlank( name ) )
			return;

		// compatible for old Action list property.
		if ( this.struct instanceof Action )
		{
			if ( ( ActionStructureState.DRILLTHROUGH_PARAM_BINDINGS_MEMBER
					.equals( name ) ) )
			{
				name = Action.PARAM_BINDINGS_MEMBER;
			}
			else if ( ActionStructureState.DRILLTHROUGH_SEARCH_MEMBER
					.equals( name ) )
			{
				name = Action.SEARCH_MEMBER;
			}
		}

		if ( struct != null )
		{
			propDefn = (PropertyDefn) struct.getDefn( ).getMember( name );
		}
		else
		{
			propDefn = element.getPropertyDefn( name );
		}

		// prop maybe is null, for example, user properties.

		if ( !DesignElement.USER_PROPERTIES_PROP.equals( name ) )
		{
			if ( propDefn == null )
			{
				// ROM does not contain public driver properties any more.

				if ( OdaDataSource.PUBLIC_DRIVER_PROPERTIES_PROP.equals( name ) )
					return;

				// compatible for "includeLibrary" in the module

				if ( element instanceof Module
						&& "includeLibraries".equalsIgnoreCase( name ) ) //$NON-NLS-1$
					return;

				if ( element instanceof ScriptDataSet
						&& "resultSet".equalsIgnoreCase( name ) ) //$NON-NLS-1$
					return;

				// If the property name is invalid, no error will be reported.

				DesignParserException e = new DesignParserException(
						new String[]{name},
						DesignParserException.DESIGN_EXCEPTION_UNDEFINED_PROPERTY );
				RecoverableError.dealUndefinedProperty( handler, e );

				valid = false;
			}
			else
			{
				if ( PropertyType.STRUCT_TYPE != propDefn.getTypeCode( ) )
				{
					DesignParserException e = new DesignParserException(
							DesignParserException.DESIGN_EXCEPTION_WRONG_STRUCTURE_LIST_TYPE );
					handler.getErrorHandler( ).semanticError( e );
					valid = false;
				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.model.util.AbstractParseState#startElement(java.lang.String)
	 */

	public AbstractParseState startElement( String tagName )
	{
		int tagValue = tagName.toLowerCase( ).toLowerCase( ).hashCode( );
		if ( ParserSchemaConstants.STRUCTURE_TAG == tagValue )
			return new StructureState( handler, element, propDefn, list );

		if ( ParserSchemaConstants.EX_PROPERTY_TAG == tagValue )
			return new ExtendedPropertyState( handler, element, propDefn, list );

		return super.startElement( tagName );
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.model.util.AbstractParseState#end()
	 */

	public void end( ) throws SAXException
	{
		if ( struct != null )
		{
			// Ensure that the member is defined.

			PropertyDefn memberDefn = (PropertyDefn) struct.getDefn( )
					.getMember( name );
			struct.setProperty( memberDefn, list );

		}
		else
			element.setProperty( name, list );
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.model.util.AbstractParseState#jumpTo()
	 */

	public AbstractParseState jumpTo( )
	{

		// the property has been removed. It must be handled before checking the
		// validation of <code>valid</code>.

		if ( handler.versionNumber > VersionUtil.VERSION_3_0_0
				&& handler.versionNumber <= VersionUtil.VERSION_3_2_1
				&& ( BOUND_DATA_COLUMNS_PROP == nameValue )
				&& ( element instanceof GroupElement ) )
		{

			CompatibleGroupBoundColumnsState state = new CompatibleGroupBoundColumnsState(
					handler, element.getContainer( ), (GroupElement) element );
			state.setName( name );
			return state;

		}

		return super.jumpTo( );
	}

	private static final int USER_PROPERTIES_PROP = DesignElement.USER_PROPERTIES_PROP
			.toLowerCase( ).hashCode( );
	private static final int INCLUDE_SCRIPTS_PROP = Module.INCLUDE_SCRIPTS_PROP
			.toLowerCase( ).hashCode( );
	private static final int LIBRARIES_PROP = Module.LIBRARIES_PROP
			.toLowerCase( ).hashCode( );
	private static final int INCLUDE_LIBRARIES_PROP = "includeLibraries"
			.toLowerCase( ).hashCode( );

	private static final int BOUND_DATA_COLUMNS_PROP = IReportItemModel.BOUND_DATA_COLUMNS_PROP
			.toLowerCase( ).hashCode( );

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.model.parser.AbstractPropertyState#generalJumpTo()
	 */

	protected AbstractParseState generalJumpTo( )
	{

		if ( USER_PROPERTIES_PROP == nameValue )
		{
			AbstractPropertyState state = new UserPropertyListState( handler,
					element );
			state.setName( name );
			return state;
		}
		if ( element instanceof Module )
		{
			if ( INCLUDE_SCRIPTS_PROP == nameValue )
			{
				SimpleStructureListState state = new SimpleStructureListState(
						handler, element );
				state.setName( name );
				state.setMemberName( IncludeScript.FILE_NAME_MEMBER );
				return state;
			}

			if ( LIBRARIES_PROP == nameValue
					|| INCLUDE_LIBRARIES_PROP == nameValue )
			{
				AbstractPropertyState state = new IncludedLibrariesStructureListState(
						handler, element );
				state.setName( Module.LIBRARIES_PROP );
				return state;
			}

		}
		if ( BOUND_DATA_COLUMNS_PROP == nameValue )
		{
			CompatibleBoundColumnState state = new CompatibleBoundColumnState(
					handler, element );
			state.setName( name );
			return state;
		}

		return super.generalJumpTo( );
	}

	private static final int PRIVATE_DRIVER_PROPERTIES_PROP = OdaDataSource.PRIVATE_DRIVER_PROPERTIES_PROP
			.toLowerCase( ).hashCode( );

	private static final int PUBLIC_DRIVER_PROPERTIES_PROP = OdaDataSource.PUBLIC_DRIVER_PROPERTIES_PROP
			.toLowerCase( ).hashCode( );

	private static final int RESULT_SET_PROP = "resultSet".toLowerCase( )
			.hashCode( );
	private static final int PARAMETERS_PROP = "parameters".toLowerCase( )
			.hashCode( );

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.model.parser.AbstractPropertyState#versionConditionalJumpTo()
	 */

	protected AbstractParseState versionConditionalJumpTo( )
	{
		if ( element instanceof OdaDataSource )
		{
			if ( PRIVATE_DRIVER_PROPERTIES_PROP == nameValue
					|| PUBLIC_DRIVER_PROPERTIES_PROP == nameValue )
			{
				if ( handler.isVersion( VersionUtil.VERSION_0 ) )
				{
					CompatibleOdaDriverPropertyStructureListState state = new CompatibleOdaDriverPropertyStructureListState(
							handler, element );
					state.setName( name );
					return state;
				}
			}
		}
		else if ( handler.versionNumber < VersionUtil.VERSION_3_2_0
				&& BOUND_DATA_COLUMNS_PROP == nameValue )
		{
			CompatibleBoundColumnState state = new CompatibleBoundColumnState(
					handler, element );
			state.setName( name );
			return state;
		}

		if ( handler.versionNumber < VersionUtil.VERSION_3_2_4
				&& element instanceof ScriptDataSet
				&& RESULT_SET_PROP == nameValue )
		{
			CompatibleRenameListPropertyState state = new CompatibleRenameListPropertyState(
					handler, element, name );
			state.setName( ScriptDataSet.RESULT_SET_HINTS_PROP );
			return state;
		}

		if ( handler.versionNumber < VersionUtil.VERSION_3_2_6
				&& element instanceof OdaDataSet && struct == null )
		{
			AbstractParseState state = null;

			if ( PARAMETERS_PROP == nameValue )
			{
				state = new CompatibleDataSetParamListPropertyState( handler,
						element );
				( (CompatibleDataSetParamListPropertyState) state )
						.setName( OdaDataSet.PARAMETERS_PROP );
				return state;
			}
		}
		return super.versionConditionalJumpTo( );
	}
}