예제 #1
0
 /// <summary>
 /// Removes the quadruples with the given literal as object
 /// </summary>
 public abstract RDFStore RemoveQuadruplesByLiteral(RDFLiteral objectLiteral);
예제 #2
0
        /// <summary>
        /// Deserializes the given N-Quads stream to a memory store.
        /// </summary>
        internal static RDFMemoryStore Deserialize(Stream inputStream)
        {
            long nquadIndex = 0;

            try
            {
                #region deserialize
                using (StreamReader sr = new StreamReader(inputStream, Encoding.ASCII))
                {
                    RDFMemoryStore result = new RDFMemoryStore();
                    string         nquad  = string.Empty;
                    string[]       tokens = new string[4];
                    RDFResource    S      = null;
                    RDFResource    P      = null;
                    RDFResource    O      = null;
                    RDFLiteral     L      = null;
                    RDFContext     C      = new RDFContext();
                    while ((nquad = sr.ReadLine()) != null)
                    {
                        nquadIndex++;

                        #region sanitize  & tokenize
                        //Cleanup previous data
                        S         = null;
                        tokens[0] = string.Empty;
                        P         = null;
                        tokens[1] = string.Empty;
                        O         = null;
                        L         = null;
                        tokens[2] = string.Empty;
                        C         = new RDFContext();
                        tokens[3] = string.Empty;

                        //Preliminary sanitizations: clean trailing space-like chars
                        nquad = nquad.Trim(new char[] { ' ', '\t', '\r', '\n' });

                        //Skip empty or comment lines
                        if (nquad == string.Empty || nquad.StartsWith("#"))
                        {
                            continue;
                        }

                        //Tokenizes the sanitized quad
                        tokens = TokenizeNQuad(nquad);
                        #endregion

                        #region subj
                        string subj = tokens[0].TrimStart(new char[] { '<' })
                                      .TrimEnd(new char[] { '>' })
                                      .Replace("_:", "bnode:");
                        S = new RDFResource(RDFModelUtilities.ASCII_To_Unicode(subj));
                        #endregion

                        #region pred
                        string pred = tokens[1].TrimStart(new char[] { '<' })
                                      .TrimEnd(new char[] { '>' });
                        P = new RDFResource(RDFModelUtilities.ASCII_To_Unicode(pred));
                        #endregion

                        #region object
                        if (tokens[2].StartsWith("<") ||
                            tokens[2].StartsWith("bnode:") ||
                            tokens[2].StartsWith("_:"))
                        {
                            string obj = tokens[2].TrimStart(new char[] { '<' })
                                         .TrimEnd(new char[] { '>' })
                                         .Replace("_:", "bnode:")
                                         .Trim(new char[] { ' ', '\n', '\t', '\r' });
                            O = new RDFResource(RDFModelUtilities.ASCII_To_Unicode(obj));
                        }
                        #endregion

                        #region literal
                        else
                        {
                            #region sanitize
                            tokens[2] = RDFNTriples.regexSqt.Replace(tokens[2], string.Empty);
                            tokens[2] = RDFNTriples.regexEqt.Replace(tokens[2], string.Empty);
                            tokens[2] = tokens[2].Replace("\\\\", "\\")
                                        .Replace("\\\"", "\"")
                                        .Replace("\\n", "\n")
                                        .Replace("\\t", "\t")
                                        .Replace("\\r", "\r");
                            tokens[2] = RDFModelUtilities.ASCII_To_Unicode(tokens[2]);
                            #endregion

                            #region plain literal
                            if (!tokens[2].Contains("^^") ||
                                tokens[2].EndsWith("^^") ||
                                tokens[2].Substring(tokens[2].LastIndexOf("^^", StringComparison.Ordinal) + 2, 1) != "<")
                            {
                                if (RDFNTriples.regexLPL.Match(tokens[2]).Success)
                                {
                                    tokens[2] = tokens[2].Replace("\"@", "@");
                                    string pLitValue = tokens[2].Substring(0, tokens[2].LastIndexOf("@", StringComparison.Ordinal));
                                    string pLitLang  = tokens[2].Substring(tokens[2].LastIndexOf("@", StringComparison.Ordinal) + 1);
                                    L = new RDFPlainLiteral(HttpUtility.HtmlDecode(pLitValue), pLitLang);
                                }
                                else
                                {
                                    L = new RDFPlainLiteral(HttpUtility.HtmlDecode(tokens[2]));
                                }
                            }
                            #endregion

                            #region typed literal
                            else
                            {
                                tokens[2] = tokens[2].Replace("\"^^", "^^");
                                string tLitValue    = tokens[2].Substring(0, tokens[2].LastIndexOf("^^", StringComparison.Ordinal));
                                string tLitDatatype = tokens[2].Substring(tokens[2].LastIndexOf("^^", StringComparison.Ordinal) + 2)
                                                      .TrimStart(new char[] { '<' })
                                                      .TrimEnd(new char[] { '>' });
                                RDFModelEnums.RDFDatatypes dt = RDFModelUtilities.GetDatatypeFromString(tLitDatatype);
                                L = new RDFTypedLiteral(HttpUtility.HtmlDecode(tLitValue), dt);
                            }
                            #endregion
                        }
                        #endregion

                        #region context
                        if (!string.IsNullOrEmpty(tokens[3]))
                        {
                            string ctx = tokens[3].TrimStart(new char[] { '<' })
                                         .TrimEnd(new char[] { '>' });

                            Uri ctxUri = null;
                            if (Uri.TryCreate(ctx, UriKind.Absolute, out ctxUri))
                            {
                                C = new RDFContext(RDFModelUtilities.ASCII_To_Unicode(ctxUri.ToString()));
                            }
                            else
                            {
                                throw new RDFModelException("found context '" + ctx + "' which is not a well-formed absolute Uri");
                            }
                        }
                        #endregion

                        #region addquadruple
                        if (O != null)
                        {
                            result.AddQuadruple(new RDFQuadruple(C, S, P, O));
                        }
                        else
                        {
                            result.AddQuadruple(new RDFQuadruple(C, S, P, L));
                        }
                        #endregion
                    }
                    return(result);
                }
                #endregion
            }
            catch (Exception ex)
            {
                throw new RDFModelException("Cannot deserialize N-Quads (line " + nquadIndex + ") because: " + ex.Message, ex);
            }
        }
