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) {

Reply via email to