You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/20 14:39:10 UTC

[sling-org-apache-sling-repoinit-parser] 01/46: SLING-5449 - move the repoinit modules under extensions, they do contain Sling-specific stuff

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-repoinit-parser.git

commit 848ab51b6eef8d6059af06fd548f0d1a436d7fd6
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Thu May 19 12:50:58 2016 +0000

    SLING-5449 - move the repoinit modules under extensions, they do contain Sling-specific stuff
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1744564 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |  92 +++++++
 .../sling/repoinit/parser/RepoInitParser.java      |  34 +++
 .../repoinit/parser/RepoInitParsingException.java  |  27 ++
 .../parser/impl/RepoInitParserService.java         |  49 ++++
 .../repoinit/parser/operations/AclGroupBase.java   |  43 ++++
 .../sling/repoinit/parser/operations/AclLine.java  |  71 ++++++
 .../repoinit/parser/operations/CreatePath.java     |  68 +++++
 .../parser/operations/CreateServiceUser.java       |  29 +++
 .../parser/operations/DeleteServiceUser.java       |  29 +++
 .../repoinit/parser/operations/Operation.java      |  30 +++
 .../parser/operations/OperationVisitor.java        |  26 ++
 .../parser/operations/PathSegmentDefinition.java   |  48 ++++
 .../parser/operations/ServiceUserOperation.java    |  34 +++
 .../repoinit/parser/operations/SetAclPaths.java    |  50 ++++
 .../parser/operations/SetAclPrincipals.java        |  50 ++++
 .../repoinit/parser/operations/package-info.java   |  21 ++
 .../apache/sling/repoinit/parser/package-info.java |  21 ++
 src/main/javacc/RepoInitGrammar.jjt                | 276 +++++++++++++++++++++
 .../parser/test/OperationToStringVisitor.java      |  87 +++++++
 .../repoinit/parser/test/ParserServiceTest.java    |  39 +++
 .../sling/repoinit/parser/test/ParserTest.java     | 130 ++++++++++
 .../repoinit/parser/test/ParsingErrorsTest.java    |  89 +++++++
 src/test/resources/testcases/test-1-output.txt     |   4 +
 src/test/resources/testcases/test-1.txt            |   2 +
 src/test/resources/testcases/test-10-output.txt    |   5 +
 src/test/resources/testcases/test-10.txt           |  10 +
 src/test/resources/testcases/test-11-output.txt    |   5 +
 src/test/resources/testcases/test-11.txt           |   8 +
 src/test/resources/testcases/test-2-output.txt     |   3 +
 src/test/resources/testcases/test-2.txt            |   2 +
 src/test/resources/testcases/test-20-output.txt    |   4 +
 src/test/resources/testcases/test-20.txt           |   5 +
 src/test/resources/testcases/test-3-output.txt     |   1 +
 src/test/resources/testcases/test-3.txt            |  10 +
 src/test/resources/testcases/test-30-output.txt    |   6 +
 src/test/resources/testcases/test-30.txt           |  12 +
 src/test/resources/testcases/test-99-output.txt    |  19 ++
 src/test/resources/testcases/test-99.txt           |  31 +++
 38 files changed, 1470 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..2df3f3c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.sling</groupId>
