//------------------------------------------------------------------------------
// Desc:	DOM Nodes tests
// Tabs:	3
//
// Copyright (c) 2006-2007 Novell, Inc. All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version 2.1
// of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail, 
// you may find current contact information at www.novell.com.
//
// $Id$
//------------------------------------------------------------------------------

using System;
using System.IO;
using System.Runtime.InteropServices;
using xflaim;

namespace cstest
{

	//--------------------------------------------------------------------------
	// Open database test.
	//--------------------------------------------------------------------------
	public class DOMNodesTest : Tester
	{
		private const uint NUM_CHILD_NODES = 500;
		private const uint BUILTIN_ATTRIBUTES =
			((uint)ReservedAttrTag.XFLM_LAST_RESERVED_ATTRIBUTE_TAG -
			 (uint)ReservedAttrTag.XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 1);

		public bool createDocumentTest(
			Db	db)
		{
			DOMNode		doc = null;
			DOMNode		docRoot = null;
			DOMNode		node = null;
			DOMNode		node2 = null;
			DOMNode		attr = null;
			ulong			ulDocId;
			ulong			ulNodeId;
			ulong			ulDocRootId;
			uint			uiTag;
			FlmDataType	eDataType;
			uint			uiSetValue = 12345;
			uint			uiGetValue;
			ulong			ulSetValue = 123456;
			ulong			ulGetValue;
			int			iSetValue = -12345;
			int			iGetValue;
			long			lSetValue = -123456;
			long			lGetValue;
			string		sSetValue = "String value";
			string		sGetValue = null;
			byte []		ucSetValue = new byte [] {0x01, 0x02, 0x03, 0x04, 0x05};
			byte []		ucGetValue = null;
			RCODE			rc;

			// Create a document
			
			beginTest( "Create document test");
			try
			{
				doc = db.createDocument( (uint)PredefinedXFlaimCollections.XFLM_DATA_COLLECTION);
			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "creating document");
				return( false);
			}

			// Create a node - can only be one element node subordinate to a document node.

			try
			{
				docRoot = doc.createNode( eDomNodeType.ELEMENT_NODE,
								(uint)ReservedElmTag.ELM_ELEMENT_TAG,
								eNodeInsertLoc.XFLM_FIRST_CHILD,
								docRoot);

			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "creating node");
				return( false);
			}

			// Create a node subordinate to the root element node.

