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