package uk.co.wingpath.mib;

import java.io.*;
import java.util.*;
import java.io.*;
import java.text.*;

/**
*	Handles the writing of the MIB file.<p>
*	To setup the MIBWriter, prior to writing the MIB file, you will need
*	to supply the necessary data, such as the filename of the MIB file.
*	This can be done either via a constructor or via the methods 
*	{@link #setParameters setParameters} and 
*	{@link #setMIBFile setMIBFile}.<p>   
*	To generate an MIB file use the following:<p> 
*	{@link #startStdMIB startStdMIB} to produce invariant parts, such as 
*	the imports, as well as the module details with data supplied 
*	when setting up the <code>MIBWriter</code> (see above).<br>
*	{@link #createObjIdf createObjIdf} for each of the top-level oids <br>
*   {@link #createOTInstance createOTInstance} for each of the managed 
*	objects.<br> 
*	{@link #endDefs endDefs} when all managed objects have been 
*	entered in the definitions.<p>
*	The easy way to create tables is to call this version of the 
*	overloaded method 
*	{@link #createOTTable(String,String,int,String,String,String,String,String[],String[]) createOTTable}, which does most of the work for you.
*	Then for each object that the table is a <code>SEQUENCE OF</code>
*	(excluding the index) call this version of the overloaded
*	method {@link #createOTInstance(String,String,int,String,String,String,String) createOTInstance}.
*	For example: 
*	<pre>
*	{@code
* 	String [] names = {"humidIndex", "humidVal" }; 
*	String [] vals = {MIBSym.INT32P, MIBSym.INT32};
*	createOTTable ("humid", "table2", 1, "Humidity table.", "Entry or row in humidity table.","15", "Room and sensor number.", names, vals); 
*}
*	createOTInstance ("humidVal", "humidEntry", 2,
				 MIBSym.INT32,  MIBSym.RO, MIBSym.CUR,
				"Humidity value for this sensor in this room.");
*	}
*	</pre>
*	The alternative method of creating tables is more complex but
*	 provides more flexibility.

*	Here you need to call several methods to do the work of the
*  <code>createOTTable</code> method above:<p>	 
*	First create just the table by calling this overloaded method 
*	(with the fewer parameters). <br>
*	{@link #createOTTable(String,String,int,String,String,String) createOTTable}
*	create only the table.<br>
*	Then create the remaining objects. <br>
*	{@link #createOTTableEntry createOTTableEntry} create the table entry.<br>
*	{@link #createTableSeqType createTableSeqType} create the sequence of types.<br>
*	{@link #createOTTableIndex createOTTableIndex} create the index. <p>
*	As before, for each object, excluding the index, you call
*	{@link #createOTInstance(String,String,int,String,String,String,String) createOTInstance}.
*	For example:
*	<pre>
*	{@code
*	createOTTable ("rmtempTable", "table1", 1, "RmtempEntry", MIBSym.CUR, "The room temperature table.")
*	createOTTableEntry ("rmtempEntry", "rmtempTable", 1,"RmtempEntry", MIBSym.CUR, "An entry or row in the room temperature table.","rmtempIndex")
*	String [] otnames = {"rmtempIndex", "rmtempVal" }
*	String [] otvals = {MIBSym.INT32, MIBSym.INT32}
*	createTableSeqType ("RmtempEntry", otnames, otvals)
*	createOTTableIndex ("rmtempIndex", "rmtempEntry", 1,"15", MIBSym.CUR,"The room number and sensor number.")
*	createOTInstance ("rmtempVal", "rmtempEntry", 2, MIBSym.INT32,  MIBSym.RO, MIBSym.CUR, "Temperature value for this sensor in this room.")
*	}
*	</pre>
*	@since 2.01.
*/
public class MIBWriter
{
	/** Print stream to which to write MIB file data */
	private PrintWriter out;

	/** Pathname of the file towhich the MIB should be written */
	private String pname;

	/** Name (supplied) of the module that is the parent or root */
	private String moduleName;	

	/** Descriptor (supplied) for the module object beneath the root*/
	private String moduleDescriptor;	

	/** Oid (supplied) for the module object referred to by the 
		moduleDescriptor and whose parent is the root  */
	private String moduleOid;	

	/** Organization (supplied) for the module  */
	private String moduleOrg;	

	/** Contact (supplied) for the module  */
	private String moduleContact;	

	/** Description (supplied) for the module  */
	private String moduleDescription;	

	/** Indicates whether any managed objects already written */
 
	private Boolean startManagedObjs = Boolean.FALSE;

	/**Constructs this object*/
	public MIBWriter ()
	{
	}


	/**
	* Constructs this object with the name specified for the MIB file.
	* @param pname pathname of the file to which the MIB should be written
	* @see MIBWriter#setMIBFile(String)
	*/
	public MIBWriter (String pname)
	{
		this.pname = pname;
	}



	/**
	* Constructs this object with the specified parameters.
	* @param pname pathname of the file to which the MIB should be written
	* @param moduleName name of the module
	* @param moduleDescriptor descriptor for the module
	* @param moduleOid number or oid of the module
	* @param moduleOrg the organization 
	* @param moduleContact contact details of the organization
	* @param moduleDescription a description of the module 
	* @see MIBWriter#setParameters(String,String,String,String,String,String)
	* @see MIBWriter#setMIBFile(String) 
	*/
	public MIBWriter (String pname,String moduleName, 
		String moduleDescriptor,
		String moduleOid, String moduleOrg, String moduleContact,
		String moduleDescription)
	{
		this (pname);
		this.moduleName = moduleName;
		this.moduleDescriptor = moduleDescriptor;
		this.moduleOid = moduleOid;
		this.moduleOrg = moduleOrg;
		this.moduleContact = moduleContact;
		this.moduleDescription = moduleDescription;
	}


