opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 1 /*
2 * Copyright (c) Contributors
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 using Mono.Addins;
28  
29 using System;
30 using System.Reflection;
31 using System.Threading;
32 using System.Text;
33 using System.Net;
34 using System.Net.Sockets;
35 using log4net;
36 using Nini.Config;
37 using OpenMetaverse;
38 using OpenMetaverse.StructuredData;
39 using OpenSim.Framework;
40 using OpenSim.Region.Framework.Interfaces;
41 using OpenSim.Region.Framework.Scenes;
42 using System.Collections.Generic;
43 using System.Text.RegularExpressions;
44  
45 namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
46 {
47 public class JsonStore
48 {
49 private static readonly ILog m_log =
50 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51  
52 protected virtual OSD ValueStore { get; set; }
53  
54 protected class TakeValueCallbackClass
55 {
56 public string Path { get; set; }
57 public bool UseJson { get; set; }
58 public TakeValueCallback Callback { get; set; }
59  
60 public TakeValueCallbackClass(string spath, bool usejson, TakeValueCallback cback)
61 {
62 Path = spath;
63 UseJson = usejson;
64 Callback = cback;
65 }
66 }
67  
68 protected List<TakeValueCallbackClass> m_TakeStore;
69 protected List<TakeValueCallbackClass> m_ReadStore;
70  
71 // add separators for quoted paths and array references
72 protected static Regex m_ParsePassOne = new Regex("({[^}]+}|\\[[0-9]+\\]|\\[\\+\\])");
73  
74 // add quotes to bare identifiers which are limited to alphabetic characters
75 protected static Regex m_ParsePassThree = new Regex("(?<!{[^}]*)\\.([a-zA-Z]+)(?=\\.)");
76  
77 // remove extra separator characters
78 protected static Regex m_ParsePassFour = new Regex("\\.+");
79  
80 // expression used to validate the full path, this is canonical representation
81 protected static Regex m_ValidatePath = new Regex("^\\.(({[^}]+}|\\[[0-9]+\\]|\\[\\+\\])\\.)*$");
82  
83 // expression used to match path components
84 protected static Regex m_PathComponent = new Regex("\\.({[^}]+}|\\[[0-9]+\\]|\\[\\+\\])");
85  
86 // extract the internals of an array reference
87 protected static Regex m_SimpleArrayPattern = new Regex("^\\[([0-9]+)\\]$");
88 protected static Regex m_ArrayPattern = new Regex("^\\[([0-9]+|\\+)\\]$");
89  
90 // extract the internals of a has reference
91 protected static Regex m_HashPattern = new Regex("^{([^}]+)}$");
92  
93 // -----------------------------------------------------------------
94 /// <summary>
95 /// This is a simple estimator for the size of the stored data, it
96 /// is not precise, but should be close enough to implement reasonable
97 /// limits on the storage space used
98 /// </summary>
99 // -----------------------------------------------------------------
100 public int StringSpace { get; set; }
101  
102 // -----------------------------------------------------------------
103 /// <summary>
104 ///
105 /// </summary>
106 // -----------------------------------------------------------------
107 public static bool CanonicalPathExpression(string ipath, out string opath)
108 {
109 Stack<string> path;
110 if (! ParsePathExpression(ipath,out path))
111 {
112 opath = "";
113 return false;
114 }
115  
116 opath = PathExpressionToKey(path);
117 return true;
118 }
119  
120 // -----------------------------------------------------------------
121 /// <summary>
122 ///
123 /// </summary>
124 // -----------------------------------------------------------------
125 public JsonStore()
126 {
127 StringSpace = 0;
128 m_TakeStore = new List<TakeValueCallbackClass>();
129 m_ReadStore = new List<TakeValueCallbackClass>();
130 }
131  
132 public JsonStore(string value) : this()
133 {
134 // This is going to throw an exception if the value is not
135 // a valid JSON chunk. Calling routines should catch the
136 // exception and handle it appropriately
137 if (String.IsNullOrEmpty(value))
138 ValueStore = new OSDMap();
139 else
140 ValueStore = OSDParser.DeserializeJson(value);
141 }
142  
143 // -----------------------------------------------------------------
144 /// <summary>
145 ///
146 /// </summary>
147 // -----------------------------------------------------------------
148 public JsonStoreNodeType GetNodeType(string expr)
149 {
150 Stack<string> path;
151 if (! ParsePathExpression(expr,out path))
152 return JsonStoreNodeType.Undefined;
153  
154 OSD result = ProcessPathExpression(ValueStore,path);
155  
156 if (result == null)
157 return JsonStoreNodeType.Undefined;
158  
159 if (result is OSDMap)
160 return JsonStoreNodeType.Object;
161  
162 if (result is OSDArray)
163 return JsonStoreNodeType.Array;
164  
165 if (OSDBaseType(result.Type))
166 return JsonStoreNodeType.Value;
167  
168 return JsonStoreNodeType.Undefined;
169 }
170  
171 // -----------------------------------------------------------------
172 /// <summary>
173 ///
174 /// </summary>
175 // -----------------------------------------------------------------
176 public JsonStoreValueType GetValueType(string expr)
177 {
178 Stack<string> path;
179 if (! ParsePathExpression(expr,out path))
180 return JsonStoreValueType.Undefined;
181  
182 OSD result = ProcessPathExpression(ValueStore,path);
183  
184 if (result == null)
185 return JsonStoreValueType.Undefined;
186  
187 if (result is OSDMap)
188 return JsonStoreValueType.Undefined;
189  
190 if (result is OSDArray)
191 return JsonStoreValueType.Undefined;
192  
193 if (result is OSDBoolean)
194 return JsonStoreValueType.Boolean;
195  
196 if (result is OSDInteger)
197 return JsonStoreValueType.Integer;
198  
199 if (result is OSDReal)
200 return JsonStoreValueType.Float;
201  
202 if (result is OSDString)
203 return JsonStoreValueType.String;
204  
205 return JsonStoreValueType.Undefined;
206 }
207  
208 // -----------------------------------------------------------------
209 /// <summary>
210 ///
211 /// </summary>
212 // -----------------------------------------------------------------
213 public int ArrayLength(string expr)
214 {
215 Stack<string> path;
216 if (! ParsePathExpression(expr,out path))
217 return -1;
218  
219 OSD result = ProcessPathExpression(ValueStore,path);
220 if (result != null && result.Type == OSDType.Array)
221 {
222 OSDArray arr = result as OSDArray;
223 return arr.Count;
224 }
225  
226 return -1;
227 }
228  
229 // -----------------------------------------------------------------
230 /// <summary>
231 ///
232 /// </summary>
233 // -----------------------------------------------------------------
234 public bool GetValue(string expr, out string value, bool useJson)
235 {
236 Stack<string> path;
237 if (! ParsePathExpression(expr,out path))
238 {
239 value = "";
240 return false;
241 }
242  
243 OSD result = ProcessPathExpression(ValueStore,path);
244 return ConvertOutputValue(result,out value,useJson);
245 }
246  
247  
248 // -----------------------------------------------------------------
249 /// <summary>
250 ///
251 /// </summary>
252 // -----------------------------------------------------------------
253 public bool RemoveValue(string expr)
254 {
255 return SetValueFromExpression(expr,null);
256 }
257  
258 // -----------------------------------------------------------------
259 /// <summary>
260 ///
261 /// </summary>
262 // -----------------------------------------------------------------
263 public bool SetValue(string expr, string value, bool useJson)
264 {
265 OSD ovalue;
266  
267 // One note of caution... if you use an empty string in the
268 // structure it will be assumed to be a default value and will
269 // not be seialized in the json
270  
271 if (useJson)
272 {
273 // There doesn't appear to be a good way to determine if the
274 // value is valid Json other than to let the parser crash
275 try
276 {
277 ovalue = OSDParser.DeserializeJson(value);
278 }
279 catch (Exception e)
280 {
281 if (value.StartsWith("'") && value.EndsWith("'"))
282 {
283 ovalue = new OSDString(value.Substring(1,value.Length - 2));
284 }
285 else
286 {
287 return false;
288 }
289 }
290 }
291 else
292 {
293 ovalue = new OSDString(value);
294 }
295  
296 return SetValueFromExpression(expr,ovalue);
297 }
298  
299 // -----------------------------------------------------------------
300 /// <summary>
301 ///
302 /// </summary>
303 // -----------------------------------------------------------------
304 public bool TakeValue(string expr, bool useJson, TakeValueCallback cback)
305 {
306 Stack<string> path;
307 if (! ParsePathExpression(expr,out path))
308 return false;
309  
310 string pexpr = PathExpressionToKey(path);
311  
312 OSD result = ProcessPathExpression(ValueStore,path);
313 if (result == null)
314 {
315 m_TakeStore.Add(new TakeValueCallbackClass(pexpr,useJson,cback));
316 return false;
317 }
318  
319 string value = String.Empty;
320 if (! ConvertOutputValue(result,out value,useJson))
321 {
322 // the structure does not match the request so i guess we'll wait
323 m_TakeStore.Add(new TakeValueCallbackClass(pexpr,useJson,cback));
324 return false;
325 }
326  
327 SetValueFromExpression(expr,null);
328 cback(value);
329  
330 return true;
331 }
332  
333 // -----------------------------------------------------------------
334 /// <summary>
335 ///
336 /// </summary>
337 // -----------------------------------------------------------------
338 public bool ReadValue(string expr, bool useJson, TakeValueCallback cback)
339 {
340 Stack<string> path;
341 if (! ParsePathExpression(expr,out path))
342 return false;
343  
344 string pexpr = PathExpressionToKey(path);
345  
346 OSD result = ProcessPathExpression(ValueStore,path);
347 if (result == null)
348 {
349 m_ReadStore.Add(new TakeValueCallbackClass(pexpr,useJson,cback));
350 return false;
351 }
352  
353 string value = String.Empty;
354 if (! ConvertOutputValue(result,out value,useJson))
355 {
356 // the structure does not match the request so i guess we'll wait
357 m_ReadStore.Add(new TakeValueCallbackClass(pexpr,useJson,cback));
358 return false;
359 }
360  
361 cback(value);
362  
363 return true;
364 }
365  
366 // -----------------------------------------------------------------
367 /// <summary>
368 ///
369 /// </summary>
370 // -----------------------------------------------------------------
371 protected bool SetValueFromExpression(string expr, OSD ovalue)
372 {
373 Stack<string> path;
374 if (! ParsePathExpression(expr,out path))
375 return false;
376  
377 if (path.Count == 0)
378 {
379 ValueStore = ovalue;
380 StringSpace = 0;
381 return true;
382 }
383  
384 // pkey will be the final element in the path, we pull it out here to make sure
385 // that the assignment works correctly
386 string pkey = path.Pop();
387 string pexpr = PathExpressionToKey(path);
388 if (pexpr != "")
389 pexpr += ".";
390  
391 OSD result = ProcessPathExpression(ValueStore,path);
392 if (result == null)
393 return false;
394  
395 // Check pkey, the last element in the path, for and extract array references
396 MatchCollection amatches = m_ArrayPattern.Matches(pkey,0);
397 if (amatches.Count > 0)
398 {
399 if (result.Type != OSDType.Array)
400 return false;
401  
402 OSDArray amap = result as OSDArray;
403  
404 Match match = amatches[0];
405 GroupCollection groups = match.Groups;
406 string akey = groups[1].Value;
407  
408 if (akey == "+")
409 {
410 string npkey = String.Format("[{0}]",amap.Count);
411  
412 if (ovalue != null)
413 {
414 StringSpace += ComputeSizeOf(ovalue);
415  
416 amap.Add(ovalue);
417 InvokeNextCallback(pexpr + npkey);
418 }
419 return true;
420 }
421  
422 int aval = Convert.ToInt32(akey);
423 if (0 <= aval && aval < amap.Count)
424 {
425 if (ovalue == null)
426 {
427 StringSpace -= ComputeSizeOf(amap[aval]);
428 amap.RemoveAt(aval);
429 }
430 else
431 {
432 StringSpace -= ComputeSizeOf(amap[aval]);
433 StringSpace += ComputeSizeOf(ovalue);
434 amap[aval] = ovalue;
435 InvokeNextCallback(pexpr + pkey);
436 }
437 return true;
438 }
439  
440 return false;
441 }
442  
443 // Check for and extract hash references
444 MatchCollection hmatches = m_HashPattern.Matches(pkey,0);
445 if (hmatches.Count > 0)
446 {
447 Match match = hmatches[0];
448 GroupCollection groups = match.Groups;
449 string hkey = groups[1].Value;
450  
451 if (result is OSDMap)
452 {
453 // this is the assignment case
454 OSDMap hmap = result as OSDMap;
455 if (ovalue != null)
456 {
457 StringSpace -= ComputeSizeOf(hmap[hkey]);
458 StringSpace += ComputeSizeOf(ovalue);
459  
460 hmap[hkey] = ovalue;
461 InvokeNextCallback(pexpr + pkey);
462 return true;
463 }
464  
465 // this is the remove case
466 if (hmap.ContainsKey(hkey))
467 {
468 StringSpace -= ComputeSizeOf(hmap[hkey]);
469 hmap.Remove(hkey);
470 return true;
471 }
472  
473 return false;
474 }
475  
476 return false;
477 }
478  
479 // Shouldn't get here if the path was checked correctly
480 m_log.WarnFormat("[JsonStore] invalid path expression");
481 return false;
482 }
483  
484 // -----------------------------------------------------------------
485 /// <summary>
486 ///
487 /// </summary>
488 // -----------------------------------------------------------------
489 protected bool InvokeNextCallback(string pexpr)
490 {
491 // Process all of the reads that match the expression first
492 List<TakeValueCallbackClass> reads =
493 m_ReadStore.FindAll(delegate(TakeValueCallbackClass tb) { return pexpr.StartsWith(tb.Path); });
494  
495 foreach (TakeValueCallbackClass readcb in reads)
496 {
497 m_ReadStore.Remove(readcb);
498 ReadValue(readcb.Path,readcb.UseJson,readcb.Callback);
499 }
500  
501 // Process one take next
502 TakeValueCallbackClass takecb =
503 m_TakeStore.Find(delegate(TakeValueCallbackClass tb) { return pexpr.StartsWith(tb.Path); });
504  
505 if (takecb != null)
506 {
507 m_TakeStore.Remove(takecb);
508 TakeValue(takecb.Path,takecb.UseJson,takecb.Callback);
509  
510 return true;
511 }
512  
513 return false;
514 }
515  
516 // -----------------------------------------------------------------
517 /// <summary>
518 /// Parse the path expression and put the components into a stack. We
519 /// use a stack because we process the path in inverse order later
520 /// </summary>
521 // -----------------------------------------------------------------
522 protected static bool ParsePathExpression(string expr, out Stack<string> path)
523 {
524 path = new Stack<string>();
525  
526 // add front and rear separators
527 expr = "." + expr + ".";
528  
529 // add separators for quoted exprs and array references
530 expr = m_ParsePassOne.Replace(expr,".$1.",-1,0);
531  
532 // add quotes to bare identifier
533 expr = m_ParsePassThree.Replace(expr,".{$1}",-1,0);
534  
535 // remove extra separators
536 expr = m_ParsePassFour.Replace(expr,".",-1,0);
537  
538 // validate the results (catches extra quote characters for example)
539 if (m_ValidatePath.IsMatch(expr))
540 {
541 MatchCollection matches = m_PathComponent.Matches(expr,0);
542 foreach (Match match in matches)
543 path.Push(match.Groups[1].Value);
544  
545 return true;
546 }
547  
548 return false;
549 }
550  
551 // -----------------------------------------------------------------
552 /// <summary>
553 ///
554 /// </summary>
555 /// <param>path is a stack where the top level of the path is at the bottom of the stack</param>
556 // -----------------------------------------------------------------
557 protected static OSD ProcessPathExpression(OSD map, Stack<string> path)
558 {
559 if (path.Count == 0)
560 return map;
561  
562 string pkey = path.Pop();
563  
564 OSD rmap = ProcessPathExpression(map,path);
565 if (rmap == null)
566 return null;
567  
568 // ---------- Check for an array index ----------
569 MatchCollection amatches = m_SimpleArrayPattern.Matches(pkey,0);
570  
571 if (amatches.Count > 0)
572 {
573 if (rmap.Type != OSDType.Array)
574 {
575 m_log.WarnFormat("[JsonStore] wrong type for key {2}, expecting {0}, got {1}",OSDType.Array,rmap.Type,pkey);
576 return null;
577 }
578  
579 OSDArray amap = rmap as OSDArray;
580  
581 Match match = amatches[0];
582 GroupCollection groups = match.Groups;
583 string akey = groups[1].Value;
584 int aval = Convert.ToInt32(akey);
585  
586 if (aval < amap.Count)
587 return (OSD) amap[aval];
588  
589 return null;
590 }
591  
592 // ---------- Check for a hash index ----------
593 MatchCollection hmatches = m_HashPattern.Matches(pkey,0);
594  
595 if (hmatches.Count > 0)
596 {
597 if (rmap.Type != OSDType.Map)
598 {
599 m_log.WarnFormat("[JsonStore] wrong type for key {2}, expecting {0}, got {1}",OSDType.Map,rmap.Type,pkey);
600 return null;
601 }
602  
603 OSDMap hmap = rmap as OSDMap;
604  
605 Match match = hmatches[0];
606 GroupCollection groups = match.Groups;
607 string hkey = groups[1].Value;
608  
609 if (hmap.ContainsKey(hkey))
610 return (OSD) hmap[hkey];
611  
612 return null;
613 }
614  
615 // Shouldn't get here if the path was checked correctly
616 m_log.WarnFormat("[JsonStore] Path type (unknown) does not match the structure");
617 return null;
618 }
619  
620 // -----------------------------------------------------------------
621 /// <summary>
622 ///
623 /// </summary>
624 // -----------------------------------------------------------------
625 protected static bool ConvertOutputValue(OSD result, out string value, bool useJson)
626 {
627 value = String.Empty;
628  
629 // If we couldn't process the path
630 if (result == null)
631 return false;
632  
633 if (useJson)
634 {
635 // The path pointed to an intermediate hash structure
636 if (result.Type == OSDType.Map)
637 {
638 value = OSDParser.SerializeJsonString(result as OSDMap,true);
639 return true;
640 }
641  
642 // The path pointed to an intermediate hash structure
643 if (result.Type == OSDType.Array)
644 {
645 value = OSDParser.SerializeJsonString(result as OSDArray,true);
646 return true;
647 }
648  
649 value = "'" + result.AsString() + "'";
650 return true;
651 }
652  
653 if (OSDBaseType(result.Type))
654 {
655 value = result.AsString();
656 return true;
657 }
658  
659 return false;
660 }
661  
662 // -----------------------------------------------------------------
663 /// <summary>
664 ///
665 /// </summary>
666 // -----------------------------------------------------------------
667 protected static string PathExpressionToKey(Stack<string> path)
668 {
669 if (path.Count == 0)
670 return "";
671  
672 string pkey = "";
673 foreach (string k in path)
674 pkey = (pkey == "") ? k : (k + "." + pkey);
675  
676 return pkey;
677 }
678  
679 // -----------------------------------------------------------------
680 /// <summary>
681 ///
682 /// </summary>
683 // -----------------------------------------------------------------
684 protected static bool OSDBaseType(OSDType type)
685 {
686 // Should be the list of base types for which AsString() returns
687 // something useful
688 if (type == OSDType.Boolean)
689 return true;
690 if (type == OSDType.Integer)
691 return true;
692 if (type == OSDType.Real)
693 return true;
694 if (type == OSDType.String)
695 return true;
696 if (type == OSDType.UUID)
697 return true;
698 if (type == OSDType.Date)
699 return true;
700 if (type == OSDType.URI)
701 return true;
702  
703 return false;
704 }
705  
706 // -----------------------------------------------------------------
707 /// <summary>
708 ///
709 /// </summary>
710 // -----------------------------------------------------------------
711 protected static int ComputeSizeOf(OSD value)
712 {
713 string sval;
714  
715 if (ConvertOutputValue(value,out sval,true))
716 return sval.Length;
717  
718 return 0;
719 }
720 }
721  
722 // -----------------------------------------------------------------
723 /// <summary>
724 /// </summary>
725 // -----------------------------------------------------------------
726 public class JsonObjectStore : JsonStore
727 {
728 private static readonly ILog m_log =
729 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
730  
731 private Scene m_scene;
732 private UUID m_objectID;
733  
734 protected override OSD ValueStore
735 {
736 get
737 {
738 SceneObjectPart sop = m_scene.GetSceneObjectPart(m_objectID);
739 if (sop == null)
740 {
741 // This is bad
742 return null;
743 }
744  
745 return sop.DynAttrs.TopLevelMap;
746 }
747  
748 // cannot set the top level
749 set
750 {
751 m_log.InfoFormat("[JsonStore] cannot set top level value in object store");
752 }
753 }
754  
755 public JsonObjectStore(Scene scene, UUID oid) : base()
756 {
757 m_scene = scene;
758 m_objectID = oid;
759  
760 // the size limit is imposed on whatever is already in the store
761 StringSpace = ComputeSizeOf(ValueStore);
762 }
763 }
764  
765 }