************************************************************************************
* nfXMLread ( VFP XML PARSER  )
* Program created by Marco Plaza , 2013-2016  @vfp2nofox
* distributed under VFPX license terms
* revision 1.0100 2016/08/08
* REVISION 1.0500 2016/08/09 
**************************************************************************************
Parameters cXml,_arraysList,xpathExp,_objectTree

#INCLUDE NFXML.H
#Define CRLF Chr(13)+Chr(10)

_arraysList = Lower(','+Evl(m._arraysList,'')+',')

Private All

stackLevels=Astackinfo(aerrs)

If m.stackLevels > 1
	calledFrom = 'called From '+aerrs(m.stackLevels-1,4)+' line '+Transform(aerrs(m.stackLevels-1,5))
Else
	calledFrom = ''
Endif

cError = ''

Do Case
Case Vartype(m.cXml) # 'C'
	xError('nfXML: Must supply a valid Xml string or file name ')
Otherwise
	oXml = nfXmlRead2( cXml,_arraysList,xpathExp , @_objectTree )
Endcase

Return Iif(Vartype(m.oXml)='O',m.oXml,.Null.)

*------------------------------------------------
Procedure xError( cMessage , Lineno )
*------------------------------------------------
Lineno = Evl(m.lineno,'?')
Error 'nfXML ('+m.calledFrom+'): '+CRLF+Transform(m.lineno)+':'+m.cMessage
Return To nfXMLread

*---------------------------------------------------------------
Procedure nfXmlRead2( cXml,_arraysList,xpathExp,_objectTree )
*---------------------------------------------------------------

oMsXml = Createobject('msXml.domdocument')
cError = ''

With oMsXml As msxml.DOMDocument

	.Async = .F.

	If Len(m.cXml) < 200 And File(m.cXml)
		.Load( m.cXml )
	Else
		.LoadXML(m.cXml)
	Endif


	If !Empty(.parseError.reason)

		cError =  'nfXmlRead: '+Transform(.parseError.Line)+') :'+.parseError.reason
		Lineno = 0

	Else


		If Vartype(m.xpathExp) = 'C'

			Try

				oXmlNodes = .selectNodes(m.xpathExp)

			Catch

				cError =  ' invalid XPATH expression ('+m.xpathExp+')'
				Lineno = 0

			Endtry


		Else

			oXmlNodes = .childnodes()

		Endif

		If Vartype(m.oXmlNodes) = 'O' And Empty(m.cError)

			Try
				oVfp = Createobject('empty')
				recnodo(m.oXmlNodes,m.oVfp,'',_arraysList,@_objectTree)
			Catch To oErr
				cError = oErr.Message
				Lineno = oErr.Lineno
			Endtry

		Endif


	Endif

Endwith

If !Empty(m.cError)
	xError( m.cError , m.lineno )
Else
	Return m.oVfp
Endif


*---------------------------------------------------------------------------------
Procedure recnodo( oNodos, esteo , _parentNode , _arraysList , _objectTree )
*---------------------------------------------------------------------------------

Private All


