Index: lucene/facet/src/java/org/apache/lucene/facet/taxonomy/CategoryPath.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/taxonomy/CategoryPath.java (revision 1439327) +++ lucene/facet/src/java/org/apache/lucene/facet/taxonomy/CategoryPath.java (working copy) @@ -1,7 +1,7 @@ package org.apache.lucene.facet.taxonomy; +import java.util.Arrays; - /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -62,6 +62,11 @@ /** Construct from the given path components. */ public CategoryPath(final String... components) { assert components.length > 0 : "use CategoryPath.EMPTY to create an empty path"; + for (String comp : components) { + if (comp == null || comp.isEmpty()) { + throw new IllegalArgumentException("empty or null components not allowed: " + Arrays.toString(components)); + } + } this.components = components; length = components.length; } @@ -73,6 +78,11 @@ components = null; length = 0; } else { + for (String comp : comps) { + if (comp == null || comp.isEmpty()) { + throw new IllegalArgumentException("empty or null components not allowed: " + Arrays.toString(comps)); + } + } components = comps; length = components.length; } Index: lucene/facet/src/test/org/apache/lucene/facet/taxonomy/TestCategoryPath.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/taxonomy/TestCategoryPath.java (revision 1439327) +++ lucene/facet/src/test/org/apache/lucene/facet/taxonomy/TestCategoryPath.java (working copy) @@ -1,5 +1,7 @@ package org.apache.lucene.facet.taxonomy; +import java.util.Arrays; + import org.apache.lucene.facet.FacetTestCase; import org.junit.Test; @@ -173,9 +175,46 @@ pother = new CategoryPath("a/b/c/e", '/'); assertTrue(pother.compareTo(p) > 0); assertTrue(p.compareTo(pother) < 0); - pother = new CategoryPath("a/b/c//e", '/'); - assertTrue(pother.compareTo(p) < 0); - assertTrue(p.compareTo(pother) > 0); } + @Test + public void testEmptyNullComponents() throws Exception { + // LUCENE-4724: CategoryPath should not allow empty or null components + String[][] components_tests = new String[][] { + new String[] { "", "test" }, // empty in the beginning + new String[] { "test", "" }, // empty in the end + new String[] { "test", "", "foo" }, // empty in the middle + new String[] { null, "test" }, // null at the beginning + new String[] { "test", null }, // null in the end + new String[] { "test", null, "foo" }, // null in the middle + }; + + for (String[] components : components_tests) { + try { + assertNotNull(new CategoryPath(components)); + fail("empty or null components should not be allowed: " + Arrays.toString(components)); + } catch (IllegalArgumentException e) { + // ok + } + } + + String[] path_tests = new String[] { + "/test", // empty in the beginning + "test//foo", // empty in the middle + }; + + for (String path : path_tests) { + try { + assertNotNull(new CategoryPath(path, '/')); + fail("empty or null components should not be allowed: " + path); + } catch (IllegalArgumentException e) { + // ok + } + } + + // a trailing path separator is produces only one component + assertNotNull(new CategoryPath("test/", '/')); + + } + } Index: lucene/facet/src/test/org/apache/lucene/facet/taxonomy/writercache/cl2o/TestCompactLabelToOrdinal.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/taxonomy/writercache/cl2o/TestCompactLabelToOrdinal.java (revision 1439327) +++ lucene/facet/src/test/org/apache/lucene/facet/taxonomy/writercache/cl2o/TestCompactLabelToOrdinal.java (working copy) @@ -56,6 +56,12 @@ .onUnmappableCharacter(CodingErrorAction.REPLACE) .onMalformedInput(CodingErrorAction.REPLACE); uniqueValues[i] = decoder.decode(ByteBuffer.wrap(buffer, 0, size)).toString(); + // we cannot have empty path components, so eliminate all prefix as well + // as middle consecuive delimiter chars. + uniqueValues[i] = uniqueValues[i].replaceAll("/+", "/"); + if (uniqueValues[i].startsWith("/")) { + uniqueValues[i] = uniqueValues[i].substring(1); + } if (uniqueValues[i].indexOf(CompactLabelToOrdinal.TERMINATOR_CHAR) == -1) { i++; }