예제 #3
0
        /// <summary>
        /// Selects the quadruples corresponding to the given pattern from the given store
        /// </summary>
        internal static List <RDFQuadruple> SelectQuadruples(RDFMemoryStore store,
                                                             RDFContext ctx,
                                                             RDFResource subj,
                                                             RDFResource pred,
                                                             RDFResource obj,
                                                             RDFLiteral lit)
        {
            var matchCtx    = new List <RDFQuadruple>();
            var matchSubj   = new List <RDFQuadruple>();
            var matchPred   = new List <RDFQuadruple>();
            var matchObj    = new List <RDFQuadruple>();
            var matchLit    = new List <RDFQuadruple>();
            var matchResult = new List <RDFQuadruple>();

            if (store != null)
            {
                //Filter by Context
                if (ctx != null)
                {
                    foreach (var q in store.StoreIndex.SelectIndexByContext(ctx))
                    {
                        matchCtx.Add(store.Quadruples[q]);
                    }
                }

                //Filter by Subject
                if (subj != null)
                {
                    foreach (var q in store.StoreIndex.SelectIndexBySubject(subj))
                    {
                        matchSubj.Add(store.Quadruples[q]);
                    }
                }

                //Filter by Predicate
                if (pred != null)
                {
                    foreach (var q in store.StoreIndex.SelectIndexByPredicate(pred))
                    {
                        matchPred.Add(store.Quadruples[q]);
                    }
                }

                //Filter by Object
                if (obj != null)
                {
                    foreach (var q in store.StoreIndex.SelectIndexByObject(obj))
                    {
                        matchObj.Add(store.Quadruples[q]);
                    }
                }

                //Filter by Literal
                if (lit != null)
                {
                    foreach (var q in store.StoreIndex.SelectIndexByLiteral(lit))
                    {
                        matchLit.Add(store.Quadruples[q]);
                    }
                }

                //Intersect the filters
                if (ctx != null)
                {
                    if (subj != null)
                    {
                        if (pred != null)
                        {
                            if (obj != null)
                            {
                                //C->S->P->O
                                matchResult = matchCtx.Intersect(matchSubj)
                                              .Intersect(matchPred)
                                              .Intersect(matchObj)
                                              .ToList();
                            }
                            else
                            {
                                if (lit != null)
                                {
                                    //C->S->P->L
                                    matchResult = matchCtx.Intersect(matchSubj)
                                                  .Intersect(matchPred)
                                                  .Intersect(matchLit)
                                                  .ToList();
                                }
                                else
                                {
                                    //C->S->P->
                                    matchResult = matchCtx.Intersect(matchSubj)
                                                  .Intersect(matchPred)
                                                  .ToList();
                                }
                            }
                        }
                        else
                        {
                            if (obj != null)
                            {
                                //C->S->->O
                                matchResult = matchCtx.Intersect(matchSubj)
                                              .Intersect(matchObj)
                                              .ToList();
                            }
                            else
                            {
                                if (lit != null)
                                {
                                    //C->S->->L
                                    matchResult = matchCtx.Intersect(matchSubj)
                                                  .Intersect(matchLit)
                                                  .ToList();
                                }
                                else
                                {
                                    //C->S->->
                                    matchResult = matchCtx.Intersect(matchSubj)
                                                  .ToList();
                                }
                            }
                        }
                    }
                    else
                    {
                        if (pred != null)
                        {
                            if (obj != null)
                            {
                                //C->->P->O
                                matchResult = matchCtx.Intersect(matchPred)
                                              .Intersect(matchObj)
                                              .ToList();
                            }
                            else
                            {
                                if (lit != null)
                                {
                                    //C->->P->L
                                    matchResult = matchCtx.Intersect(matchPred)
                                                  .Intersect(matchLit)
                                                  .ToList();
                                }
                                else
                                {
                                    //C->->P->
                                    matchResult = matchCtx.Intersect(matchPred)
                                                  .ToList();
                                }
                            }
                        }
                        else
                        {
                            if (obj != null)
                            {
                                //C->->->O
                                matchResult = matchCtx.Intersect(matchObj)
                                              .ToList();
                            }
                            else
                            {
                                if (lit != null)
                                {
                                    //C->->->L
                                    matchResult = matchCtx.Intersect(matchLit)
                                                  .ToList();
                                }
                                else
                                {
                                    //C->->->
                                    matchResult = matchCtx;
                                }
                            }
                        }
                    }
                }
                else
                {
                    if (subj != null)
                    {
                        if (pred != null)
                        {
                            if (obj != null)
                            {
                                //->S->P->O
                                matchResult = matchSubj.Intersect(matchPred)
                                              .Intersect(matchObj)
                                              .ToList();
                            }
                            else
                            {
                                if (lit != null)
                                {
                                    //->S->P->L
                                    matchResult = matchSubj.Intersect(matchPred)
                                                  .Intersect(matchLit)
                                                  .ToList();
                                }
                                else
                                {
                                    //->S->P->
                                    matchResult = matchSubj.Intersect(matchPred)
                                                  .ToList();
                                }
                            }
                        }
                        else
                        {
                            if (obj != null)
                            {
                                //->S->->O
                                matchResult = matchSubj.Intersect(matchObj)
                                              .ToList();
                            }
                            else
                            {
                                if (lit != null)
                                {
                                    //->S->->L
                                    matchResult = matchSubj.Intersect(matchLit)
                                                  .ToList();
                                }
                                else
                                {
                                    //->S->->
                                    matchResult = matchSubj;
                                }
                            }
                        }
                    }
                    else
                    {
                        if (pred != null)
                        {
                            if (obj != null)
                            {
                                //->->P->O
                                matchResult = matchPred.Intersect(matchObj)
                                              .ToList();
                            }
                            else
                            {
                                if (lit != null)
                                {
                                    //->->P->L
                                    matchResult = matchPred.Intersect(matchLit)
                                                  .ToList();
                                }
                                else
                                {
                                    //->->P->
                                    matchResult = matchPred;
                                }
                            }
                        }
                        else
                        {
                            if (obj != null)
                            {
                                //->->->O
                                matchResult = matchObj;
                            }
                            else
                            {
                                if (lit != null)
                                {
                                    //->->->L
                                    matchResult = matchLit;
                                }
                                else
                                {
                                    //->->->
                                    matchResult = store.Quadruples.Values.ToList();
                                }
                            }
                        }
                    }
                }
            }
            return(matchResult);
        }