	/**
	* Sets the parameters on this object.
	* @param moduleName name of the module
	* @param moduleDescriptor descriptor for the module
	* @param moduleOid number or oid of the module
	* @param moduleOrg the organization 
	* @param moduleContact contact details of the organization
	* @param moduleDescription a description of the module 
	* @see MIBWriter#MIBWriter(String,String,String,String,String,String,String)
	*/
	public void setParameters (String moduleName, String moduleDescriptor,
		String moduleOid, String moduleOrg, String moduleContact,
		String moduleDescription)
	{
		this.moduleName = moduleName;
		this.moduleDescriptor = moduleDescriptor;
		this.moduleOid = moduleOid;
		this.moduleOrg = moduleOrg;
		this.moduleContact = moduleContact;
		this.moduleDescription = moduleDescription;
	}

	/**
	* Sets the file name for the MIB file.
	* @param pname pathname of the file to which the MIB should be written
	* @see MIBWriter#MIBWriter(String)
	*/
	public void setMIBFile (String pname)
	{
		this.pname = pname;
	}
	

	/**
	*	Writes standard invariant data to the beginning of the MIB file,
	* 	such as the imported symbols, along with module details using
	*	the values supplied to the <code>MIBWriter</code>.
	*	@throws FileNotFoundException when the file cannot be opened with
	*			the pathname provided to the <code>MIBWriter</code>
	*/	
	public void startStdMIB () throws FileNotFoundException
	{
		// check data has been supplied
		checkData ();
		out = new PrintWriter (pname);
		startDefs (moduleName);
		createImports ();
		createModule (moduleName, moduleDescriptor, moduleOid, moduleOrg,
				moduleContact, moduleDescription);
	}


	/**Checks that all necessary data has been supplied including the
	filename and module details*/
	private void checkData ()
	{
		if (pname ==null)
			throw new RuntimeException ("No filename specified");
		else if ( moduleName.equals(null) 
				|| moduleDescriptor.equals(null) 
				|| moduleOid.equals(null) || moduleOrg.equals(null) 
				|| moduleContact.equals(null)  
				|| moduleDescription.equals(null))
			throw new RuntimeException ("Unspecified parameter");
/*
		if (pname.equals(""))
			throw new RuntimeException ("No filename specified");
		else if ( moduleName.equals("") || moduleDescriptor.equals("") 
				|| moduleOid.equals("") || moduleOrg.equals("") 
				|| moduleContact.equals("")  
				|| moduleDescription.equals(""))
			throw new RuntimeException ("Unspecified parameter");
*/
	}

	/**
	* Creates and prints a standard header for the <code>Wingpath</code>
	*	 MIB file
	*/

	/*=================IMPORT Methods ====================================*/

	/**
	*	Creates and prints a standard sets of imports from SNMPv2-SMI
	*	and from SNMPv2-TC. Not all imports are used.
	*/
	private void createImports ()
	{
		out.println ( MIBSym.IMPORTS );
//			SnmpAdminString from SNMP-FRAMEWORK-MIB
	
		prV2smi ();
		prV2tc ();
		prSnmpf ();
		prWingpathMIB ();
	}

	/**
	*	Prints symbols, separated by spaces,  imported from SNMPv2-SMI,
	*	with a note to say where they are from. (Currently the symbols
	*	include: <code>TimeTicks, Counter64, Gauge32, OBJECT-TYPE, </code>
	*	<code>Unsigned32, Counter32, MODULE-IDENTITY, Integer32</code>.)
	*/
	private void prV2smi ()
	{
		Set keyset =  MIBSym.v2smi.keySet();
        Object [] keys = keyset.toArray();

        String keyelement = "";
        Object keyval;
		MIBSym.indent (out, 1);
		for (int n = 0 ; n < keys.length ; n++)
        {
			keyelement = (String) keys[n];
			out.print (MIBSym.v2smi.get (keyelement) );
			if (n != (keys.length -1))	
				out.print (MIBSym.COMMA + " ");	
		}
		out.print (MIBSym.NL);
		MIBSym.indent (out, 2); 
		out.println ( MIBSym.SNMPV2SM1 + MIBSym.NL);
	}


	/**
	*	Prints symbols, separated by spaces, imported from SNMPv2-TC,
	*	with a note to say where they are from. (Currently the only symbol
	*	imported is <code>DisplayString</code>.)
	*/
	private void prV2tc ()
	{
		Set keyset =  MIBSym.v2tc.keySet();
        Object [] keys = keyset.toArray();

        String keyelement = "";
        Object keyval;
		MIBSym.indent (out,1);
		for (int n = 0 ; n < keys.length ; n++)
        {
			keyelement = (String) keys[n];
			out.print (MIBSym.v2tc.get (keyelement) );
			if (n != (keys.length -1))	
				out.print (", ");	
		}
		out.print (MIBSym.NL);
		MIBSym.indent (out,2);
		out.println ( MIBSym.SNMPV2TC + MIBSym.NL);
	}

	/**
	*	Prints symbols, separated by spaces, imported from 
	*	SNMP-FRAMEWORK-MIB
	*	with a note to say where they are from.
	*/
	private void prSnmpf ()
	{
		Set keyset =  MIBSym.snmpf.keySet();
        Object [] keys = keyset.toArray();

        String keyelement = "";
        Object keyval;
		MIBSym.indent (out,1);
		for (int n = 0 ; n < keys.length ; n++)
        {
			keyelement = (String) keys[n];
			out.print (MIBSym.snmpf.get (keyelement) );
			if (n != (keys.length -1))	
				out.print (", ");	
		}

		out.print (MIBSym.NL);
		MIBSym.indent (out, 2);
		out.println ( MIBSym.SNMPF + MIBSym.NL);
	}



