-- $Date: 2001-01-09 19:18:43 -0700 (Tue, 09 Jan 2001) $ -- $Revision: 895 $ -- Copyright 2000-2001, Bill Castrogiovanni -- This is a superclass for all of the data types -- It can easily be reused for other XML DTDs. -- However, it assumes that every element has some -- common attributes. Currently, these attributes are: -- "id", "name" -- Also - AND THIS IS VERY IMPORTANT - these objects -- make cast managment and download calls through a couple -- of global objects: global gMediaManager, gDownloadManager, gErrorManager, gPageManager, gStatusBarManager -- These objects need to be properly initialized before -- xml-based objects can be created. -- These properties should only be referred to through -- their handlers, they should never be called directly property myDataType, myMama, myDataList, myAttributeList -- myDataType is a string that corresponds to the XML element tag; it's the class type -- myMama - if this object was created by another object (not by the user), -- then myMama is a pointer to the object's creator -- myDataList will contain child objects, processing commands, and text data -- it is a linear list -- myAttributeList will contain attribute name/value pairs for this object. -- It is formatted as: -- [attributeName:attributeValue] -- NOTE: To make attribute requests case-insensative, all attributes/request arguments -- will be converted to lowercase. -- This property stores information sent by gDownloadManager. -- THEREFORE, gDownloadManager MUST BE INITIALIZED FOR XML-ELEMENT OBJECTS TO WORK -- IN THIS IMPLEMENTATION, gDownloadManager MANIPULATES myLoadStatus BY CALLING childObject.setLoadStatus(). property myLoadStatus -- Some XML objects link to downloadable media. This variable will contain each object's downloaded status. -- The download manager will store more detailed information. For the XML-Element classes, -- the only available myLoadStatus values are: -- #none - the Object doesn't link to any media or there has been a download error (default value) -- #error - an error was encountered during the download -- #queued - the media is queued for download, but hasn't been processed -- #loading - media relevant to this object is currently being downloaded -- #done - all downloads for this object (and its children) are complete on new me, parserObject -- create the objects me.myDataList = [] me.myAttributeList = [:] if objectP(parserObject) then thisObject = me.createChildrenFromParserChild(parserObject) return thisObject else return me end if end -- requires an XML parser object -- This populates the DataList based on the XML parser object's data -- If the parser object returns another element, a new object is created -- of from a class bearing the element's name (parserChild.name). This -- object is then configured (recursively) and added to the parent's internal -- data list (myDataList). If the element type is textual content, then the -- text is added directly to the parent's internal data list. -- returns a linear list of child objects on createChildrenFromParserChild me, parser, aParent returnList = [] -- find out how many children this portion of the parser has childrenCount = parser.child.count repeat with x = 1 to childrenCount parserChild = parser.child[x] if (parserChild.type = #element) then -- we must test to see if this cast member name exists -- if the cast member has a number associated with it (other than -1) -- then all is good. if (the number of member parserChild.name <> -1) then if objectP(aParent) then thisElement = new (script parserChild.name, parserChild, aParent) --this handler is being called by a child, store that child in the grandchild append (me.myDataList, thisElement) --add the data to the CHILD's OWN data list else thisElement = new (script parserChild.name, parserChild) --this handler is being called directly through xml-element, don't pass in a parent's name as the first born will become the parent -- the old-way: appends all the top level objects to a list -- append (returnList, thisElement) -- add the data to the handler's return list. -- originally, I returned top-level objects in a list (so you can parse several XML chunks -- simultaneously. Now, I only want to allow ONE TOP-LEVEL XML chunk. So let's return -- this top-level object now (since no parent was specified during this call to createChildrenFromParserChild, -- thisElement IS the top-level object) return thisElement end if end if else if (parserChild.type = #text) then append (me.myDataList, parserChild.text) --This is text, add it to the element's internal data list end if end repeat return returnList end -- requires an XML parser object on initObjectFromParserChild me, parserChild, aParent -- generic attributes used by all elements -- dataType is represents the XML tag name me.setDataType(parserChild.name) -- sets the data type based on the XML Tag name me.myLoadStatus = #none -- by default, all objects have no load status repeat with x = 1 to parserChild.attributeName.count -- adds XML attributes to myAttributeList in the format [AttributeName:Value] setAProp(me.myAttributeList, me.convertToLowerCase(parserChild.attributeName[x]), parserChild.attributeValue[x]) end repeat me.createChildrenFromParserChild(parserChild, aParent) --init the Data for this object end -- deletes this object and its children, returns 0 on destroy me myKillList = me.getAllObjects() repeat with x = 1 to myKillList.count thisObject = getAt(myKillList, x) thisObject.destroy() end repeat if objectP(me.myMama) then success = me.disown() end if -- put "deleting " & me.myDataType me = VOID return me end -- pops this object off of parent's data list, sets the child's -- mama variable to VOID, returns 1 if sucessful -- if this child doesn't have a parent, it is removed from it's own -- datalist on disown me if objectP(me.myMama) then success = deleteOne(me.myMama.myDataList, me) else success = deleteOne(me.myDataList, me) end if if (success) then me.setParent(VOID) return 1 else return 0 end if end --returns a linear list of sub-classed xml-element objects on getAllObjects me returnList = [] repeat with x = 1 to me.myDataList.count thisData = getAt(me.myDataList, x) if (objectP(thisData)) then add(returnList, thisData) end if end repeat return returnList end -- returns a Nth instance of a specific data type -- dataIndex defaults to 1 on getChild me, dataType, dataIndex if voidP(dataIndex) then dataIndex = 1 end if objectList = me.getAllDataOfType(dataType) if ((dataIndex >= 1) and (dataIndex <= objectList.count)) then return getAt(objectList, dataIndex) else return 0 end if end --returns a linear list of all items of type dataType on getAllDataOfType me, dataType returnList = [] repeat with x = 1 to me.myDataList.count thisData = getAt(me.myDataList, x) if ((dataType = #charData) and not (objectP(thisData))) then add(returnList, thisData) else if (objectP(thisData)) then if thisData.getDataType() = dataType then add(returnList, thisData) end if end if end repeat return returnList end -- returns Nth instance of the charData -- if dataIndex exceeds the count of the string array, returns 0 on getTextByIndex me, dataIndex if voidP(dataIndex) then dataIndex = 1 end if objectList = me.getAllDataOfType(#charData) if ((dataIndex >= 1) and (dataIndex <= objectList.count)) then return getAt(objectList, dataIndex) else return 0 end if end -- get all the charData this object contains in one big string -- returns an empty string if this object contains no text on getAllText me returnString = "" objectList = me.getAllDataOfType(#charData) if (objectList.count) then repeat with x = 1 to objectList.count returnString = returnString && getAt(objectList,x) end repeat end if return returnString end -- This is a generic handler that is SUPPOSED to be subclassed -- As it stands, this text will return tabs, returns, and charData -- But, each subclass can define its own XML tag and insert into the returnString -- This super class method will automatically handle the recursion -- args is a generic variable used to hold any information that's -- to be passed to the child on convertToXML me, indentLevel returnText = "" --create the empty string hasChildFlag = 0 -- this flag is used to add extra line breaks if this object has baby objects -- create tab indentation for this object -- and increment the indent level by one (for -- passing indentation to its children) if voidP(indentLevel) then indentLevel = 0 else myIndent = "" repeat with x = 0 to indentLevel myIndent = myIndent & TAB end repeat indentLevel = indentLevel + 1 end if -- end tab indentation initialization -- add newlines and tabs if (indentLevel) then returnText = returnText & RETURN returnText = returnText & myIndent end if -- declare the xml tag name returnText = returnText & "<" & me.myDataType -- alphabetically sort the Attribute list sort me.myAttributeList -- cycle through the attribute list and add the name=value pairs repeat with x = 1 to me.myAttributeList.count returnText = returnText & SPACE & getPropAt(me.myAttributeList, x) & "=" & QUOTE & getAt(me.myAttributeList, x) & QUOTE end repeat --close the opening xml tag returnText = returnText & ">" -- call this method on each child owned, and add their return data repeat with x = 1 to myDataList.count set thisData = getAt(myDataList, x) -- This is a child object, recurse if objectP(thisData) then hasChildFlag = 1 returnText = returnText & thisData.convertToXML(indentLevel) else -- This is charData, insert text if (hasChildFlag) then returnText = returnText & RETURN -- add an extra return for pretty spacing returnText = returnText & myIndent -- tab in to the proper position end if returnText = returnText & thisData end if end repeat if (hasChildFlag) then returnText = returnText & RETURN -- add an extra return for pretty spacing returnText = returnText & myIndent -- tab in to the proper position end if returnText = returnText & "" return returnText end -- The main attribute list accessors -- give it an attribute name and this returns its associated value -- returns VOID if the attribute doesn't exist -- all attributes are converted to lower case for case-insensative look-ups on getAttribute me, attributeName return getAProp(me.myAttributeList, me.convertToLowerCase(attributeName)) end -- pass it a name and a value and it will add the pair to -- the attribute list (or update the list's value if the name already -- contained in myAttributeList). -- all attributes are converted to lower case for case-insensative look-ups on setAttribute me, attributeName, attributeValue setAProp(me.myAttributeList, me.convertToLowerCase(attributeName), attributeValue) end -- End main attribute list accessors -- returns true on setDataType me, dataType me.myDataType = dataType return 1 end -- returns true on setID me, id me.setAttribute("id", id) return 1 end -- returns true on setName me, aName me.setAttribute("name", aName) return 1 end -- returns true on setParent me, aParent me.myMama = aParent return 1 end on getParent me return me.myMama end on getID me return me.getAttribute("id") end on getName me return me.getAttribute("name") end on getDataType me return me.myDataType end -- LOAD STATUS HANDLERS -- return this object's current load status on getLoadStatus me return me.myLoadStatus end -- set this object's current load status and notify -- this object's parent if loading is complete on setLoadStatus me, aStatus case aStatus of: #none: if (aStatus <> me.myLoadStatus) then -- make sure the passed-in status is different before sending a #none me.myLoadStatus = #none if objectP(me.getParent()) then me.getParent().setLoadStatus(#none) end if end if #error: if (aStatus <> me.myLoadStatus) then -- make sure the passed-in status is different before sending a #error me.myLoadStatus = #error if objectP(me.getParent()) then me.getParent().setLoadStatus(#error) end if end if #queued: if (aStatus <> me.myLoadStatus) then -- make sure the passed-in status is different before sending a #queued me.myLoadStatus = #queued if objectP(me.getParent()) then me.getParent().setLoadStatus(#queued) end if end if #loading: -- make sure the passed-in status is different before sending back #loading if (aStatus <> me.myLoadStatus) then me.myLoadStatus = #loading if objectP(me.getParent()) then me.getParent().setLoadStatus(#loading) end if end if #done: -- make sure all children are done loading before passing along a #done. if not #done, then send #loading tempLoadStatus = #done childrenLoadStatus = me.getLoadStatusOfChildren() if (childrenLoadStatus = #loading) then tempLoadStatus = #loading end if if (tempLoadStatus <> me.myLoadStatus) then me.myLoadStatus = tempLoadStatus if objectP(me.getParent()) then me.getParent().setLoadStatus(tempLoadStatus) end if end if end case end -- This is a private (internal) handler. -- Users should never call this method directly -- returns #none if no children are scheduled to load (or if the object has no children) -- returns #queued if the media is queued for download, but hasn't been processed -- returns #loading if any child is currently loading -- returns #done if all chidlren are done loading and nothing is left in the #queue (even if other children are set to #none) on getLoadStatusOfChildren me childList = me.getAllObjects() doneCount = 0 queueCount = 0 errorCount = 0 loadingCount = 0 if (childList.count()) then repeat with x = 1 to childList.count() thisChild = childList.getAt(x) case thisChild.getLoadStatus() of #error: errorCount = errorCount + 1 #queued: queueCount = queueCount + 1 #loading: loadingCount = loadingCount + 1 #done: doneCount = doneCount + 1 end case end repeat end if -- return values in order of importance if (errorCount) then return #error else if (loadingCount) then return #loading else if (queueCount) then return #queued else if (doneCount) then return #done else return #none end if end -- END LOAD STATUS HANDLERS -- UTILITY HANDLERS -- converts aString to lower case and returns it on convertToLowerCase me, aString returnString = "" repeat with x = 1 to aString.length thisChar = charToNum(aString.char[x]) if ((thisChar >= 65) and (thisChar <=90)) then returnString = returnString & numToChar(thisChar + 32) else returnString = returnString & aString.char[x] end if end repeat return returnString end -- END UTILITY HANDLERS