/*******************************************************************************
 *  Copyright 2017 Centre for Development of Advanced Computing(C-DAC), Pune
 *  
 *  Licensed 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 cdac.medinfo.sdk.hl7282.baselibs;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Locale;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import cdac.medinfo.sdk.hl7282.baselibs.datatypefactory.DataTypeMapReader;
import cdac.medinfo.sdk.hl7282.baselibs.segmentfactory.DataElementFactory;
import cdac.medinfo.sdk.hl7282.commoninterface.IDataType;
import cdac.medinfo.sdk.hl7282.commoninterface.IDataTypeKey;
import cdac.medinfo.sdk.hl7282.commoninterface.IDelimiter;
import cdac.medinfo.sdk.hl7282.commoninterface.IHL7Stream;
import cdac.medinfo.sdk.hl7282.commoninterface.ISingleCollection;
import cdac.medinfo.sdk.hl7282.exceptions.DelimiterMissingException;
import cdac.medinfo.sdk.hl7282.exceptions.DumpException;
import cdac.medinfo.sdk.hl7282.exceptions.FieldMissingException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidDataTypeException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidDelimiterException;
import cdac.medinfo.sdk.hl7282.exceptions.InvalidLengthException;
import cdac.medinfo.sdk.hl7282.hl7streams.HL7BufferStream;
import cdac.medinfo.sdk.hl7282.utils.HL7Config;
import cdac.medinfo.sdk.hl7282.utils.HL7Constants;
import cdac.medinfo.sdk.hl7282.utils.HL7Logger;
import cdac.medinfo.sdk.hl7282.utils.IDataTypeMap;
import cdac.medinfo.sdk.hl7282.utils.IDataTypeMapReader;
import cdac.medinfo.sdk.hl7282.utils.IMultiValuedPairedCollection;
import cdac.medinfo.sdk.hl7282.utils.MultiValuedPairedCollection;
import cdac.medinfo.sdk.hl7282.utils.ResourceBundleWrapper;

/**
 * The class is Extended from DataType class.
 * Used as the base class for all the composite dataTypes.
 */
