From 4858542bb029786965e2dddd97dbacaa78f066a8 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Wed, 10 Sep 2014 13:30:44 +0400 Subject: [PATCH] extracted interface, eliminated possible exceptions due to small indent size diversity --- .../autodetect/IndentOptionsDetector.java | 24 +-- .../autodetect/IndentUsageStatistics.java | 122 +------------- .../autodetect/IndentUsageStatisticsImpl.java | 149 ++++++++++++++++++ .../autodetect/IndentAutoDetectionTest.java | 4 +- 4 files changed, 172 insertions(+), 127 deletions(-) create mode 100644 platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatisticsImpl.java diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetector.java b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetector.java index f677707ca79c..457a285a9e9c 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetector.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetector.java @@ -50,7 +50,7 @@ public class IndentOptionsDetector { if (myDocument != null) { List linesInfo = new LineIndentInfoBuilder(myDocument.getCharsSequence()).build(); - IndentUsageStatistics stats = new IndentUsageStatistics(linesInfo); + IndentUsageStatistics stats = new IndentUsageStatisticsImpl(linesInfo); adjustIndentOptions(indentOptions, stats); } @@ -80,18 +80,24 @@ public class IndentOptionsDetector { } private static int getPositiveIndentSize(@NotNull IndentUsageStatistics stats) { - IndentUsageInfo maxUsedIndentInfo = stats.getMaxUsedIndentInfo(0); - int maxIndentSize = maxUsedIndentInfo.getIndentSize(); - if (maxIndentSize == 0) { - maxUsedIndentInfo = stats.getMaxUsedIndentInfo(1); - maxIndentSize = maxUsedIndentInfo.getIndentSize(); + int totalIndentSizesDetected = stats.getTotalIndentSizesDetected(); + if (totalIndentSizesDetected == 0) return -1; + + IndentUsageInfo maxUsedIndentInfo = stats.getKMostUsedIndentInfo(0); + int maxUsedIndentSize = maxUsedIndentInfo.getIndentSize(); + + if (maxUsedIndentSize == 0) { + if (totalIndentSizesDetected < 1) return -1; + + maxUsedIndentInfo = stats.getKMostUsedIndentInfo(1); + maxUsedIndentSize = maxUsedIndentInfo.getIndentSize(); } - if (maxIndentSize <= MAX_INDENT_TO_DETECT) { - int totalUsagesWithoutZeroIndent = stats.getTotalLinesWithLeadingSpaces() - stats.getTimesUsedIndent(0); + if (maxUsedIndentSize <= MAX_INDENT_TO_DETECT) { + int totalUsagesWithoutZeroIndent = stats.getTotalLinesWithLeadingSpaces() - stats.getTimesIndentUsed(0); double usageRate = (double)maxUsedIndentInfo.getTimesUsed() / totalUsagesWithoutZeroIndent; if (usageRate > RATE_THRESHOLD) { - return maxIndentSize; + return maxUsedIndentSize; } } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatistics.java b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatistics.java index 27cfcdd22341..7e3bee7221da 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatistics.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatistics.java @@ -15,126 +15,16 @@ */ 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; +public interface IndentUsageStatistics { -import java.util.Comparator; -import java.util.List; + int getTotalLinesWithLeadingTabs(); -public class IndentUsageStatistics { - private static final Comparator DECREASING_ORDER = new Comparator() { - @Override - public int compare(@NotNull IndentUsageInfo o1, @NotNull IndentUsageInfo o2) { - return o1.getTimesUsed() < o2.getTimesUsed() ? 1 : o1.getTimesUsed() == o2.getTimesUsed() ? 0 : -1; - } - }; + int getTotalLinesWithLeadingSpaces(); - private List myLineInfos; + IndentUsageInfo getKMostUsedIndentInfo(int k); - private int myPreviousLineIndent; - private int myPreviousRelativeIndent; + int getTotalIndentSizesDetected(); - private int myTotalLinesWithTabs = 0; - private int myTotalLinesWithWhiteSpaces = 0; + int getTimesIndentUsed(int indent); - private TIntIntHashMap myIndentToUsages = new TIntIntHashMap(); - private List myIndentUsages = ContainerUtil.newArrayList(); - private Stack myParentIndents = ContainerUtil.newStack(new IndentData(0, 0)); - - public IndentUsageStatistics(@NotNull List lineInfos) { - myLineInfos = lineInfos; - buildIndentToUsagesMap(); - myIndentUsages = toIndentUsageList(myIndentToUsages); - ContainerUtil.sort(myIndentUsages, DECREASING_ORDER); - } - - @NotNull - private static List toIndentUsageList(@NotNull TIntIntHashMap indentToUsages) { - List 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 = myIndentToUsages.get(relativeIndent); - myIndentToUsages.put(relativeIndent, ++timesUsed); - } - - public int getTotalLinesWithLeadingTabs() { - return myTotalLinesWithTabs; - } - - public int getTotalLinesWithLeadingSpaces() { - return myTotalLinesWithWhiteSpaces; - } - - public IndentUsageInfo getMaxUsedIndentInfo(int rank) { - return myIndentUsages.get(rank); - } - - public int getTimesUsedIndent(int indent) { - return myIndentToUsages.get(indent); - } - - private static class IndentData { - public final int indent; - public final int relativeIndent; - - public IndentData(int indent, int relativeIndent) { - this.indent = indent; - this.relativeIndent = relativeIndent; - } - } } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatisticsImpl.java b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatisticsImpl.java new file mode 100644 index 000000000000..6578b5faa3e3 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatisticsImpl.java @@ -0,0 +1,149 @@ +/* + * 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 DECREASING_ORDER = new Comparator() { + @Override + public int compare(@NotNull IndentUsageInfo o1, @NotNull IndentUsageInfo o2) { + return o1.getTimesUsed() < o2.getTimesUsed() ? 1 : o1.getTimesUsed() == o2.getTimesUsed() ? 0 : -1; + } + }; + + private List myLineInfos; + + private int myPreviousLineIndent; + private int myPreviousRelativeIndent; + + private int myTotalLinesWithTabs = 0; + private int myTotalLinesWithWhiteSpaces = 0; + + private TIntIntHashMap myIndentToUsages = new TIntIntHashMap(); + private List myIndentUsages = ContainerUtil.newArrayList(); + private Stack myParentIndents = ContainerUtil.newStack(new IndentData(0, 0)); + + public IndentUsageStatisticsImpl(@NotNull List lineInfos) { + myLineInfos = lineInfos; + buildIndentToUsagesMap(); + myIndentUsages = toIndentUsageList(myIndentToUsages); + ContainerUtil.sort(myIndentUsages, DECREASING_ORDER); + } + + @NotNull + private static List toIndentUsageList(@NotNull TIntIntHashMap indentToUsages) { + List 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 = myIndentToUsages.get(relativeIndent); + myIndentToUsages.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 myIndentToUsages.get(indent); + } + + @Override + public int getTotalIndentSizesDetected() { + return myIndentToUsages.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; + } + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java b/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java index 9d76dba6aff8..4b59416de0d9 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java +++ b/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java @@ -112,8 +112,8 @@ public class IndentAutoDetectionTest extends LightPlatformCodeInsightTestCase { configureByFile(getTestName(true) + ".java"); Document document = getDocument(myFile); List lines = new LineIndentInfoBuilder(document.getCharsSequence()).build(); - IndentUsageStatistics statistics = new IndentUsageStatistics(lines); - return statistics.getMaxUsedIndentInfo(0); + IndentUsageStatistics statistics = new IndentUsageStatisticsImpl(lines); + return statistics.getKMostUsedIndentInfo(0); } private static void doTestLineToIndentMapping(@NotNull CharSequence text, int... spacesForLine) {