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