public abstract class CompositeDataType implements IDataType   
{
	/**
	 * This variable holds minimum length
	 */
	protected int iMinLength;
	/**
	 * This variable holds maximum length
	 */
	protected int iMaxLength;
	/**
	 * This variable holds Conformance length
	 */
	protected String iConformanceLength;
	/**
	 * This variable holds the table number of HL7 or UserDefined Table.
	 */
	protected int iTableNo;
	/**
	 * The Variable holds the information about the type of the Component. i.e Component type or SubComponent type.
	 */
	protected int iComponentType;
	/**
	 * This holds constant of name of Data Type.
	 */
	IDataTypeKey objIDataTypeKey;
	/**
	 * This holds collection of DataType for the  
	 */
	private IMultiValuedPairedCollection<Integer,IDataType> objDataValueElementList = null;
	/**
	 * This holds the map for the DataType. 
	 */
	IDataTypeMap objIDataTypeMap;
	/**
	 * DataElementFactory which gives the Data Element as per Map.  
	 */
	private DataElementFactory objDataElementFactory; 
	/**
	 * constructor for the Class CompositeDataType 
	 */
	public CompositeDataType(IDataTypeKey objIDataTypeKey)
	{
		this.objIDataTypeKey = objIDataTypeKey;

		objDataValueElementList = new MultiValuedPairedCollection<Integer, IDataType>();

		objDataElementFactory = DataElementFactory.createInstance();
		iComponentType = HL7Constants.HL7_COMPONENT;

		IDataTypeMapReader objIDataTypeMapReader = DataTypeMapReader.createInstance();
		this.objIDataTypeMap = objIDataTypeMapReader.getDataTypeMap(objIDataTypeKey.getValue()); 
	}
	/**
	 * Constructor for CompositeDataType which takes five arguments and 
	 * sets the value in the respective member variable.
	 * @param objIDataTypeKey object of IDataTypeKey.
	 * @param iMinLength The Minimum length the DataType can have.
	 * @param iMaxLength The Maximum length the DataType can have.
	 * @param iCLength The Conformance length the DataType can have.
	 * @param iComponentType The Type of the component i.e Component or a subcomponent.
	 */
	public CompositeDataType(IDataTypeKey objIDataTypeKey,int iMinLength, int iMaxLength, String strCLength,int iComponentType)
	{
		this.objIDataTypeKey = objIDataTypeKey;
		this.iMinLength = iMinLength;
		this.iMaxLength = iMaxLength;
		this.iConformanceLength = strCLength;
		this.iComponentType = iComponentType;

		objDataValueElementList = new MultiValuedPairedCollection<Integer, IDataType>();

		objDataElementFactory = DataElementFactory.createInstance();

		IDataTypeMapReader objIDataTypeMapReader = DataTypeMapReader.createInstance();
		this.objIDataTypeMap = objIDataTypeMapReader.getDataTypeMap(objIDataTypeKey.getValue()); 
	}
	/**
	 * Constructor for CompositeDataType which takes six arguments and 
	 * sets the value in the respective member variable.
	 * @param iTableNo integer value of Table no. 
	 * @param objIDataTypeKey object of IDataTypeKey.
	 * @param iMinLength The Minimum length the DataType can have.
	 * @param iMaxLength The Maximum length the DataType can have.
	 * @param iCLength The Conformance length the DataType can have.
	 * @param iComponentType The Type of the component i.e Component or a subcomponent.
	 */
	public CompositeDataType(IDataTypeKey objIDataTypeKey,int iMinLength, int iMaxLength, String strCLength,int iTableNo,int iComponentType)
	{
		this.objIDataTypeKey = objIDataTypeKey;
		this.iMinLength = iMinLength;
		this.iMaxLength = iMaxLength;
		this.iConformanceLength = strCLength;
		this.iTableNo = iTableNo;
		this.iComponentType = iComponentType;

		objDataValueElementList = new MultiValuedPairedCollection<Integer, IDataType>();

		objDataElementFactory = DataElementFactory.createInstance();

		IDataTypeMapReader objIDataTypeMapReader = DataTypeMapReader.createInstance();
		this.objIDataTypeMap = objIDataTypeMapReader.getDataTypeMap(objIDataTypeKey.getValue());
	}
	/**
	 * Constructor for CompositeDataType which takes three arguments and 
	 * sets the value in the respective member variable.
	 * @param iTableNo integer value of Table no. 
	 * @param objIDataTypeKey object of IDataTypeKey.
	 * @param iMinLength The Minimum length the DataType can have.
	 * @param iMaxLength The Maximum length the DataType can have.
	 * @param iCLength The Conformance length the DataType can have.
	 * @param iComponentType The Type of the component i.e Component or a subcomponent.
	 * @param objIDataTypeMap object of DataTypeMap
	 */
	public CompositeDataType(IDataTypeKey objIDataTypeKey,int iComponentType,IDataTypeMap objIDataTypeMap)
	{
		this.objIDataTypeKey = objIDataTypeKey;
		this.objIDataTypeMap = objIDataTypeMap; 
		this.iComponentType = iComponentType;

		objDataElementFactory = DataElementFactory.createInstance();

		objDataValueElementList = new MultiValuedPairedCollection<Integer, IDataType>();
	}
	/**
	 * Constructor for CompositeDataType which takes six arguments and 
	 * sets the value in the respective member variable.
	 * @param iTableNo integer value of Table no. 
	 * @param objIDataTypeKey object of IDataTypeKey.
	 * @param iMinLength The Minimum length the DataType can have.
	 * @param iMaxLength The Maximum length the DataType can have.
	 * @param iCLength The Conformance length the DataType can have.
	 * @param iComponentType The Type of the component i.e Component or a subcomponent.
	 * @param objIDataTypeMap object of DataTypeMap
	 */
	public CompositeDataType(IDataTypeKey objIDataTypeKey,int iMinLength, int iMaxLength, String strCLength,int iComponentType,IDataTypeMap objIDataTypeMap)
	{
		this.objIDataTypeKey = objIDataTypeKey;
		this.iMinLength = iMinLength;
		this.iMaxLength = iMaxLength;
		this.iConformanceLength = strCLength;
		this.objIDataTypeMap = objIDataTypeMap;
		this.iComponentType = iComponentType;

		objDataElementFactory = DataElementFactory.createInstance();

		objDataValueElementList = new MultiValuedPairedCollection<Integer, IDataType>();
	}
	/**
	 * Constructor for CompositeDataType which takes seven arguments and 
	 * sets the value in the respective member variable.
	 * @param iTableNo integer value of Table no. 
	 * @param objIDataTypeKey object of IDataTypeKey.
	 * @param iMinLength The Minimum length the DataType can have.
	 * @param iMaxLength The Maximum length the DataType can have.
	 * @param iCLength The Conformance length the DataType can have.
	 * @param iComponentType The Type of the component i.e Component or a subcomponent.
	 * @param objIDataTypeMap object of DataTypeMap
	 */
	public CompositeDataType(IDataTypeKey objIDataTypeKey,int iMinLength, int iMaxLength, String strCLength,int iTableNo,int iComponentType,IDataTypeMap objIDataTypeMap)
	{
		this.objIDataTypeKey = objIDataTypeKey;
		this.iMinLength = iMinLength;
		this.iMaxLength = iMaxLength;
		this.iConformanceLength = strCLength;
		this.iTableNo = iTableNo;
		this.objIDataTypeMap = objIDataTypeMap;
		this.iComponentType = iComponentType;

		objDataElementFactory = DataElementFactory.createInstance();

		objDataValueElementList = new MultiValuedPairedCollection<Integer, IDataType>();
	}

