oXML = CREATEOBJECT("myXML") THIS.Async = .T. * note that INIT() of oXML ( instance of myXML session class) * assigns a value to the property which does not exist in it. * But the try to set it does not cause an error "PEM does not exist... " etc. * because This_Access method fires first and checks the existence of the property. IF PEMSTATUS(THIS, cPEM, 5) && This_Access method fired the first time * As the .Async property does not exist. the method returns This.oDom property * which at the moment is still NULL. RETURN THIS.oDOM && At this point oDom_Access method fires && as RETURN statement is trying to access it * oDom_Access method: IF ISNULL(THIS.oDom) && It's still NULL at the moment THIS.oDom = CREATEOBJECT("Microsoft.XMLDOM") && create it ENDIF RETURN THIS.oDom && Returns just created This.oDom object. * That's where it get substituted instead of oXML itself * to actually assign the .Async property value * Setting the second property (which does not exist in oXML object) THIS.PreserveWhiteSpace = .T. * This_Access method fires the second time IF PEMSTATUS(THIS, cPEM, 5) && As the .PreserveWhiteSpace property does not exist. * the method returns This.oDom property * which at the moment is already pointing to Microsoft.XMLDOM object (this.oDom property) RETURN THIS.oDOM && Returns This.oDom object reference which causes oDom_Access method to fire again * oDom_Access method: IF ISNULL(THIS.oDom) && It exists now. RETURN THIS.oDom && Return it. * That's where it get substituted instead of oXML itself * to actually assign the .PreserveWhiteSpace property value * oXML instantiation finished * Next command: ?oXML.Async && Causes This_Access to fire IF PEMSTATUS(THIS, cPEM, 5) && It exists RETURN THIS.oDOM && Returns This.oDom object reference which causes oDom_Access method to fire again * oDom_Access method: IF ISNULL(THIS.oDom) && It exists RETURN THIS.oDom && return it ?oXML.loadxml('') && Causes This_Access to fire * The sequence repeats IF PEMSTATUS(THIS, cPEM, 5) && This "overridden" method exists for oXML itself RETURN THIS && the reference to oXML object to continue with oXML.LoadXML() method * oXML.LoadXML method fires IF cXML <> '' && cXML is a parameter. * Calling THIS.oDOM.LoadXML(cXML) method RETURN THIS.oDOM.LoadXML(cXML) && Call to THIS.oDOM.LoadXML method causes This_Access method to fire again * Here it is more complicated as it causes This_Access to fire twice and oDom_Access once IF PEMSTATUS(THIS, cPEM, 5) && it exists RETURN THIS && the reference to oXML object * oDom_Access method fires IF ISNULL(THIS.oDom) && Causes This_Access to fire * This_Access method fires IF PEMSTATUS(THIS, cPEM, 5) RETURN THIS && it exists RETURN THIS.oDom && return it. (causes This_Access method fire once again) IF PEMSTATUS(THIS, cPEM, 5) && it exists RETURN THIS && return it ?oXML.XML * the sequence for querying the property repeats * like in case of ?oXML.Async command. IF PEMSTATUS(THIS, cPEM, 5) RETURN THIS.oDOM IF ISNULL(THIS.oDom) RETURN THIS.oDom *********************>Believe it or not, the following technique arose out of a real need, and I think that it might come in useful in several places.
> >oXML = CREATEOBJECT("myXML") >?oXML.Async >?oXML.loadxml('<test></test>') >?oXML.XML > >DEFINE CLASS myXML AS Session > > oDOM = .NULL. > LastXML = "" > > FUNCTION Init > THIS.Async = .T. > THIS.PreserveWhiteSpace = .T. > ENDFUNC > > FUNCTION oDOM_Access > IF ISNULL(THIS.oDom) > THIS.oDom = CREATEOBJECT("Microsoft.XMLDOM") > ENDIF > RETURN THIS.oDom > ENDFUNC > > FUNCTION THIS_Access > LPARAMETERS cPEM > IF PEMSTATUS(THIS, cPEM, 5) > RETURN THIS > ELSE > RETURN THIS.oDOM > ENDIF > ENDFUNC > > FUNCTION LoadXML > LPARAMETERS cXML > IF cXML <> '<?xml version="1.0">' > cXML = '<?xml version="1.0"?>' + cXML > ENDIF > RETURN THIS.oDOM.LoadXML(cXML) > ENDFUNC >ENDDEFINE > >