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    *      http://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.internal;
18  
19  import java.util.ArrayDeque;
20  import java.util.Deque;
21  
22  /**
23   * The set of valued symbols defined in a lexical frame.
24   * <p>The symbol identifiers are determined by the functional scope.
25   */
26  public class LexicalFrame extends LexicalScope {
27      /**
28       * The script frame.
29       */
30      private final Frame frame;
31      /**
32       * Previous frame.
33       */
34      protected final LexicalFrame previous;
35      /**
36       * The stack of values in the lexical frame.
37       */
38      private Deque<Object> stack;
39  
40      /**
41       * Lexical frame ctor.
42       *
43       * @param scriptf the script frame
44       * @param outerf  the previous lexical frame
45       */
46      public LexicalFrame(final Frame scriptf, final LexicalFrame outerf) {
47          this.previous = outerf;
48          this.frame = scriptf;
49      }
50  
51      /**
52       * Copy ctor.
53       *
54       * @param src the frame to copy
55       */
56      public LexicalFrame(final LexicalFrame src) {
57          super(src);
58          frame = src.frame;
59          previous = src.previous;
60          stack = src.stack != null ? new ArrayDeque<>(src.stack) : null;
61      }
62  
63      /**
64       * Define the arguments.
65       *
66       * @return this frame
67       */
68      public LexicalFrame defineArgs() {
69          if (frame != null) {
70              final int argc = frame.getScope().getArgCount();
71              for (int a = 0; a < argc; ++a) {
72                  super.addSymbol(a);
73              }
74          }
75          return this;
76      }
77  
78      /**
79       * Defines a symbol.
80       *
81       * @param symbol  the symbol to define
82       * @param capture whether this redefines a captured symbol
83       * @return true if symbol is defined, false otherwise
84       */
85      public boolean defineSymbol(final int symbol, final boolean capture) {
86          final boolean declared = addSymbol(symbol);
87          if (declared && capture) {
88              if (stack == null) {
89                  stack = new ArrayDeque<>();
90              }
91              stack.push(symbol);
92              Object value = frame.get(symbol);
93              if (value == null) {
94                  value = this;
95              }
96              stack.push(value);
97          }
98          return declared;
99      }
100 
101     /**
102      * Pops back values and lexical frame.
103      *
104      * @return the previous frame
105      */
106     public LexicalFrame pop() {
107         // undefine all symbols
108         clearSymbols(s ->   frame.set(s, Scope.UNDEFINED) );
109         // restore values of captured symbols that were overwritten
110         if (stack != null) {
111             while (!stack.isEmpty()) {
112                 Object value = stack.pop();
113                 if (value == Scope.UNDECLARED) {
114                     value = Scope.UNDEFINED;
115                 } else if (value == this) {
116                     value = null;
117                 }
118                 final int symbol = (Integer) stack.pop();
119                 frame.set(symbol, value);
120             }
121         }
122         return previous;
123     }
124 
125 }