blob: 99de432f101d5006905e62e6996910fd501f1365 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.codeStyle.autodetect;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Stack;
import gnu.trove.TIntIntHashMap;
import gnu.trove.TIntIntIterator;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
import java.util.List;
public class IndentUsageStatisticsImpl implements IndentUsageStatistics {
private static final Comparator<IndentUsageInfo> DECREASING_ORDER = new Comparator<IndentUsageInfo>() {
@Override
public int compare(@NotNull IndentUsageInfo o1, @NotNull IndentUsageInfo o2) {
return o1.getTimesUsed() < o2.getTimesUsed() ? 1 : o1.getTimesUsed() == o2.getTimesUsed() ? 0 : -1;
}
};
private List<LineIndentInfo> myLineInfos;
private int myPreviousLineIndent;
private int myPreviousRelativeIndent;
private int myTotalLinesWithTabs = 0;
private int myTotalLinesWithWhiteSpaces = 0;
private TIntIntHashMap myIndentToUsagesMap = new TIntIntHashMap();
private List<IndentUsageInfo> myIndentUsages = ContainerUtil.newArrayList();
private Stack<IndentData> myParentIndents = ContainerUtil.newStack(new IndentData(0, 0));
public IndentUsageStatisticsImpl(@NotNull List<LineIndentInfo> lineInfos) {
myLineInfos = lineInfos;
buildIndentToUsagesMap();
myIndentUsages = toIndentUsageList(myIndentToUsagesMap);
ContainerUtil.sort(myIndentUsages, DECREASING_ORDER);
}
@NotNull
private static List<IndentUsageInfo> toIndentUsageList(@NotNull TIntIntHashMap indentToUsages) {
List<IndentUsageInfo> indentUsageInfos = ContainerUtil.newArrayList();
TIntIntIterator it = indentToUsages.iterator();
while (it.hasNext()) {
it.advance();
indentUsageInfos.add(new IndentUsageInfo(it.key(), it.value()));
}
return indentUsageInfos;
}
public void buildIndentToUsagesMap() {
myPreviousLineIndent = 0;
myPreviousRelativeIndent = 0;
for (LineIndentInfo lineInfo : myLineInfos) {
if (lineInfo.isLineWithTabs()) {
myTotalLinesWithTabs++;
}
else if (lineInfo.isLineWithWhiteSpaceIndent()) {
handleWhiteSpaceIndent(lineInfo.getIndentSize());
}
}
}
@NotNull
private IndentData findParentIndent(int indent) {
while (myParentIndents.size() != 1 && myParentIndents.peek().indent > indent) {
myParentIndents.pop();
}
return myParentIndents.peek();
}
private void handleWhiteSpaceIndent(int currentIndent) {
int relativeIndent = currentIndent - myPreviousLineIndent;
if (relativeIndent < 0) {
IndentData indentData = findParentIndent(currentIndent);
myPreviousLineIndent = indentData.indent;
myPreviousRelativeIndent = indentData.relativeIndent;
relativeIndent = currentIndent - myPreviousLineIndent;
}
if (relativeIndent == 0) {
relativeIndent = myPreviousRelativeIndent;
}
else {
myParentIndents.push(new IndentData(currentIndent, relativeIndent));
}
increaseIndentUsage(relativeIndent);
myPreviousRelativeIndent = relativeIndent;
myPreviousLineIndent = currentIndent;
myTotalLinesWithWhiteSpaces++;
}
private void increaseIndentUsage(int relativeIndent) {
int timesUsed = myIndentToUsagesMap.get(relativeIndent);
myIndentToUsagesMap.put(relativeIndent, ++timesUsed);
}
@Override
public int getTotalLinesWithLeadingTabs() {
return myTotalLinesWithTabs;
}
@Override
public int getTotalLinesWithLeadingSpaces() {
return myTotalLinesWithWhiteSpaces;
}
@Override
public IndentUsageInfo getKMostUsedIndentInfo(int k) {
return myIndentUsages.get(k);
}
@Override
public int getTimesIndentUsed(int indent) {
return myIndentToUsagesMap.get(indent);
}
@Override
public int getTotalIndentSizesDetected() {
return myIndentToUsagesMap.size();
}
private static class IndentData {
public final int indent;
public final int relativeIndent;
public IndentData(int indent, int relativeIndent) {
this.indent = indent;
this.relativeIndent = relativeIndent;
}
}
}