// A very basic XML Parser written in LSL.
// global variables for reading, holding, and handling the initial XML read
string xmlFileName = "xml-test";
string xmlBody = "";
integer xmlBodyLine = 0;
key xmlBodyKey;
// Chops off the string up to the first, found element
// xml - XML string
// start - where the llSubStringIndex begins
// offset - the length to the right the substring needs to begin
string llReadTo(string xml, integer start, integer offset)
{
integer ReadTo = start + offset;
return llGetSubString(xml, ReadTo, -1);
}
// Returns the inner text of a node.
string llGetNodeText(string xml, string nodeName)
{
string elStart = "<" + nodeName;
string elStop = "" + nodeName + ">";
integer elCloseTag = llSubStringIndex(xml, elStop);
integer elOpenTag = llSubStringIndex(xml, "/>");
// There has to be some kind of closing tag for the parsing to be valid
if (elCloseTag != -1 || elOpenTag != -1)
{
// For: ...
if (llSubStringIndex(xml, elStart) != -1 &&
(elCloseTag < elOpenTag || elOpenTag == -1))
{
integer nodeStart = llSubStringIndex(xml, elStart) + llStringLength(elStart) + 1;
integer nodeStop = llSubStringIndex(xml, elStop) - 1;
string nodeText = llGetSubString(xml, nodeStart, nodeStop);
return nodeText;
}
// For:
else if (llSubStringIndex(xml, elStart) != -1 &&
(elCloseTag > elOpenTag || elCloseTag == -1))
{
integer nodeStart = llSubStringIndex(xml, elStart) + llStringLength(elStart) + 1;
integer nodeStop = llSubStringIndex(xml, "/>") - 1;
string nodeText = llGetSubString(xml, nodeStart, nodeStop);
return nodeText;
}
// For: invalid XML
else
{
return "";
}
}
else
{
return "";
}
}
// Counts the number of nodes in a string
integer llCountElements(string xml, string nodeName)
{
string elStart = "<" + nodeName;
integer elStartLen = llStringLength(elStart) + 1;
integer nodeCount = 0;
while (llSubStringIndex(xml, elStart) != -1)
{
// Increments node count
++nodeCount;
// Chops off the string up to the first, found element
xml = llReadTo(xml, llSubStringIndex(xml, elStart), elStartLen);
}
return nodeCount;
}
// Checks for attributes in an element
list llGetElementAttributes(string xml)
{
integer elTagEnd = 0;
if (llSubStringIndex(xml, "/>") == -1)
{
elTagEnd = llSubStringIndex(xml, ">") - 1;
}
else
{
if (llSubStringIndex(xml, " />") == -1)
{
elTagEnd = llSubStringIndex(xml, "/>") - 1;
}
else
{
elTagEnd = llSubStringIndex(xml, "/>") - 2;
}
}
xml = llGetSubString(xml, 0, elTagEnd);
list attributes = llParseString2List(xml, ["=\"", "\""], []);
return attributes;
}
// Gets an attribute value, based of an attribute's name, from apaired list
string llGetAttributeValue(list attributes, string name)
{
string value = "";
integer isName = 0;
integer isCntr = 0;
// isName is switched to 1 when the loop finds a list valuen with the attribute name
// in the next iteration, the next list value is assigned to the variable "value"
// and isName is reset to 0
while (llList2String(attributes, isCntr) != "")
{
if (isName == 1)
{
value = llList2String(attributes, isCntr);
isName = 0;
}
else
{
if ((isCntr % 2) == 0)
{
string temp = llList2String(attributes, isCntr);
if (llGetSubString(temp, 0, 0) == " ")
{
temp = llGetSubString(temp, 1, -1);
}
if (temp == name)
{
isName = 1;
}
}
}
isCntr++;
}
return value;
}
// Gets a list of nodes
list llGetNodeList(string xml, string nodeName)
{
list nodeList;
integer elementCount = llCountElements(xml, nodeName);
// If nodes exist with the XML string, proceed
if (elementCount > 0)
{
integer nodeCntr;
for (nodeCntr = 0; nodeCntr < elementCount; nodeCntr++)
{
string tempText = llGetNodeText(xml, nodeName);
nodeList = nodeList + [tempText];
xml = llReadTo(xml, llSubStringIndex(xml, tempText), llStringLength(tempText));
xml = llGetSubString(xml, llSubStringIndex(xml, "<" + nodeName), -1);
}
}
// If there are no such nodes in the string, return a one element
// list with "No nodes" inside.
else
{
nodeList = ["No nodes"];
}
return nodeList;
}
// This has to be here
default
{
state_entry()
{
if (xmlBody == "")
{
state readfile;
}
else
{
llSay(0, xmlBody);
}
}
touch_start(integer total_number)
{
list spies = llGetNodeList(xmlBody, "spy");
integer spyCntr = 0;
while (llList2String(spies, spyCntr) != "")
{
string xmlTemp = llList2String(spies, spyCntr);
list spyName = llGetElementAttributes(xmlTemp);
string hairSpy = llGetAttributeValue(spyName, "hair");
string lenSpy = llGetAttributeValue(spyName, "length");
string nameSpy = llGetAttributeValue(spyName, "name");
string color = llGetNodeText(xmlTemp, "suit");
llSay(0, nameSpy + " has a " + color + " uniform and " + lenSpy + ", " + hairSpy + " hair.");
++spyCntr;
}
}
}
// This simply reads XML from a Notecard.
// This can be removed when grabbing information from the web.
state readfile
{
state_entry()
{
// Makes sure xmlBody is empty
xmlBody = "";
// Starts the reading of the notecard
xmlBodyKey = llGetNotecardLine(xmlFileName, xmlBodyLine);
}
dataserver(key query_id, string data)
{
if (query_id == xmlBodyKey)
{
// Perform the following while there is text
if (data != EOF)
{
// Removes leading spaces or tabs
if (llSubStringIndex(data, "<") > 0)
{
data = llDeleteSubString(data, 0, (llSubStringIndex(data, "<") - 1));
}
// Appends the next line to xmlBody
xmlBody = xmlBody + data;
// Increments the line counter
++xmlBodyLine;
// Reads the next line
xmlBodyKey = llGetNotecardLine(xmlFileName, xmlBodyLine);
}
// If you've gotten to the end of the notecard, go back
else
{
state default;
}
}
}
state_exit()
{
xmlBodyLine = 0;
}
}