This is an automated email from the ASF dual-hosted git repository. reschke pushed a commit to branch OAK-11397 in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
commit 018d480f26b7cc87f2b52796aadd99e01c5ba31d Author: Konrad Windszus <[email protected]> AuthorDate: Thu Jan 16 16:14:21 2025 +0100 OAK-11397 Map Oak to expanded JCR name in NameMapper --- .../apache/jackrabbit/oak/namepath/NameMapper.java | 23 ++++++++++++++-- .../jackrabbit/oak/namepath/NamePathMapper.java | 12 +++++++++ .../apache/jackrabbit/oak/namepath/PathMapper.java | 16 +++++++++++ .../jackrabbit/oak/namepath/package-info.java | 2 +- .../oak/namepath/impl/GlobalNameMapper.java | 27 +++++++++++++++++++ .../oak/namepath/impl/NamePathMapperImpl.java | 23 +++++++++++++--- .../oak/namepath/impl/GlobalNameMapperTest.java | 31 +++++++++++++++++----- .../oak/namepath/impl/NamePathMapperImplTest.java | 20 ++++++++++++++ .../jackrabbit/oak/jcr/session/SessionContext.java | 6 +++++ 9 files changed, 147 insertions(+), 13 deletions(-) diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java index ec62432521..c2d796c1f4 100644 --- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java @@ -63,16 +63,35 @@ public interface NameMapper { Map<String, String> getSessionLocalMappings(); /** - * Returns the JCR name for the given Oak name. The given name is + * Returns the JCR name in qualified form for the given Oak name. The given name is * expected to have come from a valid Oak repository that contains * only valid names with proper namespace mappings. If that's not * the case, either a programming error or a repository corruption * has occurred and an appropriate unchecked exception gets thrown. * * @param oakName Oak name - * @return JCR name + * @return JCR name in qualified form + * + * @see <a href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.5.2%20Qualified%20Form">JCR 2.0, 3.2.5.2 Qualifed Form</a> */ @NotNull String getJcrName(@NotNull String oakName); + /** + * Returns the JCR name in expanded form for the given Oak name. The given name is + * expected to have come from a valid Oak repository that contains + * only valid names with proper namespace mappings. If that's not + * the case, either a programming error or a repository corruption + * has occurred and an appropriate unchecked exception gets thrown. + * + * @param oakName Oak name + * @return JCR name in expanded form + * @since Oak 1.76.0 + * @throws IllegalStateException in case the namespace URI for the given Oak name cannot be resolved + * + * @see <a href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.5.1%20Expanded%20Form">JCR 2.0, 3.2.5.1 Expanded Form</a> + */ + @NotNull + String getExpandedJcrName(@NotNull String oakName); + } diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NamePathMapper.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NamePathMapper.java index 5ab341a0c2..5e756e0dbe 100644 --- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NamePathMapper.java +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NamePathMapper.java @@ -62,6 +62,12 @@ public interface NamePathMapper extends NameMapper, PathMapper { return oakName; } + @NotNull + @Override + public String getExpandedJcrName(@NotNull String oakName) { + throw new UnsupportedOperationException("Cannot create expanded JCR name as no namespace mappings are available"); + } + @Override public String getOakPath(String jcrPath) { return jcrPath; @@ -72,5 +78,11 @@ public interface NamePathMapper extends NameMapper, PathMapper { public String getJcrPath(String oakPath) { return oakPath; } + + @NotNull + @Override + public String getExpandedJcrPath(@NotNull String oakPath) { + throw new UnsupportedOperationException("Cannot create expanded JCR path as no namespace mappings are available"); + } } } diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/PathMapper.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/PathMapper.java index d99cdb41b2..5e451f3059 100644 --- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/PathMapper.java +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/PathMapper.java @@ -56,4 +56,20 @@ public interface PathMapper { @NotNull String getJcrPath(String oakPath); + /** + * Returns the JCR path in expanded form for the given Oak path. The given path is + * expected to have come from a valid Oak repository that contains + * only valid names with proper namespace mappings. If that's not + * the case, either a programming error or a repository corruption + * has occurred and an appropriate unchecked exception gets thrown. + * + * @param oakPath Oak path + * @return JCR path in expanded form + * @since Oak 1.76.0 + * @throws IllegalStateException in case the namespace URI for the given Oak name cannot be resolved + * + * @see <a href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.5.1%20Expanded%20Form">JCR 2.0, 3.2.5.1 Expanded Form</a> + */ + @NotNull + String getExpandedJcrPath(@NotNull String oakPath); } diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/package-info.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/package-info.java index b372e71426..746dffb91b 100644 --- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/package-info.java +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/package-info.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@Version("1.0.1") +@Version("1.1.0") package org.apache.jackrabbit.oak.namepath; import org.osgi.annotation.versioning.Version; diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java index 507f1ab2cb..528df08de1 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java @@ -130,6 +130,33 @@ public class GlobalNameMapper implements NameMapper { return oakName; } + @Override + @NotNull + public String getExpandedJcrName(@NotNull String oakName) { + String qualifiedName = getJcrName(oakName); // sanity check + String uri; + final String localName; + int colon = qualifiedName.indexOf(':'); + if (colon > 0) { + String oakPrefix = qualifiedName.substring(0, colon); + // local mapping must take precedence... + uri = getSessionLocalMappings().get(oakPrefix); + if (uri == null) { + // ...over global mappings + uri = getNamespacesProperty(oakPrefix); + } + if (uri == null) { + throw new IllegalStateException( + "No namespace mapping found for " + oakName); + } + localName = qualifiedName.substring(colon + 1); + } else { + uri = ""; + localName = qualifiedName; + } + return "{" + uri + "}" + localName; + } + @Override @Nullable public String getOakNameOrNull(@NotNull String jcrName) { if (jcrName.startsWith("{")) { diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/NamePathMapperImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/NamePathMapperImpl.java index 3f9c3451c9..c38c2cbeb5 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/NamePathMapperImpl.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/NamePathMapperImpl.java @@ -19,6 +19,8 @@ package org.apache.jackrabbit.oak.namepath.impl; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.BooleanSupplier; +import java.util.function.UnaryOperator; import javax.jcr.RepositoryException; @@ -73,6 +75,12 @@ public class NamePathMapperImpl implements NamePathMapper { return nameMapper.getJcrName(oakName); } + @NotNull + @Override + public String getExpandedJcrName(@NotNull String oakName) { + return nameMapper.getExpandedJcrName(oakName); + } + @Override @NotNull public Map<String, String> getSessionLocalMappings() { return nameMapper.getSessionLocalMappings(); @@ -160,14 +168,23 @@ public class NamePathMapperImpl implements NamePathMapper { @Override @NotNull public String getJcrPath(final String oakPath) { + return getJcrPath(oakPath, nameMapper::getJcrName, () -> nameMapper.getSessionLocalMappings().isEmpty()); + } + + @Override + @NotNull + public String getExpandedJcrPath(@NotNull String oakPath) { + return getJcrPath(oakPath, nameMapper::getExpandedJcrName, () -> false); + } + + public String getJcrPath(final String oakPath, final UnaryOperator<String> mapFunction, final BooleanSupplier shouldSkipMappingFunction) { if ("/".equals(oakPath)) { // avoid the need to special case the root path later on return "/"; } else if (oakPath.isEmpty()) { // empty path: map to "." return "."; - } else if (nameMapper.getSessionLocalMappings().isEmpty()) { - // no local namespace mappings + } else if (shouldSkipMappingFunction.getAsBoolean()) { return oakPath; } @@ -185,7 +202,7 @@ public class NamePathMapperImpl implements NamePathMapper { @Override public boolean name(String name, int index) { - String p = nameMapper.getJcrName(name); + String p = mapFunction.apply(name); if (index == 0) { elements.add(p); } else { diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapperTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapperTest.java index 268116cd37..7a33ffd2f3 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapperTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapperTest.java @@ -48,6 +48,7 @@ public class GlobalNameMapperTest { assertEquals("", mapper.getJcrName("")); assertEquals("", mapper.getOakNameOrNull("")); assertEquals("", mapper.getOakName("")); + assertEquals("{}", mapper.getExpandedJcrName("")); } @@ -84,6 +85,11 @@ public class GlobalNameMapperTest { for (String jcrName : jcrToOak.keySet()) { assertEquals(jcrToOak.get(jcrName), mapper.getOakNameOrNull(jcrName)); assertEquals(jcrToOak.get(jcrName), mapper.getOakName(jcrName)); + if (GlobalNameMapper.isExpandedName(jcrName)) { + assertEquals(jcrName, mapper.getExpandedJcrName(jcrToOak.get(jcrName))); + } else { + assertEquals("{}"+jcrName, mapper.getExpandedJcrName(jcrToOak.get(jcrName))); + } } assertNull(mapper.getOakNameOrNull("{http://www.example.com/bar}bar")); @@ -97,17 +103,28 @@ public class GlobalNameMapperTest { @Test public void testPrefixedNames() throws RepositoryException { - List<String> prefixed = new ArrayList<String>(); - prefixed.add("nt:base"); - prefixed.add("foo: bar"); - prefixed.add("quu:bar "); - // unknown prefixes are only captured by the NameValidator - prefixed.add("unknown:bar"); + Map<String, String> prefixedToExpanded = new HashMap<>(); + prefixedToExpanded.put("nt:base", "{http://www.jcp.org/jcr/nt/1.0}base"); + prefixedToExpanded.put("foo: bar", "{http://www.example.com/foo} bar"); + prefixedToExpanded.put("quu:bar ", "{http://www.example.com/quu}bar "); - for (String name : prefixed) { + for (String name : prefixedToExpanded.keySet()) { assertEquals(name, mapper.getOakNameOrNull(name)); assertEquals(name, mapper.getOakName(name)); assertEquals(name, mapper.getJcrName(name)); + assertEquals(prefixedToExpanded.get(name), mapper.getExpandedJcrName(name)); + } + + // unknown prefixes are only captured by the NameValidator + String unknownPrefix = "unknown:bar"; + assertEquals(unknownPrefix, mapper.getOakNameOrNull(unknownPrefix)); + assertEquals(unknownPrefix, mapper.getOakName(unknownPrefix)); + assertEquals(unknownPrefix, mapper.getJcrName(unknownPrefix)); + try { + mapper.getExpandedJcrName(unknownPrefix); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + // successs } } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/impl/NamePathMapperImplTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/impl/NamePathMapperImplTest.java index 190bd2e4bf..6aae24b8c9 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/impl/NamePathMapperImplTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/namepath/impl/NamePathMapperImplTest.java @@ -135,6 +135,26 @@ public class NamePathMapperImplTest { } } + @Test + public void testOakToExpandedJcr() { + assertEquals("/{http://www.example.com/foo}bar", npMapper.getExpandedJcrPath("/oak-foo:bar")); + assertEquals("/{http://www.example.com/foo}bar/{http://www.example.com/quu}qux", npMapper.getExpandedJcrPath("/oak-foo:bar/oak-quu:qux")); + assertEquals("{http://www.example.com/foo}bar", npMapper.getExpandedJcrPath("oak-foo:bar")); + assertEquals(".", npMapper.getExpandedJcrPath("")); + + try { + npMapper.getExpandedJcrPath("{http://www.jcp.org/jcr/nt/1.0}unstructured"); + fail("expanded name should not be accepted"); + } catch (IllegalArgumentException expected) { + } + + try { + npMapper.getExpandedJcrPath("foobar/{http://www.jcp.org/jcr/1.0}content"); + fail("expanded name should not be accepted"); + } catch (IllegalArgumentException expected) { + } + } + @Test public void testInvalidJcrPaths() { String[] paths = { diff --git a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionContext.java b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionContext.java index fadd201209..902e2200c5 100644 --- a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionContext.java +++ b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionContext.java @@ -353,6 +353,12 @@ public class SessionContext implements NamePathMapper { return namePathMapper.getJcrName(oakName); } + @NotNull + @Override + public String getExpandedJcrName(@NotNull String oakName) { + return namePathMapper.getExpandedJcrName(oakName); + } + @Override @Nullable public String getOakPath(String jcrPath) {