+    <artifactId>sling</artifactId>
+    <version>26</version>
+    <relativePath/>
+  </parent>
+
+  <artifactId>org.apache.sling.repoinit.parser</artifactId>
+  <packaging>bundle</packaging>
+  <version>0.0.1-SNAPSHOT</version>
+  <name>Apache Sling RepoInit Parser</name>
+  <description>Parser for the Repository Initialization language used in Sling</description>
+
+  <scm>
+    <connection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/repoinit/parser</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/repoinit/parser</developerConnection>
+    <url>https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/repoinit/parser</url>
+  </scm>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-scr-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>src/test/resources/testcases/**</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+       <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>javacc-maven-plugin</artifactId>
+        <version>2.5</version>
+        <executions>
+          <execution>
+            <id>calculator</id>
+            <configuration>
+              <sourceDirectory>${basedir}/src/main/javacc</sourceDirectory>
+            </configuration>
+            <goals>
+              <goal>jjtree-javacc</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>2.4</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/repoinit/parser/RepoInitParser.java b/src/main/java/org/apache/sling/repoinit/parser/RepoInitParser.java
new file mode 100644
index 0000000..8fe70eb
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/RepoInitParser.java
@@ -0,0 +1,34 @@
+/*
+ * 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.sling.repoinit.parser;
+
+import java.io.Reader;
+import java.util.List;
+
+import org.apache.sling.repoinit.parser.operations.Operation;
+
+/** ACL definitions parser service interface */
+public interface RepoInitParser {
+    /** Parse the supplied input.
+     * @param r Input in ACL definitions format. The reader is closed
+     *  by this method. 
+     * @throws AclParsingException on parsing errors
+     */
+    List<Operation> parse(Reader r) throws RepoInitParsingException;
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/RepoInitParsingException.java b/src/main/java/org/apache/sling/repoinit/parser/RepoInitParsingException.java
new file mode 100644
index 0000000..fa40e52
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/RepoInitParsingException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.sling.repoinit.parser;
+
+public class RepoInitParsingException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public RepoInitParsingException(String reason, Throwable cause) {
+        super(reason, cause);
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/impl/RepoInitParserService.java b/src/main/java/org/apache/sling/repoinit/parser/impl/RepoInitParserService.java
new file mode 100644
index 0000000..d599f55
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/impl/RepoInitParserService.java
@@ -0,0 +1,49 @@
+/*
+ * 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.sling.repoinit.parser.impl;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.List;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.repoinit.parser.RepoInitParser;
+import org.apache.sling.repoinit.parser.RepoInitParsingException;
+import org.apache.sling.repoinit.parser.operations.Operation;
+
+/** ACL definitions parser service */
+@Component
+@Service(value=RepoInitParser.class)
+public class RepoInitParserService implements RepoInitParser {
+
+    @Override
+    public List<Operation> parse(Reader r) throws RepoInitParsingException {
+        try {
+            return new RepoInitParserImpl(r).parse();
+        } catch (ParseException pe) {
+            throw new RepoInitParsingException(pe.getMessage(), pe);
+        } finally {
+            try {
+                r.close();
+            } catch(IOException ignore) {
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/AclGroupBase.java b/src/main/java/org/apache/sling/repoinit/parser/operations/AclGroupBase.java
new file mode 100644
index 0000000..3e67eae
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/AclGroupBase.java
@@ -0,0 +1,43 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/** Base class for operations that group AclLines */
+ abstract class AclGroupBase extends Operation {
+    private final List<AclLine> lines;
+    
+    protected AclGroupBase(List<AclLine> lines) {
+        this.lines = Collections.unmodifiableList(lines);
+    }
+    
+    protected String getParametersDescription() {
+        final StringBuilder sb = new StringBuilder();
+        for(AclLine line : lines) {
+            sb.append("\n  ").append(line.toString());
+        }
+        return sb.toString(); 
+    }
+    
+    public Collection<AclLine> getLines() {
+        return lines;
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/AclLine.java b/src/main/java/org/apache/sling/repoinit/parser/operations/AclLine.java
new file mode 100644
index 0000000..dc4da8a
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/AclLine.java
@@ -0,0 +1,71 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/** A single "set ACL" line */
+public class AclLine {
+    
+    private final Action action;
+    private static final List<String> EMPTY_LIST = Collections.unmodifiableList(new ArrayList<String>());
+    
+    public static final String PROP_PATHS = "paths";
+    public static final String PROP_PRINCIPALS = "principals";
+    public static final String PROP_PRIVILEGES = "privileges";
+    public static final String PROP_NODETYPES = "nodetypes";
+
+    public enum Action {
+        REMOVE,
+        REMOVE_ALL,
+        DENY,
+        ALLOW
+    };
+    
+    private final Map<String, List<String>> properties;
+    
+    public AclLine(Action a) {
+        action = a;
+        properties = new TreeMap<String, List<String>>();
+    }
+    
+    public Action getAction() {
+        return action;
+    }
+    
+    /** Return the named multi-value property, or an empty list
+     *  if not found. 
+     */
+    public List<String> getProperty(String name) {
+        List<String> value = properties.get(name);
+        return value != null ? value : EMPTY_LIST;
+    }
+    
+    public void setProperty(String name, List<String> values) {
+        properties.put(name, Collections.unmodifiableList(values));
+    }
+    
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " " + action + " " + properties;
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/CreatePath.java b/src/main/java/org/apache/sling/repoinit/parser/operations/CreatePath.java
new file mode 100644
index 0000000..d1b87d0
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/CreatePath.java
@@ -0,0 +1,68 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class CreatePath extends Operation {
+    private List<PathSegmentDefinition> pathDef;
+    private final String defaultPrimaryType;
+    
+    public CreatePath(String defaultPrimaryType) {
+        this.pathDef = new ArrayList<PathSegmentDefinition>();
+        this.defaultPrimaryType = defaultPrimaryType;
+    }
+    
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " " + pathDef;
+    }
+    
+    @Override
+    protected String getParametersDescription() {
+        return pathDef.toString();
+    }
+
+    @Override
+    public void accept(OperationVisitor v) {
+        v.visitCreatePath(this);
+    }
+    
+    public void addSegment(String path, String primaryType) {
+        // We might get a path like /var/discovery, in which case
+        // the specified primary type applies to the last
+        // segment only
+        final String [] segments = path.split("/");
+        for(int i=0; i < segments.length; i++) {
+            if(segments[i].length() == 0) {
+                continue;
+            }
+            String pt = defaultPrimaryType;
+            if(i == segments.length -1 && primaryType != null) {
+                pt = primaryType;
+            }
+            pathDef.add(new PathSegmentDefinition(segments[i], pt));
+        }
+    }
+    
+    public List<PathSegmentDefinition> getDefinitions() {
+        return Collections.unmodifiableList(pathDef);
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/CreateServiceUser.java b/src/main/java/org/apache/sling/repoinit/parser/operations/CreateServiceUser.java
new file mode 100644
index 0000000..ef10180
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/CreateServiceUser.java
@@ -0,0 +1,29 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+public class CreateServiceUser extends ServiceUserOperation {
+    public CreateServiceUser(String username) {
+        super(username);
+    }
+
+    @Override
+    public void accept(OperationVisitor v) {
+        v.visitCreateServiceUser(this);
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/DeleteServiceUser.java b/src/main/java/org/apache/sling/repoinit/parser/operations/DeleteServiceUser.java
new file mode 100644
index 0000000..f8cce8c
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/DeleteServiceUser.java
@@ -0,0 +1,29 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+public class DeleteServiceUser extends ServiceUserOperation {
+    public DeleteServiceUser(String username) {
+        super(username);
+    }
+    
+    @Override
+    public void accept(OperationVisitor v) {
+        v.visitDeleteServiceUser(this);
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/Operation.java b/src/main/java/org/apache/sling/repoinit/parser/operations/Operation.java
new file mode 100644
index 0000000..693cfc9
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/Operation.java
@@ -0,0 +1,30 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+public abstract class Operation {
+    public abstract void accept(OperationVisitor v);
+    
+    protected abstract String getParametersDescription();
+    
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " " + getParametersDescription();
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/OperationVisitor.java b/src/main/java/org/apache/sling/repoinit/parser/operations/OperationVisitor.java
new file mode 100644
index 0000000..4079386
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/OperationVisitor.java
@@ -0,0 +1,26 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+public interface OperationVisitor {
+    void visitCreateServiceUser(CreateServiceUser s);
+    void visitDeleteServiceUser(DeleteServiceUser s);
+    void visitSetAclPrincipal(SetAclPrincipals s);
+    void visitSetAclPaths(SetAclPaths s);
+    void visitCreatePath(CreatePath cp);
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/PathSegmentDefinition.java b/src/main/java/org/apache/sling/repoinit/parser/operations/PathSegmentDefinition.java
new file mode 100644
index 0000000..62f95b8
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/PathSegmentDefinition.java
@@ -0,0 +1,48 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+/** Defines a segment of a path to be created, 
+ *  with its name and an optional primary type
+ */
+public class PathSegmentDefinition {
+    private final String segment;
+    private final String primaryType;
+    
+    public PathSegmentDefinition(String segment, String primaryType) {
+        this.segment = segment;
+        this.primaryType = primaryType;
+    }
+
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(segment);
+        if(primaryType != null) {
+            sb.append("(").append(primaryType).append(")");
+        }
+        return sb.toString();
+    }
+    
+    public String getSegment() {
+        return segment;
+    }
+
+    public String getPrimaryType() {
+        return primaryType;
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/ServiceUserOperation.java b/src/main/java/org/apache/sling/repoinit/parser/operations/ServiceUserOperation.java
new file mode 100644
index 0000000..7f055e2
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/ServiceUserOperation.java
@@ -0,0 +1,34 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+public abstract class ServiceUserOperation extends Operation {
+    protected final String username;
+    
+    ServiceUserOperation(String username) {
+        this.username = username;
+    }
+    
+    protected String getParametersDescription() {
+        return username;
+    }
+    
+    public String getUsername() {
+        return username;
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/SetAclPaths.java b/src/main/java/org/apache/sling/repoinit/parser/operations/SetAclPaths.java
new file mode 100644
index 0000000..b2673dc
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/SetAclPaths.java
@@ -0,0 +1,50 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+import java.util.Collections;
+import java.util.List;
+
+/** Set ACL statement that groups a set of AclLines
+ *  that all refer to the same set of paths.
+ */
+public class SetAclPaths extends AclGroupBase {
+    
+    private final List<String> paths;
+    
+    public SetAclPaths(List<String> paths, List<AclLine> lines) {
+        super(lines);
+        this.paths = Collections.unmodifiableList(paths);
+    }
+    
+    protected String getParametersDescription() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(paths);
+        sb.append(super.getParametersDescription());
+        return sb.toString(); 
+    }
+    
+    public List<String> getPaths() {
+        return paths;
+    }
+
+    @Override
+    public void accept(OperationVisitor v) {
+        v.visitSetAclPaths(this);
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/SetAclPrincipals.java b/src/main/java/org/apache/sling/repoinit/parser/operations/SetAclPrincipals.java
new file mode 100644
index 0000000..b3bb548
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/SetAclPrincipals.java
@@ -0,0 +1,50 @@
+/*
+ * 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.sling.repoinit.parser.operations;
+
+import java.util.Collections;
+import java.util.List;
+
+/** Set ACL statement that groups a set of AclLines
+ *  that all refer to the same set of principals.
+ */
+public class SetAclPrincipals extends AclGroupBase {
+    
+    private final List<String> principals;
+    
+    public SetAclPrincipals(List<String> principals, List<AclLine> lines) {
+        super(lines);
+        this.principals = Collections.unmodifiableList(principals);
+    }
+    
+    protected String getParametersDescription() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(principals);
+        sb.append(super.getParametersDescription());
+        return sb.toString(); 
+    }
+    
+    public List<String> getPrincipals() {
+        return principals;
+    }
+
+    @Override
+    public void accept(OperationVisitor v) {
+        v.visitSetAclPrincipal(this);
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/package-info.java b/src/main/java/org/apache/sling/repoinit/parser/operations/package-info.java
new file mode 100644
index 0000000..fad0a42
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/package-info.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * 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.
+ ******************************************************************************/
+
+@Version("1.0.0")
+package org.apache.sling.repoinit.parser.operations;
+
+import aQute.bnd.annotation.Version;
diff --git a/src/main/java/org/apache/sling/repoinit/parser/package-info.java b/src/main/java/org/apache/sling/repoinit/parser/package-info.java
new file mode 100644
index 0000000..3c9ed48
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/package-info.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * 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.
+ ******************************************************************************/
+
+@Version("1.0.0")
+package org.apache.sling.repoinit.parser;
+
+import aQute.bnd.annotation.Version;
diff --git a/src/main/javacc/RepoInitGrammar.jjt b/src/main/javacc/RepoInitGrammar.jjt
new file mode 100644
index 0000000..66b52b0
--- /dev/null
+++ b/src/main/javacc/RepoInitGrammar.jjt
@@ -0,0 +1,276 @@
+/* JavaCC grammar for the SLING-5449 content repository 
+ * initialization language
+ * See https://javacc.java.net/doc/docindex.html for the JavaCC docs
+ */
+ 
+options
+{
+    STATIC=false;
+    LOOKAHEAD=3;
+    //FORCE_LA_CHECK=true;
+}
+
+PARSER_BEGIN(RepoInitParserImpl)
+
+package org.apache.sling.repoinit.parser.impl;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.sling.repoinit.parser.operations.*;
+
+/*
+ * 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.
+ */
+
+public class RepoInitParserImpl 
+{
+}
+PARSER_END(RepoInitParserImpl)
+
+SKIP :
+{
+    " "
+|   "\r"
+|   "\t"
+|   < COMMENT: "#" (~["\n"])* "\n" >
+}
+
+TOKEN:
+{
+    < SET: "set" >
+|   < ACL: "ACL" >
+|   < ON: "on" >
+|   < REMOVE: "remove" >
+|   < ALLOW: "allow" >
+|   < DENY: "deny" >
+|   < FOR: "for" >
+|   < CREATE: "create" >
+|   < DELETE: "delete" >
+|   < SERVICE: "service" >
+|   < PATH: "path" >
+|   < END: "end" >
+|   < USER: "user" >
+|   < NODETYPES: "nodetypes" >
+|   < LPAREN: "(" >
+|   < RPAREN: ")" >
+|   < PRINCIPAL: (["a"-"z"] | ["A"-"Z"] | ["0"-"9"] | "_" | "-")+ >
+|   < COMMA: "," >
+|   < STAR: "*" >
+|   < NAMESPACED_ITEM: (["a"-"z"] | ["A"-"Z"])+ ":" (["a"-"z"] | ["A"-"Z"])+ >
+|   < PATH_STRING: "/" (["a"-"z"] | ["A"-"Z"] | ["0"-"9"] | ["-"] | ["_"] | ["."] | ["/"]) * >
+|   < EOL: "\n" >
+}
+
+List<Operation> parse() :
+{}
+{
+    { final List<Operation> result = new ArrayList<Operation>(); }
+    
+    ( 
+        serviceUserStatement(result) 
+        | setAclPaths(result) 
+        | setAclPrincipals(result)
+        | createPathStatement(result)
+        | blankLine() 
+    ) * 
+    <EOF>
+    
+    { return result; }
+}
+
+void blankLine() :
+{}
+{
+    <EOL>
+}
+
+List<String> principalsList() :
+{
+    Token t = null;
+    List<String> principals = new ArrayList<String>(); 
+}
+{
+    t = <PRINCIPAL> { principals.add(t.image); } 
+    ( <COMMA> t = <PRINCIPAL> { principals.add(t.image); } )* 
+    { return principals; }
+}
+
+void serviceUserStatement(List<Operation> result) :
+{
+    Token t;
+    List<String> principals;
+}
+{
+    (t=<CREATE> | t=<DELETE>) 
+    <SERVICE> <USER> 
+    principals = principalsList() 
+    (<EOL> | <EOF>)
+    
+    {
+        for(String PRINCIPAL : principals) {
+            if(CREATE == t.kind) {
+                result.add(new CreateServiceUser(PRINCIPAL));
+            } else {
+                result.add(new DeleteServiceUser(PRINCIPAL));
+            }
+        } 
+    }
+}
+
+List<String> namespacedItemsList() :
+{
+    Token t = null;
+    List<String> priv = new ArrayList<String>(); 
+    
+}
+{
+    t = <NAMESPACED_ITEM> { priv.add(t.image); } 
+    ( <COMMA> t = <NAMESPACED_ITEM> { priv.add(t.image); } )* 
+    { return priv; }
+}
+
+List<String> pathsList() :
+{
+    Token t = null;
+    List<String> paths = new ArrayList<String>();
+}
+{
+    t = <PATH_STRING> { paths.add(t.image); } 
+    ( <COMMA> t = <PATH_STRING> { paths.add(t.image); } )* 
+    { return paths; }
+}
+
+void createPathStatement(List<Operation> result) :
+{
+    CreatePath cp = null;
+    String defaultPrimaryType = null;
+    Token t1 = null;
+    Token t2 = null;
+}
+{
+    <CREATE> <PATH> 
+    ( <LPAREN> t1 = <NAMESPACED_ITEM> <RPAREN> { defaultPrimaryType = t1.image; } ) ?
+    
+    ( t1 = <PATH_STRING> ( <LPAREN> t2 = <NAMESPACED_ITEM> <RPAREN> ) ?
+        {
+            if(cp == null) {
+                cp = new CreatePath(defaultPrimaryType);
+            } 
+            cp.addSegment(t1.image, t2 == null ? null : t2.image); 
+            t2 = null; 
+        }
+    ) +
+    
+    (<EOL> | <EOF>)
+    
+    { if(cp != null) result.add(cp); }
+}
+
+void setAclPaths(List<Operation> result) :
+{
+    List<String> paths;
+    List<AclLine> lines = new ArrayList<AclLine>();
+} 
+{
+    <SET> <ACL> <ON> paths  = pathsList() <EOL>
+    ( removeStarLine(lines) | userPrivilegesLine(lines) | blankLine() ) +
+    <END> 
+    ( <EOL> | <EOF> )
+    
+    {
+        result.add(new SetAclPaths(paths, lines));
+    }
+}
+
+void removeStarLine(List<AclLine> lines) : 
+{
+    List<String> tmp = null;
+    AclLine line = new AclLine(AclLine.Action.REMOVE_ALL);
+}
+{
+    <REMOVE> <STAR> 
+    (
+        <FOR> tmp = principalsList() { line.setProperty(AclLine.PROP_PRINCIPALS, tmp); }
+        
+        | <ON> tmp = pathsList() { line.setProperty(AclLine.PROP_PATHS, tmp); }
+    )     
+    <EOL>
+    
+    {
+        lines.add(line);
+    }
+}
+
+AclLine privilegesLineOperation() :
+{}
+{
+    ( 
+        <REMOVE>        { return new AclLine(AclLine.Action.REMOVE); }
+        | ( <ALLOW>     { return new AclLine(AclLine.Action.ALLOW); } )
+        | ( <DENY>      { return new AclLine(AclLine.Action.DENY); } )    
+    ) 
+}
+
+void userPrivilegesLine(List<AclLine> lines) :
+{
+    AclLine line;
+    List<String> tmp;
+}
+{
+    line = privilegesLineOperation()
+    tmp = namespacedItemsList() { line.setProperty(AclLine.PROP_PRIVILEGES, tmp); } 
+    <FOR>
+    tmp = principalsList() { line.setProperty(AclLine.PROP_PRINCIPALS, tmp); }
+    <EOL>
+
+    {   
+        lines.add(line); 
+    }
+}
+
+void pathPrivilegesLine(List<AclLine> lines) : 
+{
+    AclLine line;
+    List<String> tmp;
+}
+{
+    line = privilegesLineOperation()
+    tmp = namespacedItemsList() { line.setProperty(AclLine.PROP_PRIVILEGES, tmp); } 
+    <ON> tmp = pathsList() { line.setProperty(AclLine.PROP_PATHS, tmp); }
+    ( <NODETYPES> tmp = namespacedItemsList() { line.setProperty(AclLine.PROP_NODETYPES, tmp); }) ?
+    <EOL>
+    
+    {    
+        lines.add(line); 
+    }
+}
+
+void setAclPrincipals(List<Operation> result) :
+{
+    List <String> principals;
+    List<AclLine> lines = new ArrayList<AclLine>();
+}
+{
+    <SET> <ACL> <FOR> principals = principalsList() <EOL>
+    ( removeStarLine(lines) | pathPrivilegesLine(lines) | blankLine() ) +
+    <END> 
+    ( <EOL> | <EOF> )
+    
+    {
+        result.add(new SetAclPrincipals(principals, lines));
+    }
+}
diff --git a/src/test/java/org/apache/sling/repoinit/parser/test/OperationToStringVisitor.java b/src/test/java/org/apache/sling/repoinit/parser/test/OperationToStringVisitor.java
new file mode 100644
index 0000000..a439549
--- /dev/null
+++ b/src/test/java/org/apache/sling/repoinit/parser/test/OperationToStringVisitor.java
@@ -0,0 +1,87 @@
+/*
+ * 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.sling.repoinit.parser.test;
+
+import java.io.PrintWriter;
+import java.util.Collection;
+
+import org.apache.sling.repoinit.parser.operations.AclLine;
+import org.apache.sling.repoinit.parser.operations.CreatePath;
+import org.apache.sling.repoinit.parser.operations.CreateServiceUser;
+import org.apache.sling.repoinit.parser.operations.DeleteServiceUser;
+import org.apache.sling.repoinit.parser.operations.OperationVisitor;
+import org.apache.sling.repoinit.parser.operations.SetAclPaths;
+import org.apache.sling.repoinit.parser.operations.SetAclPrincipals;
+
+/** OperationVisitor that dumps the operations using
+ *  their toString() methods
+ */
+class OperationToStringVisitor implements OperationVisitor {
+
+    private final PrintWriter out;
+    
+    OperationToStringVisitor(PrintWriter pw) {
+        out = pw;
+    }
+    
+    @Override
+    public void visitCreateServiceUser(CreateServiceUser s) {
+        out.println(s.toString());
+    }
+
+    @Override
+    public void visitDeleteServiceUser(DeleteServiceUser s) {
+        out.println(s.toString());
+    }
+
+    @Override
+    public void visitSetAclPrincipal(SetAclPrincipals s) {
+        out.print(s.getClass().getSimpleName());
+        out.print(" for ");
+        for(String p : s.getPrincipals()) {
+            out.print(p);
+            out.print(' ');
+        }
+        out.println();
+        dumpAclLines(s.getLines());
+    }
+    
+    @Override
+    public void visitSetAclPaths(SetAclPaths s) {
+        out.print(s.getClass().getSimpleName());
+        out.print(" on ");
+        for(String p : s.getPaths()) {
+            out.print(p);
+            out.print(' ');
+        }
+        out.println();
+        dumpAclLines(s.getLines());
+    }
+    
+    @Override
+    public void visitCreatePath(CreatePath cp) {
+        out.println(cp.toString());
+    }
+
+    private void dumpAclLines(Collection<AclLine> c) {
+        for(AclLine line : c) {
+            out.print("  ");
+            out.println(line);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/sling/repoinit/parser/test/ParserServiceTest.java b/src/test/java/org/apache/sling/repoinit/parser/test/ParserServiceTest.java
new file mode 100644
index 0000000..41a0495
--- /dev/null
+++ b/src/test/java/org/apache/sling/repoinit/parser/test/ParserServiceTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.sling.repoinit.parser.test;
+
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.apache.sling.repoinit.parser.RepoInitParsingException;
+import org.apache.sling.repoinit.parser.impl.RepoInitParserService;
+import org.junit.Test;
+
+public class ParserServiceTest {
+    @Test
+    public void noErrors() throws RepoInitParsingException {
+        final Reader r = new StringReader("create service user foo");
+        new RepoInitParserService().parse(r);
+    }
+    
+    @Test(expected = RepoInitParsingException.class)
+    public void syntaxError() throws RepoInitParsingException {
+        final Reader r = new StringReader("not a valid statement");
+        new RepoInitParserService().parse(r);
+    }
+}
diff --git a/src/test/java/org/apache/sling/repoinit/parser/test/ParserTest.java b/src/test/java/org/apache/sling/repoinit/parser/test/ParserTest.java
new file mode 100644
index 0000000..94c77e2
--- /dev/null
+++ b/src/test/java/org/apache/sling/repoinit/parser/test/ParserTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.sling.repoinit.parser.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.sling.repoinit.parser.impl.ParseException;
+import org.apache.sling.repoinit.parser.impl.RepoInitParserImpl;
+import org.apache.sling.repoinit.parser.operations.Operation;
+import org.apache.sling.repoinit.parser.operations.OperationVisitor;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Test the parser using our test-* input/expected output files. 
+ *  The code of this class doesn't contain any actual tests, it
+ *  just looks for test-*.txt files, parses them and verifies the
+ *  results according to the test-*-output.txt files.
+ */
+@RunWith(Parameterized.class)
+public class ParserTest {
+    
+    public static final String DEFAULT_ENCODING = "UTF-8";
+    
+    static class TestCase {
+        final InputStream input;
+        final String inputFilename;
+        final InputStream expected;
+        final String outputFilename;
+        
+        private static final String PREFIX = "/testcases/test-"; 
+        
+        @Override
+        public String toString() {
+            return inputFilename;
+        }
+        
+        private TestCase(int index) {
+            inputFilename = PREFIX + index + ".txt";
+            input = getClass().getResourceAsStream(inputFilename);
+            outputFilename = PREFIX + index + "-output.txt";
+            expected = getClass().getResourceAsStream(outputFilename);
+        }
+        
+        static TestCase build(int index) {
+            final TestCase result = new TestCase(index);
+            if(result.input == null || result.expected == null) {
+                return null;
+            }
+            return result;
+        }
+        
+        void close() {
+            try {
+                input.close();
+            } catch(IOException ignored) {
+            }
+            try {
+                expected.close();
+            } catch(IOException ignored) {
+            }
+        }
+    }
+    
+    private final TestCase tc;
+    
+    @Parameters(name="{0}")
+    public static Collection<Object[]> data() {
+        final List<Object []> result = new ArrayList<Object[]>();
+        for(int i=0; i < 100; i++) {
+            final TestCase tc = TestCase.build(i);
+            if(tc != null) {
+                result.add(new Object[] { tc });
+            }
+        }
+        return result;
+        
+    }
+    
+    public ParserTest(TestCase tc) {
+        this.tc = tc;
+    }
+
+    @Test
+    public void checkResult() throws ParseException, IOException {
+        final String expected = IOUtils.toString(tc.expected, DEFAULT_ENCODING).trim();
+        try {
+            final StringWriter sw = new StringWriter();
+            final OperationVisitor v = new OperationToStringVisitor(new PrintWriter(sw)); 
+            final List<Operation> result = new RepoInitParserImpl(tc.input).parse(); 
+            for(Operation o : result) {
+                o.accept(v);
+            }
+            sw.flush();
+            String actual = sw.toString().trim();
+            
+            // normalize line endings to ensure tests run on windows as well
+            actual = actual.replaceAll("\r\n", "\n");
+            
+            assertEquals(expected, actual);
+        } finally {
+            tc.close();
+        }
+    }
+}
diff --git a/src/test/java/org/apache/sling/repoinit/parser/test/ParsingErrorsTest.java b/src/test/java/org/apache/sling/repoinit/parser/test/ParsingErrorsTest.java
new file mode 100644
index 0000000..fb46eef
--- /dev/null
+++ b/src/test/java/org/apache/sling/repoinit/parser/test/ParsingErrorsTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.sling.repoinit.parser.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sling.repoinit.parser.impl.ParseException;
+import org.apache.sling.repoinit.parser.impl.RepoInitParserImpl;
+import org.apache.sling.repoinit.parser.impl.TokenMgrError;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Test various parsing errors */
+@RunWith(Parameterized.class)
+public class ParsingErrorsTest {
+
+    private String input;
+    private Class<? extends Throwable> expected;
+    
+    @Parameters
+    public static Collection<Object[]> data() {
+        @SuppressWarnings("serial")
+        final List<Object []> result = new ArrayList<Object []>() {{
+            add(new Object[] { "foo", ParseException.class  });
+            add(new Object[] { "12", ParseException.class  });
+            
+            add(new Object[] { "set ACL on /apps \n remove * for u \n end", null });
+            add(new Object[] { "set ACL on /apps \n badkeyword * for u \n end", ParseException.class });
+            add(new Object[] { "set ACL on appsWithoutSlash \n remove * for u \n end", ParseException.class  });
+            add(new Object[] { "set ACL", ParseException.class  });
+            add(new Object[] { "set ACL \n end", ParseException.class  });
+            
+            add(new Object[] { "create service user bob, alice, tom21", null  });
+            add(new Object[] { "create service user bob-221_BOB", null  });
+            add(new Object[] { "create service user bob/221", ParseException.class  });
+            add(new Object[] { "create service user bob,/alice, tom21", ParseException.class  });
+            add(new Object[] { "create service user bob,alice,tom21 # comment not allowed here", TokenMgrError.class  });
+            add(new Object[] { "CREATE service user bob, alice, tom21", ParseException.class });
+            add(new Object[] { "create SERVICE user bob, alice, tom21", ParseException.class });
+        }};
+        return result;
+    }
+    
+    public ParsingErrorsTest(String input, Class<? extends Throwable> expected) {
+        this.input = input;
+        this.expected = expected;
+    }
+
+    @Test
+    public void checkResult() throws ParseException, IOException {
+        final StringReader r = new StringReader(input);
+        try {
+            new RepoInitParserImpl(r).parse();
+            if(expected != null) {
+                fail("Expected a " + expected.getSimpleName() + " for [" + input + "]");
+            }
+        } catch(Exception e) {
+            assertEquals(expected, e.getClass());
+        } catch(Error err) {
+            assertEquals(expected, err.getClass());
+        } finally {
+            r.close();
+        }
+    }
+}
diff --git a/src/test/resources/testcases/test-1-output.txt b/src/test/resources/testcases/test-1-output.txt
new file mode 100644
index 0000000..d4fde81
--- /dev/null
+++ b/src/test/resources/testcases/test-1-output.txt
@@ -0,0 +1,4 @@
+CreateServiceUser bob
+CreateServiceUser alice
+CreateServiceUser tom21
+CreateServiceUser lonesome
diff --git a/src/test/resources/testcases/test-1.txt b/src/test/resources/testcases/test-1.txt
new file mode 100644
index 0000000..1ad451a
--- /dev/null
+++ b/src/test/resources/testcases/test-1.txt
@@ -0,0 +1,2 @@
+create service user bob,alice, tom21
+create service user lonesome
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-10-output.txt b/src/test/resources/testcases/test-10-output.txt
new file mode 100644
index 0000000..c9106ac
--- /dev/null
+++ b/src/test/resources/testcases/test-10-output.txt
@@ -0,0 +1,5 @@
+SetAclPaths on /libs /apps / /content/example.com/some-other_path 
+  AclLine REMOVE_ALL {principals=[user1, user2]}
+  AclLine ALLOW {principals=[user1, user2], privileges=[jcr:read]}
+  AclLine DENY {principals=[user2], privileges=[jcr:write, something:else, another:one]}
+  AclLine DENY {principals=[user1], privileges=[jcr:lockManagement]}
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-10.txt b/src/test/resources/testcases/test-10.txt
new file mode 100644
index 0000000..db91e9f
--- /dev/null
+++ b/src/test/resources/testcases/test-10.txt
@@ -0,0 +1,10 @@
+# Set ACL example from SLING-5355
+# Without the "with glob" option, we're not planning to support
+# that at this time. 
+set ACL on /libs,/apps, /, /content/example.com/some-other_path
+    remove * for user1,user2
+    allow jcr:read for user1,user2
+
+    deny jcr:write,something:else,another:one for user2
+    deny jcr:lockManagement for user1
+end
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-11-output.txt b/src/test/resources/testcases/test-11-output.txt
new file mode 100644
index 0000000..aceed38
--- /dev/null
+++ b/src/test/resources/testcases/test-11-output.txt
@@ -0,0 +1,5 @@
+SetAclPaths on /libs /apps 
+  AclLine REMOVE_ALL {principals=[user1, user2]}
+  AclLine ALLOW {principals=[user1, user2], privileges=[jcr:read]}
+  AclLine REMOVE_ALL {principals=[another]}
+  AclLine ALLOW {principals=[another], privileges=[x:y]}
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-11.txt b/src/test/resources/testcases/test-11.txt
new file mode 100644
index 0000000..7d27a2a
--- /dev/null
+++ b/src/test/resources/testcases/test-11.txt
@@ -0,0 +1,8 @@
+# Test multiple remove lines
+set ACL on /libs,/apps
+    remove * for user1,user2
+    allow jcr:read for user1,user2
+
+    remove * for another
+    allow x:y for another
+end
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-2-output.txt b/src/test/resources/testcases/test-2-output.txt
new file mode 100644
index 0000000..d2b3951
--- /dev/null
+++ b/src/test/resources/testcases/test-2-output.txt
@@ -0,0 +1,3 @@
+CreateServiceUser Mark-21
+DeleteServiceUser Leonardo
+DeleteServiceUser Winston_32
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-2.txt b/src/test/resources/testcases/test-2.txt
new file mode 100644
index 0000000..c0c7b7e
--- /dev/null
+++ b/src/test/resources/testcases/test-2.txt
@@ -0,0 +1,2 @@
+create service user Mark-21
+delete service user Leonardo,Winston_32
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-20-output.txt b/src/test/resources/testcases/test-20-output.txt
new file mode 100644
index 0000000..68fa997
--- /dev/null
+++ b/src/test/resources/testcases/test-20-output.txt
@@ -0,0 +1,4 @@
+CreatePath [var(sling:Folder), discovery(nt:unstructured), somefolder(sling:Folder)]
+CreatePath [one, two, three]
+CreatePath [three, four(nt:folk), five(nt:jazz), six]
+CreatePath [seven(nt:x), eight(nt:x), nine(nt:x)]
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-20.txt b/src/test/resources/testcases/test-20.txt
new file mode 100644
index 0000000..30f1355
--- /dev/null
+++ b/src/test/resources/testcases/test-20.txt
@@ -0,0 +1,5 @@
+# Various create path tests
+create path (sling:Folder) /var/discovery(nt:unstructured)/somefolder
+create path /one/two/three
+create path /three/four(nt:folk)/five(nt:jazz)/six
+create path (nt:x) /seven/eight/nine
diff --git a/src/test/resources/testcases/test-3-output.txt b/src/test/resources/testcases/test-3-output.txt
new file mode 100644
index 0000000..0178953
--- /dev/null
+++ b/src/test/resources/testcases/test-3-output.txt
@@ -0,0 +1 @@
+CreateServiceUser comments_test_passed
diff --git a/src/test/resources/testcases/test-3.txt b/src/test/resources/testcases/test-3.txt
new file mode 100644
index 0000000..49f1c07
--- /dev/null
+++ b/src/test/resources/testcases/test-3.txt
@@ -0,0 +1,10 @@
+#
+# single-word
+# We're testing the comments now
+# This is A COMMENT with other things like 12, 34
+# And now for a tag, <ok> ?
+# And some punctuation: .,;-_[]+"*ç%&/()=?^`"
+   # Also with leading whitespace.
+
+# blank lines work, of course   
+create service user comments_test_passed
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-30-output.txt b/src/test/resources/testcases/test-30-output.txt
new file mode 100644
index 0000000..e619a2f
--- /dev/null
+++ b/src/test/resources/testcases/test-30-output.txt
@@ -0,0 +1,6 @@
+SetAclPrincipals for user1 u2 
+  AclLine REMOVE_ALL {paths=[/libs, /apps]}
+  AclLine ALLOW {paths=[/content], privileges=[jcr:read]}
+  AclLine DENY {paths=[/apps], privileges=[jcr:write]}
+  AclLine DENY {nodetypes=[sling:Folder, nt:unstructured], paths=[/apps, /content], privileges=[jcr:lockManagement]}
+  AclLine REMOVE {paths=[/apps], privileges=[jcr:understand, some:other]}
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-30.txt b/src/test/resources/testcases/test-30.txt
new file mode 100644
index 0000000..c70f928
--- /dev/null
+++ b/src/test/resources/testcases/test-30.txt
@@ -0,0 +1,12 @@
+# Test the principal-centered ACL syntax
+
+set ACL for user1,u2
+    remove * on /libs,/apps
+    allow jcr:read on /content
+
+    deny jcr:write on /apps
+    
+    # Optional nodetypes clause 
+    deny jcr:lockManagement on /apps, /content nodetypes sling:Folder, nt:unstructured   
+    remove jcr:understand,some:other on /apps
+end
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-99-output.txt b/src/test/resources/testcases/test-99-output.txt
new file mode 100644
index 0000000..2761b1b
--- /dev/null
+++ b/src/test/resources/testcases/test-99-output.txt
@@ -0,0 +1,19 @@
+CreateServiceUser user1
+CreateServiceUser u-ser_2
+SetAclPaths on /libs /apps 
+  AclLine REMOVE_ALL {principals=[user1, u-ser_2]}
+  AclLine ALLOW {principals=[user1, u-ser_2], privileges=[jcr:read]}
+  AclLine DENY {principals=[u-ser_2], privileges=[jcr:write]}
+  AclLine DENY {principals=[user1], privileges=[jcr:lockManagement]}
+  AclLine REMOVE {principals=[u3], privileges=[jcr:understand, some:other]}
+CreateServiceUser bob_the_service
+SetAclPaths on /tmp 
+  AclLine ALLOW {principals=[bob_the_service], privileges=[some:otherPrivilege]}
+CreatePath [content, example.com(sling:Folder)]
+CreatePath [var(nt:unstructured)]
+SetAclPrincipals for alice bob fred 
+  AclLine REMOVE_ALL {paths=[/]}
+  AclLine ALLOW {paths=[/content, /var], privileges=[jcr:read]}
+  AclLine DENY {paths=[/content/example.com], privileges=[jcr:write]}
+  AclLine DENY {nodetypes=[example:Page], paths=[/], privileges=[jcr:all]}
+CreateServiceUser the-last-one
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-99.txt b/src/test/resources/testcases/test-99.txt
new file mode 100644
index 0000000..df013a8
--- /dev/null
+++ b/src/test/resources/testcases/test-99.txt
@@ -0,0 +1,31 @@
+# Complete example with several intermixed statement types
+
+create service user user1, u-ser_2
+set ACL on /libs,/apps
+    remove * for user1,u-ser_2
+    allow jcr:read for user1,u-ser_2
+
+    deny jcr:write for u-ser_2
+    deny jcr:lockManagement for user1
+    remove jcr:understand,some:other for u3
+end
+
+create service user bob_the_service
+
+# Verify that indent is not required
+set ACL on /tmp
+allow some:otherPrivilege for bob_the_service
+end
+
+create path /content/example.com(sling:Folder)
+create path (nt:unstructured) /var
+
+set ACL for alice, bob,fred
+    remove * on /
+    allow jcr:read on /content,/var
+    deny jcr:write on /content/example.com
+    deny jcr:all on / nodetypes example:Page
+    
+end
+
+create service user the-last-one
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.