View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl3.parser;
18  
19  import org.apache.commons.jexl3.JexlException;
20  import org.apache.commons.jexl3.JexlFeatures;
21  import org.apache.commons.jexl3.JexlInfo;
22  import org.apache.commons.jexl3.internal.Debugger;
23  import org.apache.commons.jexl3.internal.ScriptVisitor;
24  
25  /**
26   * Controls that a script only uses enabled features.
27   */
28  public class FeatureController extends ScriptVisitor {
29  
30      /** The set of features. */
31      private JexlFeatures features;
32  
33      /**
34       * Creates a feature controller.
35       */
36      public FeatureController(final JexlFeatures features) {
37          this.features = features;
38      }
39  
40      /**
41       * Perform the control on a node.
42       * <p>Note that controlNode() does *not* visit node children in this class.
43       *
44       * @param node the node to controlNode
45       * @throws JexlException.Feature if required feature is disabled
46       */
47      public void controlNode(final JexlNode node) {
48          node.jjtAccept(this, null);
49      }
50  
51      private Object controlSideEffect(final JexlNode node, final Object data) {
52          final JexlNode lv = node.jjtGetChild(0);
53          if (!features.supportsSideEffectGlobal() && lv.isGlobalVar()) {
54              throwFeatureException(JexlFeatures.SIDE_EFFECT_GLOBAL, lv);
55          }
56          if (features.supportsConstCapture() && lv instanceof ASTIdentifier && ((ASTIdentifier) lv).isCaptured()) {
57              throwFeatureException(JexlFeatures.CONST_CAPTURE, lv);
58          }
59          if (!features.supportsSideEffect()) {
60              throwFeatureException(JexlFeatures.SIDE_EFFECT, lv);
61          }
62          return data;
63      }
64  
65      /**
66       * @return the controlled features
67       */
68      public JexlFeatures getFeatures() {
69          return features;
70      }
71  
72      /**
73       * Checks whether a node is a string or an integer.
74       *
75       * @param child the child node
76       * @return true if string / integer, false otherwise
77       */
78      private boolean isArrayReferenceLiteral(final JexlNode child) {
79          if (child instanceof ASTStringLiteral) {
80              return true;
81          }
82          if (child instanceof ASTNumberLiteral && ((ASTNumberLiteral) child).isInteger()) {
83              return true;
84          }
85          return false;
86      }
87  
88      /**
89       * Sets the features to controlNode.
90       *
91       * @param fdesc the features
92       */
93      public void setFeatures(final JexlFeatures fdesc) {
94          this.features = fdesc;
95      }
96  
97      /**
98       * Throws a feature exception.
99       *
100      * @param feature the feature code
101      * @param node    the node that caused it
102      */
103     public void throwFeatureException(final int feature, final JexlNode node) {
104         final JexlInfo dbgInfo = node.jexlInfo();
105         final Debugger dbg = new Debugger().depth(1);
106         final String msg = dbg.data(node);
107         throw new JexlException.Feature(dbgInfo, feature, msg);
108     }
109 
110     @Override
111     protected Object visit(final ASTAnnotation node, final Object data) {
112         if (!features.supportsAnnotation()) {
113             throwFeatureException(JexlFeatures.ANNOTATION, node);
114         }
115         return data;
116     }
117 
118     @Override
119     protected Object visit(final ASTArrayAccess node, final Object data) {
120         if (!features.supportsArrayReferenceExpr()) {
121             for (int i = 0; i < node.jjtGetNumChildren(); ++i) {
122                 final JexlNode child = node.jjtGetChild(i);
123                 if (!isArrayReferenceLiteral(child)) {
124                     throwFeatureException(JexlFeatures.ARRAY_REF_EXPR, child);
125                 }
126             }
127         }
128         return data;
129     }
130 
131     @Override
132     protected Object visit(final ASTArrayLiteral node, final Object data) {
133         if (!features.supportsStructuredLiteral()) {
134             throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node);
135         }
136         return data;
137     }
138 
139     @Override
140     protected Object visit(final ASTAssignment node, final Object data) {
141         return controlSideEffect(node, data);
142     }
143 
144     @Override
145     protected Object visit(final ASTConstructorNode node, final Object data) {
146         if (!features.supportsNewInstance()) {
147             throwFeatureException(JexlFeatures.NEW_INSTANCE, node);
148         }
149         return data;
150     }
151 
152     @Override
153     protected Object visit(final ASTDecrementGetNode node, final Object data) {
154         return controlSideEffect(node, data);
155     }
156 
157     @Override
158     protected Object visit(final ASTDoWhileStatement node, final Object data) {
159         if (!features.supportsLoops()) {
160             throwFeatureException(JexlFeatures.LOOP, node);
161         }
162         return data;
163     }
164 
165     @Override
166     protected Object visit(final ASTForeachStatement node, final Object data) {
167         if (!features.supportsLoops()) {
168             throwFeatureException(JexlFeatures.LOOP, node);
169         }
170         return data;
171     }
172 
173     @Override
174     protected Object visit(final ASTGetDecrementNode node, final Object data) {
175         return controlSideEffect(node, data);
176     }
177 
178     @Override
179     protected Object visit(final ASTGetIncrementNode node, final Object data) {
180         return controlSideEffect(node, data);
181     }
182 
183     @Override
184     protected Object visit(final ASTIncrementGetNode node, final Object data) {
185         return controlSideEffect(node, data);
186     }
187 
188     @Override
189     protected Object visit(final ASTMapLiteral node, final Object data) {
190         if (!features.supportsStructuredLiteral()) {
191             throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node);
192         }
193         return data;
194     }
195 
196     @Override
197     protected Object visit(final ASTMethodNode node, final Object data) {
198         if (!features.supportsMethodCall()) {
199             throwFeatureException(JexlFeatures.METHOD_CALL, node);
200         }
201         return data;
202     }
203 
204     @Override
205     protected Object visit(final ASTRangeNode node, final Object data) {
206         if (!features.supportsStructuredLiteral()) {
207             throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node);
208         }
209         return data;
210     }
211 
212     @Override
213     protected Object visit(final ASTSetAddNode node, final Object data) {
214         return controlSideEffect(node, data);
215     }
216 
217     @Override
218     protected Object visit(final ASTSetAndNode node, final Object data) {
219         return controlSideEffect(node, data);
220     }
221 
222     @Override
223     protected Object visit(final ASTSetDivNode node, final Object data) {
224         return controlSideEffect(node, data);
225     }
226 
227     @Override
228     protected Object visit(final ASTSetLiteral node, final Object data) {
229         if (!features.supportsStructuredLiteral()) {
230             throwFeatureException(JexlFeatures.STRUCTURED_LITERAL, node);
231         }
232         return data;
233     }
234 
235     @Override
236     protected Object visit(final ASTSetModNode node, final Object data) {
237         return controlSideEffect(node, data);
238     }
239 
240     @Override
241     protected Object visit(final ASTSetMultNode node, final Object data) {
242         return controlSideEffect(node, data);
243     }
244 
245     @Override
246     protected Object visit(final ASTSetOrNode node, final Object data) {
247         return controlSideEffect(node, data);
248     }
249 
250     @Override
251     protected Object visit(final ASTSetShiftLeftNode node, final Object data) {
252         return controlSideEffect(node, data);
253     }
254 
255     @Override
256     protected Object visit(final ASTSetShiftRightNode node, final Object data) {
257         return controlSideEffect(node, data);
258     }
259 
260     @Override
261     protected Object visit(final ASTSetShiftRightUnsignedNode node, final Object data) {
262         return controlSideEffect(node, data);
263     }
264 
265     @Override
266     protected Object visit(final ASTSetSubNode node, final Object data) {
267         return controlSideEffect(node, data);
268     }
269 
270     @Override
271     protected Object visit(final ASTSetXorNode node, final Object data) {
272         return controlSideEffect(node, data);
273     }
274 
275     @Override
276     protected Object visit(final ASTTryStatement node, final Object data) {
277 //        if (!features.supportsLoops()) {
278 //            throwFeatureException(JexlFeatures.LOOP, node);
279 //        }
280         return data;
281     }
282 
283     @Override
284     protected Object visit(final ASTWhileStatement node, final Object data) {
285         if (!features.supportsLoops()) {
286             throwFeatureException(JexlFeatures.LOOP, node);
287         }
288         return data;
289     }
290 
291     @Override
292     protected Object visitNode(final JexlNode node, final Object data) {
293         // no need to visit them since we close them one by one
294         return data;
295     }
296 }