	/**
	*	Prints symbols, separated by spaces, imported from Wingpath MIB,
	*	with a note to say where they are from.
	*/
	private void prWingpathMIB ()
	{
		Set keyset =  MIBSym.wmib.keySet();
        Object [] keys = keyset.toArray();

        String keyelement = "";
        Object keyval;
		MIBSym.indent (out,1);
		for (int n = 0 ; n < keys.length ; n++)
        {
			keyelement = (String) keys[n];
			out.print (MIBSym.wmib.get (keyelement) );
			if (n != (keys.length -1))	
				out.print (", ");	
		}

		out.print (MIBSym.NL);
		MIBSym.indent (out, 2);
		out.println ( MIBSym.WINGPATHMIB + MIBSym.SC + MIBSym.NL);
	}





	/*==================MODULE-INDENTITY Methods ======================*/


	// 	<descriptor> <macro> <clauses> ::= <value>
	/**
	*	Creates and prints a  <code>MODULE-IDENTITY</code> using the
	*	specified data. <p>
	*	NB Each time a revision to the MIB is made, a <code>REVISION</code> 
	*	and <code>DESCRIPTION</code> clause will need to be added to the 
	*	existing clauses, and the <code>LAST-UPDATED</code> field changed.
	*	@param moduleName name of module
	*	@param moduleDescriptor	descriptor for module 
	*	@param moduleOid oid or number of module
	*	@param moduleOrg organization 
	*	@param moduleContact contact details 
	*	@param moduleDescription description of module
	*/
	private void createModule (String moduleName, 
		String moduleDescriptor, String moduleOid, String moduleOrg, 
		String moduleContact, String moduleDescription)
	{
		out.println ( moduleDescriptor + " " 
				+ MIBSym.v2smi.get ("MODULEID"));

		//Format e.g: "201302070000Z"
		DateFormat dateformat  = new SimpleDateFormat ("yyyyMMddHHmm");
        dateformat.setTimeZone (TimeZone.getTimeZone ("UTC"));
		String lastupdated = dateformat.format(new Date()) + "Z";
		out.println (MIBSym.INDENT 
				+ "LAST-UPDATED " +   MIBSym.QUOTE + lastupdated
				+ MIBSym.QUOTE);
		out.println (MIBSym.INDENT 
				+ "ORGANIZATION "+ MIBSym.QUOTE + moduleOrg
					+ MIBSym.QUOTE);
		out.println (MIBSym.INDENT 
			+ "CONTACT-INFO " + MIBSym.QUOTE + moduleContact
					+ MIBSym.QUOTE);
				
		out.println (MIBSym.INDENT 
				+ "DESCRIPTION " + MIBSym.QUOTE +moduleDescription
					+ MIBSym.QUOTE);

       	out.println (MIBSym.INDENT 
                + "REVISION "+ MIBSym.QUOTE + lastupdated
				+ MIBSym.QUOTE);
 
        out.println (MIBSym.INDENT 
                + "DESCRIPTION " + MIBSym.QUOTE + MIBSym.REVDESC 
				+ MIBSym.QUOTE);
			

		out.println (MIBSym.INDENT + MIBSym.INAMEDS 
				+ MIBSym.MODSNMP + " " +  moduleOid
				+ MIBSym.INAMEDE + MIBSym.NL);
	}

	
	
	/*========== OBJECT INDENTIFIER Methods =======================*/


	/**
	*	Creates and prints each top-level <code>OBJECT IDENTIFIER</code>
	*	 under the root using the data provided. <p> 
	*	The <code>rootname</code> together with <code>nbranch</code> 
	* 	specify the <code>OID</code> for each instance, as in 'autosys 1', 
	*	'autosys 2'.
	*	The value of <code>desc</code> specifies the descriptor for 
	*	the macro instance. 
	*	The <code>comment</code> parameter allows you to specify a 
	*	description for the <code>OBJECT IDENTIFIER</code>, which 
	*	is useful since the syntax does not include any description 
	*	(unlike <code>OBJECT-TYPE</code>).   <p>
	* 	Some examples:<br>
	*	<pre>
	*	{@code
	*	createObjIdf ("slave1",	"slave1 - device monitoring room 3", 4, "autosys") 
	*	createObjIdf ("table1","table1 - room temperature values", 5, "autosys") 
	* 	}
	*	</pre>
	*	@param desc descriptor to use for the identifier e.g. 'Slave1', 
	*	'Slave2'. 
	*	@param comment a description for the 
	*		 <code>OBJECT IDENTIFIER</code>	e.g. 
	*			'Slave 1 - device monitoring the computer room.'
	*	@param nbranch number to use for the <code>OID</code> in the instance name 
	*			i.e. the root + branch number.
	*	@param rootname name of the root of this object (to use 
	*			with the <code>OID</code>) i.e. the root + branch number.
	*/
	public void createObjIdf (String desc, String comment, int nbranch,
			String rootname)
	{
		makeComment ( comment ) ;
		out.println (desc + " " + MIBSym.OBJIDF + " " +
			MIBSym.INAMEDS  + " " + rootname + " " +  nbranch 
					+ " " + MIBSym.INAMEDE + MIBSym.NL);
		
	}


