Ejemplo n.º 1
0
        /// <summary>
        /// Produces a hash for the paths of adjacent bnodes for a bnode,
        /// incorporating all information about its subgraph of bnodes.
        /// </summary>
        /// <remarks>
        /// Produces a hash for the paths of adjacent bnodes for a bnode,
        /// incorporating all information about its subgraph of bnodes. This method
        /// will recursively pick adjacent bnode permutations that produce the
        /// lexicographically-least 'path' serializations.
        /// </remarks>
        /// <param name="id">the ID of the bnode to hash paths for.</param>
        /// <param name="bnodes">the map of bnode quads.</param>
        /// <param name="namer">the canonical bnode namer.</param>
        /// <param name="pathNamer">the namer used to assign names to adjacent bnodes.</param>
        /// <param name="callback">(err, result) called once the operation completes.</param>
        private static NormalizeUtils.HashResult HashPaths(string id, IDictionary <string, IDictionary <string, object> > bnodes, UniqueNamer namer, UniqueNamer pathNamer)
        {
#if !PORTABLE
            MessageDigest md = null;

            try
            {
                // create SHA-1 digest
                md = MessageDigest.GetInstance("SHA-1");
                JObject                 groups = new JObject();
                IList <string>          groupHashes;
                IList <RDFDataset.Quad> quads = (IList <RDFDataset.Quad>)bnodes[id]["quads"];
                for (int hpi = 0; ; hpi++)
                {
                    if (hpi == quads.Count)
                    {
                        // done , hash groups
                        groupHashes = new List <string>(groups.GetKeys());
                        ((List <string>)groupHashes).Sort(StringComparer.CurrentCultureIgnoreCase);
                        for (int hgi = 0; ; hgi++)
                        {
                            if (hgi == groupHashes.Count)
                            {
                                NormalizeUtils.HashResult res = new NormalizeUtils.HashResult();
                                res.hash      = EncodeHex(md.Digest());
                                res.pathNamer = pathNamer;
                                return(res);
                            }
                            // digest group hash
                            string groupHash = groupHashes[hgi];
                            md.Update(JsonLD.JavaCompat.GetBytesForString(groupHash, "UTF-8"));
                            // choose a path and namer from the permutations
                            string      chosenPath  = null;
                            UniqueNamer chosenNamer = null;
                            NormalizeUtils.Permutator permutator = new NormalizeUtils.Permutator((JArray)groups[groupHash]);
                            while (true)
                            {
                                bool        contPermutation = false;
                                bool        breakOut        = false;
                                JArray      permutation     = permutator.Next();
                                UniqueNamer pathNamerCopy   = pathNamer.Clone();
                                // build adjacent path
                                string path    = string.Empty;
                                JArray recurse = new JArray();
                                foreach (string bnode in permutation)
                                {
                                    // use canonical name if available
                                    if (namer.IsNamed(bnode))
                                    {
                                        path += namer.GetName(bnode);
                                    }
                                    else
                                    {
                                        // recurse if bnode isn't named in the path
                                        // yet
                                        if (!pathNamerCopy.IsNamed(bnode))
                                        {
                                            recurse.Add(bnode);
                                        }
                                        path += pathNamerCopy.GetName(bnode);
                                    }
                                    // skip permutation if path is already >= chosen
                                    // path
                                    if (chosenPath != null && path.Length >= chosenPath.Length && string.CompareOrdinal
                                            (path, chosenPath) > 0)
                                    {
                                        // return nextPermutation(true);
                                        if (permutator.HasNext())
                                        {
                                            contPermutation = true;
                                        }
                                        else
                                        {
                                            // digest chosen path and update namer
                                            md.Update(JsonLD.JavaCompat.GetBytesForString(chosenPath, "UTF-8"));
                                            pathNamer = chosenNamer;
                                            // hash the nextGroup
                                            breakOut = true;
                                        }
                                        break;
                                    }
                                }
                                // if we should do the next permutation
                                if (contPermutation)
                                {
                                    continue;
                                }
                                // if we should stop processing this group
                                if (breakOut)
                                {
                                    break;
                                }
                                // does the next recursion
                                for (int nrn = 0; ; nrn++)
                                {
                                    if (nrn == recurse.Count)
                                    {
                                        // return nextPermutation(false);
                                        if (chosenPath == null || string.CompareOrdinal(path, chosenPath) < 0)
                                        {
                                            chosenPath  = path;
                                            chosenNamer = pathNamerCopy;
                                        }
                                        if (!permutator.HasNext())
                                        {
                                            // digest chosen path and update namer
                                            md.Update(JsonLD.JavaCompat.GetBytesForString(chosenPath, "UTF-8"));
                                            pathNamer = chosenNamer;
                                            // hash the nextGroup
                                            breakOut = true;
                                        }
                                        break;
                                    }
                                    // do recursion
                                    string bnode_1 = (string)recurse[nrn];
                                    NormalizeUtils.HashResult result = HashPaths(bnode_1, bnodes, namer, pathNamerCopy);
                                    path         += pathNamerCopy.GetName(bnode_1) + "<" + result.hash + ">";
                                    pathNamerCopy = result.pathNamer;
                                    // skip permutation if path is already >= chosen
                                    // path
                                    if (chosenPath != null && path.Length >= chosenPath.Length && string.CompareOrdinal
                                            (path, chosenPath) > 0)
                                    {
                                        // return nextPermutation(true);
                                        if (!permutator.HasNext())
                                        {
                                            // digest chosen path and update namer
                                            md.Update(JsonLD.JavaCompat.GetBytesForString(chosenPath, "UTF-8"));
                                            pathNamer = chosenNamer;
                                            // hash the nextGroup
                                            breakOut = true;
                                        }
                                        break;
                                    }
                                }
                                // do next recursion
                                // if we should stop processing this group
                                if (breakOut)
                                {
                                    break;
                                }
                            }
                        }
                    }
                    // get adjacent bnode
                    IDictionary <string, object> quad = (IDictionary <string, object>)quads[hpi];
                    string bnode_2 = GetAdjacentBlankNodeName((IDictionary <string, object>)quad["subject"
                                                              ], id);
                    string direction = null;
                    if (bnode_2 != null)
                    {
                        // normal property
                        direction = "p";
                    }
                    else
                    {
                        bnode_2 = GetAdjacentBlankNodeName((IDictionary <string, object>)quad["object"], id
                                                           );
                        if (bnode_2 != null)
                        {
                            // reverse property
                            direction = "r";
                        }
                    }
                    if (bnode_2 != null)
                    {
                        // get bnode name (try canonical, path, then hash)
                        string name;
                        if (namer.IsNamed(bnode_2))
                        {
                            name = namer.GetName(bnode_2);
                        }
                        else
                        {
                            if (pathNamer.IsNamed(bnode_2))
                            {
                                name = pathNamer.GetName(bnode_2);
                            }
                            else
                            {
                                name = HashQuads(bnode_2, bnodes, namer);
                            }
                        }
                        // hash direction, property, end bnode name/hash
                        using (MessageDigest md1 = MessageDigest.GetInstance("SHA-1"))
                        {
                            // String toHash = direction + (String) ((Map<String,
                            // Object>) quad.get("predicate")).get("value") + name;
                            md1.Update(JsonLD.JavaCompat.GetBytesForString(direction, "UTF-8"));
                            md1.Update(JsonLD.JavaCompat.GetBytesForString(((string)((IDictionary <string, object>)quad["predicate"])["value"]), "UTF-8"));
                            md1.Update(JsonLD.JavaCompat.GetBytesForString(name, "UTF-8"));
                            string groupHash = EncodeHex(md1.Digest());
                            if (groups.ContainsKey(groupHash))
                            {
                                ((JArray)groups[groupHash]).Add(bnode_2);
                            }
                            else
                            {
                                JArray tmp = new JArray();
                                tmp.Add(bnode_2);
                                groups[groupHash] = tmp;
                            }
                        }
                    }
                }
            }
            catch
            {
                // TODO: i don't expect that SHA-1 is even NOT going to be
                // available?
                // look into this further
                throw;
            }
            finally
            {
                md?.Dispose();
            }
#else
            throw new PlatformNotSupportedException();
#endif
        }
