/// <summary>
 /// Property value constructor
 /// </summary>
 /// <param name="def"></param>
 /// <param name="val"></param>
 public PropVal(PropDef def, object val)
 {
     this.def = def;
     this.val = val;
 }
        private IEnumerable SearchReferenceProperty(VersionHistory type, string uri, NameVal[] patterns, SearchKind kind, DateTime timestamp, NameVal prop, bool compound, PropDef def, ArrayList refs)
        {
            refs.Add(def);

            NameVal[] restOfPatterns = compound ? patterns : SubArray(patterns);

            object val = prop.val;
            switch (prop.name) 
            {
                case Symbols.Timestamp: 
            
                    if (val is Range) 
                    { 
                        Range range = (Range)val;
                        if (range.from is DateTime) 
                        {
                            Key fromKey = new Key((DateTime)range.from, range.fromInclusive);
                            Key tillKey = new Key((DateTime)range.till, range.tillInclusive);
                            return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                                new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                                root.timeIndex.GetEnumerator(fromKey, tillKey), kind, timestamp));
                        }
                    } 
                    else if (val is DateTime) 
                    {
                        Key key = new Key((DateTime)val);
                        return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                            root.timeIndex.GetEnumerator(key, key), kind, timestamp));                            
                    } 
                    return new object[0]; // empty selection
                case Symbols.Rectangle:
                    if (val is NameVal[]) 
                    {
                        NameVal[] coord = (NameVal[])val;
                        if (coord.Length == 4) 
                        {
                            RectangleR2 r = new RectangleR2((double)coord[0].val, 
                                (double)coord[1].val, 
                                (double)coord[2].val, 
                                (double)coord[3].val);
                            return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                                new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                                root.spatialIndex.Overlaps(r).GetEnumerator(), kind, timestamp));
                        }
                    }
                    break;
                case Symbols.Point:
                    if (val is NameVal[]) 
                    {
                        NameVal[] coord = (NameVal[])val;
                        if (coord.Length == 2) 
                        {
                            double x = (double)coord[0].val;
                            double y = (double)coord[1].val;
                            RectangleR2 r = new RectangleR2(x, y, x, y);
                            return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                                new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                                root.spatialIndex.Overlaps(r).GetEnumerator(), kind, timestamp));
                        }
                    }
                    break;
                case Symbols.Keyword:
                    if (val is string) 
                    {
                        ArrayList keywords = new ArrayList();
                        foreach (string keyword in ((string)val).ToLower().Split(keywordSeparators)) 
                        {
                            if (keyword.Length > 0 && !keywordStopList.ContainsKey(keyword))
                            {
                                keywords.Add(keyword);
                            }
                        }
                        IEnumerator[] occurences = new IEnumerator[keywords.Count];
                        for (int i = 0; i < occurences.Length; i++) 
                        { 
                            Key key = new Key((string)keywords[i]);
                            occurences[i] = root.inverseIndex.GetEnumerator(key, key);
                        }
                        return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                            db.Merge(occurences), kind, timestamp));
                    }
                    break;
            }

            def = (PropDef)root.propDefIndex[prop.name];
            if (def == null) 
            { 
                return new object[0]; // empty selection
            }
            if (val is Range) 
            { 
                Range range = (Range)val;
                if (range.from is double) 
                {
                    Key fromKey = new Key(new object[]{def, range.from}, range.fromInclusive);
                    Key tillKey = new Key(new object[]{def, range.till}, range.tillInclusive);
                    return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                        root.numPropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp));
                } 
                else if (range.from is DateTime) 
                {
                    Key fromKey = new Key(new object[]{def, range.from}, range.fromInclusive);
                    Key tillKey = new Key(new object[]{def, range.till}, range.tillInclusive);
                    return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                        root.timePropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp));
                } 
                else 
                { 
                    Key fromKey = new Key(new object[]{def, range.from}, range.fromInclusive);
                    Key tillKey = new Key(new object[]{def, range.till}, range.tillInclusive);
                    return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                        root.strPropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp));
                }
            } 
            if (val is string) 
            {
                string str = (string)prop.val;
                int wc = str.IndexOf('*');
                if (wc < 0) 
                { 
                    Key key = new Key(new object[]{def, str});
                    return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                        root.strPropIndex.GetEnumerator(key, key), kind, timestamp));
                } 
                else if (wc > 0) 
                { 
                    string prefix = str.Substring(0, wc);
                    Key fromKey = new Key(new object[]{def, prefix});
                    Key tillKey = new Key(new object[]{def, prefix + Char.MaxValue}, false);                        
                    return new SearchResult(root, type, uri, wc == str.Length-1 ? restOfPatterns : patterns, kind, timestamp, 
                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                        root.strPropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp));
                } 
            } 
            else if (val is double) 
            {
                Key key = new Key(new object[]{def, val});
                return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                    new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                    root.numPropIndex.GetEnumerator(key, key), kind, timestamp));
            } 
            else if (val is DateTime) 
            {
                Key key = new Key(new object[]{def, (DateTime)val});
                return new SearchResult(root, type, uri, restOfPatterns, kind, timestamp, 
                    new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)), 
                    root.timePropIndex.GetEnumerator(key, key), kind, timestamp));
            } 
            else if (val is NameVal) 
            {
                return SearchReferenceProperty(type, uri, patterns, kind, timestamp, (NameVal)val, compound, def, refs);
            }
            else if (val is NameVal[]) 
            {
                NameVal[] props = (NameVal[])val;
                if (props.Length > 0) 
                {
                    return SearchReferenceProperty(type, uri, patterns, kind, timestamp, props[0], true, def, refs);
                }
            }
            return null;
        }
 /// <summary>
 /// Property value constructor
 /// </summary>
 /// <param name="def"></param>
 /// <param name="val"></param>
 public PropVal(PropDef def, object val) 
 {
     this.def = def;
     this.val = val;
 }
 public ReferenceIterator(DatabaseRoot root, PropDef[] defs, IEnumerator iterator, SearchKind kind, DateTime timestamp) 
 {
     this.root = root;
     this.defs = defs;
     this.kind = kind;
     this.timestamp = timestamp;
     iterators = new IEnumerator[defs.Length+1];
     iterators[iterators.Length-1] = iterator;
     Reset();
 }
 private Thing CreateObject(Thing type, VersionHistory vh, NameVal[] props) 
 {
     Thing thing = new Thing();
     thing.vh = vh;
     thing.type = type;
     thing.timestamp = DateTime.Now;
     thing.props = new PropVal[props.Length];
     for (int i = 0; i < props.Length; i++) 
     { 
         NameVal prop = props[i];
         PropDef def = (PropDef)root.propDefIndex[prop.name];
         if (def == null) 
         {
             def = new PropDef();
             def.name = prop.name;
             root.propDefIndex.Put(def);
         }
         object val = prop.val;
         PropVal pv = new PropVal(def, val);
         Key key = new Key(new object[]{def, val});
         if (val is string) 
         { 
             root.strPropIndex.Put(key, thing);
             foreach (string keyword in ((string)val).ToLower().Split(keywordSeparators)) 
             {
                 if (keyword.Length > 0 && !keywordStopList.ContainsKey(keyword)) 
                 {
                     root.inverseIndex.Put(keyword, thing);
                 }
             }
         } 
         else if (val is double) 
         { 
             root.numPropIndex.Put(key, thing);
         } 
         else if (val is DateTime) 
         { 
             root.timePropIndex.Put(key, thing);
         } 
         else if (val is VersionHistory || val == null) 
         { 
             root.refPropIndex.Put(key, thing);
             if (prop.name == Symbols.Rectangle) 
             {
                 PropVal[] coord = ((VersionHistory)val).Latest.props;
                 RectangleR2 r = new RectangleR2((double)coord[0].val, 
                     (double)coord[1].val, 
                     (double)coord[2].val, 
                     (double)coord[3].val);
                 root.spatialIndex.Put(r, thing);   
             }
             else if (prop.name == Symbols.Point) 
             {
                 PropVal[] coord = ((VersionHistory)val).Latest.props;
                 double x = (double)coord[0].val;
                 double y = (double)coord[1].val;
                 RectangleR2 r = new RectangleR2(x, y, x, y);
                 root.spatialIndex.Put(r, thing);   
             }
         } 
         else 
         { 
             throw new InvalidOperationException("Invalid propery value type " + prop.val.GetType());
         }
         thing.props[i] = pv;                  
     }
     thing.Modify();
     vh.versions.Add(thing);
     root.timeIndex.Put(thing);
     root.latest.Add(thing);
     return thing;
 }
        private IEnumerable SearchReferenceProperty(VersionHistory type, string uri, NameVal[] patterns, SearchKind kind, DateTime timestamp, NameVal prop, bool compound, PropDef def, ArrayList refs)
        {
            refs.Add(def);

            NameVal[] restOfPatterns = compound ? patterns : SubArray(patterns);

            object val = prop.val;

            switch (prop.name)
            {
            case Symbols.Timestamp:

                if (val is Range)
                {
                    Range range = (Range)val;
                    if (range.from is DateTime)
                    {
                        Key fromKey = new Key((DateTime)range.from, range.fromInclusive);
                        Key tillKey = new Key((DateTime)range.till, range.tillInclusive);
                        return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                                new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                                      root.timeIndex.GetEnumerator(fromKey, tillKey), kind, timestamp)));
                    }
                }
                else if (val is DateTime)
                {
                    Key key = new Key((DateTime)val);
                    return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                                  root.timeIndex.GetEnumerator(key, key), kind, timestamp)));
                }
                return(new object[0]);    // empty selection

            case Symbols.Rectangle:
                if (val is NameVal[])
                {
                    NameVal[] coord = (NameVal[])val;
                    if (coord.Length == 4)
                    {
                        RectangleR2 r = new RectangleR2((double)coord[0].val,
                                                        (double)coord[1].val,
                                                        (double)coord[2].val,
                                                        (double)coord[3].val);
                        return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                                new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                                      root.spatialIndex.Overlaps(r).GetEnumerator(), kind, timestamp)));
                    }
                }
                break;

            case Symbols.Point:
                if (val is NameVal[])
                {
                    NameVal[] coord = (NameVal[])val;
                    if (coord.Length == 2)
                    {
                        double      x = (double)coord[0].val;
                        double      y = (double)coord[1].val;
                        RectangleR2 r = new RectangleR2(x, y, x, y);
                        return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                                new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                                      root.spatialIndex.Overlaps(r).GetEnumerator(), kind, timestamp)));
                    }
                }
                break;

            case Symbols.Keyword:
                if (val is string)
                {
                    ArrayList keywords = new ArrayList();
                    foreach (string keyword in ((string)val).ToLower().Split(keywordSeparators))
                    {
                        if (keyword.Length > 0 && !keywordStopList.ContainsKey(keyword))
                        {
                            keywords.Add(keyword);
                        }
                    }
                    IEnumerator[] occurences = new IEnumerator[keywords.Count];
                    for (int i = 0; i < occurences.Length; i++)
                    {
                        Key key = new Key((string)keywords[i]);
                        occurences[i] = root.inverseIndex.GetEnumerator(key, key);
                    }
                    return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                                  db.Merge(occurences), kind, timestamp)));
                }
                break;
            }

            def = (PropDef)root.propDefIndex[prop.name];
            if (def == null)
            {
                return(new object[0]); // empty selection
            }
            if (val is Range)
            {
                Range range = (Range)val;
                if (range.from is double)
                {
                    Key fromKey = new Key(new object[] { def, range.from }, range.fromInclusive);
                    Key tillKey = new Key(new object[] { def, range.till }, range.tillInclusive);
                    return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                                  root.numPropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp)));
                }
                else if (range.from is DateTime)
                {
                    Key fromKey = new Key(new object[] { def, range.from }, range.fromInclusive);
                    Key tillKey = new Key(new object[] { def, range.till }, range.tillInclusive);
                    return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                                  root.timePropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp)));
                }
                else
                {
                    Key fromKey = new Key(new object[] { def, range.from }, range.fromInclusive);
                    Key tillKey = new Key(new object[] { def, range.till }, range.tillInclusive);
                    return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                                  root.strPropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp)));
                }
            }
            if (val is string)
            {
                string str = (string)prop.val;
                int    wc  = str.IndexOf('*');
                if (wc < 0)
                {
                    Key key = new Key(new object[] { def, str });
                    return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                                  root.strPropIndex.GetEnumerator(key, key), kind, timestamp)));
                }
                else if (wc > 0)
                {
                    string prefix  = str.Substring(0, wc);
                    Key    fromKey = new Key(new object[] { def, prefix });
                    Key    tillKey = new Key(new object[] { def, prefix + Char.MaxValue }, false);
                    return(new SearchResult(root, type, uri, wc == str.Length - 1 ? restOfPatterns : patterns, kind, timestamp,
                                            new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                                  root.strPropIndex.GetEnumerator(fromKey, tillKey), kind, timestamp)));
                }
            }
            else if (val is double)
            {
                Key key = new Key(new object[] { def, val });
                return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                              root.numPropIndex.GetEnumerator(key, key), kind, timestamp)));
            }
            else if (val is DateTime)
            {
                Key key = new Key(new object[] { def, (DateTime)val });
                return(new SearchResult(root, type, uri, restOfPatterns, kind, timestamp,
                                        new ReferenceIterator(root, (PropDef[])refs.ToArray(typeof(PropDef)),
                                                              root.timePropIndex.GetEnumerator(key, key), kind, timestamp)));
            }
            else if (val is NameVal)
            {
                return(SearchReferenceProperty(type, uri, patterns, kind, timestamp, (NameVal)val, compound, def, refs));
            }
            else if (val is NameVal[])
            {
                NameVal[] props = (NameVal[])val;
                if (props.Length > 0)
                {
                    return(SearchReferenceProperty(type, uri, patterns, kind, timestamp, props[0], true, def, refs));
                }
            }
            return(null);
        }
        /// <summary>Get iterator through object matching specified search parameters</summary>
        /// <param name="type">String representing type of the object (direct or indirect - IsInstanceOf
        /// method will be used to check if object belongs to the specified type). It may be null,
        /// in this case type criteria is skipped.</param>
        /// <param name="uri">Object URI pattern. It may be null, in this case URI is not inspected.</param>
        /// <param name="patterns">array of name:value pairs specifying search condition for object properties</param>
        /// <param name="kind">search kind used to select inspected versions</param>
        /// <param name="timestamp">timestamp used to select versions, if kind is SearchKind.LatestVersion
        /// or SearchKind.AllVersions this parameter is ignored</param>
        /// <returns>Enumerator through object meet search criteria.</returns>
        public IEnumerable Search(string type, string uri, NameVal[] patterns, SearchKind kind, DateTime timestamp)
        {
            VersionHistory typeVh = null;

            root.SharedLock();
            try
            {
                if (type != null)
                {
                    typeVh = GetObject(type);
                    if (typeVh == null)
                    {
                        return(new object[0]); // empty selection
                    }
                }
                if (uri != null)
                {
                    int wc = uri.IndexOf('*');
                    if (wc < 0)
                    {
                        return(new SearchResult(root, typeVh, null, patterns, kind, timestamp, root.prefixUriIndex.GetEnumerator(uri, uri)));
                    }
                    else if (wc > 0)
                    {
                        String prefix = uri.Substring(0, wc);
                        return(new SearchResult(root, typeVh, uri, patterns, kind, timestamp, root.prefixUriIndex.GetEnumerator(prefix)));
                    }
                    else if ((wc = uri.LastIndexOf('*')) < uri.Length - 1)
                    {
                        String suffix = ReverseString(uri.Substring(wc + 1, uri.Length - wc - 1));
                        return(new SearchResult(root, typeVh, uri, patterns, kind, timestamp, root.suffixUriIndex.GetEnumerator(suffix)));
                    }
                }
                if (patterns.Length > 0)
                {
                    NameVal   prop           = patterns[0];
                    object    val            = prop.val;
                    NameVal[] restOfPatterns = SubArray(patterns);

                    switch (prop.name)
                    {
                    case Symbols.Timestamp:

                        if (val is Range)
                        {
                            Range range = (Range)val;
                            if (range.from is DateTime)
                            {
                                Key fromKey = new Key((DateTime)range.from, range.fromInclusive);
                                Key tillKey = new Key((DateTime)range.till, range.tillInclusive);
                                return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp,
                                                        root.timeIndex.GetEnumerator(fromKey, tillKey)));
                            }
                        }
                        else if (val is DateTime)
                        {
                            Key key = new Key((DateTime)val);
                            return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp,
                                                    root.timeIndex.GetEnumerator(key, key)));
                        }
                        return(new object[0]);    // empty selection

                    case Symbols.Rectangle:
                        if (val is NameVal[])
                        {
                            NameVal[] coord = (NameVal[])val;
                            if (coord.Length == 4)
                            {
                                RectangleR2 r = new RectangleR2((double)coord[0].val,
                                                                (double)coord[1].val,
                                                                (double)coord[2].val,
                                                                (double)coord[3].val);
                                return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp,
                                                        root.spatialIndex.Overlaps(r).GetEnumerator()));
                            }
                        }
                        break;

                    case Symbols.Point:
                        if (val is NameVal[])
                        {
                            NameVal[] coord = (NameVal[])val;
                            if (coord.Length == 2)
                            {
                                double      x = (double)coord[0].val;
                                double      y = (double)coord[1].val;
                                RectangleR2 r = new RectangleR2(x, y, x, y);
                                return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp,
                                                        root.spatialIndex.Overlaps(r).GetEnumerator()));
                            }
                        }
                        break;

                    case Symbols.Keyword:
                        if (val is string)
                        {
                            ArrayList keywords = new ArrayList();
                            foreach (string keyword in ((string)val).ToLower().Split(keywordSeparators))
                            {
                                if (keyword.Length > 0 && !keywordStopList.ContainsKey(keyword))
                                {
                                    keywords.Add(keyword);
                                }
                            }
                            IEnumerator[] occurences = new IEnumerator[keywords.Count];
                            for (int i = 0; i < occurences.Length; i++)
                            {
                                Key key = new Key((string)keywords[i]);
                                occurences[i] = root.inverseIndex.GetEnumerator(key, key);
                            }
                            return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp, db.Merge(occurences)));
                        }
                        break;
                    }

                    PropDef def = (PropDef)root.propDefIndex[prop.name];
                    if (def == null)
                    {
                        return(new object[0]); // empty selection
                    }
                    if (val is Range)
                    {
                        Range range = (Range)val;
                        if (range.from is double)
                        {
                            Key fromKey = new Key(new object[] { def, range.from }, range.fromInclusive);
                            Key tillKey = new Key(new object[] { def, range.till }, range.tillInclusive);
                            return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp,
                                                    root.numPropIndex.GetEnumerator(fromKey, tillKey)));
                        }
                        else if (range.from is DateTime)
                        {
                            Key fromKey = new Key(new object[] { def, range.from }, range.fromInclusive);
                            Key tillKey = new Key(new object[] { def, range.till }, range.tillInclusive);
                            return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp,
                                                    root.timePropIndex.GetEnumerator(fromKey, tillKey)));
                        }
                        else
                        {
                            Key fromKey = new Key(new object[] { def, range.from }, range.fromInclusive);
                            Key tillKey = new Key(new object[] { def, range.till }, range.tillInclusive);
                            return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp,
                                                    root.strPropIndex.GetEnumerator(fromKey, tillKey)));
                        }
                    }
                    else if (val is string)
                    {
                        string str = (string)val;
                        int    wc  = str.IndexOf('*');
                        if (wc < 0)
                        {
                            Key key = new Key(new object[] { def, str });
                            return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp,
                                                    root.strPropIndex.GetEnumerator(key, key)));
                        }
                        else if (wc > 0)
                        {
                            string prefix  = str.Substring(0, wc);
                            Key    fromKey = new Key(new object[] { def, prefix });
                            Key    tillKey = new Key(new object[] { def, prefix + Char.MaxValue }, false);
                            return(new SearchResult(root, typeVh, uri, wc == str.Length - 1 ? restOfPatterns : patterns, kind, timestamp,
                                                    root.strPropIndex.GetEnumerator(fromKey, tillKey)));
                        }
                    }
                    else if (val is double)
                    {
                        Key key = new Key(new object[] { def, val });
                        return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp,
                                                root.numPropIndex.GetEnumerator(key, key)));
                    }
                    else if (val is DateTime)
                    {
                        Key key = new Key(new object[] { def, val });
                        return(new SearchResult(root, typeVh, uri, restOfPatterns, kind, timestamp,
                                                root.timePropIndex.GetEnumerator(key, key)));
                    }
                    else if (val is NameVal)
                    {
                        IEnumerable iterator = SearchReferenceProperty(typeVh, uri, patterns, kind, timestamp, (NameVal)val, false, def, new ArrayList());
                        if (iterator != null)
                        {
                            return(iterator);
                        }
                    }
                    else if (val is NameVal[])
                    {
                        NameVal[] props = (NameVal[])val;
                        if (props.Length > 0)
                        {
                            IEnumerable iterator = SearchReferenceProperty(typeVh, uri, patterns, kind, timestamp, props[0], props.Length > 1, def, new ArrayList());
                            if (iterator != null)
                            {
                                return(iterator);
                            }
                        }
                    }
                }
                if (kind == SearchKind.LatestVersion)
                {
                    return(new SearchResult(root, typeVh, uri, patterns, kind, timestamp, root.latest.GetEnumerator()));
                }
                return(new SearchResult(root, typeVh, uri, patterns, kind, timestamp, root.timeIndex.GetEnumerator()));
            }
            finally
            {
                root.Unlock();
            }
        }
        private Thing CreateObject(Thing type, VersionHistory vh, NameVal[] props)
        {
            Thing thing = new Thing();

            thing.vh        = vh;
            thing.type      = type;
            thing.timestamp = DateTime.Now;
            thing.props     = new PropVal[props.Length];
            for (int i = 0; i < props.Length; i++)
            {
                NameVal prop = props[i];
                PropDef def  = (PropDef)root.propDefIndex[prop.name];
                if (def == null)
                {
                    def      = new PropDef();
                    def.name = prop.name;
                    root.propDefIndex.Put(def);
                }
                object  val = prop.val;
                PropVal pv  = new PropVal(def, val);
                Key     key = new Key(new object[] { def, val });
                if (val is string)
                {
                    root.strPropIndex.Put(key, thing);
                    foreach (string keyword in ((string)val).ToLower().Split(keywordSeparators))
                    {
                        if (keyword.Length > 0 && !keywordStopList.ContainsKey(keyword))
                        {
                            root.inverseIndex.Put(keyword, thing);
                        }
                    }
                }
                else if (val is double)
                {
                    root.numPropIndex.Put(key, thing);
                }
                else if (val is DateTime)
                {
                    root.timePropIndex.Put(key, thing);
                }
                else if (val is VersionHistory || val == null)
                {
                    root.refPropIndex.Put(key, thing);
                    if (prop.name == Symbols.Rectangle)
                    {
                        PropVal[]   coord = ((VersionHistory)val).Latest.props;
                        RectangleR2 r     = new RectangleR2((double)coord[0].val,
                                                            (double)coord[1].val,
                                                            (double)coord[2].val,
                                                            (double)coord[3].val);
                        root.spatialIndex.Put(r, thing);
                    }
                    else if (prop.name == Symbols.Point)
                    {
                        PropVal[]   coord = ((VersionHistory)val).Latest.props;
                        double      x     = (double)coord[0].val;
                        double      y     = (double)coord[1].val;
                        RectangleR2 r     = new RectangleR2(x, y, x, y);
                        root.spatialIndex.Put(r, thing);
                    }
                }
                else
                {
                    throw new InvalidOperationException("Invalid propery value type " + prop.val.GetType());
                }
                thing.props[i] = pv;
            }
            thing.Modify();
            vh.versions.Add(thing);
            root.timeIndex.Put(thing);
            root.latest.Add(thing);
            return(thing);
        }