For Each nodo In oNodos

	With nodo


		If .nodeType # 1 And .nodeType # 3
			Loop
		Endif


		esTextNode = .nodeType = 3 Or ( .childnodes.Length = 1 And ( .firstchild.nodeType = 3 Or .firstchild.nodeType = 4 ) )
		emptyNode = .childnodes.Length = 0

		TRY 
			tieneAtributos = .Attributes.Length > 0
		CATCH 
			tieneAtributos = .F.
		ENDTRY 	

		NombreNodo = conv2asc(.nodeName)
		* Sunt cazuri in care comentariul este in interiorul nodului si numele nodului este #text
		IF NombreNodo = "#text"
			Loop
		ENDIF 
		NombreNodoFix = '_'+m.NombreNodo

		nuevoNodo = Type('esteo.&nombreNodo') = 'U' And Type('esteo.&nombreNodoFix') = 'U'

		Do Case
		Case m.emptyNode And !m.tieneAtributos
			nuevoValor = ''
		Case m.esTextNode And !m.tieneAtributos
			nuevoValor = .firstchild.nodeValue
		Otherwise
			nuevoValor = Createobject('empty')
		Endcase

		If m.nuevoNodo

			llSelectNodes = .F.
			TRY 
				llSelectNodes = (.selectNodes('../'+.nodeName).Length >  1)
			CATCH 
			ENDTRY 
			
			If  Lower(','+_parentNode+'.'+.nodeName+'[],') $ m._arraysList Or m.llSelectNodes

				Try
					AddProperty(m.esteo,(m.NombreNodo+'(1)'))

				Catch

					NombreNodo = m.NombreNodoFix

					AddProperty(m.esteo,(m.NombreNodo+'(1)'))

				Endtry

				esteo.&NombreNodo(1) = m.nuevoValor
				elemProc = 'esteo.'+m.NombreNodo+'(1)'
				arrayType = .T.

			Else

				AddProperty(m.esteo,m.NombreNodo,m.nuevoValor)
				elemProc = 'esteo.'+m.NombreNodo
				arrayType = .F.

			Endif

			propertyName = m.NombreNodo

		Else

			If Type('esteo.&nombreNodoFix') = 'U'
				nombreArray = m.NombreNodo
			Else
				nombreArray = m.NombreNodoFix
			Endif

			nvoelem = Alen(esteo.&nombreArray)+1
			nelemx  = Alen(esteo.&nombreArray)

			Dimension esteo.&nombreArray( m.nvoelem )
			esteo.&nombreArray( nvoelem ) = m.nuevoValor
			elemProc = 'esteo.'+m.nombreArray+'('+Transform(m.nvoelem)+')'

			propertyName = m.nombreArray
			arrayType = .T.

		Endif

		If Vartype(m._objectTree) = 'C'

			estaProp = m._parentNode+'.'+m.propertyName+Iif(m.arrayType,'[]','')

			If Not m.estaProp $ m._objectTree
				_objectTree = m._objectTree+ m.estaProp+Iif( .nodeName # m.propertyName,' <= (.'+.nodeName+')','')+CRLF
			Endif

		Endif


		If m.tieneAtributos

			If m.esTextNode
				AddProperty(&elemProc,NODETEXTTAG,.firstchild.nodeValue)

				If Vartype(m._objectTree) = 'C'
					estaProp = m._parentNode+'.'+NODETEXTTAG
					If Not m.estaProp $ m._objectTree
						_objectTree = m._objectTree+m.estaProp+CRLF
					Endif
				Endif


			Endif

			AddProperty(&elemProc,ATTRIBUTETAG,Createobject('empty'))

			oa = m.elemProc+'.'+ATTRIBUTETAG

			For Each atributo In .Attributes

				nombreAtr = conv2asc(atributo.Name)

				Try
					AddProperty(&oa,m.nombreAtr,atributo.Value)
				Catch
					m.nombreAtr = '_'+m.nombreAtr
					AddProperty(&oa,m.nombreAtr,atributo.Value)
				Endtry

				If Vartype(m._objectTree) = 'C'
					estaProp = m._parentNode+'.'+m.propertyName+'.'+ATTRIBUTETAG+'.'+m.nombreAtr
					If Not m.estaProp $ m._objectTree
						_objectTree = m._objectTree+m.estaProp+Iif( atributo.Name # m.nombreAtr,' <= (.'+atributo.Name+')','')+CRLF
					Endif
				Endif

			Endfor

		Endif

		If !m.esTextNode And .childnodes.Length > 0
			=recnodo( .childnodes(), &elemProc , _parentNode+'.'+m.propertyName+Iif(m.arrayType,'[]',''), _arraysList , @_objectTree )
		Endif

	Endwith


Endfor


*------------------------------
Function conv2asc( cTag )
*------------------------------

cTag = Strtran(m.cTag,'_','__')

cTag = Strtran(m.cTag,':','_')
cTag = Strtran(m.cTag,'(','_l_')
cTag = Strtran(m.cTag,')','_r_')
cTag = Strtran(m.cTag,'-','_h_')
cTag = Strtran(m.cTag,'.','_d_')


Return cTag



*************************************************************************