Ejemplo n.º 2
0
        private IDictionary <string, Object> hashNDegreeQuads(IdentifierIssuer issuer, string id)
        {
            /*
             * 1) Create a hash to related blank nodes map for storing hashes that
             * identify related blank nodes.
             * Note: 2) and 3) handled within `createHashToRelated`
             */

            IDictionary <string, IList <string> > hashToRelated = this.createHashToRelated(issuer, id);

            /*
             * 4) Create an empty string, data to hash.
             * Note: We create a hash object instead.
             */

            string mdString = "";

            /*
             * 5) For each related hash to blank node list mapping in hash to
             * related blank nodes map, sorted lexicographically by related hash:
             */
            sortMapKeys(hashToRelated);
            foreach (var hash in hashToRelated.Keys)
            {
                var blankNodes = hashToRelated[hash];
                // 5.1) Append the related hash to the data to hash.
                mdString += hash;

                // 5.2) Create a string chosen path.

                string chosenPath = " ";

                // 5.3) Create an unset chosen issuer variable.

                IdentifierIssuer chosenIssuer = null;

                // 5.4) For each permutation of blank node list:

                string                    path           = "";
                List <string>             recursionList  = null;
                IdentifierIssuer          issuerCopy     = null;
                bool                      skipToNextPerm = false;
                NormalizeUtils.Permutator permutator     = new NormalizeUtils.Permutator(JArray.FromObject(blankNodes));

                while (permutator.HasNext())
                {
                    var permutation = permutator.Next();
                    // 5.4.1) Create a copy of issuer, issuer copy.

                    issuerCopy = (IdentifierIssuer)issuer.Clone();

                    // 5.4.2) Create a string path.

                    path = "";

                    /*
                     * 5.4.3) Create a recursion list, to store blank node
                     * identifiers that must be recursively processed by this
                     * algorithm.
                     */

                    recursionList = new List <string>();

                    // 5.4.4) For each related in permutation:
                    foreach (var related in permutation)
                    {
                        /*
                         * 5.4.4.1) If a canonical identifier has been issued for
                         * related, append it to path.
                         */

                        if (this.canonicalIssuer.hasID(related.ToString()))
                        {
                            path += this.canonicalIssuer.getId(related.ToString());
                        }
                        // 5.4.4.2) Otherwise:
                        else
                        {
                            /*
                             * 5.4.4.2.1) If issuer copy has not issued an
                             * identifier for related, append related to recursion
                             * list.
                             */

                            if (!issuerCopy.hasID(related.ToString()))
                            {
                                recursionList.Add(related.ToString());
                            }

                            /*
                             * 5.4.4.2.2) Use the Issue Identifier algorithm,
                             * passing issuer copy and related and append the result
                             * to path.
                             */

                            path += issuerCopy.getId(related.ToString());
                        }

                        /*
                         * 5.4.4.3) If chosen path is not empty and the length of
                         * path is greater than or equal to the length of chosen
                         * path and path is lexicographically greater than chosen
                         * path, then skip to the next permutation.
                         */

                        if (chosenPath.Length != 0 && path.Length >= chosenPath.Length && path.CompareTo(chosenPath) == 1)
                        {
                            skipToNextPerm = true;
                            break;
                        }
                    }
                }

                if (skipToNextPerm)
                {
                    continue;
                }

                // 5.4.5) For each related in recursion list:

                foreach (var related in recursionList)
                {
                    /*
                     * 5.4.5.1) Set result to the result of recursively
                     * executing the Hash N-Degree Quads algorithm, passing
                     * related for identifier and issuer copy for path
                     * identifier issuer.
                     */
                    IDictionary <string, object> result = hashNDegreeQuads(issuerCopy, related);

                    /*
                     * 5.4.5.2) Use the Issue Identifier algorithm, passing
                     * issuer copy and related and append the result to path.
                     */

                    path += "<" + (string)result["hash"] + ">";

                    /*
                     * 5.4.5.4) Set issuer copy to the identifier issuer in
                     * result.
                     */

                    issuerCopy = (IdentifierIssuer)result["issuer"];

                    /*
                     * 5.4.5.5) If chosen path is not empty and the length of
                     * path is greater than or equal to the length of chosen
                     * path and path is lexicographically greater than chosen
                     * path, then skip to the next permutation.
                     */

                    if (chosenPath.Length != 0 && path.Length >= chosenPath.Length && path.CompareTo(chosenPath) == 1)
                    {
                        skipToNextPerm = true;
                        break;
                    }
                }

                if (skipToNextPerm)
                {
                    continue;
                }

                /*
                 * 5.4.6) If chosen path is empty or path is lexicographically
                 * less than chosen path, set chosen path to path and chosen
                 * issuer to issuer copy.
                 */

                if (chosenPath.Length == 0 || path.CompareTo(chosenPath) == -1)
                {
                    chosenPath   = path;
                    chosenIssuer = issuerCopy;
                }

                // 5.5) Append chosen path to data to hash.
                mdString += chosenPath;

                // 5.6) Replace issuer, by reference, with chosen issuer.
                issuer = chosenIssuer;

                /*
                 * 6) Return issuer and the hash that results from passing data to hash
                 * through the hash algorithm.
                 */
            }

            /*
             * 6) Return issuer and the hash that results from passing data to hash
             * through the hash algorithm.
             */

            IDictionary <string, object> hashQuad = new Dictionary <string, object>();

            hashQuad.Add("hash", NormalizeUtils.sha256Hash(Encoding.ASCII.GetBytes(mdString)));
            hashQuad.Add("issuer", issuer);

            return(hashQuad);
        }