	/**
	 * This method returns the name of Data Type.
	 * @return IDataTypeKey - Name of the Data Type.
	 */
	public IDataTypeKey getDataTypeName()
	{
		return this.objIDataTypeKey;
	}

	/**
	 *Method writes the data to the Stream.
	 *@param objIHL7Stream Object of HL7Stream
	 *@param objIDelimiter Object of class Delimiter
	 *@return int - Success or failure of the read.
	 *@throws DelimiterMissingException - if Delimiter is absent.
	 *@throws IOException - if  IO operation fails 
	 */
	public int write(IHL7Stream objIHL7Stream,IDelimiter objIDelimiter) throws DelimiterMissingException , IOException
	{
		//check if the Objects are null
		char cSeperator = '\0';

		//Get component separator.
		if(iComponentType == HL7Constants.HL7_COMPONENT)
		{
			cSeperator = objIDelimiter.getComponentSeparator();
			if(cSeperator == '\0')
			{
				String strMessage = ResourceBundleWrapper.getValue("exceptions","MissingComponentSeparator",new Locale(HL7Config.createInstance().getLocale()));
				throw new DelimiterMissingException(strMessage);
			}
		}
		else if(iComponentType == HL7Constants.HL7_SUBCOMPONENT)
		{
			cSeperator = objIDelimiter.getSubComponentSeparator();
			if(cSeperator == '\0')
			{
				String strMessage = ResourceBundleWrapper.getValue("exceptions","MissingSubcomponentSeparator",new Locale(HL7Config.createInstance().getLocale()));
				throw new DelimiterMissingException(strMessage);
			}
		}
		ISingleCollection<Integer> objSeqCollection = objIDataTypeMap.getSequenceNumberList();
		for(Integer iIndex :objSeqCollection)
		{
			ISingleCollection<IDataType> objDataCollection = objDataValueElementList.get(iIndex);
			if(objDataCollection!=null)
			{
				for(IDataType objDataType : objDataCollection)
				{
					if(objDataType == null && iIndex<objSeqCollection.size())
					{
						objIHL7Stream.writeChar(cSeperator);
						break;
					}
					else if(objDataType == null && iIndex==objSeqCollection.size())
						break;
					else
						objDataType.write(objIHL7Stream,objIDelimiter);
					
						//This Code checks next elements are present or not
						for(int i=((int)iIndex)+1; i<= objSeqCollection.size();i++)
						{
							ISingleCollection<IDataType> objDataCollection1 = objDataValueElementList.get(i);
							//If next element is present then it appends delimiter
							if(iIndex < objSeqCollection.size() && objDataCollection1 != null)
							{
								objIHL7Stream.writeChar(cSeperator);//if component is not present for last component no need to append delimiter
								break;
							}
							
						}
					
				}
			}
			/*Increment counter for number of delimiters.
			  This is required if components and subcomponents are not present consecutively.
			  (ex. A^^^^B)
			 */
			else if(iIndex < objSeqCollection.size() && ( cSeperator == objIDelimiter.getComponentSeparator() || cSeperator == objIDelimiter.getSubComponentSeparator()))
			{
				//This Code checks next elements are present or not
				for(int i=((int)iIndex+1); i<= objSeqCollection.size(); i++)
				{
					ISingleCollection<IDataType> objDataCollection2 = objDataValueElementList.get(i);
					//If next element is present then it appends delimiter
					if(objDataCollection2 != null)
					{
						objIHL7Stream.writeChar(cSeperator);
						break;
					}
				}
			}
		}
		return HL7Constants.HL7_FUNCTION_SUCCESS;	
	}


