View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.grammar;
21  
22  import java.util.ArrayDeque;
23  import java.util.Deque;
24  
25  import org.antlr.v4.runtime.Lexer;
26  
27  import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageLexer;
28  
29  /**
30   * This class is used to keep track of the lexer context to help us determine
31   * when to switch lexer modes.
32   */
33  public final class CompositeLexerContextCache {
34  
35      /** Stack for tracking string template contexts. */
36      private final Deque<StringTemplateContext> stringTemplateContextStack;
37  
38      /** The lexer to use. */
39      private final Lexer lexer;
40  
41      /**
42       * Creates a new CompositeLexerContextCache instance.
43       *
44       * @param lexer the lexer to use
45       */
46      public CompositeLexerContextCache(Lexer lexer) {
47          stringTemplateContextStack = new ArrayDeque<>();
48          this.lexer = lexer;
49      }
50  
51      /**
52       * Enter a string template context.
53       *
54       * @param mode the lexer mode to enter
55       */
56      public void enterTemplateContext(int mode) {
57          final StringTemplateContext newContext =
58                  new StringTemplateContext(mode, 0);
59          stringTemplateContextStack.push(newContext);
60          lexer.pushMode(mode);
61      }
62  
63      /**
64       * Exit a string template context.
65       */
66      public void exitTemplateContext() {
67          stringTemplateContextStack.pop();
68          lexer.popMode();
69      }
70  
71      /**
72       * Update the left curly brace context if we are in a string template.
73       */
74      public void updateLeftCurlyBraceContext() {
75          if (isInStringTemplateContext()) {
76              final StringTemplateContext currentContext = stringTemplateContextStack.pop();
77              final StringTemplateContext newContext = new StringTemplateContext(
78                      currentContext.getMode(),
79                      currentContext.getCurlyBraceDepth() + 1
80              );
81              stringTemplateContextStack.push(newContext);
82          }
83      }
84  
85      /**
86       * Update the right curly brace context if we are in a string template.
87       */
88      public void updateRightCurlyBraceContext() {
89          if (isInStringTemplateContext()) {
90              final StringTemplateContext currentContext = stringTemplateContextStack.pop();
91              if (currentContext.getCurlyBraceDepth() == 0) {
92                  // This right curly brace is the start delimiter
93                  // of a template middle or end. We consume
94                  // the right curly brace to be used as the first token
95                  // in the appropriate lexer mode rule, enter
96                  // the corresponding lexer mode, and keep consuming
97                  // the rest of the template middle or end.
98                  stringTemplateContextStack.push(currentContext);
99                  lexer.setType(JavaLanguageLexer.EMBEDDED_EXPRESSION_END);
100                 lexer.pushMode(currentContext.getMode());
101             }
102             else {
103                 // We've consumed a right curly brace within an embedded expression.
104                 final StringTemplateContext newContext = new StringTemplateContext(
105                         currentContext.getMode(),
106                         currentContext.getCurlyBraceDepth() - 1
107                 );
108                 stringTemplateContextStack.push(newContext);
109             }
110         }
111     }
112 
113     /**
114      * Check if we are in a string template context.
115      *
116      * @return true if we are in a string template context
117      */
118     private boolean isInStringTemplateContext() {
119         return !stringTemplateContextStack.isEmpty();
120     }
121 
122     /**
123      * A class to represent the context of a string template.
124      */
125     private static final class StringTemplateContext {
126 
127         /** The lexer mode of this context. */
128         private final int mode;
129 
130         /** The depth of this context. */
131         private final int curlyBraceDepth;
132 
133         /**
134          * Creates a new TemplateContext instance.
135          *
136          * @param mode the lexer mode of this context
137          * @param curlyBraceDepth the depth of this context
138          */
139         private StringTemplateContext(int mode, int curlyBraceDepth) {
140             this.mode = mode;
141             this.curlyBraceDepth = curlyBraceDepth;
142         }
143 
144         /**
145          * Get the lexer mode of this context.
146          *
147          * @return current lexer mode
148          */
149         public int getMode() {
150             return mode;
151         }
152 
153         /**
154          * Current depth of this context.
155          *
156          * @return current depth
157          */
158         public int getCurlyBraceDepth() {
159             return curlyBraceDepth;
160         }
161     }
162 
163 }