public void Dispose() { _revWalk.Dispose(); COMPLETE.Dispose(); IN_WORK_QUEUE.Dispose(); LOCALLY_SEEN.Dispose(); _objectDigest.Dispose(); }
/// <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 }