1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.checks.indentation;
21  
22  import java.util.BitSet;
23  
24  /**
25   * Encapsulates representation of notion of expected indentation levels.
26   * Provide a way to have multiple acceptable levels.
27   * This class is immutable.
28   */
29  public class IndentLevel {
30  
31      /** Set of acceptable indentation levels. */
32      private final BitSet levels = new BitSet();
33  
34      /**
35       * Creates new instance with one acceptable indentation level.
36       * @param indent acceptable indentation level.
37       */
38      public IndentLevel(int indent) {
39          levels.set(indent);
40      }
41  
42      /**
43       * Creates new instance for nested structure.
44       * @param base parent's level
45       * @param offsets offsets from parent's level.
46       */
47      public IndentLevel(IndentLevel base, int... offsets) {
48          final BitSet src = base.levels;
49          for (int i = src.nextSetBit(0); i >= 0; i = src.nextSetBit(i + 1)) {
50              for (int offset : offsets) {
51                  levels.set(i + offset);
52              }
53          }
54      }
55  
56      /**
57       * Creates new instance with no acceptable indentation level.
58       * This is only used internally to combine multiple levels.
59       */
60      private IndentLevel() {
61      }
62  
63      /**
64       * Checks whether we have more than one level.
65       * @return whether we have more than one level.
66       */
67      public final boolean isMultiLevel() {
68          return levels.cardinality() > 1;
69      }
70  
71      /**
72       * Checks if given indentation is acceptable.
73       * @param indent indentation to check.
74       * @return true if given indentation is acceptable,
75       *         false otherwise.
76       */
77      public boolean isAcceptable(int indent) {
78          return levels.get(indent);
79      }
80  
81      /**
82       * Returns true if indent less than minimal of
83       * acceptable indentation levels, false otherwise.
84       * @param indent indentation to check.
85       * @return true if {@code indent} less than minimal of
86       *         acceptable indentation levels, false otherwise.
87       */
88      public boolean isGreaterThan(int indent) {
89          return levels.nextSetBit(0) > indent;
90      }
91  
92      /**
93       * Adds one or more acceptable indentation level.
94       * @param base class to add new indentations to.
95       * @param additions new acceptable indentation.
96       * @return New acceptable indentation level instance.
97       */
98      public static IndentLevel addAcceptable(IndentLevel base, int... additions) {
99          final IndentLevel result = new IndentLevel();
100         result.levels.or(base.levels);
101         for (int addition : additions) {
102             result.levels.set(addition);
103         }
104         return result;
105     }
106 
107     /**
108      * Combines 2 acceptable indentation level classes.
109      * @param base class to add new indentations to.
110      * @param addition new acceptable indentation.
111      * @return New acceptable indentation level instance.
112      */
113     public static IndentLevel addAcceptable(IndentLevel base, IndentLevel addition) {
114         final IndentLevel result = new IndentLevel();
115         result.levels.or(base.levels);
116         result.levels.or(addition.levels);
117         return result;
118     }
119 
120     /**
121      * Returns first indentation level.
122      * @return indentation level.
123      */
124     public int getFirstIndentLevel() {
125         return levels.nextSetBit(0);
126     }
127 
128     /**
129      * Returns last indentation level.
130      * @return indentation level.
131      */
132     public int getLastIndentLevel() {
133         return levels.length() - 1;
134     }
135 
136     @Override
137     public String toString() {
138         final String result;
139         if (levels.cardinality() == 1) {
140             result = String.valueOf(levels.nextSetBit(0));
141         }
142         else {
143             final StringBuilder sb = new StringBuilder(50);
144             for (int i = levels.nextSetBit(0); i >= 0;
145                  i = levels.nextSetBit(i + 1)) {
146                 if (sb.length() > 0) {
147                     sb.append(", ");
148                 }
149                 sb.append(i);
150             }
151             result = sb.toString();
152         }
153         return result;
154     }
155 
156 }