Index: modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrNode.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrNode.java (revision 1787) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrNode.java (working copy) @@ -446,35 +446,47 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node CheckArg.isNotNull(namePattern, "namePattern"); namePattern = namePattern.trim(); if (namePattern.length() == 0) return new JcrEmptyPropertyIterator(); + if ("*".equals(namePattern)) { + Collection properties = cache.findJcrPropertiesFor(nodeId, location.getPath()); + return new JcrPropertyIterator(properties); + } + + return getProperties(namePattern.split("[|]")); + } + + public PropertyIterator getProperties( String[] nameGlobs ) throws RepositoryException { + CheckArg.isNotNull(nameGlobs, "nameGlobs"); + if (nameGlobs.length == 0) return new JcrEmptyPropertyIterator(); Collection properties = cache.findJcrPropertiesFor(nodeId, location.getPath()); - if ("*".equals(namePattern)) return new JcrPropertyIterator(properties); // Figure out the patterns for each of the different disjunctions in the supplied pattern ... - List patterns = createPatternsFor(namePattern); + List patterns = createPatternsFor(nameGlobs); - // Go through the properties and remove any property that doesn't match a pattern ... - boolean foundMatch = true; + boolean foundMatch = false; Collection matchingProperties = new LinkedList(); Iterator iter = properties.iterator(); while (iter.hasNext()) { AbstractJcrProperty property = iter.next(); String propName = property.getName(); - assert foundMatch == true; + assert foundMatch == false; for (Object patternOrMatch : patterns) { if (patternOrMatch instanceof Pattern) { Pattern pattern = (Pattern)patternOrMatch; - if (pattern.matcher(propName).matches()) break; + if (pattern.matcher(propName).matches()) { + foundMatch = true; + break; + } } else { String match = (String)patternOrMatch; - if (propName.equals(match)) break; + if (propName.equals(match)) { + foundMatch = true; + break; + } } - // No pattern matched ... - foundMatch = false; } if (foundMatch) { matchingProperties.add(property); - } else { - foundMatch = true; // for the next iteration .. + foundMatch = false; // for the next iteration .. } } return new JcrPropertyIterator(matchingProperties); @@ -755,9 +767,16 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node namePattern = namePattern.trim(); if (namePattern.length() == 0) return new JcrEmptyNodeIterator(); if ("*".equals(namePattern)) return getNodes(); - List patterns = createPatternsFor(namePattern); - // Implementing exact-matching only for now to prototype types as properties + return getNodes(namePattern.split("[|]")); + } + + public NodeIterator getNodes( String[] nameGlobs ) throws RepositoryException { + CheckArg.isNotNull(nameGlobs, "nameGlobs"); + if (nameGlobs.length == 0) return new JcrEmptyNodeIterator(); + + List patterns = createPatternsFor(nameGlobs); + List matchingChildren = new LinkedList(); NamespaceRegistry registry = namespaces(); boolean foundMatch = false; @@ -1839,9 +1858,9 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node this.editor().orderChildBefore(sourceSegment, destSegment); } - protected static List createPatternsFor( String namePattern ) throws RepositoryException { + protected static List createPatternsFor( String[] namePatterns ) throws RepositoryException { List patterns = new LinkedList(); - for (String stringPattern : namePattern.split("[|]")) { + for (String stringPattern : namePatterns) { stringPattern = stringPattern.trim(); int length = stringPattern.length(); if (length == 0) continue; @@ -1864,7 +1883,7 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node case '\t': case '\n': case '\r': - String msg = JcrI18n.invalidNamePattern.text(c, namePattern); + String msg = JcrI18n.invalidNamePattern.text(c, stringPattern); throw new RepositoryException(msg); // The following characters must be escaped when used in regular expressions ... case '?': Index: modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrNodeTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrNodeTest.java (revision 1787) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrNodeTest.java (working copy) @@ -31,6 +31,8 @@ import static org.hamcrest.core.IsSame.sameInstance; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.util.HashMap; +import java.util.Map; import javax.jcr.Item; import javax.jcr.ItemNotFoundException; import javax.jcr.ItemVisitor; @@ -38,6 +40,7 @@ import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; import javax.jcr.Property; +import javax.jcr.PropertyIterator; import javax.jcr.PropertyType; import javax.jcr.Repository; import javax.jcr.UnsupportedRepositoryOperationException; @@ -318,6 +321,127 @@ public class AbstractJcrNodeTest extends AbstractJcrTest { } @Test + public void shouldProvideFilteredNodeIteratorForPattern() throws Exception { + NodeIterator iter = hybrid.getNodes(" Toyota P*|*lander "); + assertThat(iter, notNullValue()); + assertThat(iter.getSize(), is(2L)); + assertThat(iter.next(), is((Object)prius)); + assertThat(iter.next(), is((Object)highlander)); + assertThat(iter.hasNext(), is(false)); + } + + @Test + public void shouldProvideFilteredNodeIteratorForPatternArray() throws Exception { + NodeIterator iter = hybrid.getNodes(new String[] {" Toyota P*", "*lander"}); + assertThat(iter, notNullValue()); + assertThat(iter.getSize(), is(2L)); + assertThat(iter.next(), is((Object)prius)); + assertThat(iter.next(), is((Object)highlander)); + assertThat(iter.hasNext(), is(false)); + } + + @Test + public void shouldReturnEmptyIteratorForEmptyPattern() throws Exception { + NodeIterator iter = hybrid.getNodes(""); + assertThat(iter, notNullValue()); + assertThat(iter.getSize(), is(0L)); + assertThat(iter.hasNext(), is(false)); + } + + @Test + public void shouldReturnEmptyIteratorForEmptyPatternArray() throws Exception { + NodeIterator iter = hybrid.getNodes(new String[] {""}); + assertThat(iter, notNullValue()); + assertThat(iter.getSize(), is(0L)); + assertThat(iter.hasNext(), is(false)); + } + + @Test + public void shouldProvidePropertyIterator() throws Exception { + assertThat(prius.getProperties(), notNullValue()); + + Map properties = new HashMap(); + for (PropertyIterator iter = prius.getProperties(); iter.hasNext(); ) { + Property prop = iter.nextProperty(); + properties.put(prop.getName(), prop); + } + + assertThat(properties.size(), is(9)); + assertThat(properties.get("vehix:maker").getString(), is("Toyota")); + assertThat(properties.get("vehix:model").getString(), is("Prius")); + assertThat(properties.get("vehix:year").getString(), is("2008")); + assertThat(properties.get("vehix:msrp").getString(), is("$21,500")); + assertThat(properties.get("vehix:userRating").getString(), is("4.2")); + assertThat(properties.get("vehix:valueRating").getString(), is("5")); + assertThat(properties.get("vehix:mpgCity").getString(), is("48")); + assertThat(properties.get("vehix:mpgHighway").getString(), is("45")); + assertThat(properties.get("jcr:primaryType").getString(), is("vehix:car")); + assertThat(prius.isNodeType(JcrMixLexicon.REFERENCEABLE), is(false)); + // If the node was referenceable, the UUID below should be returned + // assertThat(properties.get("jcr:uuid").getString(), is("3b9e4a2f-040e-4d65-9778-abe70ae48324")); + } + + @Test + public void shouldProvidePropertyIteratorForPattern() throws Exception { + String pattern = "vehix:ma*|vehix:mo*"; + assertThat(prius.getProperties(pattern), notNullValue()); + + Map properties = new HashMap(); + for (PropertyIterator iter = prius.getProperties(pattern); iter.hasNext();) { + Property prop = iter.nextProperty(); + properties.put(prop.getName(), prop); + } + + assertThat(properties.size(), is(2)); + assertThat(properties.get("vehix:maker").getString(), is("Toyota")); + assertThat(properties.get("vehix:model").getString(), is("Prius")); + } + + @Test + public void shouldProvideEmptyPropertyIteratorForEmptyPattern() throws Exception { + String pattern = ""; + assertThat(prius.getProperties(pattern), notNullValue()); + + Map properties = new HashMap(); + for (PropertyIterator iter = prius.getProperties(pattern); iter.hasNext();) { + Property prop = iter.nextProperty(); + properties.put(prop.getName(), prop); + } + + assertThat(properties.size(), is(0)); + } + + @Test + public void shouldProvidePropertyIteratorForPatternArray() throws Exception { + String[] pattern = new String[] {"vehix:ma*", "vehix:mo*"}; + assertThat(prius.getProperties(pattern), notNullValue()); + + Map properties = new HashMap(); + for (PropertyIterator iter = prius.getProperties(pattern); iter.hasNext();) { + Property prop = iter.nextProperty(); + properties.put(prop.getName(), prop); + } + + assertThat(properties.size(), is(2)); + assertThat(properties.get("vehix:maker").getString(), is("Toyota")); + assertThat(properties.get("vehix:model").getString(), is("Prius")); + } + + @Test + public void shouldProvideEmptyPropertyIteratorForEmptyPatternArray() throws Exception { + String[] pattern = new String[] {""}; + assertThat(prius.getProperties(pattern), notNullValue()); + + Map properties = new HashMap(); + for (PropertyIterator iter = prius.getProperties(pattern); iter.hasNext();) { + Property prop = iter.nextProperty(); + properties.put(prop.getName(), prop); + } + + assertThat(properties.size(), is(0)); + } + + @Test public void shouldReturnTrueFromHasNodeWithValidName() throws Exception { assertThat(hybrid.hasNode("Toyota Prius"), is(true)); }