|
21 | 21 | */ |
22 | 22 | package org.exist.xmldb; |
23 | 23 |
|
24 | | -import java.util.HashMap; |
| 24 | +import java.util.LinkedHashMap; |
25 | 25 | import java.util.Map; |
26 | | -import java.util.Set; |
27 | 26 |
|
| 27 | +import org.exist.dom.persistent.NodeProxy; |
28 | 28 | import org.xmldb.api.base.Resource; |
29 | 29 | import org.xmldb.api.base.ResourceSet; |
30 | 30 | import org.xmldb.api.base.XMLDBException; |
31 | 31 |
|
32 | 32 | /** |
33 | 33 | * @author jmv |
34 | | - * |
35 | 34 | */ |
36 | 35 | public class ResourceSetHelper { |
37 | 36 |
|
| 37 | + /** |
| 38 | + * Computes the intersection of two resource sets. |
| 39 | + * |
| 40 | + * <p>For {@link LocalXMLResource} instances backed by persistent nodes, |
| 41 | + * intersection is determined by node identity (document ID + node ID). |
| 42 | + * For other resources, the resource ID ({@link Resource#getId()}) is used |
| 43 | + * as the identity key.</p> |
| 44 | + * |
| 45 | + * @param s1 the first resource set |
| 46 | + * @param s2 the second resource set |
| 47 | + * @return a new resource set containing only resources present in both sets |
| 48 | + * @throws XMLDBException if an error occurs accessing the resources |
| 49 | + */ |
38 | 50 | public static ResourceSet intersection(final ResourceSet s1, final ResourceSet s2) throws XMLDBException { |
39 | | - final Map<String, Resource> m1 = new MapResourceSet(s1).getResourcesMap(); |
40 | | - final Map<String, Resource> m2 = new MapResourceSet(s2).getResourcesMap(); |
41 | | - final Set<String> set1 = m1.keySet(); |
42 | | - final Set<String> set2 = m2.keySet(); |
43 | | - set1.retainAll(set2); |
44 | | - final Map<String, Resource> m = new HashMap<>(); |
45 | | - for (String key : set1) { |
46 | | - final Resource resource = m1.get(key); |
47 | | - m.put(key, resource); |
| 51 | + // Build a map from identity key to resource for the first set |
| 52 | + final Map<String, Resource> m1 = buildIdentityMap(s1); |
| 53 | + |
| 54 | + // Build the intersection: for each resource in s2, check if it's also in s1. |
| 55 | + // Use the node identity key (not res.getId()) as the MapResourceSet key |
| 56 | + // to avoid collapsing multiple nodes from the same document into one entry. |
| 57 | + final Map<String, Resource> result = new LinkedHashMap<>(); |
| 58 | + for (long i = 0; i < s2.getSize(); i++) { |
| 59 | + final Resource res = s2.getResource(i); |
| 60 | + final String key = getIdentityKey(res); |
| 61 | + if (m1.containsKey(key) && !result.containsKey(key)) { |
| 62 | + result.put(key, m1.get(key)); |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + return new MapResourceSet(result); |
| 67 | + } |
| 68 | + |
| 69 | + /** |
| 70 | + * Builds a map from identity key to resource. |
| 71 | + */ |
| 72 | + private static Map<String, Resource> buildIdentityMap(final ResourceSet rs) throws XMLDBException { |
| 73 | + final Map<String, Resource> map = new LinkedHashMap<>(); |
| 74 | + for (long i = 0; i < rs.getSize(); i++) { |
| 75 | + final Resource res = rs.getResource(i); |
| 76 | + final String key = getIdentityKey(res); |
| 77 | + map.putIfAbsent(key, res); |
| 78 | + } |
| 79 | + return map; |
| 80 | + } |
| 81 | + |
| 82 | + /** |
| 83 | + * Returns a key that uniquely identifies a resource by node identity. |
| 84 | + * |
| 85 | + * <p>For {@link LocalXMLResource} instances backed by a {@link NodeProxy}, |
| 86 | + * the key combines the document ID and the node ID to uniquely identify |
| 87 | + * the node within the database. For other resources, the resource ID string |
| 88 | + * is used as a fallback.</p> |
| 89 | + */ |
| 90 | + private static String getIdentityKey(final Resource res) throws XMLDBException { |
| 91 | + if (res instanceof LocalXMLResource localRes) { |
| 92 | + final NodeProxy proxy = localRes.getNode(); |
| 93 | + if (proxy != null) { |
| 94 | + return proxy.getOwnerDocument().getDocId() + "#" + proxy.getNodeId(); |
| 95 | + } |
48 | 96 | } |
49 | | - final MapResourceSet res = new MapResourceSet(m); |
50 | | - /* |
51 | | - VectorResourceSet res = new VectorResourceSet(); |
52 | | - Collection c1 = new VectorResourceSet(s1).getResources(); |
53 | | - res.getResources().addAll( c1 ); |
54 | | - Collection c2 = new VectorResourceSet(s2).getResources(); |
55 | | - res.getResources().retainAll(c2); |
56 | | - return res; |
57 | | - */ |
58 | | - return res; |
| 97 | + return res.getId(); |
59 | 98 | } |
60 | 99 | } |
0 commit comments