001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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 *
028 * @author o_sukhodolsky
029 */
030public class IndentLevel {
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     * Checks whether we have more than one level.
058     * @return whether we have more than one level.
059     */
060    public final boolean isMultiLevel() {
061        return levels.cardinality() > 1;
062    }
063
064    /**
065     * Checks if given indentation is acceptable.
066     * @param indent indentation to check.
067     * @return true if given indentation is acceptable,
068     *         false otherwise.
069     */
070    public boolean isAcceptable(int indent) {
071        return levels.get(indent);
072    }
073
074    /**
075     * Returns true if indent less then minimal of
076     * acceptable indentation levels, false otherwise.
077     * @param indent indentation to check.
078     * @return true if {@code indent} less then minimal of
079     *         acceptable indentation levels, false otherwise.
080     */
081    public boolean isGreaterThan(int indent) {
082        return levels.nextSetBit(0) > indent;
083    }
084
085    /**
086     * Adds one more acceptable indentation level.
087     * @param indent new acceptable indentation.
088     */
089    public void addAcceptedIndent(int indent) {
090        levels.set(indent);
091    }
092
093    /**
094     * Adds one more acceptable indentation level.
095     * @param indent new acceptable indentation.
096     */
097    public void addAcceptedIndent(IndentLevel indent) {
098        levels.or(indent.levels);
099    }
100
101    /**
102     * Returns first indentation level.
103     * @return indentation level.
104     */
105    public int getFirstIndentLevel() {
106        return levels.nextSetBit(0);
107    }
108
109    /**
110     * Returns last indentation level.
111     * @return indentation level.
112     */
113    public int getLastIndentLevel() {
114        return levels.length() - 1;
115    }
116
117    @Override
118    public String toString() {
119        final String result;
120        if (levels.cardinality() == 1) {
121            result = String.valueOf(levels.nextSetBit(0));
122        }
123        else {
124            final StringBuilder sb = new StringBuilder(50);
125            for (int i = levels.nextSetBit(0); i >= 0;
126                 i = levels.nextSetBit(i + 1)) {
127                if (sb.length() > 0) {
128                    sb.append(", ");
129                }
130                sb.append(i);
131            }
132            result = sb.toString();
133        }
134        return result;
135    }
136}