# See the file LICENSE for redistribution information.
#
# Copyright (c) 2000-2003
#	Sleepycat Software.  All rights reserved.
#
# $Id: xml014.tcl,v 1.9 2004/01/26 18:45:18 gmf Exp $
#
# TEST	xml014
# TEST	modifyDocument
# TEST	Test use, and abuse of the XmlModify class as well as
# TEST	the ::modifyDocument() methods on XmlContainer and XmlDocument
# TEST	Must cover matrix of operations and types, against both containers
# TEST  and document, with and without transactions.

proc xml014 { args } {
	source ./include.tcl
	puts "\nxml014: modifyDocument ($args)"

	# Determine whether procedure has been called within an environment, 
	# and whether it is a transactional environment.
	# If we are using an env, then the filename should just be the test 
	# number.  Otherwise it is the test directory and the test number.
	set eindex [lsearch -exact $args "-env"]
	set txnenv 0
	set tnum 6	
	set oargs [eval {set_openargs} $args]

	if { $eindex == -1 } {
		set env NULL
		set basename $testdir/$tnum
	} else {
		incr eindex
		set env [lindex $args $eindex]
		set txnenv [is_txnenv $env]
		set testdir [get_home $env]
		set basename $tnum
	}

	xml014_1 $env $txnenv $basename $oargs
	xml014_2 $env $txnenv $basename $oargs
	xml014_3 $env $txnenv $basename $oargs
	xml014_4 $env $txnenv $basename $oargs
}

#
# generic modify routine for documents, with content checking
#
proc xml014_modify_document { testid content expected modify {modifyVerbose 0} } {
    new XmlDocument xd
    $xd setContent $content
    $xd modifyDocument $modify

    dbxml_error_check_good $testid [$xd getContentAsString] $expected

    if { $modifyVerbose == 1 } { puts [$xd getContentAsString] }
    delete xd
}

#
# generic modify routine for containers
#
proc xml014_modify_container { cont modify {txn "NULL" } { context "NULL" } { flags 0 } } {
  $cont modifyDocument $txn $modify $context $flags
}

#
# test all basic operations on an empty document
#
proc xml014_1 { {env "NULL"} {txnenv 0} {basename $tnum} oargs } {
    source ./include.tcl
    source $test_path/xml014_globals.tcl
    puts "\tXml014.1: test basic document modification"

    # append element
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_Element "new" "new content"
    xml014_modify_document "xml014.1.1" $content1 $expected1_1 $xmod1
    delete xmod1

    # append attribute
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_Attribute "new" "foo"
    xml014_modify_document "xml014.1.2" $content1 $expected1_2 $xmod1
    delete xmod1

    # append PI
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_ProcessingInstruction "newPI" "PIcontent"
    xml014_modify_document "xml014.1.3" $content1 $expected1_3 $xmod1
    delete xmod1

    # append comment
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_Comment "" "comment content"
    xml014_modify_document "xml014.1.4" $content1 $expected1_4 $xmod1
    delete xmod1

    # append text
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_Text "" "text content"
    xml014_modify_document "xml014.1.5" $content1 $expected1_5 $xmod1
    delete xmod1
}

