diff --git a/FEATURE.md b/FEATURE.md
new file mode 100644
index 0000000..5ea49da
--- /dev/null
+++ b/FEATURE.md
@@ -0,0 +1,8 @@
+## FEATURES
+
+# xml2src
+
+Write a SAX parser to handle the wow.xml files for addons.
+
+http://www.jelks.nu/XML/xmlebnf.html
+
diff --git a/src/scripts/runmany.sh b/src/scripts/runmany.sh
index 2d80c30..a20d6a3 100755
--- a/src/scripts/runmany.sh
+++ b/src/scripts/runmany.sh
@@ -8,11 +8,12 @@ for n in $(seq -f "%05g" 9999 1) ; do
if [ ! "$?" == "0" ]; then
cp target/reports/antout.txt target/reports/antOut$n.txt
#mv $reportFile target/reports/testOut$n.xml
- ls -alt target/reports/testOut$n.xml
+ ls -alt $reportFile
until $(~/Scripts/checkFileChanged.sh ./test/test.lua); do
sleep 1
done
else
- ls -alt $reportFile
+ #ls -alt $reportFile
+ sleep 1
fi
done
diff --git a/src/wowStubs.lua b/src/wowStubs.lua
index bb99f94..f80bb2e 100644
--- a/src/wowStubs.lua
+++ b/src/wowStubs.lua
@@ -1730,10 +1730,170 @@ function C_ChatInfo.SendAddonMessage()
return true
end
------------------------------------------
--- XML functions
+-- A SAX parser takes a content handler, which provides these methods:
+-- startDocument() -- called at the start of the Document
+-- endDocument() -- called at the end of the Document
+-- startElement( tagName, attrs ) -- with each tag start. attrs is a table of attributes and values
+-- endElement( tagName ) -- when a tag ends
+-- characters( char ) -- for each character not being in a tag
+-- The parser calls each of these methods as these events happen.
+
+-- A SAX parser defines a parser (created with makeParser)
+-- a Parser has a ContentHandler assigned
+-- a Parser also defines these methods:
+-- setContentHandler( contentHandler )
+-- setFeature() -- prob not going to implement
+-- parse( text )
+-- parse( file )
+
+-- https://www.w3schools.com/xml/xml_elements.asp
+-- https://www.w3schools.com/xml/xml_syntax.asp
+
+
+contentHandler = {}
+-- normally the contentHandler is an object, where data structures are created in the new object.
+function contentHandler.startDocument( this )
+end
+function contentHandler.endDocument( this )
+end
+function contentHandler.startElement( this, tagName, attrs )
+end
+function contentHandler.endElement( this, tagName )
+end
+function contentHandler.characters( this, char )
+end
+
+saxParser = {}
+-- SAX Parser
+-- interface
+function saxParser.makeParser()
+ -- make a parser. This is probably intended to be a factory function
+ return saxParser
+end
+function saxParser.setContentHandler( contentHandlerIn )
+ -- takes a table
+ saxParser.contentHandler = contentHandlerIn
+end
+function saxParser.setFeature()
+ -- research this
+end
+function saxParser.parse( fileIn )
+ f = io.open( fileIn, "r" )
+ if f then fileIn = f:read( "*all" ) end -- read the contents of the file
+
+ -- call the startDocument method for the given contentHandler
+ if saxParser.contentHandler and saxParser.contentHandler.startDocument then
+ saxParser.contentHandler:startDocument()
+ end
+
+ -- loop through each char
+ State = {
+ Outside = { 0 }, -- outside of a tag
+ ElementName = { 1 }, -- When to parse for a name
+ InElement = { 2 }, -- In the element
+ }
+ currentState = State.Outside
+ elementDepth = {} -- table of current element depth
+ elementName = ""
+ chars = ""
+
+ while( #fileIn > 0 ) do
+ -- print( currentState[1].."\t"..#fileIn, "fileIn: "..string.sub( fileIn, 1, 60 ) )
+ c = string.sub( fileIn, 1, 1 )
+ n = string.sub( fileIn, 2, 2 )
+ handled = false
+ if currentState == State.Outside then
+ if c == "<" then
+ if n == "?" then
+ local endProlog = string.find( fileIn, "?>" )
+ if endProlog then
+ fileIn = string.sub( fileIn, endProlog+2 )
+ end
+ elseif n == "!" then
+ local endComment = string.find( fileIn, "-->" )
+ if endComment then
+ fileIn = string.sub( fileIn, endComment+3 )
+ end
+ else
+ currentState = State.ElementName
+ elementName = ""
+ fileIn = string.sub( fileIn, 2 )
+ end
+ else
+ saxParser.contentHandler:characters( c )
+ fileIn = string.sub( fileIn, 2 )
+ end
+ elseif currentState == State.ElementName then
+ tagStart, tagEnd, tagName = string.find( fileIn, "^([%a_][%a%d-_.]*)" )
+ if tagStart then
+ elementName = tagName
+ attributes = {}
+ currentState = State.InElement
+ fileIn = string.sub( fileIn, tagEnd + 1 )
+ end
+ tagStart, tagEnd, tagName = string.find( fileIn, "^/([%a_][%a%d-_.]*)" )
+ if tagStart then
+ elementName = tagName
+ -- print( "Fire endElement( "..tagName.." )" )
+ depthElement = table.remove( elementDepth )
+ saxParser.contentHandler:endElement( tagName )
+ if depthElement ~= elementName then
+ fail( "ERROR: Closing "..elementName.." is not the expected element to close; "..depthElement.." is expected." )
+ end
+ currentState = State.Outside
+ fileIn = string.sub( fileIn, tagEnd + 2 )
+ end
+ elseif currentState == State.InElement then
+ attribStart, attribEnd, key, value = string.find( fileIn, "^%s*(%S+)%s*=%s*[\"\'](.-)[\"\']" )
+ if attribStart then
+ attributes[key] = value
+ fileIn = string.sub( fileIn, attribEnd+1 )
+ elseif c == " " then
+ fileIn = string.sub( fileIn, 2 )
+ elseif c == ">" or n == ">" then
+ -- print( "Fire startElement( "..elementName.." )" )
+ -- print( "\twith attributes: ")
+ -- for k,v in pairs( attributes ) do
+ -- print( "\t\t"..k..":="..v )
+ -- end
+ table.insert( elementDepth, elementName )
+ saxParser.contentHandler:startElement( elementName, attributes )
+ currentState = State.Outside
+ if c == "/" and n == ">" then
+ -- print( "Fire endElement( "..elementName.." )" )
+ depthElement = table.remove( elementDepth )
+ saxParser.contentHandler:endElement( elementName )
+ if depthElement ~= elementName then
+ fail( "ERROR: Closing "..elementName.." is not the expected element to close; "..depthElement.." is expected." )
+ end
+ currentState = State.Outside
+ end
+ fileIn = string.sub( fileIn, (n==">" and 3 or 2) )
+ end
+ end
+ -- print( "elementDepth: "..table.concat( elementDepth, "\t" ) )
+ end
+
+ -- call the endDocument method for the given contentHandler
+ if saxParser.contentHandler and saxParser.contentHandler.endDocument then
+ saxParser.contentHandler:endDocument()
+ end
+end
function ParseXML( xmlFile )
- print("parse: "..xmlFile )
+ ch = contentHandler
+ ch.startElement = function( self, tagIn, attribs )
+ if _G["Create"..tagIn] then
+ if attribs.name then
+ _G[attribs.name] = _G["Create"..tagIn]( attribs.name )
+ _G[attribs.name].framename = attribs.name
+ else
+ fail("A "..tagIn.." needs a name")
+ end
+ end
+ end
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( xmlFile )
end
-----------------------------------------
diff --git a/test/test.lua b/test/test.lua
index 2201f95..6a68de9 100644
--- a/test/test.lua
+++ b/test/test.lua
@@ -1062,13 +1062,173 @@ function CreateFile( filename, contents )
return tocFile
end
function test.testTOC_()
- CreateFile( "test.xml", "\n\n" )
+ CreateFile( "test.xml", "\n\n" )
CreateFile( "test.lua", "print(\"hi\")\n")
generatedFile = CreateFile( "test.toc", "test.lua\ntest.xml\n" )
ParseTOC( generatedFile )
end
+----- Sax tests
+function test.before_testContentHandler()
+ originalContentHandler = {}
+ for k,v in pairs( contentHandler ) do
+ originalContentHandler[k] = v
+ end
+end
+function test.after_testContentHandler()
+ contentHandler = {}
+ for k,v in pairs( originalContentHandler ) do
+ contentHandler[k] = v
+ end
+end
+function test.testContentHandler_hasStartDocument()
+ test.before_testContentHandler()
+ assertTrue( contentHandler.startDocument )
+ test.after_testContentHandler()
+end
+function test.testContentHandler_hasEndDocument()
+ test.before_testContentHandler()
+ assertTrue( contentHandler.endDocument )
+ test.after_testContentHandler()
+end
+function test.testContentHandler_hasStartElement()
+ test.before_testContentHandler()
+ assertTrue( contentHandler.startElement )
+ test.after_testContentHandler()
+end
+function test.testContentHandler_hasEndElement()
+ test.before_testContentHandler()
+ assertTrue( contentHandler.endElement )
+ test.after_testContentHandler()
+end
+function test.testContentHandler_hasCharacters()
+ test.before_testContentHandler()
+ assertTrue( contentHandler.characters )
+ test.after_testContentHandler()
+end
+
+
+function test.before_testSax()
+ originalContentHandler = {}
+ for k,v in pairs( contentHandler ) do
+ originalContentHandler[k] = v
+ end
+end
+function test.after_testSax()
+ contentHandler = {}
+ for k,v in pairs( originalContentHandler ) do
+ contentHandler[k] = v
+ end
+end
+function test.testSAX_MakeParser()
+ test.before_testSax()
+ assertTrue( saxParser.makeParser() )
+ test.after_testSax()
+end
+function test.testSAX_setContentHandler()
+ test.before_testSax()
+ ch = contentHandler
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ assertTrue( parser.contentHandler )
+ --ch = nil
+ --parser = nil
+ test.after_testSax()
+end
+function test.notestSAX_Parse_StartDocument_TextIn()
+ test.before_testSax()
+ ch = contentHandler
+ ch.startDocument = function( this ) this.started = true; end
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( "" )
+ assertTrue( ch.started )
+end
+function test.notestSAX_Parse_StartDocument_FileIn()
+ test.before_testSax()
+ ch = contentHandler
+ ch.startDocument = function( this ) this.started = true; end
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( "../build.xml" )
+ assertTrue( ch.started )
+end
+function test.notestSAX_Parse_StartDocument_NotGiven_TextIn()
+ test.before_testSax()
+ ch = contentHandler
+ ch.startDocument = nil
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( "" )
+ assertIsNil( ch.started )
+end
+function test.testSAX_Parse_EndDocument_TextIn()
+ test.before_testSax()
+ ch = contentHandler
+ ch.endDocument = function( this ) this.ended = true; end
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( "" )
+ assertTrue( ch.ended )
+end
+function test.testSAX_Parse_StartElement_TextIn()
+ -- affirm that the startElement method is called
+ test.before_testSax()
+ ch = contentHandler
+ ch.startElement = function( this, tagIn, attribs ) this.tagIn = tagIn; end
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( "" )
+ assertEquals( "xml", ch.tagIn )
+end
+function test.testSAX_Parse_StartElementAttribs_TextIn()
+ -- affirm that the startElement method is called
+ test.before_testSax()
+ ch = contentHandler
+ ch.startElement = function( this, tagIn, attribs ) this.version = attribs["version"]; end
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( "" )
+ assertEquals( "1", ch.version )
+end
+function test.testSAX_Parse_StartElementAttribs_TextIn2()
+ -- affirm that the startElement method is called
+ test.before_testSax()
+ ch = contentHandler
+ ch.startElement = function( this, tagIn, attribs ) this.version = attribs["version"]; end
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( "" )
+ assertEquals( "2", ch.version )
+end
+function test.testSax_Parse_StartElement_Prolog()
+ test.before_testSax()
+ ch = contentHandler
+ ch.startElement = function( this, tagIn, attribs ) this.version = attribs["version"]; end
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( "" )
+ assertEquals( "3", ch.version )
+end
+function test.testSax_Parse_StartElement_Comment()
+ test.before_testSax()
+ ch = contentHandler
+ ch.startElement = function( this, tagIn, attribs ) this.version = attribs["version"]; end
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( "" )
+ assertEquals( "4", ch.version )
+end
+function test.testSax_Parse_NestedElements()
+ test.before_testSax()
+ ch = contentHandler
+ ch.startElement = function( this, tagIn, attribs ) this.broken = attribs["broken"]; end
+ parser = saxParser.makeParser()
+ parser.setContentHandler( ch )
+ parser.parse( "" )
+ assertEquals( "5", ch.broken )
+end
----------------------------------
-- Run the tests
----------------------------------