View Javadoc

1   package org.apache.jsieve;
2   
3   import java.util.ArrayList;
4   import java.util.HashSet;
5   import java.util.List;
6   import java.util.Set;
7   
8   import org.apache.jsieve.exception.LookupException;
9   import org.apache.jsieve.exception.SieveException;
10  import org.apache.jsieve.parser.generated.ASTargument;
11  import org.apache.jsieve.parser.generated.ASTarguments;
12  import org.apache.jsieve.parser.generated.ASTblock;
13  import org.apache.jsieve.parser.generated.ASTcommand;
14  import org.apache.jsieve.parser.generated.ASTcommands;
15  import org.apache.jsieve.parser.generated.ASTstart;
16  import org.apache.jsieve.parser.generated.ASTstring;
17  import org.apache.jsieve.parser.generated.ASTstring_list;
18  import org.apache.jsieve.parser.generated.ASTtest;
19  import org.apache.jsieve.parser.generated.ASTtest_list;
20  import org.apache.jsieve.parser.generated.SieveParserVisitor;
21  import org.apache.jsieve.parser.generated.SimpleNode;
22  
23  import static org.apache.jsieve.Constants.*;
24  
25  /****************************************************************
26   * Licensed to the Apache Software Foundation (ASF) under one   *
27   * or more contributor license agreements.  See the NOTICE file *
28   * distributed with this work for additional information        *
29   * regarding copyright ownership.  The ASF licenses this file   *
30   * to you under the Apache License, Version 2.0 (the            *
31   * "License"); you may not use this file except in compliance   *
32   * with the License.  You may obtain a copy of the License at   *
33   *                                                              *
34   *   http://www.apache.org/licenses/LICENSE-2.0                 *
35   *                                                              *
36   * Unless required by applicable law or agreed to in writing,   *
37   * software distributed under the License is distributed on an  *
38   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
39   * KIND, either express or implied.  See the License for the    *
40   * specific language governing permissions and limitations      *
41   * under the License.                                           *
42   ****************************************************************/
43  
44  /**
45   * Validates nodes visited. Some checks are more conveniently carried out what
46   * then tree has already been constructed.
47   */
48  public class SieveValidationVisitor implements SieveParserVisitor {
49  
50      private final CommandManager commandManager;
51      private final TestManager testManager;
52      private final ComparatorManager comparatorManager;
53  
54      private final Set<String> declaredComparators;
55      
56      private boolean requireAllowed = true;
57      /** Is the visitor within a <code>require</code>? */
58      private boolean isInRequire = false;
59      /** Is the next argument expected to be a comparator name? */
60      private boolean nextArgumentIsComparatorName = false;
61      /** Is the visitor within a comparator name argument? */
62      private boolean isInComparatorNameArgument = false;
63      
64      protected SieveValidationVisitor(final CommandManager commandManager,
65              final TestManager testManager, final ComparatorManager comparatorManager) {
66          super();
67          this.commandManager = commandManager;
68          this.testManager = testManager;
69          this.comparatorManager = comparatorManager;
70          declaredComparators = new HashSet<String>();
71      }
72  
73      public Object visit(SimpleNode node, Object data) throws SieveException {
74          return visitNode(node, data);
75      }
76  
77      private Object visitNode(SimpleNode node, Object data)
78              throws SieveException {
79          List children = new ArrayList(node.jjtGetNumChildren());
80          node.childrenAccept(this, children);
81          return data;
82      }
83  
84      public Object visit(ASTstart node, Object data) throws SieveException {
85          return visitNode(node, data);
86      }
87  
88      public Object visit(ASTcommands node, Object data) throws SieveException {
89          declaredComparators.clear();
90          return visitNode(node, data);
91      }
92  
93      public Object visit(ASTcommand node, Object data) throws SieveException {
94          final String name = node.getName();
95          commandManager.getCommand(name);
96          if ("require".equalsIgnoreCase(name)) {
97              if (requireAllowed) {
98                  isInRequire = true;
99              } else {
100                 throw new SieveException(
101                         "'require' is only allowed before other commands");
102             }
103         } else {
104             requireAllowed = false;
105             isInRequire = false;
106         }
107         return visitNode(node, data);
108     }
109 
110     public Object visit(ASTblock node, Object data) throws SieveException {
111         return visitNode(node, data);
112     }
113 
114     public Object visit(ASTarguments node, Object data) throws SieveException {
115         // Reset test for explicitly required comparator types
116         nextArgumentIsComparatorName = false;
117         return visitNode(node, data);
118     }
119 
120     public Object visit(ASTargument node, Object data) throws SieveException {
121         final Object value = node.getValue();
122         if (value == null) { 
123             if (nextArgumentIsComparatorName) {
124                 // Mark enclosed string for check against required list
125                 isInComparatorNameArgument = true;
126             }
127             nextArgumentIsComparatorName = false;
128         } else {
129             if (value instanceof TagArgument) {
130                 final TagArgument tag = (TagArgument) value;
131                 nextArgumentIsComparatorName = tag.isComparator();
132             } else {
133                 nextArgumentIsComparatorName = false;
134             }
135         }
136         final Object result = visitNode(node, data);
137         isInComparatorNameArgument = false;
138         return result;
139     }
140 
141     public Object visit(ASTtest node, Object data) throws SieveException {
142         return visitNode(node, data);
143     }
144 
145     public Object visit(ASTtest_list node, Object data) throws SieveException {
146         return visitNode(node, data);
147     }
148 
149     public Object visit(ASTstring node, Object data) throws SieveException {
150         if (isInRequire) {
151             requirements(node);
152         }
153         if (isInComparatorNameArgument) {
154             comparatorNameArgument(node);
155         }
156         return visitNode(node, data);
157     }
158 
159     private void comparatorNameArgument(ASTstring node) throws SieveException {
160         final Object value = node.getValue();
161         if (value != null && value instanceof String) {
162             final String name = (String) value;
163             // Comparators must either be declared (either implicitly or explicitly)
164             if (!comparatorManager.isImplicitlyDeclared(name)) {
165                 if (!declaredComparators.contains(name)) {
166                     // TODO: replace with better exception
167                     throw new SieveException("Comparator must be explicitly declared in a require statement.");
168                 }
169             }
170         }
171     }
172 
173     private void requirements(ASTstring node) throws SieveException {
174         final Object value = node.getValue();
175         if (value != null && value instanceof String) {
176             final String name = (String) value;
177             if (name.startsWith(COMPARATOR_PREFIX)) {
178                 final String comparatorName = name.substring(COMPARATOR_PREFIX_LENGTH);
179                 if (comparatorManager.isSupported(comparatorName)) {
180                     declaredComparators.add(comparatorName);
181                 } else {
182 //                  TODO: Replace with more finely grained exception
183                     throw new SieveException("Comparator " + comparatorName + " is not supported");
184                 }
185             } else {
186                 try {
187                     commandManager.getCommand(name);
188                 } catch (LookupException e) {
189                     // TODO: catching is inefficient, should just check
190                     testManager.getTest(name);
191                 }
192             }
193         }
194     }
195 
196     public Object visit(ASTstring_list node, Object data) throws SieveException {
197         return visitNode(node, data);
198     }
199 
200 }