001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2018 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
032    /** Set of acceptable indentation levels. */
033    private final BitSet levels = new BitSet();
034
035    /**
036     * Creates new instance with one acceptable indentation level.
037     * @param indent acceptable indentation level.
038     */
039    public IndentLevel(int indent) {
040        levels.set(indent);
041    }
042
043    /**
044     * Creates new instance for nested structure.
045     * @param base parent's level
046     * @param offsets offsets from parent's level.
047     */
048    public IndentLevel(IndentLevel base, int... offsets) {
049        final BitSet src = base.levels;
050        for (int i = src.nextSetBit(0); i >= 0; i = src.nextSetBit(i + 1)) {
051            for (int offset : offsets) {
052                levels.set(i + offset);
053            }
054        }
055    }
056
057    /**
058     * Checks whether we have more than one level.
059     * @return whether we have more than one level.
060     */
061    public final boolean isMultiLevel() {
062        return levels.cardinality() > 1;
063    }
064
065    /**
066     * Checks if given indentation is acceptable.
067     * @param indent indentation to check.
068     * @return true if given indentation is acceptable,
069     *         false otherwise.
070     */
071    public boolean isAcceptable(int indent) {
072        return levels.get(indent);
073    }
074
075    /**
076     * Returns true if indent less then minimal of
077     * acceptable indentation levels, false otherwise.
078     * @param indent indentation to check.
079     * @return true if {@code indent} less then minimal of
080     *         acceptable indentation levels, false otherwise.
081     */
082    public boolean isGreaterThan(int indent) {
083        return levels.nextSetBit(0) > indent;
084    }
085
086    /**
087     * Adds one more acceptable indentation level.
088     * @param indent new acceptable indentation.
089     */
090    public void addAcceptedIndent(int indent) {
091        levels.set(indent);
092    }
093
094    /**
095     * Adds one more acceptable indentation level.
096     * @param indent new acceptable indentation.
097     */
098    public void addAcceptedIndent(IndentLevel indent) {
099        levels.or(indent.levels);
100    }
101
102    /**
103     * Returns first indentation level.
104     * @return indentation level.
105     */
106    public int getFirstIndentLevel() {
107        return levels.nextSetBit(0);
108    }
109
110    /**
111     * Returns last indentation level.
112     * @return indentation level.
113     */
114    public int getLastIndentLevel() {
115        return levels.length() - 1;
116    }
117
118    @Override
119    public String toString() {
120        final String result;
121        if (levels.cardinality() == 1) {
122            result = String.valueOf(levels.nextSetBit(0));
123        }
124        else {
125            final StringBuilder sb = new StringBuilder(50);
126            for (int i = levels.nextSetBit(0); i >= 0;
127                 i = levels.nextSetBit(i + 1)) {
128                if (sb.length() > 0) {
129                    sb.append(", ");
130                }
131                sb.append(i);
132            }
133            result = sb.toString();
134        }
135        return result;
136    }
137
138}