OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
JsonStore.cs
Go to the documentation of this file.
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  // -----------------------------------------------------------------
99  // -----------------------------------------------------------------
100  public int StringSpace { get; set; }
101 
102  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
252  // -----------------------------------------------------------------
253  public bool RemoveValue(string expr)
254  {
255  return SetValueFromExpression(expr,null);
256  }
257 
258  // -----------------------------------------------------------------
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)
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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  // -----------------------------------------------------------------
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 }
static OSD ProcessPathExpression(OSD map, Stack< string > path)
Definition: JsonStore.cs:557
OpenMetaverse.StructuredData.OSDArray OSDArray
static bool ConvertOutputValue(OSD result, out string value, bool useJson)
Definition: JsonStore.cs:625
bool SetValue(string expr, string value, bool useJson)
Definition: JsonStore.cs:263
bool TakeValue(string expr, bool useJson, TakeValueCallback cback)
Definition: JsonStore.cs:304
OpenMetaverse.StructuredData.OSDMap OSDMap
static string PathExpressionToKey(Stack< string > path)
Definition: JsonStore.cs:667
delegate void TakeValueCallback(string s)
OpenMetaverse.StructuredData.OSD OSD
bool ReadValue(string expr, bool useJson, TakeValueCallback cback)
Definition: JsonStore.cs:338
Interactive OpenSim region server
Definition: OpenSim.cs:55
static bool CanonicalPathExpression(string ipath, out string opath)
Definition: JsonStore.cs:107
bool GetValue(string expr, out string value, bool useJson)
Definition: JsonStore.cs:234
TakeValueCallbackClass(string spath, bool usejson, TakeValueCallback cback)
Definition: JsonStore.cs:60
static bool ParsePathExpression(string expr, out Stack< string > path)
Parse the path expression and put the components into a stack. We use a stack because we process the ...
Definition: JsonStore.cs:522