	/**
	 *Method writes the data to the Stream.
	 *@param eventWriter Object of XMLEventWriter
	 *@return int - Success or failure of the read.
	 *@throws IOException - if  IO operation fails 
	 * @throws XMLStreamException 
	 */
	public int writeToXML(XMLEventWriter eventWriter) throws IOException, XMLStreamException
	{

		ISingleCollection<Integer> objSeqCollection = objIDataTypeMap.getSequenceNumberList();
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		XMLEvent tab = eventFactory.createDTD("\t");

		for(Integer iIndex :objSeqCollection)
		{
			String strFieldName = objIDataTypeMap.getFieldName(iIndex);
			String strDatatypeName = objIDataTypeMap.getDataTypeName(iIndex);
			ISingleCollection<IDataType> objDataCollection = objDataValueElementList.get(iIndex);
			if(objDataCollection!=null && objDataCollection.size()>0)
			{
				for(IDataType objDataType : objDataCollection)
				{
					// Create Start node
					StartElement sElement = eventFactory.createStartElement("", "","Component");
					eventWriter.add(tab);
					eventWriter.add(sElement);

					Attribute attribFieldName = eventFactory.createAttribute("Name", strFieldName);
					eventWriter.add(attribFieldName);

					Attribute attribType = eventFactory.createAttribute("Type", strDatatypeName);
					eventWriter.add(attribType);

					if(objDataType.isPrimitive() == false)
					{
						CompositeDataType objCompositeDataType = (CompositeDataType)objDataType;
						objCompositeDataType.writeToXML(eventWriter);
					}
					else
					{
						PrimitiveDataType objPrimitiveDataType = (PrimitiveDataType)objDataType;
						String strValue = (String)objPrimitiveDataType.getValue();

						Attribute attribFieldValue = eventFactory.createAttribute("Value", strValue);
						eventWriter.add(attribFieldValue);
					}

					// Create End node
					EndElement SegEndElement = eventFactory.createEndElement("", "", "Component");
					eventWriter.add(SegEndElement);
					eventWriter.add(end);
				}
			}
		}
		return HL7Constants.HL7_FUNCTION_SUCCESS;	
	}