예제 #4
0
 /// <summary>
 /// Gets a memory store containing quadruples with the specified literal
 /// </summary>
 public RDFMemoryStore SelectQuadruplesByLiteral(RDFLiteral objectLiteral)
 {
     return(this.SelectQuadruples(null, null, null, null, objectLiteral));
 }
예제 #5
0
 /// <summary>
 /// Gets a store containing quadruples satisfying the given pattern
 /// </summary>
 internal abstract RDFMemoryStore SelectQuadruples(RDFContext contextResource,
                                                   RDFResource subjectResource,
                                                   RDFResource predicateResource,
                                                   RDFResource objectResource,
                                                   RDFLiteral objectLiteral);
예제 #6
0
 /// <summary>
 /// Removes the quadruples with the given predicate and literal
 /// </summary>
 public abstract RDFStore RemoveQuadruplesByPredicateLiteral(RDFResource predicateResource, RDFLiteral objectLiteral);
예제 #7
0
        // Открывающая скобка транзакции ввода. Выдает DbCommand c транзакцией DbTransaction, если она поддерживается данной СУБД
        protected override void InitAdapterBuffers()
        {
            //if (append)
            if (ENT_INDEX < 0 || LIT_INDEX < 0)
            { // можно еще проверять по ENT_INDEX < 0 || LIT_INDEX < 0
                //using (connection)
                {
                    try {
                    } catch (Exception) {}
                }
                DbCommand sqlcommand = connection.CreateCommand();
                sqlcommand.CommandText = "SELECT MAX(entityid) FROM rdf_entities;";
                connection.Open();
                var oind = sqlcommand.ExecuteScalar();
                if (oind != null) ENT_INDEX = (int)oind + 1;
                else { throw new Exception("Не найден максимальный индекс сущностей"); }
                sqlcommand.CommandText = "SELECT MAX(literalid) FROM rdf_literals;";
                var oind2 = sqlcommand.ExecuteScalar();
                connection.Close();
                if (oind2 != null) LIT_INDEX = (int)oind2 + 1;
                else { throw new Exception("Не найден максимальный индекс литералов"); }
            }
            // размер буферов
            int bufferportion = 60000;
            // размер порции для внедрения данных
            int portion = 20;
            b_entities = new sema2012m.BufferredProcessing<RDFEntity>(bufferportion, flow =>
            {
                var query = flow.Select((ent, i) => new { e = ent, i = i }).GroupBy(ei => ei.i / portion, ei => ei);
                DbCommand runcommand = RunStart();
                try
                {
                    foreach (var q in query)
                    {
                        var qq = q.Select(ei => "(" + ei.e.entityid + ",'" + ei.e.entityvalue + "')")
                            .Aggregate((sum, s) => sum + "," + s);
                        runcommand.CommandText = "INSERT INTO rdf_entities VALUES " + qq + ";";
                        runcommand.ExecuteNonQuery();
                    }
                    RunStop(runcommand);
                }
                catch (Exception)
                {
                    RunCancel(runcommand);
                    throw new Exception("Error 2938");
                }
            });
            b_literals = new sema2012m.BufferredProcessing<RDFLiteral>(bufferportion, flow =>
            {
                var query = flow.Select((ent, i) => new { e = ent, i = i }).GroupBy(ei => ei.i / portion, ei => ei);
                DbCommand runcommand = RunStart();
                try
                {
                    foreach (var q in query)
                    {
                        var qq = q.Select(ei => "(" + ei.e.literalid + ",'" + ei.e.literalvalue.Replace('\'', '"') + "'," + (ei.e.literallang == null ? "NULL" : "'" + ei.e.literallang + "'") + ")")
                            .Aggregate((sum, s) => sum + "," + s);
                        runcommand.CommandText = "INSERT INTO rdf_literals VALUES " + qq + ";";
                        runcommand.ExecuteNonQuery();
                    }

                    RunStop(runcommand);
                }
                catch (Exception)
                {
                    RunCancel(runcommand);
                    throw new Exception("Error 2939");
                }
            });
            b_dstatements = new sema2012m.BufferredProcessing<RDFDStatement>(bufferportion, flow =>
            {
                var query = flow.Select((ent, i) => new { e = ent, i = i }).GroupBy(ei => ei.i / portion, ei => ei);
                DbCommand runcommand = RunStart();
                foreach (var q in query)
                {
                    var qq = q.Select(ei => "(" + ei.e.dsubject + "," + ei.e.dpredicate + "," + ei.e.data + ")")
                        .Aggregate((sum, s) => sum + "," + s);
                    runcommand.CommandText = "INSERT INTO rdf_dstatements VALUES " + qq + ";";
                    runcommand.ExecuteNonQuery();
                }
                RunStop(runcommand);
            });
            b_ostatements = new sema2012m.BufferredProcessing<RDFOStatement>(bufferportion, flow =>
            {
                var query = flow.Select((ent, i) => new { e = ent, i = i }).GroupBy(ei => ei.i / portion, ei => ei);
                DbCommand runcommand = RunStart();
                foreach (var q in query)
                {
                    var qq = q.Select(ei => "(" + ei.e.osubject + "," + ei.e.opredicate + "," + ei.e.oobj + ")")
                        .Aggregate((sum, s) => sum + "," + s);
                    runcommand.CommandText = "INSERT INTO rdf_ostatements VALUES " + qq + ";";
                    runcommand.ExecuteNonQuery();
                }
                RunStop(runcommand);
            });

            if (use_entity_dic) b_triplets = new BufferredProcessing<Triplet>(10, flow =>
            {
                foreach (var tri in flow)
                {
                    int i_s = GetSetEntityIndex(tri.s);
                    int i_p = GetSetEntityIndex(tri.p);
                    if (tri is OProp)
                    {
                        int i_o = GetSetEntityIndex(((OProp)tri).o);
                        b_ostatements.Add(new RDFOStatement() { osubject = i_s, opredicate = i_p, oobj = i_o });
                    }
                    else
                    {
                        int i_lit = LIT_INDEX;
                        LIT_INDEX++;
                        DProp dp = (DProp)tri;
                        var rdflit = new RDFLiteral() { literalid = i_lit, literalvalue = dp.d };
                        if (dp.lang != null) rdflit.literallang = dp.lang;
                        b_literals.Add(rdflit);
                        b_dstatements.Add(new RDFDStatement() { dsubject = i_s, dpredicate = i_p, data = i_lit });
                    }
                }
            });

            int tbuffervolume = 20000;
            int tbufferportion = 200;
            if (!use_entity_dic) b_triplets = new BufferredProcessing<Triplet>(tbuffervolume, flow =>
            {
                var query = flow.Select((ent, i) => new { e = ent, i = i }).GroupBy(ei => ei.i / tbufferportion, ei => ei);
                // отложенная буферизация
                List<RDFLiteral> l_list = new List<RDFLiteral>();
                List<RDFOStatement> o_list = new List<RDFOStatement>();
                List<RDFDStatement> d_list = new List<RDFDStatement>();

                DbCommand runcommand = RunStart();
                foreach (var q in query)
                {
                    var triplets = q.Select(ei => ei.e).ToArray();
                    var entities = triplets.Select(tr => tr.s)
                        .Concat(triplets.Select(tr => tr.p))
                        .Concat(triplets.Where(tr => tr is OProp).Cast<OProp>().Select(op => op.o))
                        .Distinct().OrderBy(x => x).ToArray();
                    List<KeyValuePair<string, int>> entityindexes;

                    entityindexes = EntityIndexes(entities, runcommand).OrderBy(x => x.Key).ToList();

                    Dictionary<string, int> dic = entityindexes.ToDictionary(pair => pair.Key, pair => pair.Value);
                    var unindexedentities = entities.Where(en => entityindexes.All(ei => ei.Key != en));

                    StringBuilder sb = new StringBuilder();
                    bool notfirst = false;
                    foreach (var uie in unindexedentities)
                    {
                        if (notfirst) sb.Append(',');
                        notfirst = true;
                        sb.Append("(" + ENT_INDEX + ",'" + uie + "')");
                        //entityindexes.Add(new KeyValuePair<string, int>(uie, ENT_INDEX));
                        dic.Add(uie, ENT_INDEX);
                        ENT_INDEX++;
                    }
                    if (sb.Length > 0)
                    {
                        runcommand.CommandText = "INSERT INTO rdf_entities VALUES " + sb.ToString() + ";";
                        runcommand.ExecuteNonQuery();
                    }

                    foreach (var tr in triplets)
                    {
                        if (tr is OProp)
                        {
                            o_list.Add(new RDFOStatement() { osubject = dic[tr.s], opredicate = dic[tr.p], oobj = dic[((OProp)tr).o] });
                        }
                        else
                        {
                            DProp dp = (DProp)tr;
                            l_list.Add(new RDFLiteral() { literalid = LIT_INDEX, literalvalue = dp.d, literallang = dp.lang });
                            d_list.Add(new RDFDStatement() { dsubject = dic[tr.s], dpredicate = dic[tr.p], data = LIT_INDEX });
                            LIT_INDEX++;
                        }
                    }
                }
                RunStop(runcommand);
                foreach(var v in l_list) b_literals.Add(v);
                foreach (var v in o_list) b_ostatements.Add(v);
                foreach (var v in d_list) b_dstatements.Add(v);
            });
        }