			try
			{
				node = docRoot.createNode( eDomNodeType.ELEMENT_NODE,
					(uint)ReservedElmTag.ELM_ELEMENT_TAG,
					eNodeInsertLoc.XFLM_FIRST_CHILD,
					node);

			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "creating node");
				return( false);
			}

			// Get the document ID

			try
			{
				ulDocId = node.getDocumentId();
			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "calling getDocumentId");
				return( false);
			}

			// Get the node ID

			try
			{
				ulNodeId = doc.getNodeId();
				ulDocRootId = docRoot.getNodeId();
			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "calling getNodeId");
				return( false);
			}
			if (ulNodeId != ulDocId)
			{
				endTest( false, false);
				System.Console.WriteLine( "Incorrect document ID: NodeID: {0}, DocID: {1}",
						ulNodeId, ulDocId);
				return( false);
			}

			// Create a bunch of siblings and add attributes to them
	
			for (uint uiLoop = 0; uiLoop < NUM_CHILD_NODES - 1; uiLoop++)
			{
				try
				{
					node2 = node.createNode( eDomNodeType.ELEMENT_NODE,
						(uint)ReservedElmTag.ELM_ELEMENT_TAG,
						uiLoop % 2 == 0 ? eNodeInsertLoc.XFLM_NEXT_SIB : eNodeInsertLoc.XFLM_PREV_SIB,
						node2);
				}
				catch (XFlaimException ex)
				{
					endTest( false, ex, "calling createNode");
					return( false);
				}

				// Node better be an element node, and better not have child nodes.

				try
				{
					if (node2.getNodeType() != eDomNodeType.ELEMENT_NODE)
					{
						endTest( false, false);
						System.Console.WriteLine( "Illegal node type returned from getNodeType: {0}",
							node2.getNodeType());
						return( false);
					}
				}
				catch (XFlaimException ex)
				{
					endTest( false, ex, "calling getNodeType");
					return( false);
				}

	
				try
				{
					if (node2.hasChildren())
					{
						endTest( false, false);
						System.Console.WriteLine( "Node erroneously claims to have children");
						return( false);
					}
				}
				catch (XFlaimException ex)
				{
					endTest( false, ex, "calling hasChildren");
					return( false);
				}

				// Create an attribute and make sure it is present.

				uiTag = (uint)ReservedAttrTag.XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + (uiLoop % BUILTIN_ATTRIBUTES);
		
				try
				{
					attr = node2.createAttribute( uiTag, attr);
				}
				catch (XFlaimException ex)
				{
					endTest( false, ex, "calling createAttribute");
					return( false);
				}

				try
				{
					if (!node2.hasAttribute( uiTag))
					{
						endTest( false, false);
						System.Console.WriteLine( "Node is missing an attribute");
						return( false);
					}
				}
				catch (XFlaimException ex)
				{
					endTest( false, ex, "calling hasAttribute");
					return( false);
				}

				// Look up the tag's data type and set an appropriate value
				// Then retrieve it again for verification

				try
				{
					eDataType = attr.getDataType();
				}
				catch (XFlaimException ex)
				{
					endTest( false, ex, "calling getDataType");
					return( false);
				}

				switch (eDataType)
				{
					case FlmDataType.XFLM_NUMBER_TYPE:
						if (uiLoop % 4 == 1)
						{
							ulGetValue = ulSetValue + 1;
							try
							{
								attr.setULong( ulSetValue);
							}
							catch (XFlaimException ex)
							{
								endTest( false, ex, "calling setULong");
								return( false);
							}
							try
							{
								ulGetValue = attr.getULong();
							}
							catch (XFlaimException ex)
							{
								endTest( false, ex, "calling getULong");
								return( false);
							}
							if (ulSetValue != ulGetValue)
							{
								endTest( false, false);
								System.Console.WriteLine( "ulSetValue {0} != ulGetValue {1}",
									ulSetValue, ulGetValue);
								return( false);
							}
						}
						else if (uiLoop % 4 == 2)
						{
							lGetValue = lSetValue + 1;
							try
							{
								attr.setLong( lSetValue);
							}
							catch (XFlaimException ex)
							{
								endTest( false, ex, "calling setLong");
								return( false);
							}
							try
							{
								lGetValue = attr.getLong();
							}
							catch (XFlaimException ex)
							{
								endTest( false, ex, "calling getLong");
								return( false);
							}
							if (lSetValue != lGetValue)
							{
								endTest( false, false);
								System.Console.WriteLine( "lSetValue {0} != lGetValue {1}",
									lSetValue, lGetValue);
								return( false);
							}
						}
						else if (uiLoop % 4 == 3)
						{
							uiGetValue = uiSetValue + 1;
							try
							{
								attr.setUInt( uiSetValue);
							}
							catch (XFlaimException ex)
							{
								endTest( false, ex, "calling setUInt");
								return( false);
							}
							try
							{
								uiGetValue = attr.getUInt();
							}
							catch (XFlaimException ex)
							{
								endTest( false, ex, "calling getUInt");
								return( false);
							}
							if (uiSetValue != uiGetValue)
							{
								endTest( false, false);
								System.Console.WriteLine( "uiSetValue {0} != uiGetValue {1}",
									uiSetValue, uiGetValue);
								return( false);
							}
						}
						else
						{
							iGetValue = iSetValue + 1;
							try
							{
								attr.setInt( iSetValue);
							}
							catch (XFlaimException ex)
							{
								endTest( false, ex, "calling setInt");
								return( false);
							}
							try
							{
								iGetValue = attr.getInt();
							}
							catch (XFlaimException ex)
							{
								endTest( false, ex, "calling getInt");
								return( false);
							}
							if (iSetValue != iGetValue)
							{
								endTest( false, false);
								System.Console.WriteLine( "iSetValue {0} != iGetValue {1}",
									iSetValue, iGetValue);
								return( false);
							}
						}
						break;
					case FlmDataType.XFLM_TEXT_TYPE:
						sGetValue = "";
						try
						{
							attr.setString( sSetValue);
						}
						catch (XFlaimException ex)
						{
							endTest( false, ex, "calling setString");
							return( false);
						}
						try
						{
							sGetValue = attr.getString();
						}
						catch (XFlaimException ex)
						{
							endTest( false, ex, "calling getString");
							return( false);
						}
						if (sSetValue != sGetValue)
						{
							endTest( false, false);
							System.Console.WriteLine( "sSetValue [{0}] != sGetValue [{1}]",
								sSetValue, sGetValue);
							return( false);
						}
						break;
					case FlmDataType.XFLM_BINARY_TYPE:
					{
						bool	bDataSame;

						ucGetValue = null;
						try
						{
							attr.setBinary( ucSetValue);
						}
						catch (XFlaimException ex)
						{
							endTest( false, ex, "calling setBinary");
							return( false);
						}
						try
						{
							ucGetValue = attr.getBinary();
						}
						catch (XFlaimException ex)
						{
							endTest( false, ex, "calling getBinary");
							return( false);
						}
						bDataSame = true;
						if (ucSetValue.Length != ucGetValue.Length)
						{
							bDataSame = false;
						}
						else
						{
							for( uint uiLoop2 = 0; uiLoop2 < ucSetValue.Length; uiLoop2++)
							{
								if (ucSetValue [uiLoop2] != ucGetValue [uiLoop2])
								{
									bDataSame = false;
									break;
								}
							}
						}
						if (!bDataSame)
						{
							endTest( false, false);
							System.Console.WriteLine( "Set binary data does not match get binary data");
							System.Console.Write( "Set Binary Data Length: {0}\n[", ucSetValue.Length);
							for( uint uiLoop2 = 0; uiLoop2 < ucSetValue.Length; uiLoop2++)
							{
								System.Console.Write( "{0} ", ucSetValue [uiLoop2]);
							}
							System.Console.WriteLine( "]");
							System.Console.Write( "Get Binary Data Length: {0}\n[", ucGetValue.Length);
							for( uint uiLoop2 = 0; uiLoop2 < ucGetValue.Length; uiLoop2++)
							{
								System.Console.Write( "{0} ", ucGetValue [uiLoop2]);
							}
							System.Console.WriteLine( "]");
							return( false);
						}
						break;
					}
					default:
						endTest( false, false);
						System.Console.WriteLine( "Invalid data type for attr {0}", eDataType);
						return( false);
				}

				// Since there's only one attribute, either one of these functions will do
				if ( (uiLoop % 2) == 0) 
				{
					try
					{
						attr = node2.getFirstAttribute( attr);
					}
					catch (XFlaimException ex)
					{
						endTest( false, ex, "calling getFirstAttribute");
						return( false);
					}
				}
				else
				{
					try
					{
						attr = node2.getAttribute( uiTag, attr);
					}
					catch (XFlaimException ex)
					{
						endTest( false, ex, "calling getAttribute");
						return( false);
					}
				}

				// We gave these nodes one and only one attribute
				// The attributes should have no siblings

				rc = RCODE.NE_XFLM_OK;
				try
				{
					node2 = attr.getPreviousSibling( node2);
				}
				catch (XFlaimException ex)
				{
					rc = ex.getRCode();
					if (rc != RCODE.NE_XFLM_DOM_NODE_NOT_FOUND)
					{
						endTest( false, ex, "calling getPreviousSibling");
						return( false);
					}
				}
				if (rc == RCODE.NE_XFLM_OK)
				{
					endTest( false, false);
					System.Console.WriteLine( "Attribute should not have a previous sibling");
					return( false);
				}

				rc = RCODE.NE_XFLM_OK;
				try
				{
					node2 = attr.getNextSibling( node2);
				}
				catch (XFlaimException ex)
				{
					rc = ex.getRCode();
					if (rc != RCODE.NE_XFLM_DOM_NODE_NOT_FOUND)
					{
						endTest( false, ex, "calling getNextSibling");
						return( false);
					}
				}
				if (rc == RCODE.NE_XFLM_OK)
				{
					endTest( false, false);
					System.Console.WriteLine( "Attribute should not have a next sibling");
					return( false);
				}
			}

			// Document node should now have children.

			try
			{
				if (!docRoot.hasChildren())
				{
					endTest( false, false);
					System.Console.WriteLine( "Document root erroneously reports that it has no child nodes");
					return( false);
				}
			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "calling hasChildren");
				return( false);
			}

			// Reposition to the first child under the document root,
			// iterate through its children (first->last) and perform 
			// various DOMNode ops

			for (uint uiLoop = 0; uiLoop < NUM_CHILD_NODES; uiLoop++)
			{
				if (uiLoop == 0)
				{
					try
					{
						node = docRoot.getFirstChild( node);
					}
					catch (XFlaimException ex)
					{
						endTest( false, ex, "calling getFirstChild");
						return( false);
					}
				}
				else
				{
					try
					{
						node = node.getNextSibling( node);
					}
					catch (XFlaimException ex)
					{
						endTest( false, ex, "calling getNextSibling");
						return( false);
					}
				}

				try
				{
					if (node.getParentId() != ulDocRootId)
					{
						endTest( false, false);
						System.Console.WriteLine( "Node's parent ID {0} does not match document id {1}",
								node.getParentId(), ulDocId);
						return( false);
					}
				}
				catch (XFlaimException ex)
				{
					endTest( false, ex, "calling getParentId");
					return( false);
				}
			}

			// There should be no more siblings

			rc = RCODE.NE_XFLM_OK;
			try
			{
				node = node.getNextSibling( node);
			}
			catch (XFlaimException ex)
			{
				rc = ex.getRCode();
				if (rc != RCODE.NE_XFLM_DOM_NODE_NOT_FOUND)
				{
					endTest( false, ex, "calling getNextSibling");
					return( false);
				}
			}
			if (rc == RCODE.NE_XFLM_OK)
			{
				endTest( false, false);
				System.Console.WriteLine( "Should have been no more next siblings");
				return( false);
			}
			endTest( false, true);


			beginTest( "Delete DOM nodes test");

			// Move backwards through the siblings deleting them (except the last one)

			for (uint uiLoop = 0; uiLoop < NUM_CHILD_NODES; uiLoop++)
			{
				if (uiLoop == 0)
				{
					try
					{
						node = docRoot.getLastChild( node);
					}
					catch (XFlaimException ex)
					{
						endTest( false, ex, "calling getLastChild");
						return( false);
					}
				}
				else
				{
					try
					{
						node2 = node.getPreviousSibling( node2);
					}
					catch (XFlaimException ex)
					{
						endTest( false, ex, "calling getPreviousSibling");
						return( false);
					}
					try
					{
						node.deleteNode();
					}
					catch (XFlaimException ex)
					{
						endTest( false, ex, "calling deleteNode");
						return( false);
					}
					node = node2;
					node2 = null;
				}
			}

			// There should be no more siblings

			rc = RCODE.NE_XFLM_OK;
			try
			{
				node2 = node.getPreviousSibling( node2);
			}
			catch (XFlaimException ex)
			{
				rc = ex.getRCode();
				if (rc != RCODE.NE_XFLM_DOM_NODE_NOT_FOUND)
				{
					endTest( false, ex, "calling getPreviousSibling");
					return( false);
				}
			}
			if (rc == RCODE.NE_XFLM_OK)
			{
				endTest( false, false);
				System.Console.WriteLine( "Should have been no more previous siblings");
				return( false);
			}
			try
			{
				node.deleteNode();
			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "calling deleteNode");
				return( false);
			}
			node = null;

			endTest( false, true);
			
			// Test next/previous document.

			beginTest( "Next/Previous Document");

			// Create a 2nd document.

			try
			{
				node = db.createDocument( (uint)PredefinedXFlaimCollections.XFLM_DATA_COLLECTION);
			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "creating document");
				return( false);
			}

			// Make sure 1st document has a next document.

			try
			{
				node2 = doc.getNextDocument( node2);
			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "calling getNextDocument");
				return( false);
			}

			// Make sure 2nd document does not have a next document.

			rc = RCODE.NE_XFLM_OK;
			try
			{
				node2 = node.getNextDocument( node2);
			}
			catch (XFlaimException ex)
			{
				rc = ex.getRCode();
				if (rc != RCODE.NE_XFLM_DOM_NODE_NOT_FOUND)
				{
					endTest( false, ex, "calling getNextDocument");
					return( false);
				}
			}
			if (rc == RCODE.NE_XFLM_OK)
			{
				endTest( false, false);
				System.Console.WriteLine( "Document should NOT have a next document");
				return( false);
			}

			// Make sure 2nd document has a previous document

			try
			{
				node2 = node.getPreviousDocument( node2);
			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "calling getPreviousDocument");
				return( false);
			}

			// Make sure 1st document does not have a previous document.

			rc = RCODE.NE_XFLM_OK;
			try
			{
				node2 = doc.getPreviousDocument( node2);
			}
			catch (XFlaimException ex)
			{
				rc = ex.getRCode();
				if (rc != RCODE.NE_XFLM_DOM_NODE_NOT_FOUND)
				{
					endTest( false, ex, "calling getPreviousDocument");
					return( false);
				}
			}
			if (rc == RCODE.NE_XFLM_OK)
			{
				endTest( false, false);
				System.Console.WriteLine( "Document should NOT have a previous document");
				return( false);
			}
			endTest( false, true);
			
			return( true);
		}

		public bool domNodesTest(
			string	sDbName,
			DbSystem	dbSystem)
		{
			bool	bOk = false;
			Db		db = null;
			bool	bStartedTrans = false;
			RCODE	rc;

			// Create the database

			beginTest( "Create database \"" + sDbName + "\"");

			for (;;)
			{
				rc = RCODE.NE_XFLM_OK;
				try
				{
					XFLM_CREATE_OPTS	createOpts = new XFLM_CREATE_OPTS();

					createOpts.uiBlockSize = 8192;
					createOpts.uiVersionNum = (uint)DBVersions.XFLM_CURRENT_VERSION_NUM;
					createOpts.uiMinRflFileSize = 2000000;
					createOpts.uiMaxRflFileSize = 20000000;
					createOpts.bKeepRflFiles = 1;
					createOpts.bLogAbortedTransToRfl = 1;
					createOpts.eDefaultLanguage = Languages.FLM_DE_LANG;
					db = dbSystem.dbCreate( sDbName, null, null, null, null, createOpts);
				}
				catch (XFlaimException ex)
				{
					rc = ex.getRCode();

					if (rc != RCODE.NE_XFLM_FILE_EXISTS)
					{
						endTest( false, ex, "creating database");
						return( false);
					}
				}
				if (rc == RCODE.NE_XFLM_OK)
				{
					break;
				}

				// rc better be NE_XFLM_FILE_EXISTS - try to delete the file

				try
				{
					dbSystem.dbRemove( sDbName, null, null, true);
				}
				catch (XFlaimException ex)
				{
					endTest( false, ex, "removing database");
					return( false);
				}
			}
			endTest( false, true);

			// Start a transaction

			beginTest( "Start Update Transaction Test");
			try
			{
				db.transBegin( eDbTransType.XFLM_UPDATE_TRANS, 255, 0);
			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "starting update transaction");
				goto Exit;
			}
			endTest( false, true);
			bStartedTrans = true;

			// Create a document

			if (!createDocumentTest( db))
			{
				goto Exit;
			}

			// Commit the transaction

			beginTest( "Commit Update Transaction Test");
			try
			{
				bStartedTrans = false;
				db.transCommit();
			}
			catch (XFlaimException ex)
			{
				endTest( false, ex, "committing update transaction");
				goto Exit;
			}
			endTest( false, true);

			bOk = true;

		Exit:
			if (bStartedTrans)
			{
				db.transAbort();
			}
			if (db != null)
			{
				db.close();
				db = null;
			}
			return( bOk);
		}
	}
}
