You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by re...@apache.org on 2013/10/04 12:13:45 UTC

svn commit: r1529116 - in /jackrabbit/trunk: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/session/ jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/

Author: reschke
Date: Fri Oct  4 10:13:44 2013
New Revision: 1529116

URL: http://svn.apache.org/r1529116
Log:
JCR-3675: test cases for "similarly" named nodes, diagnostics for names not in NFC

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/session/NodeNameNormalizer.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
    jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/AddNodeTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=1529116&r1=1529115&r2=1529116&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Fri Oct  4 10:13:44 2013
@@ -92,6 +92,7 @@ import org.apache.jackrabbit.core.query.
 import org.apache.jackrabbit.core.security.AccessManager;
 import org.apache.jackrabbit.core.security.authorization.Permission;
 import org.apache.jackrabbit.core.session.AddNodeOperation;
+import org.apache.jackrabbit.core.session.NodeNameNormalizer;
 import org.apache.jackrabbit.core.session.SessionContext;
 import org.apache.jackrabbit.core.session.SessionOperation;
 import org.apache.jackrabbit.core.session.SessionWriteOperation;
@@ -1283,6 +1284,9 @@ public class NodeImpl extends ItemImpl i
             nt = (NodeTypeImpl) def.getDefaultPrimaryType();
         }
 
+        // check the new name
+        NodeNameNormalizer.check(nodeName);
+
         // check for name collisions
         NodeState thisState = data.getNodeState();
         ChildNodeEntry cne = thisState.getChildNodeEntry(nodeName, 1);

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/session/NodeNameNormalizer.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/session/NodeNameNormalizer.java?rev=1529116&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/session/NodeNameNormalizer.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/session/NodeNameNormalizer.java Fri Oct  4 10:13:44 2013
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.session;
+
+import java.text.Normalizer;
+import java.text.Normalizer.Form;
+
+import org.apache.jackrabbit.spi.Name;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Convenience class for checking/logging "weird" node names.
+ * <p>
+ * For now, it only checks that a node name being created uses NFC
+ * 
+ * @see http://www.unicode.org/reports/tr15/tr15-23.html
+ */
+public class NodeNameNormalizer {
+
+    private static Logger log = LoggerFactory.getLogger(NodeNameNormalizer.class);
+
+    public static void check(Name name) {
+        if (log.isDebugEnabled()) {
+            String lname = name.getLocalName();
+            String normalized = Normalizer.normalize(lname, Form.NFC);
+            if (!lname.equals(normalized)) {
+                String message = "The new node name '" + dump(lname) + "' is not in Unicode NFC form ('" + dump(normalized) + "').";
+                log.debug(message, new Exception("Call chain"));
+            }
+        }
+    }
+
+    private static String dump(String lname) {
+        StringBuilder sb = new StringBuilder();
+        for (char c : lname.toCharArray()) {
+            if (c > ' ' && c < 127) {
+                sb.append(c);
+            } else {
+                sb.append(String.format("\\u%04x", (int) c));
+            }
+        }
+        return sb.toString();
+    }
+}

Modified: jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/AddNodeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/AddNodeTest.java?rev=1529116&r1=1529115&r2=1529116&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/AddNodeTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-tests/src/main/java/org/apache/jackrabbit/test/api/AddNodeTest.java Fri Oct  4 10:13:44 2013
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.test.api;
 
+import java.text.Normalizer;
+
 import org.apache.jackrabbit.test.AbstractJCRTest;
 import org.apache.jackrabbit.test.NotExecutableException;
 
@@ -264,4 +266,61 @@ public class AddNodeTest extends Abstrac
             // ok, works as expected.
         }
     }
+
+    /**
+     * Tests the behavior with respect to case-sensitivity
+     */
+    public void testSimilarNodeNamesUpperLower() throws RepositoryException {
+
+        internalTestSimilarNodeNames("Test-a", "Test-A");
+    }
+
+    /**
+     * Tests the behavior with respect to Unicode normalization
+     */
+    public void testSimilarNodeNamesNfcNfd() throws RepositoryException {
+
+        String precomposed = "Test-\u00e4"; // a umlaut
+        String decomposed = Normalizer.normalize(precomposed, Normalizer.Form.NFD);
+        assertFalse(precomposed.equals(decomposed)); // sanity check
+        internalTestSimilarNodeNames(precomposed, decomposed);
+    }
+
+    /**
+     * Tests behavior for creation of "similarly" named nodes
+     * @throws RepositoryException 
+     */
+    private void internalTestSimilarNodeNames(String name1, String name2) throws RepositoryException {
+
+        Node n1 = null, n2 = null;
+        Session s = testRootNode.getSession();
+
+        try {
+            n1 = testRootNode.addNode(name1);
+            assertEquals(name1, n1.getName());
+            s.save();
+
+            assertFalse(testRootNode.hasNode(name2));
+        } catch (ConstraintViolationException e) {
+            // accepted
+        }
+        try {
+            n2 = testRootNode.addNode(name2);
+            assertEquals(name2, n2.getName());
+            s.save();
+        } catch (ConstraintViolationException e) {
+            // accepted
+        }
+
+        // If both nodes have been created, do further checks
+        if (n1 != null && n2 != null) {
+            assertFalse(n1.isSame(n2));
+            assertFalse(n1.getIdentifier().equals(n2.getIdentifier()));
+            String n2path = n2.getPath();
+            n1.remove();
+            s.save();
+            Node n3 = s.getNode(n2path);
+            assertTrue(n3.isSame(n2));
+        }
+    }
 }
\ No newline at end of file