예제 #8
0
 /// <summary>
 /// Removes the quadruples with the given context and literal
 /// </summary>
 public abstract RDFStore RemoveQuadruplesByContextLiteral(RDFContext contextResource, RDFLiteral objectLiteral);
예제 #9
0
 /// <summary>
 /// Removes the quadruples with the given subject and literal
 /// </summary>
 public abstract RDFStore RemoveQuadruplesBySubjectLiteral(RDFResource subjectResource, RDFLiteral objectLiteral);
 /// <summary>
 /// Selects the quadruples indexed by the given literal
 /// </summary>
 internal HashSet <long> SelectIndexByLiteral(RDFLiteral objectLiteral)
 => objectLiteral != null && this.Literals.ContainsKey(objectLiteral.PatternMemberID)
         ? this.Literals[objectLiteral.PatternMemberID] : EmptyHashSet;
예제 #11
0
 /// <summary>
 /// Gets an ontology literal from the given RDF literal
 /// </summary>
 public static RDFOntologyLiteral ToRDFOntologyLiteral(this RDFLiteral ontLiteral)
 {
     return(new RDFOntologyLiteral(ontLiteral));
 }
예제 #12
0
 /// <summary>
 /// Removes the quadruples with the given predicate and literal
 /// </summary>
 public override RDFStore RemoveQuadruplesByPredicateLiteral(RDFResource predicateResource, RDFLiteral objectLiteral)
 {
     if (predicateResource != null && objectLiteral != null)
     {
         foreach (var quad in this.SelectQuadruplesByPredicate(predicateResource)
                  .SelectQuadruplesByLiteral(objectLiteral))
         {
             //Remove quadruple
             this.Quadruples.Remove(quad.QuadrupleID);
             //Remove index
             this.StoreIndex.RemoveIndex(quad);
             //Raise event
             RDFStoreEvents.RaiseOnQuadrupleRemoved(String.Format("Quadruple '{0}' has been removed from the Store '{1}'.", quad, this));
         }
     }
     return(this);
 }
