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