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 java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.LinkedHashMap;
22  import java.util.List;
23  import java.util.Map;
24  
25  public class ASTSwitchStatement extends JexlNode {
26    /**
27     * Whether this switch is a statement (true) or an expression (false).
28     */
29    protected boolean isStatement = true;
30    /**
31     * The map of cases, where the key is the case value and the value is the switch index.
32     */
33    protected Map<Object, Integer> cases = Collections.emptyMap();
34  
35    public ASTSwitchStatement(final int id) {
36      super(id);
37    }
38  
39    @Override
40    public Object jjtAccept(final ParserVisitor visitor, final Object data) {
41      return visitor.visit(this, data);
42    }
43  
44    /**
45     * Returns the array of cases values.
46     * <p>Meant only for verification during tests.</p>
47     * The list at each index contains the case values for that index.
48     * If the values-list is empty for an index, it is the default case.
49     *
50     * @return an array of case values
51     */
52    public List<Object>[] getCasesList() {
53      @SuppressWarnings("unchecked")
54      final List<Object>[] list = new List[jjtGetNumChildren() -1];
55      for (final Map.Entry<Object, Integer> entry : cases.entrySet()) {
56        final int index = entry.getValue();
57        if (index < 0 || index >= list.length) {
58          throw new IndexOutOfBoundsException("switch index out of bounds: " + index);
59        }
60        List<Object> values = list[index];
61        if (values == null) {
62          list[index] = values = new ArrayList<>();
63        }
64        values.add(entry.getValue());
65      }
66      return list;
67    }
68  
69    public boolean isStatement() {
70      return isStatement;
71    }
72  
73    public int switchIndex(final Object value) {
74      final Object code = JexlParser.switchCode(value);
75      Integer index = cases.get(code);
76      if (index == null) {
77        index = cases.get(JexlParser.DFLT);
78      }
79      if (index != null && index >= 1 && index < jjtGetNumChildren()) {
80        return index; // index is 1-based, children are 0-based
81      }
82      return -1;
83    }
84  
85    /**
86     * Helper for switch statements.
87     * <p>It detects duplicates cases and default.</p>
88     */
89    public static class Helper {
90      private int switchIndex = 1; // switch index, starts at 1 since the first child is the switch expression
91      private boolean defaultDefined;
92      private final Map<Object, Integer> dispatch = new LinkedHashMap<>();
93  
94      void defineCase(final JexlParser.SwitchSet switchSet) throws ParseException {
95        if (switchSet.isEmpty()) {
96          if (defaultDefined) {
97            throw new ParseException("default clause is already defined");
98          }
99          defaultDefined = true;
100         dispatch.put(JexlParser.DFLT, switchIndex);
101       } else {
102         for (final Object constant : switchSet) {
103           if (dispatch.put(constant == null ? JexlParser.NIL : constant, switchIndex) != null) {
104             throw new ParseException("duplicate case in switch statement for value: " + constant);
105           }
106         }
107         switchSet.clear();
108       }
109       switchIndex += 1;
110     }
111 
112     void defineSwitch(final ASTSwitchStatement statement) {
113       statement.cases = dispatch;
114     }
115   }
116 
117 }