#
# test basic operations on a document with a little structure
#
proc xml014_2 { {env "NULL"} {txnenv 0} {basename $tnum} oargs } {
    source ./include.tcl
    source $test_path/xml014_globals.tcl
    puts "\tXml014.2: test more complex document modification"

    # REMOVE
    # remove attribute
    new XmlModify xmod1 "/root/b/@att1" $XmlModify_Remove $XmlModify_None "" ""
    xml014_modify_document "xml014.2.1" $content2 $expected2_1 $xmod1
    delete xmod1

    # remove element
    new XmlModify xmod1 "/root/b\[text()='b content 2'\]" $XmlModify_Remove $XmlModify_None "" ""
    xml014_modify_document "xml014.2.2" $content2 $expected2_2 $xmod1
    delete xmod1

    # remove comment
    new XmlModify xmod1 "/root/comment()" $XmlModify_Remove $XmlModify_None "" ""
    xml014_modify_document "xml014.2.3" $content2 $expected2_3 $xmod1
    delete xmod1

    # remove text
    new XmlModify xmod1 "/root/a/text()" $XmlModify_Remove $XmlModify_None "" ""
    xml014_modify_document "xml014.2.4" $content2 $expected2_4 $xmod1
    delete xmod1

    # APPEND (default (-1), 0, and non-zero positive)
    # append default
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_Element "new" ""
    xml014_modify_document "xml014.2.5" $content2 $expected2_5 $xmod1
    delete xmod1

    # append at 0
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_Element "new" "" 0
    xml014_modify_document "xml014.2.6" $content2 $expected2_6 $xmod1
    delete xmod1

    # append at 2
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_Element "new" "" 2
    xml014_modify_document "xml014.2.7" $content2 $expected2_7 $xmod1
    delete xmod1

    # InsertBefore
    new XmlModify xmod1 "/root/a" $XmlModify_InsertBefore $XmlModify_Element "new" ""
    xml014_modify_document "xml014.2.8" $content2 $expected2_8 $xmod1
    delete xmod1

    # InsertAfter
    new XmlModify xmod1 "/root/a" $XmlModify_InsertAfter $XmlModify_Element "new" ""
    xml014_modify_document "xml014.2.9" $content2 $expected2_9 $xmod1
    delete xmod1
    
    # RENAME (elem, attr)
    # element
    new XmlModify xmod1 "/root/a" $XmlModify_Rename $XmlModify_None "x" ""
    xml014_modify_document "xml014.2.10" $content2 $expected2_10 $xmod1
    delete xmod1

    # attribute
    new XmlModify xmod1 "/root/a/@att1" $XmlModify_Rename $XmlModify_None "att2" ""
    xml014_modify_document "xml014.2.11" $content2 $expected2_11 $xmod1
    delete xmod1
    

    # Update (need to handle normal text, mixed content, and also empty new text,
    #  which acts like a simple removal of all text
    # comment text
    new XmlModify xmod1 "/root/comment()" $XmlModify_Update $XmlModify_None "" "new comment"    
    xml014_modify_document "xml014.2.12" $content2u $expected2_12 $xmod1
    delete xmod1

    # replace mixed content with single text node
    new XmlModify xmod1 "/root/a" $XmlModify_Update $XmlModify_None "" "new a text"
    xml014_modify_document "xml014.2.13" $content2u $expected2_13 $xmod1
    delete xmod1

    # remove text from mixed content
    new XmlModify xmod1 "/root/a" $XmlModify_Update $XmlModify_None "" ""
    xml014_modify_document "xml014.2.14" $content2u $expected2_14 $xmod1
    delete xmod1

    # new text for root
    new XmlModify xmod1 "/root" $XmlModify_Update $XmlModify_None "" "new root text"
    xml014_modify_document "xml014.2.15" $content2u $expected2_15 $xmod1
    delete xmod1

    # replace simple text node
    new XmlModify xmod1 "/root/b" $XmlModify_Update $XmlModify_None "" "new b text"
    xml014_modify_document "xml014.2.16" $content2u $expected2_16 $xmod1
    delete xmod1
}

proc xml014_failcheck { val testid } {
    if { $val != 1 } { puts "FAIL:[timestamp] $testid should have thrown" }
}

