SLING-9171 Support setting node properties via repoinit
diff --git a/pom.xml b/pom.xml
index e3eb852..f96cea4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
   </parent>
 
   <artifactId>org.apache.sling.repoinit.parser</artifactId>
-  <version>1.5.3-SNAPSHOT</version>
+  <version>1.6.1-SNAPSHOT</version>
   <name>Apache Sling Repoinit Parser</name>
   <description>Parser for the Repository Initialization language used in Sling</description>
 
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
index c46e2ce..d793797 100644
--- a/src/main/java/org/apache/sling/repoinit/parser/operations/OperationVisitor.java
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/OperationVisitor.java
@@ -37,4 +37,5 @@
     void visitDisableServiceUser(DisableServiceUser dsu);
     void visitAddGroupMembers(AddGroupMembers am);
     void visitRemoveGroupMembers(RemoveGroupMembers rm);
+    void visitSetProperties(SetProperties sp);
 }
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/PropertyLine.java b/src/main/java/org/apache/sling/repoinit/parser/operations/PropertyLine.java
new file mode 100644
index 0000000..482099e
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/PropertyLine.java
@@ -0,0 +1,64 @@
+/*
+ * 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.List;
+
+/** A single "set property" line */
+public class PropertyLine {
+
+    private final String name;
+    private final String type;
+    private final List<String> values;
+
+    /**
+     * Operation that sets property on a node.
+     *  @param name  name of the property
+     *  @param type   property type
+     *  @param values  values of the property
+     */
+    public PropertyLine(String name, String type, List<String> values) {
+        this.name = name;
+        this.type = type;
+        this.values = values;
+    }
+
+    public String getPropertyName() {return name;};
+
+    public String getPropertyType() {return type;};
+
+    public List<String> getPropertyValues() {
+        return values;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" ");
+        sb.append(name);
+        sb.append("=");
+        if (type != null) {
+            sb.append("{");
+            sb.append(type);
+            sb.append("}");
+        }
+        sb.append(values);
+        return sb.toString();
+    }
+}
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/SetProperties.java b/src/main/java/org/apache/sling/repoinit/parser/operations/SetProperties.java
new file mode 100644
index 0000000..e0f4ba8
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/SetProperties.java
@@ -0,0 +1,53 @@
+/*
+ * 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.List;
+
+public class SetProperties extends Operation {
+    private final String nodePath;
+    private final List<PropertyLine> lines;
+
+    public SetProperties(String nodePath, List<PropertyLine> lines) {
+        this.nodePath = nodePath;
+        this.lines = lines;
+    }
+
+    @Override
+    public void accept(OperationVisitor v) {
+        v.visitSetProperties(this);
+    }
+
+    @Override
+    protected String getParametersDescription() {
+        final StringBuilder sb = new StringBuilder();
+        for(PropertyLine line : lines) {
+            sb.append("\n  ").append(line.toString());
+        }
+        return sb.toString();
+    }
+
+    public String getNodePath() {
+        return nodePath;
+    }
+
+    public List<PropertyLine> getPropertyLines () {return lines;};
+
+
+}
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
index bc04ed8..ca8c688 100644
--- 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
@@ -15,6 +15,6 @@
  * limitations under the License.
  ******************************************************************************/
 
-@org.osgi.annotation.versioning.Version("4.5.0")
+@org.osgi.annotation.versioning.Version("4.6.0")
 package org.apache.sling.repoinit.parser.operations;
 
diff --git a/src/main/javacc/RepoInitGrammar.jjt b/src/main/javacc/RepoInitGrammar.jjt
index 0e94eac..48fcd41 100644
--- a/src/main/javacc/RepoInitGrammar.jjt
+++ b/src/main/javacc/RepoInitGrammar.jjt
@@ -94,6 +94,7 @@
 |   < EQUALS: "=" >
 |   < RESTRICTION: "restriction" >
 |   < ACL_OPTIONS: "ACLOptions" >
+|   < PROPERTIES: "properties" >
 
 /* The order of these fuzzy statements is important (first match wins?) */ 
 |   < NAMESPACED_ITEM: (["a"-"z"] | ["A"-"Z"])+ ":" (["a"-"z"] | ["A"-"Z"])+ >