	/*===============================================================
				OBJECT-TYPE Methods for TABLES
	=================================================================*/
	/**
	*	Creates a table, and also automatically creates for you an 
	*	entry (row), an index and an object defining the
	*	<code>SEQUENCE</code> of types. Each of these have the default 
	*	<code>STATUS</code> type <code>current</code>, and their
	*	descriptors are constructed using the  <code>stem</code> 
	*	parameter as a prefix to the standard suffixes 'Table', 'Entry',
	*	and 'Index'. (NB by convention, <code>stem</code>+'Entry', 
	*	with an initial capitalized letter, is used for the 
	*	<code>SEQUENCE</code> type that defines the syntax of the 
	*	objects in the table.<p> 
	*	This method is useful if there is no need for different status 
	*	types and if you are happy with the naming convention. 
	*	(If you need more flexibility use the overloaded method
	*	{@link #createOTTable(String,String,int,String,String,String) createOTTable}). 
	*	Once this method has been called the only remaining task is to
	*	create the objects in the sequence using this overloaded version 
	*	of {@link #createOTInstance(String,String,int,String,String,String,String) createOTInstance}.   <p>
	*	Example of the easy method to create a table:
	*	<pre>		
	*	{@code
	*	String [] names = {"humidIndex", "humidVal" };
	*	String [] vals = {MIBSym.INT32, MIBSym.INT32};
	*	createOTTable ("humid", "table2", 1, "Humidity table.", "Entry or row in humidity table.", "Room and sensor number.", names, vals);
	*	}
	*	</pre>
	*	
	*	@param stem		stem of all descriptors to be concatenated
	*					for each <code> OBJECT-TYPE</code> 
	*					created by this method 
	*					with a suffix e.g. stemval + 'Table', 
	*					stemval+'Entry' stemval+'Index'
	*	@param rname	name of the root of the subtree entered by 
	*					the user e.g. <code>table2</code> (for use 
	*					in oid naming)
	*	@param nbranch	Number of branch (to use with root for oid) e.g. 
	*					code>table2 1</code>
	*	@param tabledesc	description of the table e.g. 
	*						"Humidity table" 
	*	@param entrydesc	description of the row entry e.g. 
	*						"Entry or row in humidity table"
	*	@param indexdesc	description of the index e.g. 
	*						"Room and sensor number"
	*	@param indexestype	end of index subtype e.g. 15 as in range 1-15
	*	@param otnames 		the descriptors of the subordinate objects 
	*						including the stem and including the index 
	*						e.g. 'humidIndex', 'humidVal'.
	* 	@param otvals 		the value of the subordinate objects 
	*						<code>SYNTAX</code> clause e.g. 
	*						Integer32 {@link MIBSym#INT32}
	*	@see MIBWriter#createOTTable(String,String,int,String,String,String)	
	*	@see MIBWriter#createOTInstance(String,String,int,String,String,String,String)	
	*/ 
	public void createOTTable (String stem, String rname, int nbranch,  
		 String tabledesc, String entrydesc, String indexdesc, 
		String indexestype, String [] otnames, String [] otvals)
	{
		// Create Table (with a sequence type that is stem+Entry with 
		// a capitalized initial letter.

		String seqtype = stem.substring (0, 1);
		String rem = stem.substring (1);
		String capStem = seqtype.toUpperCase().concat (rem);
		String name = capStem.concat ("Entry");

		createOTTable (stem+"Table", rname, nbranch, name,
			MIBSym.CUR, tabledesc);

		// Create entry or row - requires INDEX object descriptor. s 
		// The access type is provided. Could use the overloaded 
		// createOTInstance where can specify an 
		// alternative index type.

		createOTTableEntry (stem+"Entry", stem+"Table", 1, name,
			MIBSym.CUR, entrydesc, stem+"Index");

		// Create the sequence type for the table

		createTableSeqType (name, otnames, otvals); 
 	
		// Think (but not sure) this has to be NOACC (if auxiliary) 
		// Could use full createOTInstance as alternative.
		
		createOTTableIndex (stem+"Index", stem+"Entry", 1, indexestype, 
			MIBSym.CUR, indexdesc);
	}