#
# test some failure conditions
#
proc xml014_3 { {env "NULL"} {txnenv 0} {basename $tnum} oargs } {
    source ./include.tcl
    source $test_path/xml014_globals.tcl
    puts "\tXml014.3: test illegal modifications"

    # insert before/after on root element
    new XmlModify xmod1 "/root" $XmlModify_InsertBefore $XmlModify_Element "new" ""
    set ret [ catch { xml014_modify_document "xml014.1.1" $content1 $content1 $xmod1 } ]
    xml014_failcheck $ret "xml014.3.1"
    delete xmod1
    # insert before/after on root element
    new XmlModify xmod1 "/root" $XmlModify_InsertAfter $XmlModify_Element "new" ""
    set ret [ catch { xml014_modify_document "xml014.3.2" $content1 $content1 $xmod1 } ]
    xml014_failcheck $ret xml014.3.2
    delete xmod1
    # remove root
    new XmlModify xmod1 "/root" $XmlModify_Remove $XmlModify_None "" ""
    set ret [ catch { xml014_modify_document "xml014.3.3" $content1 $content1 $xmod1 } ]
    xml014_failcheck $ret xml014.3.3
    delete xmod1
    # attribute without name
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_Attribute "" "val"
    set ret [ catch { xml014_modify_document "xml014.3.4" $content1 $content1 $xmod1 } ]
    xml014_failcheck $ret xml014.3.4
    delete xmod1
    # attribute without value
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_Attribute "name" ""
    set ret [ catch { xml014_modify_document "xml014.3.5" $content1 $content1 $xmod1 } ]
    xml014_failcheck $ret xml014.3.5
    delete xmod1
    # element without name
    new XmlModify xmod1 "/root" $XmlModify_Append $XmlModify_Element "" "val"
    set ret [ catch { xml014_modify_document "xml014.3.6" $content1 $content1 $xmod1 } ]
    xml014_failcheck $ret xml014.3.6
    delete xmod1
    # append to attribute
    new XmlModify xmod1 "/root/a/@att1" $XmlModify_Append $XmlModify_Attribute "name" "val"
    set ret [ catch { xml014_modify_document "xml014.3.7" $content2 $content2 $xmod1 } ]
    xml014_failcheck $ret xml014.3.7
    delete xmod1
    # append to comment
    new XmlModify xmod1 "/root/comment()" $XmlModify_Append $XmlModify_Attribute "name" "val"
    set ret [ catch { xml014_modify_document "xml014.3.8" $content2 $content2 $xmod1 } ]
    xml014_failcheck $ret xml014.3.8
    delete xmod1
    # append elem to attribute
    new XmlModify xmod1 "/root/a/@att1" $XmlModify_Append $XmlModify_Element "name" "val"
    set ret [ catch { xml014_modify_document "xml014.3.9" $content2 $content2 $xmod1 } ]
    xml014_failcheck $ret xml014.3.9
    delete xmod1
    # append elem to comment
    new XmlModify xmod1 "/root/comment()" $XmlModify_Append $XmlModify_Element "name" "val"
    set ret [ catch { xml014_modify_document "xml014.3.10" $content2 $content2 $xmod1 } ]
    xml014_failcheck $ret xml014.3.10
    delete xmod1

}

proc addDocToContainer { container content txn } {
	if { [catch {
		new XmlDocument xd
		$xd setContent $content
		$container putDocument $txn $xd
		set id [$xd getID]
		delete xd
	}] != 0 } {
		set id 0
	}
	return $id
}

proc loadContainer_1 { container txn } {
    source ./include.tcl
    source $test_path/xml014_globals.tcl
    addDocToContainer $container $ccontent1 $txn
    addDocToContainer $container $ccontent2 $txn
    addDocToContainer $container $ccontent3 $txn
}

