001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.indentation;
021
022import java.util.BitSet;
023
024/**
025 * Encapsulates representation of notion of expected indentation levels.
026 * Provide a way to have multiple acceptable levels.
027 * This class is immutable.
028 */
029public class IndentLevel {
030
031    /** Set of acceptable indentation levels. */
032    private final BitSet levels = new BitSet();
033
034    /**
035     * Creates new instance with one acceptable indentation level.
036     * @param indent acceptable indentation level.
037     */
038    public IndentLevel(int indent) {
039        levels.set(indent);
040    }
041
042    /**
043     * Creates new instance for nested structure.
044     * @param base parent's level
045     * @param offsets offsets from parent's level.
046     */
047    public IndentLevel(IndentLevel base, int... offsets) {
048        final BitSet src = base.levels;
049        for (int i = src.nextSetBit(0); i >= 0; i = src.nextSetBit(i + 1)) {
050            for (int offset : offsets) {
051                levels.set(i + offset);
052            }
053        }
054    }
055
056    /**
057     * Creates new instance with no acceptable indentation level.
058     * This is only used internally to combine multiple levels.
059     */
060    private IndentLevel() {
061    }
062
063    /**
064     * Checks whether we have more than one level.
065     * @return whether we have more than one level.
066     */
067    public final boolean isMultiLevel() {
068        return levels.cardinality() > 1;
069    }
070
071    /**
072     * Checks if given indentation is acceptable.
073     * @param indent indentation to check.
074     * @return true if given indentation is acceptable,
075     *         false otherwise.
076     */
077    public boolean isAcceptable(int indent) {
078        return levels.get(indent);
079    }
080
081    /**
082     * Returns true if indent less than minimal of
083     * acceptable indentation levels, false otherwise.
084     * @param indent indentation to check.
085     * @return true if {@code indent} less than minimal of
086     *         acceptable indentation levels, false otherwise.
087     */
088    public boolean isGreaterThan(int indent) {
089        return levels.nextSetBit(0) > indent;
090    }
091
092    /**
093     * Adds one or more acceptable indentation level.
094     * @param base class to add new indentations to.
095     * @param additions new acceptable indentation.
096     * @return New acceptable indentation level instance.
097     */
098    public static IndentLevel addAcceptable(IndentLevel base, int... additions) {
099        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}