	/**
	*	Creates a table with the 
	*	invariant access-type of <code>no-access</code>,  and makes the 
	*	table a <code>SEQUENCE OF</code> the objects named by the 
	*	parameter <code>rowname</code>. (An alternative is to enter these
	*	values yourself for the <code>MAX-ACCESS</code> and 
	*	<code>SYNTAX</code> fields by calling 
	*	<code>createOTInstance</code> with the appropriate symbols from
	*	{@link MIBSym}. <p>
	*	Note that, unlike its overloaded method, if you use this 
	*	method you will still need to create a table entry (or row), 
	*	a table index, and a <code>SEQUENCE</code> type, using 
	*	{@link #createOTTableEntry(String,String,int,String,String,String,String) createOTTableEntry },
	*	{@link #createTableSeqType(String,String[],String[]) createTableSeqType }, and
	*	{@link #createOTTableIndex(String,String,int,String,String,String) createOTTableIndex } respectively. 	<p>
	*	For example:
	*	<pre>		
	*	{@code
	*	createOTTable ("rmtempTable", "table1", 1, "RmtempEntry", MIBSym.CUR, "The room temperature table.");
	*	}
	*	</pre>
	*	
	*	@param dname	Descriptor for table - entered by user  
	*					e.g. "rmtempTable"
	*	@param rname	Name of root of subtree entered by user e.g. 
	*					<code>table1</code> (for use in oid naming)
	*	@param nbranch	Number of branch (to use with root for oid) e.g. 
	*					<code>table1 1</code>
	*	@param rowname  Name of the row 'sequence type', which the table 
	*					is a 'sequence of',	e.g. 'RmtempEntry'.
	*					(This is the value of <code>SYNTAX</code>
	*	@param status	Status clause e.g. {@link MIBSym#CUR} ("current")
	*	@param desc		Description clause e.g.
	*					om Temperature table"
	*	@see MIBWriter#createOTTable(String,String,int,String,String,String,String, String[],String[])
	*	@see MIBWriter#createOTInstance(String,String,int,String,String,String,String)	
	*/
	public void createOTTable (String dname, String rname, int nbranch, 
			String rowname, String status, String desc)	
	{
		if (startManagedObjs == Boolean.FALSE)
		{
//			makeComment ( "Managed objects" );
			startManagedObjs = Boolean.TRUE;
		}
		out.println ( dname + " " + MIBSym.OBJTYPE);
		makeSyntax (MIBSym.SEQOF + rowname);
		makeMaxAcc (MIBSym.NOACC);
		makeStatus (status);
		makeDesc (desc);
		
		out.println (MIBSym.INDENT + MIBSym.INAMEDS + 
			rname + " " + nbranch + " " + 	
			MIBSym.INAMEDE + MIBSym.NL);
	}

 
	/**
	*	Creates a table entry or row  using the data provided. That is, 
	*	it creates an <code>OBJECT-TYPE</code> and itself provides the
	*	invariant access-type of <code>no-access</code>. Additionally it
	*	creates the <code>INDEX</code> clause using the specified value. <p>
	*	(You could alternatively use the overloaded createOTInstance that
	*	has an <code>INDEX</code> parameter and  
	*	enter the invariant access-type yourself. )
	*	
	*	@param dname	name of row - descriptor entered by user for 
	*					instance e.g. 'rmtempEntry'
	*	@param rname	name of root of subtree entered by user e.g. 
	*					'rmtempTable' (for use in oid naming)
	*	@param nbranch	number of branch (to use with root for oid) e.g. 
	*					<code>rmtempTable  1</code>
	*	@param syntax   name of the row 'sequence type',
	*					which the table is a '<code>SEQUENCE OF</code>', e.g.
	*					'RmtempEntry'. (This is the value of the
	*					code>SYNTAX</code> clause)
	*	@param status	status clause e.g. {@link MIBSym#CUR} ('current')
	*	@param desc		description clause e.g. 'An entry or row in the
	*					 Room Temperature table'
	*	@param index	the descriptor for the object to be the value
	*					of the <code>INDEX</code> clause e.g.'rmtempIndex'
	*/
	public void createOTTableEntry (String dname, String rname, int nbranch, 
			String syntax,  String status, String desc, String index)
	{
		out.println ( dname + " " + MIBSym.OBJTYPE );
		makeSyntax (syntax);
		makeMaxAcc (MIBSym.NOACC);
		makeStatus (status);
		makeDesc (desc);
		makeIndex (index);
		out.println (MIBSym.INDENT + MIBSym.INAMEDS + 
			rname + " " + nbranch + " " + 	
			MIBSym.INAMEDE + MIBSym.NL);
	}

	/**
	*	Makes the <code>INDEX</code> clause in the <code>OBJECT-TYPE</code>
	*	 macro instance.
	*	@param 	ival the index object descriptor.
	*/
	private void makeIndex (String ival)
	{
		out.println (MIBSym.INDENT + "INDEX\t\t" + "{" + ival+" }" );
	}


	/**
	* Creates an object that defines the <code>SEQUENCE</code> type that the
	* table is a <code>SEQUENCE OF</code> (and is the value of the 
	* <code>SYNTAX</code> clause of the table). The <code>SEQUENCE</code> type
	* is a sequence of the subordinate objects as defined by the descriptors
	* provided and their <code>syntax</code> values (<code>otnames</code>  
	* and <code>otvals</code> respectively).
	* 
	* @param rowsyntax 	the name of the object that defines the row syntax
	*					e.g. 'RmTempEntry' 
	* @param otnames 	the descriptors of the subordinate objects 
	*					e.g. 'rmtempVal'
	* @param otvals the value of the subordinate objects 
	*					<code>SYNTAX</code> lause e.g.
	*					 <code>Integer32</code> defined at
	*		 			{@link MIBSym#INT32}. Not that sub-typing is not
	*					allowed here.
	*/
	public void createTableSeqType (String rowsyntax, String [] otnames,
			 String [] otvals )
	{
		out.println (
			rowsyntax + " " + MIBSym.INAMED + MIBSym.SEQ + " {");
		for (int n = 0 ; n < otnames.length ; n++)
		{
			out.print (MIBSym.INDENT + otnames [n]);
			out.print (MIBSym.INDENT + otvals [n]);
			
			if  (n < (otnames.length - 1))
				out.println (MIBSym.COMMA);
			else
				out.println ("");
		}
		out.println ("}" +MIBSym.NL);
	}	


	/** 
	*	Creates the index object for the table. Access is provided
	*	as <code>no-access</code> (I think, but am not sure, that this
	*	is mandatory if it is auxiliary, and I think it is.)<p> 
	*	This also provides a standard syntax value for the index, 
	*	as well as part of the range for
	*	positive sub-typing as in "<code>Integer 32 (1..</code>", it
	*	is up to you to provide the number for the end of the range 
	* 	via the parameter <code>endstype</code> . 
	*	(You can always use the standard {@link #createOTInstance(String,String,int,String,String,String,String) createOTInstance} if you want to be more 
	*	flexible.) 
	*	@param dname	name of index - descriptor entered by user for 
	*					instance e.g. 'rmtempIndex'
	*	@param rname	name of root of subtree entered by user e.g. 
	*					'rmtempEntry' (for use in oid naming)
	*	@param nbranch	number of branch (to use with root for oid) e.g. 
	*					<code>rmtempEntry  1</code>
	* 	@param endstype	end of syntax subtype  e.g. '15' as in '1-15' 
	*					modbus registers.e
	*	@param status	status clause e.g. {@link MIBSym#CUR} ('current')
	*	@param desc		description clause 
	*					e.g.'Room number and sensor number'
	*	@see MIBWriter#createOTInstance(String,String,int,String,String,String,String)	
	*/	
	public void createOTTableIndex (String dname, String rname, int nbranch, 
			String endstype, String status, String desc ) 
	{

		out.println ( dname + " " + MIBSym.OBJTYPE );
		makeSyntax (MIBSym.INT32 + " " + MIBSym.SPSTYPE 
				+ endstype + MIBSym.ESTYPE);
		makeMaxAcc (MIBSym.NOACC);
		makeStatus (status);
		makeDesc (desc);
		out.println (MIBSym.INDENT + MIBSym.INAMEDS + 
			rname + " " + nbranch + " " + 	
			MIBSym.INAMEDE + MIBSym.NL);
	}