@@ -149,6 +150,7 @@
         | disableServiceUserStatement(result)
         | addToGroupStatement(result)
         | removeFromGroupStatement(result)
+        | setPropertiesStatement(result)
         | blankLine() 
     ) * 
     <EOF>
@@ -716,3 +718,45 @@
         result.add(new RemoveGroupMembers(members, group.image));
     }
 }
+
+List<String> valuesList() :
+{
+    Token t = null;
+    List<String> values = new ArrayList<String>();
+}
+{
+    (t = <STRING> | t = <PATH_STRING>) { values.add(t.image); }
+    ( <COMMA> t = <STRING> { values.add(t.image); } )*
+    { return values; }
+}
+
+void propertyLine(List<PropertyLine> lines) :
+{
+    Token name = null;
+    Token type = null;
+    List<String> values;
+}
+{
+    ( name = <STRING> | name = <NAMESPACED_ITEM>)
+    ( <LCURLY> (type = <STRING>) <RCURLY> )?
+    <LPAREN> values = valuesList() <RPAREN>
+    <EOL>
+    {
+        lines.add(new PropertyLine(name.image,  type == null ? null : type.image, values));
+    }
+}
+
+void setPropertiesStatement(List<Operation> result) :
+{
+    List<PropertyLine> lines = new ArrayList<PropertyLine>();
+    Token t = null;
+}
+{
+    <SET> <PROPERTIES> <ON>  ( t = <PATH_STRING> ) <EOL>
+    ( propertyLine(lines) | blankLine() ) +
+    <END>
+    ( <EOL> | <EOF> )
+    {
+        result.add(new SetProperties(t.image, 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
index 88f4a04..cfd9391 100644
--- a/src/test/java/org/apache/sling/repoinit/parser/test/OperationToStringVisitor.java
+++ b/src/test/java/org/apache/sling/repoinit/parser/test/OperationToStringVisitor.java
@@ -39,6 +39,8 @@
 import org.apache.sling.repoinit.parser.operations.SetAclPrincipals;
 import org.apache.sling.repoinit.parser.operations.AddGroupMembers;
 import org.apache.sling.repoinit.parser.operations.RemoveGroupMembers;
+import org.apache.sling.repoinit.parser.operations.SetProperties;
+import org.apache.sling.repoinit.parser.operations.PropertyLine;
 
 /** OperationVisitor that dumps the operations using
  *  their toString() methods
@@ -199,4 +201,18 @@
         out.print(rm.getGroupname());
         out.println();
     }
+
+    @Override
+    public void visitSetProperties(SetProperties sp) {
+        System.out.println("nitin: ");
+        out.print(sp.getClass().getSimpleName());
+        out.print(" on ");
+        out.print(sp.getNodePath());
+        out.println();
+        List<PropertyLine> lines =  sp.getPropertyLines();
+        for(PropertyLine p : lines) {
+            out.print("  ");
+            out.println(p);
+        }
+    }
 }
diff --git a/src/test/resources/testcases/test-67-output.txt b/src/test/resources/testcases/test-67-output.txt
new file mode 100644
index 0000000..caa4a5e
--- /dev/null
+++ b/src/test/resources/testcases/test-67-output.txt
@@ -0,0 +1,6 @@
+SetProperties on /a/b/c
+  PropertyLine sling:ResourceType={String}[/x/y/z]
+  PropertyLine cq:allowedTemplates={String}[/d/e/f/*, m/n/*]
+  PropertyLine customSingleValueStringProp=[test]
+  PropertyLine customMultiValueStringProp=[test1, test2]
+
diff --git a/src/test/resources/testcases/test-67.txt b/src/test/resources/testcases/test-67.txt
new file mode 100644
index 0000000..ed7fb57
--- /dev/null
+++ b/src/test/resources/testcases/test-67.txt
@@ -0,0 +1,7 @@
+# Test set property statements
+set properties on /a/b/c
+    sling:ResourceType{String}(/x/y/z)
+    cq:allowedTemplates{String}(/d/e/f/*, m/n/*)
+    customSingleValueStringProp(test)
+    customMultiValueStringProp(test1, test2)
+end
\ No newline at end of file