SLING-6422 - support JCR style restrictions in the repoinit language. Contributed by Nitin Nizhawan , thanks!

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1778001 13f79535-47bb-0310-9956-ffa450edef68
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
index dc4da8a..ad4c85d 100644
--- a/src/main/java/org/apache/sling/repoinit/parser/operations/AclLine.java
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/AclLine.java
@@ -42,6 +42,7 @@
     };
     
     private final Map<String, List<String>> properties;
+    private List<RestrictionClause> restrictions;
     
     public AclLine(Action a) {
         action = a;
@@ -63,9 +64,16 @@
     public void setProperty(String name, List<String> values) {
         properties.put(name, Collections.unmodifiableList(values));
     }
+
+    public void setRestrictions(List<RestrictionClause> restrictions){
+        this.restrictions = restrictions;
+    }
+
+    public List<RestrictionClause> getRestrictions() { return this.restrictions; }
     
     @Override
     public String toString() {
-        return getClass().getSimpleName() + " " + action + " " + properties;
+        return getClass().getSimpleName() + " " + action + " " + properties + (restrictions == null || restrictions.isEmpty() ? "" : " restrictions="+restrictions);
+
     }
 }
diff --git a/src/main/java/org/apache/sling/repoinit/parser/operations/RestrictionClause.java b/src/main/java/org/apache/sling/repoinit/parser/operations/RestrictionClause.java
new file mode 100644
index 0000000..795c7d1
--- /dev/null
+++ b/src/main/java/org/apache/sling/repoinit/parser/operations/RestrictionClause.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+/**
+ * Single restriction(name,values*)
+ */
+public final class RestrictionClause {
+
+    private final String name;
+    private final List<String> values;
+
+    public RestrictionClause(final String name,final List<String> values){
+        this.name = name;
+        this.values = values;
+    }
+
+    public String getName(){
+        return name;
+    }
+
+    public List<String> getValues(){
+        return values;
+    }
+
+    @Override
+    public String toString(){
+        return name + "=" + values;
+    }
+}
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 f7d3600..d261ec4 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("3.0.0")
+@org.osgi.annotation.versioning.Version("3.1.0")
 package org.apache.sling.repoinit.parser.operations;
 
diff --git a/src/main/javacc/RepoInitGrammar.jjt b/src/main/javacc/RepoInitGrammar.jjt
index 930c4a7..eae990c 100644
--- a/src/main/javacc/RepoInitGrammar.jjt
+++ b/src/main/javacc/RepoInitGrammar.jjt
@@ -78,11 +78,12 @@
 |   < RCURLY: "}" >
 |   < COMMA: "," >
 |   < STAR: "*" >
+|   < RESTRICTION: "restriction" >
 
 /* The order of these fuzzy statements is important (first match wins?) */ 
 |   < NAMESPACED_ITEM: (["a"-"z"] | ["A"-"Z"])+ ":" (["a"-"z"] | ["A"-"Z"])+ >
 |   < PATH_STRING: "/" (["a"-"z"] | ["A"-"Z"] | ["0"-"9"] | ["-"] | ["_"] | ["."] | ["/"]) * >
-|   < STRING: (["a"-"z"] | ["A"-"Z"] | ["0"-"9"] | ["-"] | ["_"] | ["."] | ["/"] | [":"]) * >
+|   < STRING: (["a"-"z"] | ["A"-"Z"] | ["0"-"9"] | ["-"] | ["_"] | ["."] | ["/"] | [":"] | ["*"]) * >
 |   < EOL: "\n" >
 }
 
@@ -255,29 +256,95 @@
 {
     AclLine line;
     List<String> tmp;
+    List<RestrictionClause> restrictions;
 }
 {
     line = privilegesLineOperation()
-    tmp = namespacedItemsList() { line.setProperty(AclLine.PROP_PRIVILEGES, tmp); } 
+    tmp = namespacedItemsList() { line.setProperty(AclLine.PROP_PRIVILEGES, tmp); }
     <FOR>
     tmp = principalsList() { line.setProperty(AclLine.PROP_PRINCIPALS, tmp); }
+    restrictions = parseRestrictions()  { line.setRestrictions(restrictions); }
     <EOL>
 
     {   
         lines.add(line); 
     }
 }
