29 using System.Collections;
30 using System.Globalization;
32 using System.Security.Cryptography;
37 namespace OpenSim.Framework.Capabilities
42 public static class LLSD
69 public static object LLSDDeserialize(byte[] b)
71 using (MemoryStream ms =
new MemoryStream(b,
false))
73 return LLSDDeserialize(ms);
82 public static object LLSDDeserialize(Stream st)
84 using (XmlTextReader reader =
new XmlTextReader(st))
89 if (reader.NodeType != XmlNodeType.Element || reader.LocalName !=
"llsd")
90 throw new LLSDParseException(
"Expected <llsd>");
93 object ret = LLSDParseOne(reader);
96 if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName !=
"llsd")
97 throw new LLSDParseException(
"Expected </llsd>");
108 public static byte[] LLSDSerialize(
object obj)
110 StringWriter sw =
new StringWriter();
111 XmlTextWriter writer =
new XmlTextWriter(sw);
112 writer.Formatting = Formatting.None;
114 writer.WriteStartElement(String.Empty,
"llsd", String.Empty);
115 LLSDWriteOne(writer, obj);
116 writer.WriteEndElement();
120 return Util.UTF8.GetBytes(sw.ToString());
128 public static void LLSDWriteOne(XmlTextWriter writer,
object obj)
132 writer.WriteStartElement(String.Empty,
"undef", String.Empty);
133 writer.WriteEndElement();
139 writer.WriteStartElement(String.Empty,
"string", String.Empty);
140 writer.WriteString((string) obj);
141 writer.WriteEndElement();
145 writer.WriteStartElement(String.Empty,
"integer", String.Empty);
146 writer.WriteString(obj.ToString());
147 writer.WriteEndElement();
149 else if (obj is
double)
151 writer.WriteStartElement(String.Empty,
"real", String.Empty);
152 writer.WriteString(obj.ToString());
153 writer.WriteEndElement();
155 else if (obj is
bool)
158 writer.WriteStartElement(String.Empty,
"boolean", String.Empty);
159 writer.WriteString(b ?
"1" :
"0");
160 writer.WriteEndElement();
162 else if (obj is ulong)
164 throw new Exception(
"ulong in LLSD is currently not implemented, fix me!");
166 else if (obj is UUID)
169 writer.WriteStartElement(String.Empty,
"uuid", String.Empty);
170 writer.WriteString(u.ToString());
171 writer.WriteEndElement();
173 else if (obj is Hashtable)
175 Hashtable h = obj as Hashtable;
176 writer.WriteStartElement(String.Empty,
"map", String.Empty);
177 foreach (
string key in h.Keys)
179 writer.WriteStartElement(String.Empty,
"key", String.Empty);
180 writer.WriteString(
key);
181 writer.WriteEndElement();
182 LLSDWriteOne(writer, h[key]);
184 writer.WriteEndElement();
186 else if (obj is ArrayList)
188 ArrayList a = obj as ArrayList;
189 writer.WriteStartElement(String.Empty,
"array", String.Empty);
190 foreach (
object item
in a)
192 LLSDWriteOne(writer, item);
194 writer.WriteEndElement();
196 else if (obj is byte[])
198 byte[] b = obj as byte[];
199 writer.WriteStartElement(String.Empty,
"binary", String.Empty);
201 writer.WriteStartAttribute(String.Empty,
"encoding", String.Empty);
202 writer.WriteString(
"base64");
203 writer.WriteEndAttribute();
215 writer.WriteString(Convert.ToBase64String(b));
216 writer.WriteEndElement();
220 throw new LLSDSerializeException(
"Unknown type " + obj.GetType().Name);
229 public static object LLSDParseOne(XmlTextReader reader)
232 if (reader.NodeType != XmlNodeType.Element)
233 throw new LLSDParseException(
"Expected an element");
235 string dtype = reader.LocalName;
242 if (reader.IsEmptyElement)
255 if (reader.IsEmptyElement)
262 string s = reader.ReadString().Trim();
264 if (s ==
String.Empty || s ==
"false" || s ==
"0")
266 else if (s ==
"true" || s ==
"1")
269 throw new LLSDParseException(
"Bad boolean value " + s);
275 if (reader.IsEmptyElement)
282 ret = Convert.ToInt32(reader.ReadString().Trim());
287 if (reader.IsEmptyElement)
294 ret = Convert.ToDouble(reader.ReadString().Trim());
299 if (reader.IsEmptyElement)
306 ret =
new UUID(reader.ReadString().Trim());
311 if (reader.IsEmptyElement)
318 ret = reader.ReadString();
323 if (reader.IsEmptyElement)
329 if (reader.GetAttribute(
"encoding") != null &&
330 reader.GetAttribute(
"encoding") !=
"base64")
332 throw new LLSDParseException(
"Unknown encoding: " + reader.GetAttribute(
"encoding"));
336 FromBase64Transform b64 =
new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
337 byte[] inp = Util.UTF8.GetBytes(reader.ReadString());
338 ret = b64.TransformFinalBlock(inp, 0, inp.Length);
344 throw new Exception(
"LLSD TODO: date");
348 return LLSDParseMap(reader);
352 return LLSDParseArray(reader);
355 throw new LLSDParseException(
"Unknown element <" + dtype +
">");
358 if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != dtype)
360 throw new LLSDParseException(
"Expected </" + dtype +
">");
372 public static Hashtable LLSDParseMap(XmlTextReader reader)
374 Hashtable ret =
new Hashtable();
376 if (reader.NodeType != XmlNodeType.Element || reader.LocalName !=
"map")
377 throw new LLSDParseException(
"Expected <map>");
379 if (reader.IsEmptyElement)
390 if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName ==
"map")
396 if (reader.NodeType != XmlNodeType.Element || reader.LocalName !=
"key")
397 throw new LLSDParseException(
"Expected <key>");
399 string key = reader.ReadString();
401 if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName !=
"key")
402 throw new LLSDParseException(
"Expected </key>");
405 object val = LLSDParseOne(reader);
417 public static ArrayList LLSDParseArray(XmlTextReader reader)
419 ArrayList ret =
new ArrayList();
421 if (reader.NodeType != XmlNodeType.Element || reader.LocalName !=
"array")
422 throw new LLSDParseException(
"Expected <array>");
424 if (reader.IsEmptyElement)
436 if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName ==
"array")
442 ret.Insert(ret.Count, LLSDParseOne(reader));
453 private static string GetSpaces(
int count)
455 StringBuilder b =
new StringBuilder();
456 for (
int i = 0; i < count; i++) b.Append(
" ");
466 public static String LLSDDump(
object obj,
int indent)
470 return GetSpaces(indent) +
"- undef\n";
472 else if (obj is
string)
474 return GetSpaces(indent) +
"- string \"" + (string) obj +
"\"\n";
478 return GetSpaces(indent) +
"- integer " + obj.ToString() +
"\n";
480 else if (obj is
double)
482 return GetSpaces(indent) +
"- float " + obj.ToString() +
"\n";
484 else if (obj is UUID)
486 return GetSpaces(indent) +
"- uuid " + ((UUID) obj).ToString() + Environment.NewLine;
488 else if (obj is Hashtable)
490 StringBuilder ret =
new StringBuilder();
491 ret.Append(GetSpaces(indent) +
"- map" + Environment.NewLine);
492 Hashtable map = (Hashtable) obj;
494 foreach (
string key
in map.Keys)
496 ret.Append(GetSpaces(indent + 2) +
"- key \"" + key +
"\"" + Environment.NewLine);
497 ret.Append(LLSDDump(map[key], indent + 3));
500 return ret.ToString();
502 else if (obj is ArrayList)
504 StringBuilder ret =
new StringBuilder();
505 ret.Append(GetSpaces(indent) +
"- array\n");
506 ArrayList list = (ArrayList) obj;
508 foreach (
object item
in list)
510 ret.Append(LLSDDump(item, indent + 2));
513 return ret.ToString();
515 else if (obj is byte[])
517 return GetSpaces(indent) +
"- binary\n" + Utils.BytesToHexString((byte[]) obj, GetSpaces(indent)) +
522 return GetSpaces(indent) +
"- unknown type " + obj.GetType().Name + Environment.NewLine;
526 public static object ParseTerseLLSD(
string llsd)
529 return ParseTerseLLSD(llsd, out notused);
532 public static object ParseTerseLLSD(
string llsd, out
int endPos)
534 if (llsd.Length == 0)
544 throw new LLSDParseException(
"Undefined value type encountered");
553 if (llsd.Length < 2)
throw new LLSDParseException(
"Integer value type with no value");
555 endPos = FindEnd(llsd, 1);
557 if (Int32.TryParse(llsd.Substring(1, endPos - 1), out value))
560 throw new LLSDParseException(
"Failed to parse integer value type");
564 if (llsd.Length < 2)
throw new LLSDParseException(
"Real value type with no value");
566 endPos = FindEnd(llsd, 1);
568 if (Double.TryParse(llsd.Substring(1, endPos - 1), NumberStyles.Float,
569 Utils.EnUsCulture.NumberFormat, out value))
572 throw new LLSDParseException(
"Failed to parse double value type");
576 if (llsd.Length < 17)
throw new LLSDParseException(
"UUID value type with no value");
578 endPos = FindEnd(llsd, 1);
580 if (UUID.TryParse(llsd.Substring(1, endPos - 1), out value))
583 throw new LLSDParseException(
"Failed to parse UUID value type");
589 throw new LLSDParseException(
"Binary value type is unimplemented");
592 if (llsd.Length < 2)
throw new LLSDParseException(
"String value type with no value");
593 endPos = FindEnd(llsd, 1);
594 return llsd.Substring(1, endPos - 1);
597 throw new LLSDParseException(
"Date value type is unimplemented");
600 if (llsd.IndexOf(
']') == -1)
throw new LLSDParseException(
"Invalid array");
603 ArrayList array =
new ArrayList();
605 while (llsd[pos] !=
']')
610 if (llsd[pos] ==
',') ++pos;
613 if (pos < llsd.Length && llsd[pos] ==
' ') ++pos;
616 array.Add(ParseTerseLLSD(llsd.Substring(pos), out end));
625 if (llsd.IndexOf(
'}') == -1)
throw new LLSDParseException(
"Invalid map");
628 Hashtable hashtable =
new Hashtable();
630 while (llsd[pos] !=
'}')
635 if (llsd[pos] ==
',') ++pos;
638 if (pos < llsd.Length && llsd[pos] ==
' ') ++pos;
640 if (llsd[pos] !=
'\'')
throw new LLSDParseException(
"Expected a map key");
641 int endquote = llsd.IndexOf(
'\'', pos + 1);
642 if (endquote == -1 || (endquote + 1) >= llsd.Length || llsd[endquote + 1] !=
':')
643 throw new LLSDParseException(
"Invalid map format");
644 string key = llsd.Substring(pos, endquote - pos);
645 key = key.Replace(
"'", String.Empty);
646 pos += (endquote - pos) + 2;
649 hashtable.Add(
key, ParseTerseLLSD(llsd.Substring(pos), out end));
657 throw new Exception(
"Unknown value type");
661 private static int FindEnd(
string llsd,
int start)
663 int end = llsd.IndexOfAny(
new char[] {
',',
']',
'}'});
664 if (end == -1) end = llsd.Length - 1;
672 private static void SkipWS(XmlTextReader reader)
675 reader.NodeType == XmlNodeType.Comment ||
676 reader.NodeType == XmlNodeType.Whitespace ||
677 reader.NodeType == XmlNodeType.SignificantWhitespace ||
678 reader.NodeType == XmlNodeType.XmlDeclaration)
LLSDSerializeException(string message)
OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString key
LLSDParseException(string message)