	/**
	 *Method writes the data to the Stream.
	 *@param eventWriter Object of XMLEventWriter
	 *@return int - Success or failure of the read.
	 *@throws IOException - if  IO operation fails 
	 * @throws XMLStreamException 
	 */
	public int writeToXML_v2(XMLEventWriter eventWriter) throws IOException, XMLStreamException
	{

		ISingleCollection<Integer> objSeqCollection = objIDataTypeMap.getSequenceNumberList();
		XMLEventFactory eventFactory = XMLEventFactory.newInstance();
		XMLEvent end = eventFactory.createDTD("\n");
		XMLEvent tab = eventFactory.createDTD("\t");
		//eventWriter.add(end);
		for(Integer iIndex :objSeqCollection)
		{
			int SequenceNo=iIndex;
			ISingleCollection<IDataType> objDataCollection = objDataValueElementList.get(iIndex);
			if(objDataCollection!=null && objDataCollection.size()>0)
			{
				for(IDataType objDataType : objDataCollection)	//Here objDataType are subcomponent datatypes
				{
					
					// Create Start node	(this.objIDataTypeKey is datatype of Component)
					StartElement sElement = eventFactory.createStartElement("", "",this.objIDataTypeKey.getValue()+"."+SequenceNo);
					
					eventWriter.add(tab);
					eventWriter.add(tab);
					eventWriter.add(tab);
					eventWriter.add(sElement);

					if(objDataType.isPrimitive() == false)
					{
						eventWriter.add(end);
						eventWriter.add(tab);
						CompositeDataType objCompositeDataType = (CompositeDataType)objDataType;
						objCompositeDataType.writeToXML_v2(eventWriter);
						
						//eventWriter.add(end);
					}
					else
					{
						PrimitiveDataType objPrimitiveDataType = (PrimitiveDataType)objDataType;
						String strValue = (String)objPrimitiveDataType.getValue();

						Characters nameChars = eventFactory.createCharacters(strValue);
						eventWriter.add(nameChars);
					}

					// Create End node
					EndElement SegEndElement = eventFactory.createEndElement("", "", this.objIDataTypeKey.getValue()+"."+SequenceNo);
					eventWriter.add(SegEndElement);
					eventWriter.add(end);
				}
			}
		}
		eventWriter.add(tab);
		eventWriter.add(tab);
		return HL7Constants.HL7_FUNCTION_SUCCESS;	
	}

	
	/**
	 * Read data from string
	 * @param strAttributeData String of segment
	 * @param objIDelimiter object of class Delimiter
	 * @return integer - Number of bytes read.
	 *				   - Fail if data read is not valid.
	 *@throws DelimiterMissingException - if Delimiter is absent.
	 *@throws InvalidDelimiterException - if Delimiter is not valid
	 *@throws IOException - if IO operation fails.
	 */
	public int read(String strAttributeData,IDelimiter objIDelimiter) throws DelimiterMissingException,InvalidDelimiterException,InvalidDataTypeException ,IOException
	{
		char cDelimiter = '\0';
		char cSeperator = '\0';
		char cFSeperator = objIDelimiter.getFieldSeparator();
		int iReadBytes = 0;
		IDataType objIDataType;
		try
		{
			if(iComponentType == HL7Constants.HL7_COMPONENT)
			{
				cSeperator = objIDelimiter.getComponentSeparator();
				if(cSeperator == '\0')
				{
					return HL7Constants.HL7_FUNCTION_FAIL;
				}
			}
			else if(iComponentType == HL7Constants.HL7_SUBCOMPONENT)
			{
				cSeperator = objIDelimiter.getSubComponentSeparator();
				if(cSeperator == '\0')
				{
					return HL7Constants.HL7_FUNCTION_FAIL;
				}
			}

			ISingleCollection<Integer> objSeqCollection = objIDataTypeMap.getSequenceNumberList();
		
			do
			{
				int i = -1;
				
				//Start reading data from stream.
				for(Integer iIndex:objSeqCollection)
				{
					int iRetVal = 0;
					//Create data type component
					do
					{
						objIDataType = objDataElementFactory.getDataTypeComponent(objIDataTypeKey.getValue(), iIndex);
						
						String strField="";
						if(objIDataType == null)
						{
							// if Data Type is withdrawn then skip that value by reading stream till next delimiter
							cDelimiter = strAttributeData.charAt(++i);
							strField=""+cDelimiter;
							while(cDelimiter != cSeperator && cDelimiter != cFSeperator && cDelimiter != '~' && cDelimiter !='\r')
							{	
								strField= strField+cDelimiter;
								cDelimiter = strAttributeData.charAt(++i);	
							}
							// Field level, if no value is provided for component and data type of that component is withdrawn then mark it.
							if(cDelimiter == cFSeperator)	
							{
							
							}
							if(strField.charAt(0)!='^' && strField!=null)
								return HL7Constants.HL7_WITHDRAWN_FIELD;
							else if(strField.charAt(0)!='^')
							{
								return 0;
							}
						}
						else
						{
							if(cSeperator == '^')
							{							
									strField = "";
									do
									{
										i++;
										strField=strField+strAttributeData.charAt(i);
									}while(strAttributeData.charAt(i)!= '~' && strAttributeData.charAt(i) != '^' && strAttributeData.charAt(i)!='|' && strAttributeData.charAt(i)!='\r' && (strAttributeData.length()-1)!= i);
									iRetVal = objIDataType.read(strField, objIDelimiter);
							}
							
							else if(cSeperator == '&' )
							{
								do
								{
									i++;
									strField=strField+strAttributeData.charAt(i);
								}while(strAttributeData.charAt(i)!= '~' && strAttributeData.charAt(i)!=cSeperator && strAttributeData.charAt(i) != '^' && strAttributeData.charAt(i)!='|' && strAttributeData.charAt(i)!='\r' && (strAttributeData.length()-1)!= i);
								iRetVal = objIDataType.read(strField, objIDelimiter);
							}
							if(iRetVal == 0)
							{
								objIDataType = null;
								cDelimiter = strAttributeData.charAt(i);
								break;
							}
							else if(iRetVal == HL7Constants.HL7_FUNCTION_FAIL)
							{
								objIDataType = null;
								return iRetVal;
							}
							else
							{
								objDataValueElementList.put(iIndex,objIDataType);
								objIDataType = null;
							}
							cDelimiter = '\0';
							//Read delimiter
							cDelimiter = strAttributeData.charAt(i);
						}						
					}while(cDelimiter == objIDelimiter.getEscapeCharacter());

					//Calculate total number of bytes read.
					iReadBytes += iRetVal;

					//check that all datatype are read and still delimiter is present then throw exception.
					if((iIndex == objSeqCollection.size())&& (cDelimiter==cSeperator))
						return HL7Constants.HL7_WITHDRAWN_FIELD;
					else if(cDelimiter != cSeperator)
					{					
						if(iReadBytes > 0)
						{

						}
						objIDataType = objDataElementFactory.getDataTypeComponent(objIDataTypeKey.getValue(), iIndex);
						//if no value is provided for component and Data Type is withdrawn then set iReadBytes as -1 . 
						if(cDelimiter == cFSeperator && objIDataType == null)
							iReadBytes=-1;
						return iReadBytes;
					}
				}
			}while(cDelimiter == objIDelimiter.getRepetitionSeparator());
		}
		catch(DelimiterMissingException ex)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();						
				objHL7Logger.log(ex.getMessage());
			}
			throw new InvalidDataTypeException(this.getClass().getSimpleName(),ex);
		}
		catch(InvalidLengthException ex)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();						
				objHL7Logger.log(ex.getMessage());
			}
			throw new InvalidDataTypeException(this.getClass().getSimpleName(),ex);
		}
		catch(InvalidDataTypeException ex)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();						
				objHL7Logger.log(ex.getMessage());
			}
			throw new InvalidDataTypeException(this.getClass().getSimpleName(),ex);
		}
		return HL7Constants.HL7_FUNCTION_FAIL;
		 
	}

	/**
	 * Validates the composite data type for its required components.  
	 * @return boolean - true or false.
	 * @throws InvalidDataTypeException - if Invalid values is entered or Mandatory field is missing. 
	 */
	public boolean validate() throws InvalidDataTypeException
	{
		try
		{
			ISingleCollection<Integer> objSeqCollection = objIDataTypeMap.getSequenceNumberList();
			boolean isOptional;
			String strField;
			int iDataTypeLength =0; 
			ISingleCollection<IDataType> objDataTypeList = null;
			for(Integer iItemID: objSeqCollection)
			{
				isOptional = objIDataTypeMap.isOptional(iItemID);
				strField = objIDataTypeMap.getFieldName(iItemID); 
				@SuppressWarnings("unused")
				String strDataType = objIDataTypeMap.getDataTypeName(iItemID);
				objDataTypeList = objDataValueElementList.get(iItemID);
				iMinLength = objIDataTypeMap.getItemMinimumLength(iItemID);
				iMaxLength = objIDataTypeMap.getItemMaximumLength(iItemID);
				iConformanceLength = objIDataTypeMap.getItemConformanceLength(iItemID);
			
//					//check that composite DataType is formed with all content inside it Component separator is considered.
//					//like for TQ DataType(CQ^RI^ST^TS^TS^ST^ST^TX^ID^OSD^CE^NM)
//					//length of each datatype 1534 and 11 (^)component separator together as 1545 is max length and for 
//					//minimum length is 11.
//			

				if(objDataTypeList == null ||objDataTypeList.contains(null) || objDataTypeList.size() == 0)
				{
					if(!isOptional)
					{ 
						if(HL7Config.createInstance().isLoggingOn())
						{
							String strMessage = ResourceBundleWrapper.getValue("exceptions", "REQUIRED_FIELD_MISSING",new Locale(HL7Config.createInstance().getLocale()));
							HL7Logger objHLogger = HL7Logger.getLogger();
							objHLogger.log(strMessage + ":" + strField);
						}
						throw new FieldMissingException(strField);
					}
				}

				// validating all datatypes of segments
				if(objDataTypeList!=null)
				{
					if((!objDataTypeList.contains(null)))
					{
						for (IDataType objDataType : objDataTypeList)
						{
							int iLength = objDataType.getLength();
							iDataTypeLength = iDataTypeLength + iLength; 
							if(objDataType.isPrimitive())
								objDataType.validate();
						}
					}
				}
				

				objDataTypeList =null;
			}
			return true;
		}
		catch(InvalidDataTypeException ex)
		{
			if(HL7Config.createInstance().isLoggingOn())
			{
				HL7Logger objHL7Logger = HL7Logger.getLogger();						
				objHL7Logger.log(ex.getMessage());
			}
			throw new InvalidDataTypeException(this.getClass().getSimpleName(),ex);
		}
	}

	/**
	 * Check for valid component type.
	 * @return boolean - true / false 
	 */
	protected boolean isValidComponentType()
	{
		if(iComponentType == HL7Constants.HL7_COMPONENT)
			return true;
		else if(iComponentType == HL7Constants.HL7_SUBCOMPONENT)
			return true;
		else
			return false;
	}
	/**
	 * This will return the object.
	 * in case of Composite Data Type it will return the IMultivaluedPairedCollection and for
	 * primitive dataType it will return the Object as String.   
	 */
	public Object getValue()
	{
		return (Object)getAttributes();
	}
	
	/**
	 * Gets The Attribute List of Composite Datatype.
	 * @return IMultiValuedPairedCollection<Integer,IDataType> - returns the Multi-Valued PairedCollection of index and IDataType
	 */
	public IMultiValuedPairedCollection<Integer,IDataType> getAttributes()
	{
		IMultiValuedPairedCollection<Integer,IDataType> objList = new MultiValuedPairedCollection<Integer, IDataType>();
		ISingleCollection<Integer> objCollection = objIDataTypeMap.getSequenceNumberList();
		for(Integer iIndex:objCollection)
		{
			ISingleCollection<IDataType> objDataType = objDataValueElementList.get(iIndex);
			objList.put(iIndex, objDataType);
		}
		return objList;
	}
	
	
	/**
	 * This will return the length of DataType. 
	 */
	public int getLength()
	{
		int iLength = 0;
		ISingleCollection<Integer> objCollection = objIDataTypeMap.getSequenceNumberList();
		for(Integer iIndex:objCollection)
		{
			ISingleCollection<IDataType> objDataTypeCollection  = objDataValueElementList.get(iIndex);
			if(objDataTypeCollection!=null)
			{
				for(IDataType objDataType: objDataTypeCollection)
				{
					int iDataLength = objDataType.getLength();
					iLength =iLength + iDataLength;
				}
			}

		}
		return iLength;
	}
	
	/**
	 * This sets the object value on DataType.
	 * in case of composite Data Type it will be set as MultivaluedPairedCollecxtion and for 
	 * primitive Data Type it will be set as String.
	 */
	@SuppressWarnings("unchecked")
	public void setValue(Object obj)
	{

		IMultiValuedPairedCollection<Integer,IDataType> objList = new MultiValuedPairedCollection<Integer, IDataType>();

		if (obj instanceof IMultiValuedPairedCollection<?, ?>)
		{
			IMultiValuedPairedCollection<?, ?> new_Collection = (IMultiValuedPairedCollection<?, ?>) obj;
			IMultiValuedPairedCollection<Integer,IDataType> objNewList = (IMultiValuedPairedCollection<Integer,IDataType>) new_Collection;
			int iIndex= 1;
			ISingleCollection<IDataType> objDataCollection =  objNewList.getAllValues();
			for(IDataType objData: objDataCollection)
			{
				objList.put(iIndex, objData);
				iIndex++;
			}
		}
	}
	/**
	 * This method returns the state for Data Type whether it 
	 * is primitive Data Type or composite DataType.
	 * @return false for all Composite DataType. 
	 */
	public boolean isPrimitive()
	{
		return false;
	} 
	
	/**
	 * This will create replicate of existing Data Type.
	 * @param objDataType object of Data Type.
	 * @param objDelimiter object of Delimiter.
	 *@throws IOException - if IO operation fails.
	 */
	public void copy(IDataType objDataType, IDelimiter objDelimiter) throws IOException
	{
		ByteArrayOutputStream objByteArrayOutputStream = new ByteArrayOutputStream();
		HL7BufferStream tempStream = new HL7BufferStream();
		tempStream.setOutputStream(objByteArrayOutputStream);

		objDataType.write(tempStream, objDelimiter);
		byte []arrByte =  new byte[objByteArrayOutputStream.size()];
		arrByte = objByteArrayOutputStream.toByteArray();
		ByteArrayInputStream objByteArrayInputStream = new ByteArrayInputStream(arrByte);
		tempStream.setInputStream(objByteArrayInputStream);
		
		String strTemp = "";
		if(tempStream!= null)
		{
			
			char readCh;
			do
			{
				readCh = tempStream.readChar();
				strTemp=strTemp+readCh;
			}while(readCh != '\0' && readCh != '\r');
		}
		this.read(strTemp, objDelimiter);
	}
	
	/**
	 * This will set the value in collection for given Sequence ID.
	 * @param objDataType Object of DataType.
	 * @param iSequenceId Integer value of Sequence ID.
	 */
	protected void setComponentType(Integer iSequenceId,IDataType objDataType)
	{
		objDataValueElementList.remove(iSequenceId);
		if(objDataType != null)
			objDataValueElementList.put(iSequenceId,objDataType);
	}

	/**
	 * This will set the value in collection for given Sequence ID with its value.
	 * @param iSequenceId Integer value of Sequence ID.
	 * @param strValue Value of the Data Type.
	 */
	protected void setComponentType(Integer iSequenceId,String strValue)
	{
		IDataType objDataType = objDataElementFactory.getDataTypeComponent(objIDataTypeKey.getValue(), iSequenceId);
		objDataType.setValue(strValue);
		setComponentType(iSequenceId,objDataType);
	}

	/**
	 * This will get the collection of DataType for the Sequence ID.
	 * @param iSequenceId Integer value of Sequence Id.
	 * @return ISingleCollection<IDataType> Collection of DataType.
	 */
	protected  ISingleCollection<IDataType> getComponentType(Integer iSequenceId)
	{
		return objDataValueElementList.get(iSequenceId);
	}
	/**
	 * This method will remove the value from collection for that sequence id.
	 * @param iSequenceId integer value of sequence id.
	 */
	protected void remove(Integer iSequenceId)
	{
		objDataValueElementList.remove(iSequenceId);
	}

	/**
	 * Serializes Composite Data Type information like it name, Attribute name and its values.
	 * @param objHL7Stream Object of IHL7Stream
	 * @param objDelimiter Object of IDelimiter
	 * @param iLevel integer shows level of an attribute.
	 * @throws DumpException Signals that dump operation is failed
	 * @throws IOException Signals that an I/O exception of some sort has occurred.
	 */
	public void dumpDataType(IHL7Stream objIHL7Stream, IDelimiter objIDelimiter, int iLevel)throws DumpException,IOException
	{	
		ISingleCollection<Integer> objSeqCollection = objIDataTypeMap.getSequenceNumberList();

		if(objSeqCollection != null)
		{
			for (Integer iIndex : objSeqCollection)
			{
				String strFieldName = objIDataTypeMap.getFieldName(iIndex);
				ISingleCollection<IDataType> objDataCollection = objDataValueElementList.get(iIndex);

				if(objDataCollection != null && objDataCollection.size() > 0)
				{
					objIHL7Stream.writeString("\n");
					for(int j = 1 ; j <= iLevel + 2; j++)
					{
						objIHL7Stream.writeChar('\t');
					}
					
					objIHL7Stream.writeString(strFieldName + " - ");

					int i = 0;
					for(IDataType objDataType : objDataCollection)
					{
						if(i != 0)
							objIHL7Stream.writeString(" , ");
						
						if(objDataType.isPrimitive())
								objIHL7Stream.writeString(objDataType.getValue().toString());
						else
							((CompositeDataType)objDataType).dumpDataType(objIHL7Stream, objIDelimiter, iLevel + 1);
							
						i++;
					}
				}
			}
		}
	}
}

