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