+/**
+ * Parses a single restriction value
+ */
+void parseRestrictionValue(List<String> values) :
+{
+   Token t;
+}
+{
+   <COMMA> ( t=<STAR> | t=<NAMESPACED_ITEM> | t=<PATH_STRING> | t=<STRING> )
+   {
+       values.add(t.image);
+   }
+}
+
+/**
+ * parses list of restriction values
+ */
+List<String> parseRestrictionValues() :
+{
+    List<String> values = new ArrayList<String>();
+}
+{
+    ( parseRestrictionValue(values) ) *
+
+    {
+        return values;
+    }
+
+}
+
+
+/**
+ * parses a single restriction
+ */
+void parseRestriction(List<RestrictionClause> restrictions) :
+{
+  Token restrictionProp;
+  List<String> values;
+}
+{
+  <RESTRICTION>
+  <LPAREN> restrictionProp=<NAMESPACED_ITEM> values=parseRestrictionValues()  <RPAREN>
+
+  {
+      restrictions.add(new RestrictionClause(restrictionProp.image,values));
+  }
+}
+
+/**
+ * parses list of restrictions
+ */
+List<RestrictionClause> parseRestrictions() :
+{
+    List<RestrictionClause> restrictions = new ArrayList<RestrictionClause>();
+}
+{
+   ( parseRestriction(restrictions) )*
+
+   {
+       return restrictions;
+   }
+}
 
 void pathPrivilegesLine(List<AclLine> lines) : 
 {
     AclLine line;
     List<String> tmp;
+    List<RestrictionClause> restrictions;
 }
 {
     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); }) ?
+     restrictions = parseRestrictions()  { line.setRestrictions(restrictions); }
     <EOL>
     
     {    
@@ -371,4 +438,4 @@
     {
         result.add(new DeleteUser(user.image));
     }
-}
\ 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
index c9106ac..67053a0 100644
--- a/src/test/resources/testcases/test-10-output.txt
+++ b/src/test/resources/testcases/test-10-output.txt
@@ -2,4 +2,5 @@
   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
+  AclLine DENY {principals=[user1], privileges=[jcr:lockManagement]}
+  AclLine DENY {principals=[user2], privileges=[jcr:modifyProperties]} restrictions=[rep:itemNames=[prop1, prop2]]
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-10.txt b/src/test/resources/testcases/test-10.txt
index db91e9f..479685b 100644
--- a/src/test/resources/testcases/test-10.txt
+++ b/src/test/resources/testcases/test-10.txt
@@ -7,4 +7,5 @@
 
     deny jcr:write,something:else,another:one for user2
     deny jcr:lockManagement for user1
+    deny jcr:modifyProperties for user2 restriction(rep:itemNames,prop1,prop2)
 end
\ 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
index e619a2f..4f5ecdb 100644
--- a/src/test/resources/testcases/test-30-output.txt
+++ b/src/test/resources/testcases/test-30-output.txt
@@ -3,4 +3,10 @@
   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
+  AclLine DENY {nodetypes=[sling:Folder, nt:unstructured], paths=[/apps, /content], privileges=[jcr:modifyProperties]} restrictions=[rep:itemNames=[prop1, prop2]]
+  AclLine REMOVE {paths=[/apps], privileges=[jcr:understand, some:other]}
+  AclLine ALLOW {paths=[/apps], privileges=[jcr:addChildNodes]} restrictions=[rep:ntNames=[sling:Folder, nt:unstructured]]
+  AclLine ALLOW {paths=[/apps], privileges=[jcr:modifyProperties]} restrictions=[rep:ntNames=[sling:Folder, nt:unstructured], rep:itemNames=[prop1, prop2]]
+  AclLine ALLOW {paths=[/apps, /content], privileges=[jcr:addChildNodes]} restrictions=[rep:glob=[/cat, /cat/, cat]]
+  AclLine ALLOW {paths=[/apps, /content], privileges=[jcr:addChildNodes]} restrictions=[rep:glob=[cat/, *, *cat]]
+  AclLine ALLOW {paths=[/apps, /content], privileges=[jcr:addChildNodes]} restrictions=[rep:glob=[/cat/*, */cat, *cat/*]]
\ No newline at end of file
diff --git a/src/test/resources/testcases/test-30.txt b/src/test/resources/testcases/test-30.txt
index c70f928..340b47c 100644
--- a/src/test/resources/testcases/test-30.txt
+++ b/src/test/resources/testcases/test-30.txt
@@ -6,7 +6,20 @@
 
     deny jcr:write on /apps
     
-    # Optional nodetypes clause 
-    deny jcr:lockManagement on /apps, /content nodetypes sling:Folder, nt:unstructured   
+    # Optional nodetypes clause
+    deny jcr:lockManagement on /apps, /content nodetypes sling:Folder, nt:unstructured
+    # nodetypes clause with restriction clause
+    deny jcr:modifyProperties on /apps, /content nodetypes sling:Folder, nt:unstructured restriction(rep:itemNames,prop1,prop2)
     remove jcr:understand,some:other on /apps
+
+    # multi value restriction
+    allow jcr:addChildNodes on /apps restriction(rep:ntNames,sling:Folder,nt:unstructured)
+
+    # multiple restrictions
+    allow jcr:modifyProperties on /apps restriction(rep:ntNames,sling:Folder,nt:unstructured) restriction(rep:itemNames,prop1,prop2)
+
+    # restrictions with glob patterns
+    allow jcr:addChildNodes on /apps,/content restriction(rep:glob,/cat,/cat/,cat)
+    allow jcr:addChildNodes on /apps,/content restriction(rep:glob,cat/,*,*cat)
+    allow jcr:addChildNodes on /apps,/content restriction(rep:glob,/cat/*,*/cat,*cat/*)
 end
\ No newline at end of file