예제 #13
0
 /// <summary>
 /// Removes the quadruples with the given predicate and literal
 /// </summary>
 public override RDFStore RemoveQuadruplesByPredicateLiteral(RDFResource predicateResource, RDFLiteral objectLiteral)
 {
     if (predicateResource != null && objectLiteral != null)
     {
         foreach (var quad in this.SelectQuadruplesByPredicate(predicateResource)
                  .SelectQuadruplesByLiteral(objectLiteral))
         {
             //Remove quadruple
             this.Quadruples.Remove(quad.QuadrupleID);
             //Remove index
             this.StoreIndex.RemoveIndex(quad);
         }
     }
     return(this);
 }
예제 #14
0
 /// <summary>
 /// Removes the quadruples with the given context, subject and literal
 /// </summary>
 public override RDFStore RemoveQuadruplesByContextSubjectLiteral(RDFContext contextResource, RDFResource subjectResource, RDFLiteral objectLiteral)
 {
     if (contextResource != null && subjectResource != null && objectLiteral != null)
     {
         foreach (var quad in this.SelectQuadruplesByContext(contextResource)
                  .SelectQuadruplesBySubject(subjectResource)
                  .SelectQuadruplesByLiteral(objectLiteral))
         {
             //Remove quadruple
             this.Quadruples.Remove(quad.QuadrupleID);
             //Remove index
             this.StoreIndex.RemoveIndex(quad);
         }
     }
     return(this);
 }