package ch.werner_randelshofer.iff;/*Copyright (C) 1999 Werner Randelshoferwerner.randelshofer@mythen.chhttp://www.mythen.ch/w.randelshofer/    Permission to use this release of IFFContext is herebygranted without fee provided that the complete copyrightnotice and this permission notice appear in all copiesand in supporting documentation.*/import java.lang.Object;import java.util.Hashtable;import java.util.Vector;/**Stores global variables during interpretation of an IFF stream.	<p>Design Pattern<li>	Interpreter<p>Design Role<li>	Context<p>Responsibility<li>	Hide access to the input stream.<li>	Store chunk type definitions.<li>	Serve as a global reference point for IFF specific constants.<li>	Supply utility functions.		@see	IFFInterpreter@author	Werner Randelshofer, Staldenmattweg 2, CH-6405 Immensee, Switzerland@version	1999-01-01	Reworked.<br>history	1997-08-30	Created.*/public class IFFContext extends java.lang.Object	{	/* ---- instance variables ---- */	/** ID for FORMGroupExpression. */	public final static int FORM_ID = 0x464f524d;		/** ID for CATGroupExpression. */	public final static int CAT_ID  = 0x43415420;		/** ID for CATGroupExpression. */	public final static int LIST_ID = 0x4c495354;		/** ID for PROPGroupExpression. */	public final static int PROP_ID = 0x50524f50;		/** ID for unlabeled CATGroupExpressions. */	public final static int NULL_ID = 0x20202020;	/**	The reserved group IDs "LIST", "FORM", "PROP", "CAT ", "    ",	"LIS1" through "LIS9", "FOR1" through "FOR9" and "CAT1" through "CAT9"	may not be used for type IDs and for local chunk IDs.	*/	public final static int[] RESERVED_IDs =		{		0x4c495354, 0x464f524d, 0x50524f50, 0x43415420, 0x20202020, 		0x4c495331, 0x4c495332, 0x4c495333, 0x4c495334, 0x4c495335, 0x4c495336, 0x4c495337, 0x4c495338, 0x4c495339,		0x464f5231, 0x464f5232, 0x464f5233, 0x464f5234, 0x464f5235, 0x464f5236, 0x464f5237, 0x464f5238, 0x464f5239,		0x43415431, 0x43415432, 0x43415433, 0x43415434, 0x43415435, 0x43415436, 0x43415437, 0x43415438, 0x43415439		};			/** Scan position within the stream. */	private long scan_;		/** The visitor traverses the parse tree. */	private IFFVisitor visitor_;		/** List of data chunks the visitor is interested in. */	private Hashtable dataChunks_;		/** List of property chunks the visitor is interested in. */	private Hashtable propertyChunks_;	/** List of collection chunks the visitor is interested in. */	private Hashtable collectionChunks_;	/** List of group chunks the visitor is interested in. */	private Hashtable groupChunks_;	/* ---- accessor methods ---- */	/** Set the visitor. */	protected void setVisitor(IFFVisitor v)		{ visitor_ = v; }	/** Get the visitor. */	protected IFFVisitor getVisitor()		{ return visitor_; }	/* ---- action methods ---- */	/**	Checks whether the ID of the chunk has been declared as a	data chunk.		<p>Pre condition	<li>	Data chunks must have been declared before the		interpretation has been started.	<li>	This method will always return true when neither		data chunks, property chunks nor collection chunks		have been declared, 	@param	Chunk to be verified.	@return True when the parameter is a data chunk.	*/	protected boolean isDataChunk(IFFChunk chunk) 		{		if (dataChunks_ == null)			{			if (collectionChunks_ == null && propertyChunks_ == null)				{ return true; }			else				{ return false; }			}		else			{ return dataChunks_.containsKey(chunk); }		}	/**	Checks wether the ID of the chunk has been declared as	a group chunk.		<p>Pre condition	<li>	Group chunks must have been declared before the		interpretation has been started.		(Otherwise the response is always true).	@param	Chunk to be verified.    	@return True when the visitor is interested in this is a group chunk.	*/	protected boolean isGroupChunk(IFFChunk chunk) 		{		if (groupChunks_ == null)			{ return true; }		else			{ return groupChunks_.containsKey(new Integer(chunk.getID())); }		}	/**	Checks wether the ID of the chunk has been declared as a	property chunk.		<p>Pre condition	<li>	Property chunks must have been declared before the		interpretation has been started.	<li>	This method will always return false when neither		data chunks, property chunks nor collection chunks		have been declared, 	*/	protected boolean isPropertyChunk(IFFChunk chunk) 		{		if (propertyChunks_ == null)			{ return false; }		else			{ return propertyChunks_.containsKey(chunk); }		}	/**	Checks wether the ID of the chunk has been declared as a	collection chunk.		<p>Pre condition	<li>	Collection chunks must have been declared before the		interpretation has been started.	<li>	This method will always return true when neither		data chunks, property chunks nor collection chunks		have been declared, 	@param	Chunk to be verified.    	@return True when the parameter is a collection chunk.	*/	protected boolean isCollectionChunk(IFFChunk chunk) 		{		if (collectionChunks_ == null)			{ return false; }		else			{ return collectionChunks_.containsKey(chunk); }		}	/**		Declares the chunk as data chunk.		<p>Pre condition	<li>	Chunk may not have already been declared as 		of a different type.	@param	Chunk to be declared as data chunk.	*/	protected void declareDataChunk(IFFChunk chunk) 		{		if (dataChunks_ == null) 			{ dataChunks_ = new Hashtable(); }		dataChunks_.put(chunk,chunk);		}			/**		Declares the chunk as an "interesting" group chunk.		<p>Pre condition	<li>	Chunk may not have already been declared as 		of a different type.	*/	protected void declareGroupChunk(IFFChunk chunk) 		{		if (groupChunks_ == null) 			{ groupChunks_ = new Hashtable(); }		groupChunks_.put(new Integer(chunk.getID()),chunk);		}	/**		Declares a chunk as property chunk.		<p>Pre condition	<li>	Chunk may not have already been declared as 		of a different type.	@param	Chunk to be declared as property chunk.	*/	protected void declarePropertyChunk(IFFChunk chunk) 		{		if (propertyChunks_ == null)			{ propertyChunks_ = new Hashtable(); }		propertyChunks_.put(chunk,chunk);		}			/**		Declares a chunk as collection chunk.		<p>Pre condition	<li>	Chunk may not have already been declared as 		of a different type.	@param	Chunk to be declared as collection chunk.	*/	protected void declareCollectionChunk(IFFChunk chunk) 		{		if (collectionChunks_ == null)			{ collectionChunks_ = new Hashtable(); }		collectionChunks_.put(chunk,chunk);		}	/* ---- Class methods ---- */	/**	Checks wether the argument represents a valid IFF GroupID.			<p>Validation	<li>	Group ID must be one of FORM_ID, CAT_ID, LIST_ID or PROP_ID.	@param	Chunk ID to be checked.	@return	True when the chunk ID is a valid Group ID.	*/	public static boolean isGroupID(int id)		{		if (id == FORM_ID ||		    id == CAT_ID ||		    id == LIST_ID ||		    id == PROP_ID) { return true; }		else { return false; }		}	/**	Checks if the argument represents a valid IFF ID.			<p>Validation	<li>	Every byte of an ID must be in the range of 0x20..0x7e	<li>	The id may not have leading spaces (unless the id is a NULL_ID).	@param	Chunk ID to be checked.	@return	True when the ID is a valid IFF chunk ID.	*/	public static boolean isID(int id)		{		int value1 = id >> 24;		int value2 = (id >> 16) & 0xff;		int value3 = (id >> 8)  & 0xff;		int value4 = id & 0xff;	   	if (value1 < 0x20 || value1 > 0x7e ||	   	    value2 < 0x20 || value2 > 0x7e ||	   	    value3 < 0x20 || value3 > 0x7e ||	   	    value4 < 0x20 || value4 > 0x7e) { return false; }	   	    		if (id != NULL_ID && value1 == 0x20) { return false; }    			return true;		}		/**	Returns whether the argument is a valid Local Chunk ID.			<p>Validation	<li>	Local Chunk IDs may not be a NULL_ID and may not have leading spaces.	<li>	Local Chunk IDs may not collid with GroupIDs.	@param	Chunk ID to be checked.	@return	True when the chunk ID is a Local Chunk ID.	*/	public static boolean isLocalChunkID(int id)		{		if (id == NULL_ID) { return false; }		if (isGroupID(id)) { return false; }		return isID(id);		}		public static boolean isReservedID(int id)		{		for (int i = 0; i < RESERVED_IDs.length; i++)			{			if (id == RESERVED_IDs[i]) { return true; }			}		return false;		}	/**	Returns wether the argument is a valid FormType.			<p>Validation:	<li>	The FORM type is a restricted ID that may not contain lower case letters		or punctuation characters.	<li>	FORM type may not collide with GroupID.	@param	Chunk ID to be checked.	@return	True when the chunk ID is a Form Type.	*/	public static boolean isFormType(int id)		{		if (isReservedID(id)) { return false; }		int value1 = id >> 24;		int value2 = (id >> 16) & 0xff;		int value3 = (id >> 8)  & 0xff;		int value4 = id & 0xff;	   	if (value1 < 0x30 || value1 > 0x5a || (value1 > 0x49 && value1 < 0x41) ||	   	    (value2 < 0x30 && value2 != 0x20) || value2 > 0x5a || (value2 > 0x49 && value2 < 0x41) ||	   	    (value3 < 0x30 && value3 != 0x20) || value3 > 0x5a || (value3 > 0x49 && value3 < 0x41) ||	   	    (value4 < 0x30 && value4 != 0x20) || value4 > 0x5a || (value4 > 0x49 && value4 < 0x41))			{ return false; }				if (isGroupID(id)) { return false; }					return true;  	    		}	/**	Returns wether the argument is a valid Content Type ID.			<p>Validation	<li>	The Content type is a FORM type ID or a NULL_ID			@param	Chunk ID to be checked.	@return	True when the chunk ID is a Contents Type.	*/	public static boolean isContentType(int id)		{		if (id == NULL_ID) { return true; }		else { return isFormType(id); }		}		/**	Convert an integer IFF identifier to String.	@param	ID to be converted.	@return	String representation of the ID.	*/	public static String idToString(int anID)		{		byte[] bytes = new byte[4];				bytes[0] = (byte)(anID >>> 24);		bytes[1] = (byte)(anID >>> 16);		bytes[2] = (byte)(anID >>>  8);		bytes[3] = (byte)(anID >>>  0);				return new String(bytes);		}		/**	Converts the first four letters of the	String into an IFF Identifier.		@param	String to be converted.	@return	ID representation of the String.	*/	public static int stringToID(String aString)		{		byte[] bytes = aString.getBytes();				return ((int)bytes[0]) << 24 |		        ((int)bytes[1]) << 16 |		        ((int)bytes[2]) <<  8 |		        ((int)bytes[3]) << 0;		}	}