	/**
	*	Creates an <code>OBJECT-TYPE</code> for a table entry or row, 
	*	which also includes an <code>INDEX</code> clause. 
	*	
	*	@param dname	Descriptor entered by user for instance 
	*					e.g. 'rmtempEntry'
	*	@param rname	Name of root of subtree entered by user 
	*					e.g. 'rmtempTable' (for use in oid naming)
	*	@param nbranch	Number of branch (to use with root for oid) 
	*					e.g. 'rmtempTable 1'
	*	@param syntax	Syntax clause e.g. 'RmtempEntry'
	*	@param maxacc	Max-access clause e.g. {@link MIBSym#NOACC} 
	*					('not-accessible')
	*	@param status	Status clause e.g. {@link MIBSym#CUR} ('current')
	*	@param desc		Description clause e.g.
	*					'An entry or row in the room temperature table'
	*	@param indexval	the descriptor for the Index object 
	*					e.g. 'rmtempIndex'.
	*/
	public void createOTInstance (String dname, String rname, 
			int nbranch, String syntax, String maxacc, String status, 
			String desc, String indexval )
	
	{
		if (startManagedObjs == Boolean.FALSE)
		{
//			makeComment ( "Managed objects" );
			startManagedObjs = Boolean.TRUE;
		}
	
		out.println ( dname + " " +  MIBSym.v2smi.get ("OBJTYPE"));
		
		makeSyntax (syntax);	
		makeMaxAcc (maxacc);
		makeStatus (status);
		makeDesc (desc);
		makeIndex (indexval);
		out.println (MIBSym.INDENT + MIBSym.INAMEDS + 
			rname + " " + nbranch + " " + 	
			MIBSym.INAMEDE + MIBSym.NL);
	}

	/*===============================================================
						OBJECT-TYPE Methods
	================================================================*/

	/**	
	*	Creates and prints an instance of an <code>OBJECT-TYPE</code>
	*	for all instance-types EXCEPT where it contains an 
	* 	<code>INDEX</code> clause and 
	*	EXCEPT where the value of the <code>SYNTAX</code> 
	*	clause is <code>BITS</code> (see the overloaded methods). <p>
	*	The descriptor and root names (<code>dname</code> and 
	*	<code>rname</code>) will have been entered by the 
	*   user, and the branch is the nth branch of this subtree. 
	*	The clause parameters, namely 
	*	<code>syntax, maxacc, status,</code> and <code>desc</code>, 
	*	can be specified using symbols from the class {@link MIBSym}.<p>
	*   For example:
	*	<pre>
	*	{@code 
	*	createOTInstance("d1sensor1","slave1", 2, MIBSym.INT32, MIBSym.RO, MIBSym.CUR, "Temperature 3")
	*	}
	*	</pre>
	*	@param dname	Descriptor entered by user for instance e.g. 
	*					'd1sensor1'
	*	@param rname	Name of root of subtree entered by user e.g. 
	*					'Slave1' 	(for use in oid naming)
	*	@param nbranch	Number of branch (to use with root for oid)
	*					e.g. 'Slave1 2'
	*	@param syntax	Syntax clause e.g. {@link MIBSym#UINT32} 
	*					('Unsigned32')
	*	@param maxacc	Max-access clause e.g. {@link MIBSym#RO} 
	*					( 'read-only')
	*	@param status	Status clause e.g. {@link MIBSym#CUR} ('current')
	*	@param desc		Description clause e.g.'Temperature of water'
	*	@see MIBWriter#createOTInstance(String,String,int,String[],String,String,String)  createOTInstance 
	*/
	public void createOTInstance (String dname, String rname, 
		int nbranch, String syntax, String maxacc, String status, 
		String desc )
	{
		if (startManagedObjs == Boolean.FALSE)
		{
//			makeComment ( "Managed objects" );
			startManagedObjs = Boolean.TRUE;
		}
	
		out.println ( dname + " " +  MIBSym.v2smi.get ("OBJTYPE"));
		
		makeSyntax (syntax);	
		makeMaxAcc (maxacc);
		makeStatus (status);
		makeDesc (desc);
		out.println (MIBSym.INDENT + MIBSym.INAMEDS + 
			rname + " " + nbranch + " " + 	
			MIBSym.INAMEDE + MIBSym.NL);
	}

