@@ -31,12 +31,12 @@ public class DefaultHighlightable implements Highlightable { | |||
private final Component component; | |||
private final ComponentDataCache cache; | |||
private final SyntaxHighlightingRuleSet.Builder builder; | |||
private final SyntaxHighlightingDataBuilder builder; | |||
public DefaultHighlightable(Component component, ComponentDataCache cache) { | |||
this.component = component; | |||
this.cache = cache; | |||
this.builder = SyntaxHighlightingRuleSet.builder(); | |||
this.builder = new SyntaxHighlightingDataBuilder(); | |||
} | |||
@Override | |||
@@ -49,8 +49,8 @@ public class DefaultHighlightable implements Highlightable { | |||
return component; | |||
} | |||
public SyntaxHighlightingRuleSet getHighlightingRules() { | |||
return builder.build(); | |||
public SyntaxHighlightingDataBuilder getHighlightingRules() { | |||
return builder; | |||
} | |||
private class DefaultHighlightingBuilder implements HighlightingBuilder { |
@@ -25,6 +25,7 @@ import com.google.common.collect.TreeMultimap; | |||
import org.sonar.api.source.Symbol; | |||
import org.sonar.api.source.Symbolizable; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.Comparator; | |||
import java.util.List; | |||
@@ -83,14 +84,14 @@ public class DefaultSymbolTable implements Symbolizable.SymbolTable { | |||
return new DefaultSymbolTable(referencesBySymbol); | |||
} | |||
private static class SymbolComparator implements Comparator<Symbol> { | |||
private static class SymbolComparator implements Comparator<Symbol>, Serializable { | |||
@Override | |||
public int compare(Symbol left, Symbol right) { | |||
return left.getDeclarationStartOffset() - right.getDeclarationStartOffset(); | |||
} | |||
} | |||
private static class ReferenceComparator implements Comparator<Integer> { | |||
private static class ReferenceComparator implements Comparator<Integer>, Serializable { | |||
@Override | |||
public int compare(Integer left, Integer right) { | |||
int result; |
@@ -0,0 +1,58 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.source; | |||
import org.sonar.batch.index.Data; | |||
import java.util.List; | |||
public class SyntaxHighlightingData implements Data { | |||
private static final String FIELD_SEPARATOR = ","; | |||
private static final String RULE_SEPARATOR = ";"; | |||
private List<SyntaxHighlightingRule> syntaxHighlightingRuleSet; | |||
public SyntaxHighlightingData(List<SyntaxHighlightingRule> syntaxHighlightingRuleSet) { | |||
this.syntaxHighlightingRuleSet = syntaxHighlightingRuleSet; | |||
} | |||
@Override | |||
public String writeString() { | |||
StringBuilder sb = new StringBuilder(); | |||
List<SyntaxHighlightingRule> orderedRules = syntaxHighlightingRuleSet; | |||
for (SyntaxHighlightingRule highlightingRule : orderedRules) { | |||
sb.append(highlightingRule.getStartPosition()) | |||
.append(FIELD_SEPARATOR) | |||
.append(highlightingRule.getEndPosition()) | |||
.append(FIELD_SEPARATOR) | |||
.append(highlightingRule.getTextType()) | |||
.append(RULE_SEPARATOR); | |||
} | |||
return sb.toString(); | |||
} | |||
@Override | |||
public void readString(String s) { | |||
throw new UnsupportedOperationException(); | |||
} | |||
} |
@@ -0,0 +1,105 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.source; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.collect.Collections2; | |||
import com.google.common.collect.Ordering; | |||
import org.slf4j.LoggerFactory; | |||
import javax.annotation.Nullable; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
/** | |||
* @since 3.6 | |||
*/ | |||
public class SyntaxHighlightingDataBuilder { | |||
private List<SyntaxHighlightingRule> syntaxHighlightingRuleSet; | |||
public SyntaxHighlightingDataBuilder() { | |||
syntaxHighlightingRuleSet = newArrayList(); | |||
} | |||
public SyntaxHighlightingDataBuilder registerHighlightingRule(int startOffset, int endOffset, String typeOfText) { | |||
if (ruleConflictsWithExistingRules(startOffset, endOffset)) { | |||
String errorMsg = String.format("Cannot register highlighting rule for characters from %s to %s as it " + | |||
"overlaps at least one existing rule", startOffset, endOffset); | |||
LoggerFactory.getLogger(SyntaxHighlightingDataBuilder.class).error(errorMsg); | |||
throw new UnsupportedOperationException(errorMsg); | |||
} | |||
SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(startOffset, endOffset, | |||
typeOfText); | |||
this.syntaxHighlightingRuleSet.add(syntaxHighlightingRule); | |||
return this; | |||
} | |||
public SyntaxHighlightingData build() { | |||
return new SyntaxHighlightingData(getSortedRules()); | |||
} | |||
private boolean ruleConflictsWithExistingRules(final int startOffset, final int endOffset) { | |||
Collection<SyntaxHighlightingRule> conflictingRules = Collections2 | |||
.filter(syntaxHighlightingRuleSet, new Predicate<SyntaxHighlightingRule>() { | |||
@Override | |||
public boolean apply(@Nullable SyntaxHighlightingRule syntaxHighlightingRule) { | |||
if (syntaxHighlightingRule != null) { | |||
boolean overlapsStartBoundary = startOffset < syntaxHighlightingRule.getStartPosition() | |||
&& endOffset >= syntaxHighlightingRule.getStartPosition() + 1 | |||
&& endOffset < syntaxHighlightingRule.getEndPosition(); | |||
boolean overlapsEndBoundary = startOffset > syntaxHighlightingRule.getStartPosition() | |||
&& startOffset < syntaxHighlightingRule.getEndPosition() | |||
&& endOffset > syntaxHighlightingRule.getEndPosition(); | |||
return overlapsStartBoundary || overlapsEndBoundary; | |||
} | |||
return false; | |||
} | |||
}); | |||
return !conflictingRules.isEmpty(); | |||
} | |||
@VisibleForTesting | |||
protected List<SyntaxHighlightingRule> getSortedRules() { | |||
Ordering<SyntaxHighlightingRule> ruleOrdering = new Ordering<SyntaxHighlightingRule>() { | |||
@Override | |||
public int compare(@Nullable SyntaxHighlightingRule left, | |||
@Nullable SyntaxHighlightingRule right) { | |||
int result; | |||
if (left != null && right != null) { | |||
result = left.getStartPosition() - right.getStartPosition(); | |||
if (result == 0) { | |||
result = left.getEndPosition() - right.getEndPosition(); | |||
} | |||
return result; | |||
} | |||
return left != null ? 1 : -1; | |||
} | |||
}; | |||
return ruleOrdering.immutableSortedCopy(syntaxHighlightingRuleSet); | |||
} | |||
} |
@@ -19,10 +19,12 @@ | |||
*/ | |||
package org.sonar.batch.source; | |||
import java.io.Serializable; | |||
/** | |||
* @since 3.6 | |||
*/ | |||
public class SyntaxHighlightingRule { | |||
public class SyntaxHighlightingRule implements Serializable { | |||
private final int startPosition; | |||
private final int endPosition; |
@@ -1,149 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.source; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.collect.Collections2; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.Ordering; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.batch.index.Data; | |||
import javax.annotation.Nullable; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
/** | |||
* @since 3.6 | |||
*/ | |||
public class SyntaxHighlightingRuleSet implements Data { | |||
private static final String FIELD_SEPARATOR = ","; | |||
private static final String RULE_SEPARATOR = ";"; | |||
private List<SyntaxHighlightingRule> syntaxHighlightingRuleSet; | |||
private SyntaxHighlightingRuleSet(List<SyntaxHighlightingRule> syntaxHighlightingRules) { | |||
syntaxHighlightingRuleSet = syntaxHighlightingRules; | |||
} | |||
public static Builder builder() { | |||
return new Builder(); | |||
} | |||
public static class Builder { | |||
private List<SyntaxHighlightingRule> syntaxHighlightingRuleSet; | |||
public Builder() { | |||
syntaxHighlightingRuleSet = newArrayList(); | |||
} | |||
public Builder registerHighlightingRule(int startOffset, int endOffset, String typeOfText) { | |||
if (ruleConflictsWithExistingRules(startOffset, endOffset)) { | |||
String errorMsg = String.format("Cannot register highlighting rule for characters from %s to %s as it " + | |||
"overlaps at least one existing rule", startOffset, endOffset); | |||
LoggerFactory.getLogger(SyntaxHighlightingRuleSet.class).error(errorMsg); | |||
throw new UnsupportedOperationException(errorMsg); | |||
} | |||
SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(startOffset, endOffset, | |||
typeOfText); | |||
this.syntaxHighlightingRuleSet.add(syntaxHighlightingRule); | |||
return this; | |||
} | |||
public SyntaxHighlightingRuleSet build() { | |||
return new SyntaxHighlightingRuleSet(ImmutableList.copyOf(syntaxHighlightingRuleSet)); | |||
} | |||
private boolean ruleConflictsWithExistingRules(final int startOffset, final int endOffset) { | |||
Collection<SyntaxHighlightingRule> conflictingRules = Collections2 | |||
.filter(syntaxHighlightingRuleSet, new Predicate<SyntaxHighlightingRule>() { | |||
@Override | |||
public boolean apply(@Nullable SyntaxHighlightingRule syntaxHighlightingRule) { | |||
if (syntaxHighlightingRule != null) { | |||
boolean overlapsStartBoundary = startOffset < syntaxHighlightingRule.getStartPosition() | |||
&& endOffset >= syntaxHighlightingRule.getStartPosition() + 1 | |||
&& endOffset < syntaxHighlightingRule.getEndPosition(); | |||
boolean overlapsEndBoundary = startOffset > syntaxHighlightingRule.getStartPosition() | |||
&& startOffset < syntaxHighlightingRule.getEndPosition() | |||
&& endOffset > syntaxHighlightingRule.getEndPosition(); | |||
return overlapsStartBoundary || overlapsEndBoundary; | |||
} | |||
return false; | |||
} | |||
}); | |||
return !conflictingRules.isEmpty(); | |||
} | |||
} | |||
public List<SyntaxHighlightingRule> getSyntaxHighlightingRuleSet() { | |||
return syntaxHighlightingRuleSet; | |||
} | |||
@Override | |||
public String writeString() { | |||
StringBuilder sb = new StringBuilder(); | |||
List<SyntaxHighlightingRule> orderedRules = getOrderedHighlightingRules(); | |||
for (SyntaxHighlightingRule highlightingRule : orderedRules) { | |||
sb.append(highlightingRule.getStartPosition()) | |||
.append(FIELD_SEPARATOR) | |||
.append(highlightingRule.getEndPosition()) | |||
.append(FIELD_SEPARATOR) | |||
.append(highlightingRule.getTextType()) | |||
.append(RULE_SEPARATOR); | |||
} | |||
return sb.toString(); | |||
} | |||
@Override | |||
public void readString(String s) { | |||
throw new UnsupportedOperationException(); | |||
} | |||
@VisibleForTesting | |||
protected List<SyntaxHighlightingRule> getOrderedHighlightingRules() { | |||
Ordering<SyntaxHighlightingRule> ruleOrdering = new Ordering<SyntaxHighlightingRule>() { | |||
@Override | |||
public int compare(@Nullable SyntaxHighlightingRule left, | |||
@Nullable SyntaxHighlightingRule right) { | |||
int result = 0; | |||
if (left != null && right != null) { | |||
result = left.getStartPosition() - right.getStartPosition(); | |||
if (result == 0) { | |||
result = left.getEndPosition() - right.getEndPosition(); | |||
} | |||
return result; | |||
} | |||
return left != null ? 1 : -1; | |||
} | |||
}; | |||
return ruleOrdering.sortedCopy(syntaxHighlightingRuleSet); | |||
} | |||
} |
@@ -39,7 +39,7 @@ public class DefaultHighlightableTest { | |||
DefaultHighlightable highlightablePerspective = new DefaultHighlightable(null, null); | |||
highlightablePerspective.newHighlighting().highlight(0, 10, "k").highlight(20, 30, "cppd"); | |||
assertThat(highlightablePerspective.getHighlightingRules().getSyntaxHighlightingRuleSet()).hasSize(2); | |||
assertThat(highlightablePerspective.getHighlightingRules().getSortedRules()).hasSize(2); | |||
} | |||
@Test |
@@ -29,9 +29,9 @@ import java.util.List; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class SyntaxHighlightingRuleSetTest { | |||
public class SyntaxHighlightingDataBuilderTest { | |||
private SyntaxHighlightingRuleSet highlightingRules; | |||
private List<SyntaxHighlightingRule> highlightingRules; | |||
@Rule | |||
public ExpectedException throwable = ExpectedException.none(); | |||
@@ -39,45 +39,34 @@ public class SyntaxHighlightingRuleSetTest { | |||
@Before | |||
public void setUpSampleRules() { | |||
SyntaxHighlightingRuleSet.Builder highlightingRuleSet = SyntaxHighlightingRuleSet.builder(); | |||
highlightingRuleSet.registerHighlightingRule(0, 10, "cd"); | |||
highlightingRuleSet.registerHighlightingRule(10, 12, "k"); | |||
highlightingRuleSet.registerHighlightingRule(24, 38, "k"); | |||
highlightingRuleSet.registerHighlightingRule(42, 50, "k"); | |||
highlightingRuleSet.registerHighlightingRule(24, 65, "cppd"); | |||
highlightingRuleSet.registerHighlightingRule(12, 20, "cd"); | |||
SyntaxHighlightingDataBuilder highlightingDataBuilder = new SyntaxHighlightingDataBuilder(); | |||
highlightingDataBuilder.registerHighlightingRule(0, 10, "cd"); | |||
highlightingDataBuilder.registerHighlightingRule(10, 12, "k"); | |||
highlightingDataBuilder.registerHighlightingRule(24, 38, "k"); | |||
highlightingDataBuilder.registerHighlightingRule(42, 50, "k"); | |||
highlightingDataBuilder.registerHighlightingRule(24, 65, "cppd"); | |||
highlightingDataBuilder.registerHighlightingRule(12, 20, "cd"); | |||
highlightingRules = highlightingRuleSet.build(); | |||
highlightingRules = highlightingDataBuilder.getSortedRules(); | |||
} | |||
@Test | |||
public void should_register_highlighting_rule() throws Exception { | |||
assertThat(highlightingRules.getSyntaxHighlightingRuleSet()).hasSize(6); | |||
assertThat(highlightingRules).hasSize(6); | |||
} | |||
@Test | |||
public void should_order_by_start_then_end_offset() throws Exception { | |||
List<SyntaxHighlightingRule> orderedRules = highlightingRules.getOrderedHighlightingRules(); | |||
assertThat(orderedRules).onProperty("startPosition").containsExactly(0, 10, 12, 24, 24, 42); | |||
assertThat(orderedRules).onProperty("endPosition").containsExactly(10, 12, 20, 38, 65, 50); | |||
assertThat(orderedRules).onProperty("textType").containsExactly("cd", "k", "cd", "k", "cppd", "k"); | |||
} | |||
@Test | |||
public void should_serialize_rules_to_string() throws Exception { | |||
String serializedRules = highlightingRules.writeString(); | |||
assertThat(serializedRules).isEqualTo("0,10,cd;10,12,k;12,20,cd;24,38,k;24,65,cppd;42,50,k;"); | |||
assertThat(highlightingRules).onProperty("startPosition").containsExactly(0, 10, 12, 24, 24, 42); | |||
assertThat(highlightingRules).onProperty("endPosition").containsExactly(10, 12, 20, 38, 65, 50); | |||
assertThat(highlightingRules).onProperty("textType").containsExactly("cd", "k", "cd", "k", "cppd", "k"); | |||
} | |||
@Test | |||
public void should_prevent_rules_overlapping() throws Exception { | |||
throwable.expect(UnsupportedOperationException.class); | |||
SyntaxHighlightingRuleSet.Builder builder = SyntaxHighlightingRuleSet.builder(); | |||
SyntaxHighlightingDataBuilder builder = new SyntaxHighlightingDataBuilder(); | |||
builder.registerHighlightingRule(0, 10, "k"); | |||
builder.registerHighlightingRule(8, 15, "k"); | |||
} |
@@ -0,0 +1,46 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2013 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.batch.source; | |||
import com.google.common.collect.Lists; | |||
import org.junit.Test; | |||
import java.util.List; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class SyntaxHighlightingDataTest { | |||
@Test | |||
public void should_serialize_rules_to_string() throws Exception { | |||
List<SyntaxHighlightingRule> orderedHighlightingRules = Lists.newArrayList( | |||
SyntaxHighlightingRule.create(0, 10, "cd"), | |||
SyntaxHighlightingRule.create(10, 12, "k"), | |||
SyntaxHighlightingRule.create(12, 20, "cd"), | |||
SyntaxHighlightingRule.create(24, 38, "k"), | |||
SyntaxHighlightingRule.create(24, 65, "cppd"), | |||
SyntaxHighlightingRule.create(42, 50, "k") | |||
); | |||
String serializedRules = new SyntaxHighlightingData(orderedHighlightingRules).writeString(); | |||
assertThat(serializedRules).isEqualTo("0,10,cd;10,12,k;12,20,cd;24,38,k;24,65,cppd;42,50,k;"); | |||
} | |||
} |
@@ -45,14 +45,7 @@ class OpeningHtmlTag { | |||
if (o == null || getClass() != o.getClass()) { | |||
return false; | |||
} | |||
OpeningHtmlTag openingHtmlTag = (OpeningHtmlTag) o; | |||
if (startOffset != openingHtmlTag.startOffset) { | |||
return false; | |||
} | |||
if (cssClass != null ? !cssClass.equals(openingHtmlTag.cssClass) : openingHtmlTag.cssClass != null) { | |||
return false; | |||
} | |||
return true; | |||
return compareTo((OpeningHtmlTag) o); | |||
} | |||
@Override | |||
@@ -61,4 +54,14 @@ class OpeningHtmlTag { | |||
result = 31 * result + (cssClass != null ? cssClass.hashCode() : 0); | |||
return result; | |||
} | |||
private boolean compareTo(OpeningHtmlTag otherTag) { | |||
if (startOffset != otherTag.startOffset) { | |||
return false; | |||
} | |||
if (cssClass != null ? !cssClass.equals(otherTag.cssClass) : otherTag.cssClass != null) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
} |