#
# test container-based modifications
#
proc xml014_4 { {env "NULL"} {txnenv 0} {basename $tnum} oargs } {
    source ./include.tcl
    source $test_path/xml014_globals.tcl
    puts "\txml014.4: test container-based modifications"
    
    set txn NULL
	
    xml_cleanup $testdir $env
	
    new XmlContainer container $env "$basename.2.dbxml" 0
    if { $txnenv == 1 } { set txn [start_txn $env] }
    $container open $txn $oargs 0
    if { $txnenv == 1 } { commit_txn $txn }
    
    # First, check some error cases:
    #
    # construct XmlModify with XmlQueryExpression that doesn't use ReturnType of
    # ResultDocumentsAndValues
    new XmlQueryContext context;
    if { $txnenv == 1 } { set txn [start_txn $env] }
    wrap XmlQueryExpression expression [$container parseXPathExpression $txn "/root" $context ]
    set ret [ catch { new XmlModify xmod $expression $XmlModify_Append $XmlModify_Attribute "a" "b" } ]
    if { $txnenv == 1 } { commit_txn $txn }
    delete expression
    delete context
    xml014_failcheck $ret xml014.4.1

    # Focus on cases that are specific to XmlContainer vs. XmlDocument, such
    # as multiple docs, parseXPathExpression, etc.
    # load the container
    #
    if { $txnenv == 1 } { set txn [start_txn $env] }
    loadContainer_1 $container $txn
    if { $txnenv == 1 } { commit_txn $txn }

    new XmlModify xmod "//d" $XmlModify_Rename $XmlModify_None "xxx" ""
    xml014_test_mod_container "xml014.4.2" $container $env $txnenv $xmod "//xxx" $expected_c3
    delete xmod

    new XmlModify xmod "/root/a" $XmlModify_Append $XmlModify_Attribute "attr" "at"
    xml014_test_mod_container "xml014.4.3" $container $env $txnenv $xmod "/root/a/@attr" $expected_c1
    delete xmod

    # this one affects all 3 docs
    new XmlModify xmod "//b" $XmlModify_Remove $XmlModify_None "" ""
    xml014_test_mod_container "xml014.4.4" $container $env $txnenv $xmod "/root/a/c" $expected_c2
    delete xmod

    # parseXPathExpression (by hand)
    if { $txnenv == 1 } { set txn [start_txn $env] }
    new XmlQueryContext qc
    $qc setReturnType $XmlQueryContext_ResultDocumentsAndValues
    wrap XmlQueryExpression expression [ $container parseXPathExpression $txn "//c" $qc ]
    new XmlModify xmod $expression $XmlModify_InsertAfter $XmlModify_Element "new" "new content"
    $container modifyDocument $txn $xmod
    new XmlValue xv
    wrap XmlResults res [ $container queryWithXPath $txn "//c" ]
    $res next $xv

    wrap XmlDocument xd [ $xv asDocument ]
    dbxml_error_check_good "xml014.4.4" [$xd getContentAsString] $expected_c4
	delete res
    delete xd
    delete xv
    delete xmod
    delete expression
    delete qc
    if { $txnenv == 1 } { commit_txn $txn }

    # similar to above, add XmlUpdateContext
    if { $txnenv == 1 } { set txn [start_txn $env] }
    new XmlModify xmod "//c" $XmlModify_InsertBefore $XmlModify_Element "new1" "new content1"
    new XmlUpdateContext uc $container
    $container modifyDocument $txn $xmod $uc
    new XmlValue xv
    wrap XmlResults res [ $container queryWithXPath $txn "//c" ]
    $res next $xv
    wrap XmlDocument xd [ $xv asDocument ]
    dbxml_error_check_good "xml014.4.5" [$xd getContentAsString] $expected_c5
	delete res
    delete xv
    delete xd
    delete uc
    delete xmod
    if { $txnenv == 1 } { commit_txn $txn }

    $container close
    delete container
}

#
# use the xmod argument to modify the container documents, then use
# the xq query to retrieve a modified document, and compare it to the
# expected string.  This means that just the first document is the
# one that must match expected.
#
proc xml014_test_mod_container { testid container env txnenv xmod xq expected } {
    set txn NULL
    if { $txnenv == 1 } { set txn [start_txn $env] }
    xml014_modify_container $container $xmod $txn
    new XmlValue xv
    wrap XmlResults res [ $container queryWithXPath $txn $xq ]
    $res next $xv
    if { [ $res size ] == 0 } { 
	dbxml_error_check_bad $testid "query did not find results" $expected
    } else {
	wrap XmlDocument xd [ $xv asDocument ]
	dbxml_error_check_good $testid [$xd getContentAsString] $expected
    }
    delete xd
    delete xv
    delete res
    if { $txnenv == 1 } { commit_txn $txn }
}