	/**	
	*	Creates and prints an instance of an <code>OBJECT-TYPE</code> 
	*	that has a <code>SYNTAX</code> value of type <code>BITS</code>
	*	It is not necessary to supply the <code>BITS</code> symbol,
	*  	just an array of the bit names. <p>
	*   For example:
	*	<pre>
	*	{@code 
	*	String [] bitnames = {getAndGetNext, set, getBulk}
	*	createOTInstance("nsmodulename","nsmoduleEnry", 5, bitnames, MIBSym.RO, MIBSym.CUR, "The modes that the particular lower level handler can cope with directly."
	*	}
	*	</pre>
	*	@param dname	Descriptor entered by user for instance e.g. 
	*					'schedmonth'
	*	@param rname	Name of root of subtree entered by user e.g. 
	*					'schedEntry' 	(for use in oid naming)
	*	@param nbranch	Number of branch (to use with root for oid)
	*					e.g. '6' as in 'schedEntry 6'
	*	@param bitnames	the names of the bits to be used in the 
	*					<code>SYNTAX</code> clause.
	*	@param maxacc	Max-access clause e.g. {@link MIBSym#RO} 
	*					( 'read-only')
	*	@param status	Status clause e.g. {@link MIBSym#CUR} ('current')
	*	@param desc		Description clause e.g.'Set of months'
	*	@see MIBWriter#createOTInstance(String,String,int,String,String,String,String)  createOTInstance 
	*/
	public void createOTInstance (String dname, String rname, 
		int nbranch, String [] bitnames, String maxacc, String status, 
		String desc )
	{
		if (startManagedObjs == Boolean.FALSE)
		{
//			makeComment ( "Managed objects" );
			startManagedObjs = Boolean.TRUE;
		}
	
		out.println ( dname + " " +  MIBSym.v2smi.get ("OBJTYPE"));
		
		makeSyntaxBITS (bitnames);
	
		makeMaxAcc (maxacc);
		makeStatus (status);
		makeDesc (desc);
		out.println (MIBSym.INDENT + MIBSym.INAMEDS + 
			rname + " " + nbranch + " " + 	
			MIBSym.INAMEDE + MIBSym.NL);
	}






	/**
	*	This creates and prints the <code>SYNTAX</code> clause in the 
	*   <code>OBJECT-TYPE</code> macro instance. 
	*	<code>sval</code> may be a basetype or a textual convention.
	*	<code>SEQUENCE</code> or <code>SEQUENCE OF</code> are also possible in 
	*	conceptual tables. (The <code>BITS</code> type is dealt with by
	*	the method {@link #makeSyntaxBITS}. <p>
	*	Subtyping of the following kinds is allowed for the 
	*	<code>BITS</code> type and these basetypes:	<br>
	*	<pre>
	*	{@code
	*   Range changes : Unsigned32, Integer32, Guage32
	*	Size changes  : OCTET STRING>
	*	Range & Enumeration changes:INTEGER
	*	Enumeration changes: BITS}
	*	</pre>
	*	No subtyping is allowed for: 
	*	<pre>
	*	{@code
	*	Counter32, Counter64, TimeTicks, IpAddress, Opaque, OBJECT IDENTIFIER, SEQUENCE}
	*	</pre>
	*	@param sval the value of the <code>SYNTAX</code> clause.
	*/
	private void makeSyntax (String sval)
	{
		//deal with size subtype
		if (sval == MIBSym.OCTSTR)
			out.println (MIBSym.INDENT + "SYNTAX\t\t" + sval );
		else	
			out.println (MIBSym.INDENT + "SYNTAX\t\t" + sval );
	}


	/**
	*	This creates and prints the <code>SYNTAX</code> clause in the 
	*   <code>OBJECT-TYPE</code> macro instance for the  
	*	<code>SYNTAX</code> value <code>BITS</code> along with the 
	*	bit names provided. <p>
	*	Subtyping of the following kinds is allowed for the 
	*	<code>BITS</code> type:	<br>
	*	<pre>
	*	{@code
	*	Enumeration changes: BITS
	*	}
	*	</pre>
	*	@param bitnames the list of bitnames supplied for the 
	*			<code>BITS</code>value of the <code>SYNTAX</code> clause.
	*/
	private void makeSyntaxBITS (String [] bitnames)
	{
		String err = "No bit names for the syntax clause";
			
		if (bitnames == null || bitnames.length == 0)
			throw new IllegalArgumentException (err);

		out.print (MIBSym.INDENT + "SYNTAX\t\t" + MIBSym.BITS +  " " 
			+MIBSym.SBITS + " ") ;
		for (int n = 0; n < bitnames.length; n++ )
		{
			if (n > 0)
				out.print (MIBSym.COMMA + " ");
			out.print (bitnames[n] + "(" + n + ")");
		}
		out.println (MIBSym.EBITS );
		
	}

	/**
	*	This creates and prints the <code>MAX-ACCESS</code> clause of 
	*	the <code>OBJECT-TYPE</code> macro instance.
	*	@param acc the access allowed for this instance. 
	*			Possible values are (in order of least to greatest 
	*			access):	<code>not-accessible</code>, 
	*	<code>accessible-for-notify</code>, <code>read-only</code>, 
	*	<code>read-write</code>, <code>read-create</code>. These
	*	are defined constants in {@link MIBSym}.
	*/
	private void makeMaxAcc( String acc)
	{
		out.println (MIBSym.INDENT + "MAX-ACCESS\t" + acc);
	}


	/**
	*	Makes the <code>STATUS</code> clause in the <code>OBJECT-TYPE</code>
	*	macro instance.
	*	@param 	status the status of this instance. Possible values are:
	*				<code>current</code>, <code>obsolete</code> and 
	*				<code>deprecated</code>, which are
	*				defined constants in {@link MIBSym}.
	*/
	private void makeStatus ( String status)
	{
		out.println (MIBSym.INDENT + "STATUS\t\t" + status );
	}


	/**
	*	Makes the <code>DESCRIPTION</code> clause in the 
	*	<code>OBJECT-TYPE</code> macro instance.
	*	This prints the text provided within quotes.
	*	@param desc the description of this instance.
	*/
	private void makeDesc (String desc)
	{
		out.println (MIBSym.INDENT + "DESCRIPTION\t" 
			+ MIBSym.QUOTE + desc + MIBSym.QUOTE);
	}


	
	/*=============MISCELLANEOUS Methods ==========================*/

	/**
	*	Constructs and prints a comment
	*	@param comment any text that want in a comment
	*/
	public void  makeComment (String comment)
	{
		out.print (MIBSym.CMT);
		out.println (comment);
	}

	/*==============DEFINITIONS Methods============================*/

	/**
	*	Starts the  <code>DEFINITIONS</code> part of the MIB.
	*	@param moduleName name of the parent or root.  
	*/
	private void startDefs (String moduleName)
	{
		out.println (moduleName + " " + MIBSym.DEFSTART 
			+ MIBSym.NL);
	}

	/**
	*	Ends the <code>DEFINITIONS</code> in the MIB file then 
	*	flushes	the output buffer and closes the file.
	*	@throws IOException thrown if an error upon closing 
	*			is found to have occurred at some time.
	*/
	public void endDefs () throws IOException
	{
		out.println (MIBSym.DEFEND + MIBSym.NL);
		out.flush ();
		closeFile ();
	}

	/**
	*	Closes the file after checking for errors.
	*	@throws IOException if an error was found when closing the file.
	*/	
	private void closeFile () throws IOException
	{
		boolean err = out.checkError();
		out.close();
		
		if (err)
		{
//			out.clearError();
			throw new IOException ("Error writing to MIB file");		
		}
	}


	/*====================  Main test method =======================*/

	public static void main (String args [])
	{

		MIBWriter mibw = new MIBWriter (
			"/home/monica/ModSNMP/src/myorg.mib", 
			"myorg", "autosys", "3", "Myorg Ltd", 
			"support@myorg.com", 
			"Root of Myorg MIB for Modbus");

		//create standard, invariant  parts of MIB file
		try 
		{ 	
			mibw.startStdMIB();
		}
		catch (FileNotFoundException fnfe)
		{
			System.exit (1);
		}
	
		// this controlled by slaves configured & what name user enters
		// BUT also maybe by branches that span slaves for tables

		String comm = " - device monitoring room ";
		String rootname = "autosys";

		for (int n = 1, nbranch = 1 ; n <= 3 ; n++, nbranch++)
			mibw.createObjIdf ("slave"+n,("slave"+n)+ (comm + n), 
				nbranch, rootname);

		// for tables MAYBE
		String [] des = {" - room temperature values", 
				" - room humidity values"};
		for (int i = 1, nbranch =4 ; i <=2 ; i++, nbranch++)
		{
			mibw.createObjIdf ("table"+i,("table"+i)+ des[(i-1)], 
				nbranch, rootname);
		}

		// this controlled by slaves & registers configured
			
		for ( int p = 0 ; p < 3 ; p++ )
		{
			for (int n = 1, nbranch = 1 ; n <= 2 ; n++, nbranch++)
			{
				mibw.createOTInstance ("d" + (p+1) + "sensor" + n, 
					"slave" + (p+1), nbranch, 
					MIBSym.INT32, MIBSym.RO, MIBSym.CUR, 
					"Temperature " + n );
		//	MIBSym.INT32, MIBSym.RO, MIBSym.CUR, "\"Temperature " + n + "\"");
			}
		}

		// this testing the BITS parameter
		String [] bitnames = {"January", "February","March", "April", "May", "June","July", "August", "September", "October", "November","December"};
		String desc = 
			"The set of months during which the scheduled action should take place. Setting multiple bits will include several months in the set of possible months for this schedule.\nSetting all bits will cause the scheduler to ignore the month.\nNote that implementations which maintain a list of pending activations must re-calculate them when this object is changed.";
		mibw.createOTInstance ("schedMonth","schedEntry", 6, 
			bitnames, MIBSym.RW, MIBSym.CUR, desc);
		String [] bitnames2 = {"getAndGetNext", "set", "getBulk"};
		mibw.createOTInstance ("nsModuleModes","nsModuleEntry", 5,  
			bitnames2, MIBSym.RO, MIBSym.CUR,"The modes that the particular lower level handler can cope with directly."); 

		//==========================================================

		// Create Table -access type is provided.
		mibw.createOTTable ("rmtempTable", "table1", 1, "RmtempEntry", 
			MIBSym.CUR, "The room temperature table.");

		// Create entry or row - requires INDEX object descriptor. s 
		// The access type is provided. Could use the overloaded 
		// createOTInstance where can specify an 
		// alternative index type.

		mibw.createOTTableEntry ("rmtempEntry", "rmtempTable", 1,
			"RmtempEntry",
			MIBSym.CUR, "An entry or row in the room temperature table.",
			"rmtempIndex");

		// Create the sequence type for the table
		// Note that sub-typing is not allowed here
		String [] otnames = {"rmtempIndex", "rmtempVal" };
		String [] otvals = {MIBSym.INT32, MIBSym.INT32};

		mibw.createTableSeqType ("RmtempEntry", otnames, otvals); 
 	
		// Think (but not sure) this has to be NOACC (if auxiliary) 
		// Could use full createOTInstance as alternative.
		
		mibw.createOTTableIndex ("rmtempIndex", "rmtempEntry", 1, "15", 
			MIBSym.CUR,"The room number and sensor number.");

		// No special method required for content od table since just
		// ordinary <code>OBJECT-TYPE</code>
		mibw.createOTInstance ("rmtempVal", "rmtempEntry", 2,
				 MIBSym.INT32,  MIBSym.RO, MIBSym.CUR,
				"Temperature value for this sensor in this room.");


		// easy method of creating tables
		// Note that sub-typing is not allowed here

		String [] names = {"humidIndex", "humidVal" };
		String [] vals = {MIBSym.INT32, MIBSym.INT32};
		
		mibw.createOTTable ("humid", "table2", 1,  
			 "Humidity table.", "Entry or row in humidity table.",
			"Room and sensor number.","15", names, vals);

		mibw.createOTInstance ("humidVal", "humidEntry", 2,
				 MIBSym.INT32,  MIBSym.RO, MIBSym.CUR,
				"Temperature value for this sensor in this room.");

		//=========================================================

		try 
		{
			mibw.endDefs();
		} 
		catch (IOException ioe)
		{
			System.exit (1);
		}
	}
 }



