+++ /dev/null
-/*
- * 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.core.source;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.util.ArrayDeque;
-import java.util.Deque;
-
-/**
- * @since 3.6
- */
-class CharactersReader {
-
- static final int END_OF_STREAM = -1;
-
- private final BufferedReader stringBuffer;
- private final Deque<String> openTags;
-
- private int currentValue;
- private int previousValue;
- private int currentIndex = -1;
-
- public CharactersReader(BufferedReader stringBuffer) {
- this.stringBuffer = stringBuffer;
- this.openTags = new ArrayDeque<String>();
- }
-
- boolean readNextChar() throws IOException {
- previousValue = currentValue;
- currentValue = stringBuffer.read();
- currentIndex++;
- return currentValue != END_OF_STREAM;
- }
-
- int getCurrentValue() {
- return currentValue;
- }
-
- int getPreviousValue() {
- return previousValue;
- }
-
- int getCurrentIndex() {
- return currentIndex;
- }
-
- void registerOpenTag(String textType) {
- openTags.push(textType);
- }
-
- void removeLastOpenTag() {
- openTags.remove();
- }
-
- Deque<String> getOpenTags() {
- return openTags;
- }
-}
+++ /dev/null
-/*
- * 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.core.source;
-
-import com.google.common.collect.Lists;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-
-class DecorationDataHolder {
-
- private static final String ENTITY_SEPARATOR = ";";
- private static final String FIELD_SEPARATOR = ",";
- private static final String SYMBOL_PREFIX = "sym-";
- private static final String HIGHLIGHTABLE = "sym";
-
- private List<OpeningHtmlTag> openingTagsEntries;
- private int openingTagsIndex;
- private List<Integer> closingTagsOffsets;
- private int closingTagsIndex;
-
- DecorationDataHolder() {
- openingTagsEntries = Lists.newArrayList();
- closingTagsOffsets = Lists.newArrayList();
- }
-
- void loadSymbolReferences(String symbolsReferences) {
- String[] symbols = symbolsReferences.split(ENTITY_SEPARATOR);
- for (String symbol : symbols) {
- String[] symbolFields = symbol.split(FIELD_SEPARATOR);
- int declarationStartOffset = Integer.parseInt(symbolFields[0]);
- int declarationEndOffset = Integer.parseInt(symbolFields[1]);
- int symbolLength = declarationEndOffset - declarationStartOffset;
- String[] symbolOccurrences = Arrays.copyOfRange(symbolFields, 2, symbolFields.length);
- loadSymbolOccurrences(declarationStartOffset, symbolLength, symbolOccurrences);
- }
- }
-
- void loadSyntaxHighlightingData(String syntaxHighlightingRules) {
- String[] rules = syntaxHighlightingRules.split(ENTITY_SEPARATOR);
- for (String rule : rules) {
- String[] ruleFields = rule.split(FIELD_SEPARATOR);
- insertAndPreserveOrder(new OpeningHtmlTag(Integer.parseInt(ruleFields[0]), ruleFields[2]), openingTagsEntries);
- insertAndPreserveOrder(Integer.parseInt(ruleFields[1]), closingTagsOffsets);
- }
- }
-
- List<OpeningHtmlTag> getOpeningTagsEntries() {
- return openingTagsEntries;
- }
-
- OpeningHtmlTag getCurrentOpeningTagEntry() {
- return openingTagsIndex < openingTagsEntries.size() ? openingTagsEntries.get(openingTagsIndex) : null;
- }
-
- void nextOpeningTagEntry() {
- openingTagsIndex++;
- }
-
- List<Integer> getClosingTagsOffsets() {
- return closingTagsOffsets;
- }
-
- int getCurrentClosingTagOffset() {
- return closingTagsIndex < closingTagsOffsets.size() ? closingTagsOffsets.get(closingTagsIndex) : -1;
- }
-
- void nextClosingTagOffset() {
- closingTagsIndex++;
- }
-
- private void loadSymbolOccurrences(int declarationStartOffset, int symbolLength, String[] symbolOccurrences) {
- for (String symbolOccurrence : symbolOccurrences) {
- int occurrenceStartOffset = Integer.parseInt(symbolOccurrence);
- int occurrenceEndOffset = occurrenceStartOffset + symbolLength;
- insertAndPreserveOrder(new OpeningHtmlTag(occurrenceStartOffset, SYMBOL_PREFIX + declarationStartOffset + " " + HIGHLIGHTABLE), openingTagsEntries);
- insertAndPreserveOrder(occurrenceEndOffset, closingTagsOffsets);
- }
- }
-
- private void insertAndPreserveOrder(OpeningHtmlTag newEntry, List<OpeningHtmlTag> openingHtmlTags) {
- int insertionIndex = 0;
- Iterator<OpeningHtmlTag> tagIterator = openingHtmlTags.iterator();
- while (tagIterator.hasNext() && tagIterator.next().getStartOffset() <= newEntry.getStartOffset()) {
- insertionIndex++;
- }
- openingHtmlTags.add(insertionIndex, newEntry);
- }
-
- private void insertAndPreserveOrder(int newOffset, List<Integer> orderedOffsets) {
- int insertionIndex = 0;
- Iterator<Integer> entriesIterator = orderedOffsets.iterator();
- while (entriesIterator.hasNext() && entriesIterator.next() <= newOffset) {
- insertionIndex++;
- }
- orderedOffsets.add(insertionIndex, newOffset);
- }
-}
+++ /dev/null
-/*
- * 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.core.source;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import org.apache.ibatis.session.SqlSession;
-import org.sonar.api.ServerComponent;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.source.db.SnapshotDataDao;
-import org.sonar.core.source.db.SnapshotDataDto;
-import org.sonar.core.source.db.SnapshotSourceDao;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
-import java.util.Collection;
-import java.util.List;
-
-public class HtmlSourceDecorator implements ServerComponent {
-
- private final MyBatis mybatis;
-
- private final SnapshotSourceDao snapshotSourceDao;
- private final SnapshotDataDao snapshotDataDao;
-
- public HtmlSourceDecorator(MyBatis mybatis, SnapshotSourceDao snapshotSourceDao, SnapshotDataDao snapshotDataDao) {
- this.mybatis = mybatis;
- this.snapshotSourceDao = snapshotSourceDao;
- this.snapshotDataDao = snapshotDataDao;
- }
-
- @CheckForNull
- public List<String> getDecoratedSourceAsHtml(String componentKey, @Nullable Integer from, @Nullable Integer to) {
- SqlSession session = mybatis.openSession();
- try {
- Collection<SnapshotDataDto> snapshotDataEntries = snapshotDataDao.selectSnapshotDataByComponentKey(componentKey, highlightingDataTypes(), session);
- if (!snapshotDataEntries.isEmpty()) {
- String snapshotSource = snapshotSourceDao.selectSnapshotSourceByComponentKey(componentKey, session);
- return decorate(snapshotSource, snapshotDataEntries, from, to);
- }
- return null;
- } finally {
- MyBatis.closeQuietly(session);
- }
- }
-
- @CheckForNull
- public List<String> getDecoratedSourceAsHtml(long snapshotId) {
- Collection<SnapshotDataDto> snapshotDataEntries = snapshotDataDao.selectSnapshotData(snapshotId, highlightingDataTypes());
- if (!snapshotDataEntries.isEmpty()) {
- String snapshotSource = snapshotSourceDao.selectSnapshotSource(snapshotId);
- if (snapshotSource != null) {
- return decorate(snapshotSource, snapshotDataEntries, null, null);
- }
- }
- return null;
- }
-
- @CheckForNull
- private List<String> decorate(@Nullable String snapshotSource, Collection<SnapshotDataDto> snapshotDataEntries, @Nullable Integer from, @Nullable Integer to) {
- if (snapshotSource != null) {
- DecorationDataHolder decorationDataHolder = new DecorationDataHolder();
- for (SnapshotDataDto snapshotDataEntry : snapshotDataEntries) {
- loadSnapshotData(decorationDataHolder, snapshotDataEntry);
- }
-
- HtmlTextDecorator textDecorator = new HtmlTextDecorator();
- return textDecorator.decorateTextWithHtml(snapshotSource, decorationDataHolder, from, to);
- }
- return null;
- }
-
- private List<String> highlightingDataTypes() {
- return Lists.newArrayList(SnapshotDataTypes.SYNTAX_HIGHLIGHTING,
- SnapshotDataTypes.SYMBOL_HIGHLIGHTING);
- }
-
- private void loadSnapshotData(DecorationDataHolder dataHolder, SnapshotDataDto entry) {
- if (!Strings.isNullOrEmpty(entry.getData())) {
- if (SnapshotDataTypes.SYNTAX_HIGHLIGHTING.equals(entry.getDataType())) {
- dataHolder.loadSyntaxHighlightingData(entry.getData());
- } else if (SnapshotDataTypes.SYMBOL_HIGHLIGHTING.equals(entry.getDataType())) {
- dataHolder.loadSymbolReferences(entry.getData());
- }
- }
- }
-}
+++ /dev/null
-/*
- * 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.core.source;
-
-import com.google.common.collect.Lists;
-import com.google.common.io.Closeables;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.Nullable;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.Collection;
-import java.util.List;
-
-import static org.sonar.core.source.CharactersReader.END_OF_STREAM;
-
-/**
- * @since 3.6
- */
-class HtmlTextDecorator {
-
- static final char CR_END_OF_LINE = '\r';
- static final char LF_END_OF_LINE = '\n';
- static final char HTML_OPENING = '<';
- static final char HTML_CLOSING = '>';
- static final char AMPERSAND = '&';
- static final String ENCODED_HTML_OPENING = "<";
- static final String ENCODED_HTML_CLOSING = ">";
- static final String ENCODED_AMPERSAND = "&";
-
- List<String> decorateTextWithHtml(String text, DecorationDataHolder decorationDataHolder) {
- return decorateTextWithHtml(text, decorationDataHolder, null, null);
- }
-
- List<String> decorateTextWithHtml(String text, DecorationDataHolder decorationDataHolder, @Nullable Integer from, @Nullable Integer to) {
-
- StringBuilder currentHtmlLine = new StringBuilder();
- List<String> decoratedHtmlLines = Lists.newArrayList();
- int currentLine = 1;
-
- BufferedReader stringBuffer = null;
- try {
- stringBuffer = new BufferedReader(new StringReader(text));
-
- CharactersReader charsReader = new CharactersReader(stringBuffer);
-
- while (charsReader.readNextChar()) {
-
- if (shouldStartNewLine(charsReader)) {
- addLine(decoratedHtmlLines, currentHtmlLine.toString(), currentLine, from, to);
- currentLine++;
- currentHtmlLine = new StringBuilder();
- if (shouldReopenPendingTags(charsReader)) {
- reopenCurrentSyntaxTags(charsReader, currentHtmlLine);
- }
- }
-
- int numberOfTagsToClose = getNumberOfTagsToClose(charsReader.getCurrentIndex(), decorationDataHolder);
- closeCompletedTags(charsReader, numberOfTagsToClose, currentHtmlLine);
-
- if (shouldClosePendingTags(charsReader)) {
- closeCurrentSyntaxTags(charsReader, currentHtmlLine);
- }
-
- Collection<String> tagsToOpen = getTagsToOpen(charsReader.getCurrentIndex(), decorationDataHolder);
- openNewTags(charsReader, tagsToOpen, currentHtmlLine);
-
- if (shouldAppendCharToHtmlOutput(charsReader)) {
- char currentChar = (char) charsReader.getCurrentValue();
- currentHtmlLine.append(normalize(currentChar));
- }
- }
-
- closeCurrentSyntaxTags(charsReader, currentHtmlLine);
-
- if (shouldStartNewLine(charsReader)) {
- addLine(decoratedHtmlLines, currentHtmlLine.toString(), currentLine, from, to);
- currentLine++;
- addLine(decoratedHtmlLines, "", currentLine, from, to);
- currentLine++;
- } else if (currentHtmlLine.length() > 0) {
- addLine(decoratedHtmlLines, currentHtmlLine.toString(), currentLine, from, to);
- currentLine++;
- }
-
- } catch (IOException exception) {
- String errorMsg = "An exception occurred while highlighting the syntax of one of the project's files";
- LoggerFactory.getLogger(HtmlTextDecorator.class).error(errorMsg);
- throw new IllegalStateException(errorMsg, exception);
- } finally {
- Closeables.closeQuietly(stringBuffer);
- }
-
- return decoratedHtmlLines;
- }
-
- private void addLine(List<String> decoratedHtmlLines, String line, int currentLine, @Nullable Integer from, @Nullable Integer to) {
- if ((from == null || currentLine >= from)
- && (to == null || to >= currentLine)) {
- decoratedHtmlLines.add(line);
- }
- }
-
- private char[] normalize(char currentChar) {
- char[] normalizedChars;
- if (currentChar == HTML_OPENING) {
- normalizedChars = ENCODED_HTML_OPENING.toCharArray();
- } else if (currentChar == HTML_CLOSING) {
- normalizedChars = ENCODED_HTML_CLOSING.toCharArray();
- } else if (currentChar == AMPERSAND) {
- normalizedChars = ENCODED_AMPERSAND.toCharArray();
- } else {
- normalizedChars = new char[]{currentChar};
- }
- return normalizedChars;
- }
-
- private boolean shouldAppendCharToHtmlOutput(CharactersReader charsReader) {
- return charsReader.getCurrentValue() != CR_END_OF_LINE && charsReader.getCurrentValue() != LF_END_OF_LINE;
- }
-
- private int getNumberOfTagsToClose(int currentIndex, DecorationDataHolder dataHolder) {
- int numberOfTagsToClose = 0;
-
- while (currentIndex == dataHolder.getCurrentClosingTagOffset()) {
- numberOfTagsToClose++;
- dataHolder.nextClosingTagOffset();
- }
- return numberOfTagsToClose;
- }
-
- private Collection<String> getTagsToOpen(int currentIndex, DecorationDataHolder dataHolder) {
- Collection<String> tagsToOpen = Lists.newArrayList();
- while (dataHolder.getCurrentOpeningTagEntry() != null && currentIndex == dataHolder.getCurrentOpeningTagEntry().getStartOffset()) {
- tagsToOpen.add(dataHolder.getCurrentOpeningTagEntry().getCssClass());
- dataHolder.nextOpeningTagEntry();
- }
- return tagsToOpen;
- }
-
- private boolean shouldClosePendingTags(CharactersReader charactersReader) {
- return charactersReader.getCurrentValue() == CR_END_OF_LINE
- || (charactersReader.getCurrentValue() == LF_END_OF_LINE && charactersReader.getPreviousValue() != CR_END_OF_LINE)
- || (charactersReader.getCurrentValue() == END_OF_STREAM && charactersReader.getPreviousValue() != LF_END_OF_LINE);
- }
-
- private boolean shouldReopenPendingTags(CharactersReader charactersReader) {
- return (charactersReader.getPreviousValue() == LF_END_OF_LINE && charactersReader.getCurrentValue() != LF_END_OF_LINE)
- || (charactersReader.getPreviousValue() == CR_END_OF_LINE && charactersReader.getCurrentValue() != CR_END_OF_LINE
- && charactersReader.getCurrentValue() != LF_END_OF_LINE);
- }
-
- private boolean shouldStartNewLine(CharactersReader charactersReader) {
- return charactersReader.getPreviousValue() == LF_END_OF_LINE
- || (charactersReader.getPreviousValue() == CR_END_OF_LINE && charactersReader.getCurrentValue() != LF_END_OF_LINE);
- }
-
- private void closeCompletedTags(CharactersReader charactersReader, int numberOfTagsToClose,
- StringBuilder decoratedText) {
- for (int i = 0; i < numberOfTagsToClose; i++) {
- injectClosingHtml(decoratedText);
- charactersReader.removeLastOpenTag();
- }
- }
-
- private void openNewTags(CharactersReader charactersReader, Collection<String> tagsToOpen,
- StringBuilder decoratedText) {
- for (String tagToOpen : tagsToOpen) {
- injectOpeningHtmlForRule(tagToOpen, decoratedText);
- charactersReader.registerOpenTag(tagToOpen);
- }
- }
-
- private void closeCurrentSyntaxTags(CharactersReader charactersReader, StringBuilder decoratedText) {
- for (int i = 0; i < charactersReader.getOpenTags().size(); i++) {
- injectClosingHtml(decoratedText);
- }
- }
-
- private void reopenCurrentSyntaxTags(CharactersReader charactersReader, StringBuilder decoratedText) {
- for (String tags : charactersReader.getOpenTags()) {
- injectOpeningHtmlForRule(tags, decoratedText);
- }
- }
-
- private void injectOpeningHtmlForRule(String textType, StringBuilder decoratedText) {
- decoratedText.append("<span class=\"").append(textType).append("\">");
- }
-
- private void injectClosingHtml(StringBuilder decoratedText) {
- decoratedText.append("</span>");
- }
-}
+++ /dev/null
-/*
- * 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.core.source;
-
-class OpeningHtmlTag {
-
- private final int startOffset;
- private final String cssClass;
-
- OpeningHtmlTag(int startOffset, String cssClass) {
- this.startOffset = startOffset;
- this.cssClass = cssClass;
- }
-
- int getStartOffset() {
- return startOffset;
- }
-
- String getCssClass() {
- return cssClass;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- return compareTo((OpeningHtmlTag) o);
- }
-
- @Override
- public int hashCode() {
- int result = startOffset;
- 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;
- }
-}
+++ /dev/null
-/*
- * 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.core.source;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.List;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class DecorationDataHolderTest {
-
- private static final String SAMPLE_SYNTAX_HIGHLIGHTING_RULES = "0,8,k;0,52,cppd;54,67,a;69,75,k;106,130,cppd;114,130,k;";
- private static final String SAMPLE_SYMBOLS_REFERENCES = "80,85,80,90,140;";
-
- private DecorationDataHolder decorationDataHolder;
-
- @Before
- public void setUpHighlightingContext() {
- decorationDataHolder = new DecorationDataHolder();
- decorationDataHolder.loadSyntaxHighlightingData(SAMPLE_SYNTAX_HIGHLIGHTING_RULES);
- decorationDataHolder.loadSymbolReferences(SAMPLE_SYMBOLS_REFERENCES);
- }
-
- @Test
- public void should_extract_lower_bounds_from_serialized_rules() throws Exception {
-
- List<OpeningHtmlTag> openingTagsEntries = decorationDataHolder.getOpeningTagsEntries();
-
- assertThat(openingTagsEntries.get(0)).isEqualTo(new OpeningHtmlTag(0, "k"));
- assertThat(openingTagsEntries.get(1)).isEqualTo(new OpeningHtmlTag(0, "cppd"));
- assertThat(openingTagsEntries.get(2)).isEqualTo(new OpeningHtmlTag(54, "a"));
- assertThat(openingTagsEntries.get(3)).isEqualTo(new OpeningHtmlTag(69, "k"));
- assertThat(openingTagsEntries.get(4)).isEqualTo(new OpeningHtmlTag(80, "sym-80 sym"));
- assertThat(openingTagsEntries.get(5)).isEqualTo(new OpeningHtmlTag(90, "sym-80 sym"));
- assertThat(openingTagsEntries.get(6)).isEqualTo(new OpeningHtmlTag(106, "cppd"));
- assertThat(openingTagsEntries.get(7)).isEqualTo(new OpeningHtmlTag(114, "k"));
- assertThat(openingTagsEntries.get(8)).isEqualTo(new OpeningHtmlTag(140, "sym-80 sym"));
- }
-
- @Test
- public void should_extract_upper_bounds_from_serialized_rules() throws Exception {
-
- List<Integer> offsets = decorationDataHolder.getClosingTagsOffsets();
-
- assertThat(offsets.get(0)).isEqualTo(8);
- assertThat(offsets.get(1)).isEqualTo(52);
- assertThat(offsets.get(2)).isEqualTo(67);
- assertThat(offsets.get(3)).isEqualTo(75);
- assertThat(offsets.get(4)).isEqualTo(85);
- assertThat(offsets.get(5)).isEqualTo(95);
- assertThat(offsets.get(6)).isEqualTo(130);
- assertThat(offsets.get(7)).isEqualTo(130);
- assertThat(offsets.get(8)).isEqualTo(145);
- }
-}
+++ /dev/null
-/*
- * 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.core.source;
-
-import com.google.common.collect.Lists;
-import org.apache.ibatis.session.SqlSession;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.core.persistence.AbstractDaoTestCase;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.source.db.SnapshotDataDao;
-import org.sonar.core.source.db.SnapshotSourceDao;
-
-import java.util.List;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.*;
-
-public class HtmlSourceDecoratorTest extends AbstractDaoTestCase {
-
-
- HtmlSourceDecorator sourceDecorator;
-
- @Before
- public void setUpDatasets() {
- setupData("shared");
-
- SnapshotSourceDao snapshotSourceDao = new SnapshotSourceDao(getMyBatis());
- SnapshotDataDao snapshotDataDao = new SnapshotDataDao(getMyBatis());
- sourceDecorator = new HtmlSourceDecorator(getMyBatis(), snapshotSourceDao, snapshotDataDao);
- }
-
- @Test
- public void highlight_syntax_with_html() throws Exception {
- List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml(11L);
-
- assertThat(decoratedSource).containsExactly(
- "<span class=\"cppd\">/*</span>",
- "<span class=\"cppd\"> * Header</span>",
- "<span class=\"cppd\"> */</span>",
- "",
- "<span class=\"k\">public </span><span class=\"k\">class </span>HelloWorld {",
- "}"
- );
- }
-
- @Test
- public void highlight_syntax_with_html_from_component() throws Exception {
- List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:Dispatcher", null, null);
-
- assertThat(decoratedSource).containsExactly(
- "<span class=\"cppd\">/*</span>",
- "<span class=\"cppd\"> * Header</span>",
- "<span class=\"cppd\"> */</span>",
- "",
- "<span class=\"k\">public </span><span class=\"k\">class </span>HelloWorld {",
- "}"
- );
- }
-
- @Test
- public void highlight_syntax_with_html_from_component_on_given_lines() throws Exception {
- assertThat(sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:Dispatcher", null, 2)).hasSize(2);
- assertThat(sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:Dispatcher", 2, null)).hasSize(5);
- assertThat(sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:Dispatcher", 1, 2)).hasSize(2);
- }
-
- @Test
- public void mark_symbols_with_html() throws Exception {
- List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml(12L);
-
- assertThat(decoratedSource).containsExactly(
- "/*",
- " * Header",
- " */",
- "",
- "public class <span class=\"sym-31 sym\">HelloWorld</span> {",
- "}"
- );
- }
-
- @Test
- public void mark_symbols_with_html_from_component() throws Exception {
- List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:VelocityManager", null, null);
-
- assertThat(decoratedSource).containsExactly(
- "/*",
- " * Header",
- " */",
- "",
- "public class <span class=\"sym-31 sym\">HelloWorld</span> {",
- "}"
- );
- }
-
- @Test
- public void decorate_source_with_multiple_decoration_strategies() throws Exception {
- List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml(13L);
-
- assertThat(decoratedSource).containsExactly(
- "<span class=\"cppd\">/*</span>",
- "<span class=\"cppd\"> * Header</span>",
- "<span class=\"cppd\"> */</span>",
- "",
- "<span class=\"k\">public </span><span class=\"k\">class </span><span class=\"sym-31 sym\">HelloWorld</span> {",
- " <span class=\"k\">public</span> <span class=\"k\">void</span> <span class=\"sym-58 sym\">foo</span>() {",
- " }",
- " <span class=\"k\">public</span> <span class=\"k\">void</span> <span class=\"sym-84 sym\">bar</span>() {",
- " <span class=\"sym-58 sym\">foo</span>();",
- " }",
- "}"
- );
- }
-
- @Test
- public void decorate_source_with_multiple_decoration_strategies_from_component() throws Exception {
- List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:DebuggingInterceptor", null, null);
-
- assertThat(decoratedSource).containsExactly(
- "<span class=\"cppd\">/*</span>",
- "<span class=\"cppd\"> * Header</span>",
- "<span class=\"cppd\"> */</span>",
- "",
- "<span class=\"k\">public </span><span class=\"k\">class </span><span class=\"sym-31 sym\">HelloWorld</span> {",
- " <span class=\"k\">public</span> <span class=\"k\">void</span> <span class=\"sym-58 sym\">foo</span>() {",
- " }",
- " <span class=\"k\">public</span> <span class=\"k\">void</span> <span class=\"sym-84 sym\">bar</span>() {",
- " <span class=\"sym-58 sym\">foo</span>();",
- " }",
- "}"
- );
- }
-
- @Test
- public void should_not_query_sources_if_no_snapshot_data() throws Exception {
- SnapshotSourceDao snapshotSourceDao = mock(SnapshotSourceDao.class);
- SnapshotDataDao snapshotDataDao = mock(SnapshotDataDao.class);
-
- HtmlSourceDecorator sourceDecorator = new HtmlSourceDecorator(mock(MyBatis.class), snapshotSourceDao, snapshotDataDao);
-
- sourceDecorator.getDecoratedSourceAsHtml(14L);
-
- verify(snapshotDataDao, times(1)).selectSnapshotData(14L, Lists.newArrayList("highlight_syntax", "symbol"));
- verify(snapshotSourceDao, times(0)).selectSnapshotSource(14L);
- }
-
- @Test
- public void should_not_query_sources_if_no_snapshot_data_from_component() throws Exception {
- SnapshotSourceDao snapshotSourceDao = mock(SnapshotSourceDao.class);
- SnapshotDataDao snapshotDataDao = mock(SnapshotDataDao.class);
-
- HtmlSourceDecorator sourceDecorator = new HtmlSourceDecorator(mock(MyBatis.class), snapshotSourceDao, snapshotDataDao);
-
- sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:DebuggingInterceptor", null, null);
-
- verify(snapshotDataDao, times(1)).selectSnapshotDataByComponentKey(eq("org.apache.struts:struts:DebuggingInterceptor"), eq(Lists.newArrayList("highlight_syntax", "symbol")),
- any(SqlSession.class));
- verify(snapshotSourceDao, times(0)).selectSnapshotSourceByComponentKey(eq("org.apache.struts:struts:DebuggingInterceptor"),
- any(SqlSession.class));
- }
-}
+++ /dev/null
-/*
- * 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.core.source;
-
-import org.junit.Test;
-
-import java.util.List;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.sonar.core.source.HtmlTextDecorator.CR_END_OF_LINE;
-import static org.sonar.core.source.HtmlTextDecorator.LF_END_OF_LINE;
-
-public class HtmlTextDecoratorTest {
-
- @Test
- public void should_decorate_simple_character_range() throws Exception {
-
- String packageDeclaration = "package org.sonar.core.source;";
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,7,k;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(packageDeclaration, decorationData);
-
- assertThat(htmlOutput).containsOnly("<span class=\"k\">package</span> org.sonar.core.source;");
- }
-
- @Test
- public void should_decorate_multiple_lines_characters_range() throws Exception {
-
- String firstCommentLine = "/*";
- String secondCommentLine = " * Test";
- String thirdCommentLine = " */";
-
- String blockComment = firstCommentLine + LF_END_OF_LINE
- + secondCommentLine + LF_END_OF_LINE
- + thirdCommentLine + LF_END_OF_LINE;
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,14,cppd;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(blockComment, decorationData);
-
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\">" + firstCommentLine + "</span>",
- "<span class=\"cppd\">" + secondCommentLine + "</span>",
- "<span class=\"cppd\">" + thirdCommentLine + "</span>",
- ""
- );
- }
-
- @Test
- public void should_highlight_multiple_words_in_one_line() throws Exception {
-
- String classDeclaration = "public class MyClass implements MyInterface {";
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,6,k;7,12,k;21,31,k;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(classDeclaration, decorationData);
-
- assertThat(htmlOutput).containsOnly(
- "<span class=\"k\">public</span> " +
- "<span class=\"k\">class</span> MyClass " +
- "<span class=\"k\">implements</span> MyInterface {");
- }
-
- @Test
- public void should_allow_multiple_levels_highlighting() throws Exception {
-
- String javaDocSample =
- "/**" + LF_END_OF_LINE +
- " * Creates a FormulaDecorator" + LF_END_OF_LINE +
- " *" + LF_END_OF_LINE +
- " * @param metric the metric should have an associated formula" + LF_END_OF_LINE +
- " * " + LF_END_OF_LINE +
- " * @throws IllegalArgumentException if no formula is associated to the metric" + LF_END_OF_LINE +
- " */" + LF_END_OF_LINE;
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,184,cppd;47,53,k;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javaDocSample, decorationData);
-
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\">/**</span>",
- "<span class=\"cppd\"> * Creates a FormulaDecorator</span>",
- "<span class=\"cppd\"> *</span>",
- "<span class=\"cppd\"> * @param <span class=\"k\">metric</span> the metric should have an associated formula</span>",
- "<span class=\"cppd\"> * </span>",
- "<span class=\"cppd\"> * @throws IllegalArgumentException if no formula is associated to the metric</span>",
- "<span class=\"cppd\"> */</span>",
- ""
- );
- }
-
- @Test
- public void should_support_crlf_line_breaks() throws Exception {
-
- String crlfCodeSample =
- "/**" + CR_END_OF_LINE + LF_END_OF_LINE +
- "* @return metric generated by the decorator" + CR_END_OF_LINE + LF_END_OF_LINE +
- "*/" + CR_END_OF_LINE + LF_END_OF_LINE +
- "@DependedUpon" + CR_END_OF_LINE + LF_END_OF_LINE +
- "public Metric generatesMetric() {" + CR_END_OF_LINE + LF_END_OF_LINE +
- " return metric;" + CR_END_OF_LINE + LF_END_OF_LINE +
- "}" + CR_END_OF_LINE + LF_END_OF_LINE;
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,52,cppd;54,67,a;69,75,k;106,112,k;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(crlfCodeSample, decorationData);
-
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\">/**</span>",
- "<span class=\"cppd\">* @return metric generated by the decorator</span>",
- "<span class=\"cppd\">*/</span>",
- "<span class=\"a\">@DependedUpon</span>",
- "<span class=\"k\">public</span> Metric generatesMetric() {",
- " <span class=\"k\">return</span> metric;",
- "}",
- ""
- );
- }
-
- @Test
- public void should_close_tags_at_end_of_file() throws Exception {
-
- String classDeclarationSample =
- "/*" + LF_END_OF_LINE +
- " * Header" + LF_END_OF_LINE +
- " */" + LF_END_OF_LINE +
- LF_END_OF_LINE +
- "public class HelloWorld {" + LF_END_OF_LINE +
- "}";
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,16,cppd;18,25,k;25,31,k;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(classDeclarationSample, decorationData);
-
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\">/*</span>",
- "<span class=\"cppd\"> * Header</span>",
- "<span class=\"cppd\"> */</span>",
- "",
- "<span class=\"k\">public </span><span class=\"k\">class </span>HelloWorld {",
- "}"
- );
- }
-
- @Test
- public void should_escape_markup_chars() throws Exception {
-
- String javadocWithHtml =
- "/**\n" +
- " * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.\n" +
- " * \n" +
- " * This framework can used for instance in order to :\n" +
- " * <ul>\n" +
- " * <li>Create a lexer in charge to generate a list of tokens from a character stream</li>\n" +
- " * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li>\n" +
- " * <li>Create a javadoc generator</li>\n" +
- " * <li>...</li>\n" +
- " * </ul>\n" +
- " */\n";
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,453,cppd;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javadocWithHtml, decorationData);
-
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\">/**</span>",
- "<span class=\"cppd\"> * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.</span>",
- "<span class=\"cppd\"> * </span>",
- "<span class=\"cppd\"> * This framework can used for instance in order to :</span>",
- "<span class=\"cppd\"> * <ul></span>",
- "<span class=\"cppd\"> * <li>Create a lexer in charge to generate a list of tokens from a character stream</li></span>",
- "<span class=\"cppd\"> * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li></span>",
- "<span class=\"cppd\"> * <li>Create a javadoc generator</li></span>",
- "<span class=\"cppd\"> * <li>...</li></span>",
- "<span class=\"cppd\"> * </ul></span>",
- "<span class=\"cppd\"> */</span>",
- "");
- }
-
- @Test
- public void should_escape_ampersand_char() throws Exception {
-
- String javadocWithAmpersandChar =
- "/**\n" +
- " * Definition of a dashboard.\n" +
- " * <p/>\n" +
- " * Its name and description can be retrieved using the i18n mechanism, using the keys \"dashboard.<id>.name\" and\n" +
- " * \"dashboard.<id>.description\".\n" +
- " *\n" +
- " * @since 2.13\n" +
- " */\n";
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,220,cppd;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javadocWithAmpersandChar, decorationData);
-
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\">/**</span>",
- "<span class=\"cppd\"> * Definition of a dashboard.</span>",
- "<span class=\"cppd\"> * <p/></span>",
- "<span class=\"cppd\"> * Its name and description can be retrieved using the i18n mechanism, using the keys \"dashboard.&lt;id&gt;.name\" and</span>",
- "<span class=\"cppd\"> * \"dashboard.&lt;id&gt;.description\".</span>",
- "<span class=\"cppd\"> *</span>",
- "<span class=\"cppd\"> * @since 2.13</span>",
- "<span class=\"cppd\"> */</span>",
- "");
- }
-
- @Test
- public void should_support_cr_line_breaks() throws Exception {
-
- String crCodeSample =
- "/**" + CR_END_OF_LINE +
- "* @return metric generated by the decorator" + CR_END_OF_LINE +
- "*/" + CR_END_OF_LINE +
- "@DependedUpon" + CR_END_OF_LINE +
- "public Metric generatesMetric() {" + CR_END_OF_LINE +
- " return metric;" + CR_END_OF_LINE +
- "}" + CR_END_OF_LINE;
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,50,cppd;51,64,a;65,71,k;101,107,k;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(crCodeSample, decorationData);
-
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\">/**</span>",
- "<span class=\"cppd\">* @return metric generated by the decorator</span>",
- "<span class=\"cppd\">*/</span>",
- "<span class=\"a\">@DependedUpon</span>",
- "<span class=\"k\">public</span> Metric generatesMetric() {",
- " <span class=\"k\">return</span> metric;",
- "}",
- ""
- );
-
- }
-
- @Test
- public void should_support_multiple_empty_lines_at_end_of_file() throws Exception {
-
- String classDeclarationSample =
- "/*" + LF_END_OF_LINE +
- " * Header" + LF_END_OF_LINE +
- " */" + LF_END_OF_LINE +
- LF_END_OF_LINE +
- "public class HelloWorld {" + LF_END_OF_LINE +
- "}" + LF_END_OF_LINE + LF_END_OF_LINE + LF_END_OF_LINE;
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,16,cppd;18,25,k;25,31,k;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(classDeclarationSample, decorationData);
-
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\">/*</span>",
- "<span class=\"cppd\"> * Header</span>",
- "<span class=\"cppd\"> */</span>",
- "",
- "<span class=\"k\">public </span><span class=\"k\">class </span>HelloWorld {",
- "}",
- "",
- "",
- ""
- );
- }
-
- @Test
- public void begin_from_given_line() throws Exception {
-
- String javadocWithHtml =
- "/**\n" +
- " * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.\n" +
- " * \n" +
- " * This framework can used for instance in order to :\n" +
- " * <ul>\n" +
- " * <li>Create a lexer in charge to generate a list of tokens from a character stream</li>\n" +
- " * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li>\n" +
- " * <li>Create a javadoc generator</li>\n" +
- " * <li>...</li>\n" +
- " * </ul>\n" +
- " */\n";
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,453,cppd;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javadocWithHtml, decorationData, 4, null);
- assertThat(htmlOutput).hasSize(9);
-
- // Begin from line 4
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\"> * This framework can used for instance in order to :</span>",
- "<span class=\"cppd\"> * <ul></span>",
- "<span class=\"cppd\"> * <li>Create a lexer in charge to generate a list of tokens from a character stream</li></span>",
- "<span class=\"cppd\"> * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li></span>",
- "<span class=\"cppd\"> * <li>Create a javadoc generator</li></span>",
- "<span class=\"cppd\"> * <li>...</li></span>",
- "<span class=\"cppd\"> * </ul></span>",
- "<span class=\"cppd\"> */</span>",
- "");
- }
-
- @Test
- public void end_to_given_line() throws Exception {
-
- String javadocWithHtml =
- "/**\n" +
- " * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.\n" +
- " * \n" +
- " * This framework can used for instance in order to :\n" +
- " * <ul>\n" +
- " * <li>Create a lexer in charge to generate a list of tokens from a character stream</li>\n" +
- " * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li>\n" +
- " * <li>Create a javadoc generator</li>\n" +
- " * <li>...</li>\n" +
- " * </ul>\n" +
- " */\n";
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,453,cppd;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javadocWithHtml, decorationData, null, 4);
- assertThat(htmlOutput).hasSize(4);
-
- // End at line 4
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\">/**</span>",
- "<span class=\"cppd\"> * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.</span>",
- "<span class=\"cppd\"> * </span>",
- "<span class=\"cppd\"> * This framework can used for instance in order to :</span>");
- }
-
- @Test
- public void return_code_from_given_lint_given_end_line() throws Exception {
-
- String javadocWithHtml =
- "/**\n" +
- " * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.\n" +
- " * \n" +
- " * This framework can used for instance in order to :\n" +
- " * <ul>\n" +
- " * <li>Create a lexer in charge to generate a list of tokens from a character stream</li>\n" +
- " * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li>\n" +
- " * <li>Create a javadoc generator</li>\n" +
- " * <li>...</li>\n" +
- " * </ul>\n" +
- " */\n";
-
- DecorationDataHolder decorationData = new DecorationDataHolder();
- decorationData.loadSyntaxHighlightingData("0,453,cppd;");
-
- HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
- List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javadocWithHtml, decorationData, 4, 8);
- assertThat(htmlOutput).hasSize(5);
-
- // Begin from line 4 and finish at line 8
- assertThat(htmlOutput).containsExactly(
- "<span class=\"cppd\"> * This framework can used for instance in order to :</span>",
- "<span class=\"cppd\"> * <ul></span>",
- "<span class=\"cppd\"> * <li>Create a lexer in charge to generate a list of tokens from a character stream</li></span>",
- "<span class=\"cppd\"> * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li></span>",
- "<span class=\"cppd\"> * <li>Create a javadoc generator</li></span>"
- );
- }
-}
+++ /dev/null
-<dataset>
-
- <projects id="1" kee="org.apache.struts:struts:Dispatcher" enabled="[true]"/>
- <projects id="2" kee="org.apache.struts:struts:VelocityManager" enabled="[true]"/>
- <projects id="3" kee="org.apache.struts:struts:DebuggingInterceptor" enabled="[true]"/>
-
- <snapshots id="11" project_id="1" islast="[true]" />
- <snapshots id="12" project_id="2" islast="[true]" />
- <snapshots id="13" project_id="3" islast="[true]" />
- <snapshots id="14" project_id="3" islast="[true]" />
-
- <snapshot_data id="101" resource_id="1" snapshot_id="11" snapshot_data="0,16,cppd;18,25,k;25,31,k;" data_type="highlight_syntax" />
- <snapshot_data id="102" resource_id="2" snapshot_id="12" snapshot_data="31,41,31;" data_type="symbol" />
- <snapshot_data id="103" resource_id="3" snapshot_id="13" snapshot_data="0,16,cppd;18,25,k;25,31,k;46,52,k;53,57,k;72,78,k;79,83,k;" data_type="highlight_syntax" />
- <snapshot_data id="104" resource_id="3" snapshot_id="13" snapshot_data="31,41,31;58,61,58,96;84,87,84;" data_type="symbol" />
-
- <snapshot_sources id="101" snapshot_id="11" data="/* * Header */ public class HelloWorld { }" />
- <snapshot_sources id="102" snapshot_id="12" data="/* * Header */ public class HelloWorld { }" />
- <snapshot_sources id="103" snapshot_id="13" data="/* * Header */ public class HelloWorld { public void foo() { } public void bar() { foo(); } }" />
-</dataset>
import org.sonar.core.purge.PurgeProfiler;
import org.sonar.core.resource.DefaultResourcePermissions;
import org.sonar.core.rule.DefaultRuleFinder;
-import org.sonar.core.source.HtmlSourceDecorator;
import org.sonar.core.technicaldebt.*;
import org.sonar.core.test.TestPlanPerspectiveLoader;
import org.sonar.core.test.TestablePerspectiveLoader;
import org.sonar.server.qualityprofile.*;
import org.sonar.server.rule.*;
import org.sonar.server.rule.ws.*;
+import org.sonar.server.source.HtmlSourceDecorator;
import org.sonar.server.source.SourceService;
import org.sonar.server.source.ws.SourcesShowWsHandler;
import org.sonar.server.source.ws.SourcesWs;
--- /dev/null
+/*
+ * 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.server.source;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+/**
+ * @since 3.6
+ */
+class CharactersReader {
+
+ static final int END_OF_STREAM = -1;
+
+ private final BufferedReader stringBuffer;
+ private final Deque<String> openTags;
+
+ private int currentValue;
+ private int previousValue;
+ private int currentIndex = -1;
+
+ public CharactersReader(BufferedReader stringBuffer) {
+ this.stringBuffer = stringBuffer;
+ this.openTags = new ArrayDeque<String>();
+ }
+
+ boolean readNextChar() throws IOException {
+ previousValue = currentValue;
+ currentValue = stringBuffer.read();
+ currentIndex++;
+ return currentValue != END_OF_STREAM;
+ }
+
+ int getCurrentValue() {
+ return currentValue;
+ }
+
+ int getPreviousValue() {
+ return previousValue;
+ }
+
+ int getCurrentIndex() {
+ return currentIndex;
+ }
+
+ void registerOpenTag(String textType) {
+ openTags.push(textType);
+ }
+
+ void removeLastOpenTag() {
+ openTags.remove();
+ }
+
+ Deque<String> getOpenTags() {
+ return openTags;
+ }
+}
--- /dev/null
+/*
+ * 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.server.source;
+
+import com.google.common.collect.Lists;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+class DecorationDataHolder {
+
+ private static final String ENTITY_SEPARATOR = ";";
+ private static final String FIELD_SEPARATOR = ",";
+ private static final String SYMBOL_PREFIX = "sym-";
+ private static final String HIGHLIGHTABLE = "sym";
+
+ private List<OpeningHtmlTag> openingTagsEntries;
+ private int openingTagsIndex;
+ private List<Integer> closingTagsOffsets;
+ private int closingTagsIndex;
+
+ DecorationDataHolder() {
+ openingTagsEntries = Lists.newArrayList();
+ closingTagsOffsets = Lists.newArrayList();
+ }
+
+ void loadSymbolReferences(String symbolsReferences) {
+ String[] symbols = symbolsReferences.split(ENTITY_SEPARATOR);
+ for (String symbol : symbols) {
+ String[] symbolFields = symbol.split(FIELD_SEPARATOR);
+ int declarationStartOffset = Integer.parseInt(symbolFields[0]);
+ int declarationEndOffset = Integer.parseInt(symbolFields[1]);
+ int symbolLength = declarationEndOffset - declarationStartOffset;
+ String[] symbolOccurrences = Arrays.copyOfRange(symbolFields, 2, symbolFields.length);
+ loadSymbolOccurrences(declarationStartOffset, symbolLength, symbolOccurrences);
+ }
+ }
+
+ void loadSyntaxHighlightingData(String syntaxHighlightingRules) {
+ String[] rules = syntaxHighlightingRules.split(ENTITY_SEPARATOR);
+ for (String rule : rules) {
+ String[] ruleFields = rule.split(FIELD_SEPARATOR);
+ insertAndPreserveOrder(new OpeningHtmlTag(Integer.parseInt(ruleFields[0]), ruleFields[2]), openingTagsEntries);
+ insertAndPreserveOrder(Integer.parseInt(ruleFields[1]), closingTagsOffsets);
+ }
+ }
+
+ List<OpeningHtmlTag> getOpeningTagsEntries() {
+ return openingTagsEntries;
+ }
+
+ OpeningHtmlTag getCurrentOpeningTagEntry() {
+ return openingTagsIndex < openingTagsEntries.size() ? openingTagsEntries.get(openingTagsIndex) : null;
+ }
+
+ void nextOpeningTagEntry() {
+ openingTagsIndex++;
+ }
+
+ List<Integer> getClosingTagsOffsets() {
+ return closingTagsOffsets;
+ }
+
+ int getCurrentClosingTagOffset() {
+ return closingTagsIndex < closingTagsOffsets.size() ? closingTagsOffsets.get(closingTagsIndex) : -1;
+ }
+
+ void nextClosingTagOffset() {
+ closingTagsIndex++;
+ }
+
+ private void loadSymbolOccurrences(int declarationStartOffset, int symbolLength, String[] symbolOccurrences) {
+ for (String symbolOccurrence : symbolOccurrences) {
+ int occurrenceStartOffset = Integer.parseInt(symbolOccurrence);
+ int occurrenceEndOffset = occurrenceStartOffset + symbolLength;
+ insertAndPreserveOrder(new OpeningHtmlTag(occurrenceStartOffset, SYMBOL_PREFIX + declarationStartOffset + " " + HIGHLIGHTABLE), openingTagsEntries);
+ insertAndPreserveOrder(occurrenceEndOffset, closingTagsOffsets);
+ }
+ }
+
+ private void insertAndPreserveOrder(OpeningHtmlTag newEntry, List<OpeningHtmlTag> openingHtmlTags) {
+ int insertionIndex = 0;
+ Iterator<OpeningHtmlTag> tagIterator = openingHtmlTags.iterator();
+ while (tagIterator.hasNext() && tagIterator.next().getStartOffset() <= newEntry.getStartOffset()) {
+ insertionIndex++;
+ }
+ openingHtmlTags.add(insertionIndex, newEntry);
+ }
+
+ private void insertAndPreserveOrder(int newOffset, List<Integer> orderedOffsets) {
+ int insertionIndex = 0;
+ Iterator<Integer> entriesIterator = orderedOffsets.iterator();
+ while (entriesIterator.hasNext() && entriesIterator.next() <= newOffset) {
+ insertionIndex++;
+ }
+ orderedOffsets.add(insertionIndex, newOffset);
+ }
+}
--- /dev/null
+/*
+ * 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.server.source;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.apache.ibatis.session.SqlSession;
+import org.sonar.api.ServerComponent;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.source.SnapshotDataTypes;
+import org.sonar.core.source.db.SnapshotDataDao;
+import org.sonar.core.source.db.SnapshotDataDto;
+import org.sonar.core.source.db.SnapshotSourceDao;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+
+public class HtmlSourceDecorator implements ServerComponent {
+
+ private final MyBatis mybatis;
+
+ private final SnapshotSourceDao snapshotSourceDao;
+ private final SnapshotDataDao snapshotDataDao;
+
+ public HtmlSourceDecorator(MyBatis mybatis, SnapshotSourceDao snapshotSourceDao, SnapshotDataDao snapshotDataDao) {
+ this.mybatis = mybatis;
+ this.snapshotSourceDao = snapshotSourceDao;
+ this.snapshotDataDao = snapshotDataDao;
+ }
+
+ @CheckForNull
+ public List<String> getDecoratedSourceAsHtml(String componentKey, @Nullable Integer from, @Nullable Integer to) {
+ SqlSession session = mybatis.openSession();
+ try {
+ Collection<SnapshotDataDto> snapshotDataEntries = snapshotDataDao.selectSnapshotDataByComponentKey(componentKey, highlightingDataTypes(), session);
+ if (!snapshotDataEntries.isEmpty()) {
+ String snapshotSource = snapshotSourceDao.selectSnapshotSourceByComponentKey(componentKey, session);
+ return decorate(snapshotSource, snapshotDataEntries, from, to);
+ }
+ return null;
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ @CheckForNull
+ public List<String> getDecoratedSourceAsHtml(long snapshotId) {
+ Collection<SnapshotDataDto> snapshotDataEntries = snapshotDataDao.selectSnapshotData(snapshotId, highlightingDataTypes());
+ if (!snapshotDataEntries.isEmpty()) {
+ String snapshotSource = snapshotSourceDao.selectSnapshotSource(snapshotId);
+ if (snapshotSource != null) {
+ return decorate(snapshotSource, snapshotDataEntries, null, null);
+ }
+ }
+ return null;
+ }
+
+ @CheckForNull
+ private List<String> decorate(@Nullable String snapshotSource, Collection<SnapshotDataDto> snapshotDataEntries, @Nullable Integer from, @Nullable Integer to) {
+ if (snapshotSource != null) {
+ DecorationDataHolder decorationDataHolder = new DecorationDataHolder();
+ for (SnapshotDataDto snapshotDataEntry : snapshotDataEntries) {
+ loadSnapshotData(decorationDataHolder, snapshotDataEntry);
+ }
+
+ HtmlTextDecorator textDecorator = new HtmlTextDecorator();
+ return textDecorator.decorateTextWithHtml(snapshotSource, decorationDataHolder, from, to);
+ }
+ return null;
+ }
+
+ private List<String> highlightingDataTypes() {
+ return Lists.newArrayList(SnapshotDataTypes.SYNTAX_HIGHLIGHTING,
+ SnapshotDataTypes.SYMBOL_HIGHLIGHTING);
+ }
+
+ private void loadSnapshotData(DecorationDataHolder dataHolder, SnapshotDataDto entry) {
+ if (!Strings.isNullOrEmpty(entry.getData())) {
+ if (SnapshotDataTypes.SYNTAX_HIGHLIGHTING.equals(entry.getDataType())) {
+ dataHolder.loadSyntaxHighlightingData(entry.getData());
+ } else if (SnapshotDataTypes.SYMBOL_HIGHLIGHTING.equals(entry.getDataType())) {
+ dataHolder.loadSymbolReferences(entry.getData());
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.server.source;
+
+import com.google.common.collect.Lists;
+import com.google.common.io.Closeables;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.List;
+
+
+
+/**
+ * @since 3.6
+ */
+class HtmlTextDecorator {
+
+ static final char CR_END_OF_LINE = '\r';
+ static final char LF_END_OF_LINE = '\n';
+ static final char HTML_OPENING = '<';
+ static final char HTML_CLOSING = '>';
+ static final char AMPERSAND = '&';
+ static final String ENCODED_HTML_OPENING = "<";
+ static final String ENCODED_HTML_CLOSING = ">";
+ static final String ENCODED_AMPERSAND = "&";
+
+ List<String> decorateTextWithHtml(String text, DecorationDataHolder decorationDataHolder) {
+ return decorateTextWithHtml(text, decorationDataHolder, null, null);
+ }
+
+ List<String> decorateTextWithHtml(String text, DecorationDataHolder decorationDataHolder, @Nullable Integer from, @Nullable Integer to) {
+
+ StringBuilder currentHtmlLine = new StringBuilder();
+ List<String> decoratedHtmlLines = Lists.newArrayList();
+ int currentLine = 1;
+
+ BufferedReader stringBuffer = null;
+ try {
+ stringBuffer = new BufferedReader(new StringReader(text));
+
+ CharactersReader charsReader = new CharactersReader(stringBuffer);
+
+ while (charsReader.readNextChar()) {
+
+ if (shouldStartNewLine(charsReader)) {
+ addLine(decoratedHtmlLines, currentHtmlLine.toString(), currentLine, from, to);
+ currentLine++;
+ currentHtmlLine = new StringBuilder();
+ if (shouldReopenPendingTags(charsReader)) {
+ reopenCurrentSyntaxTags(charsReader, currentHtmlLine);
+ }
+ }
+
+ int numberOfTagsToClose = getNumberOfTagsToClose(charsReader.getCurrentIndex(), decorationDataHolder);
+ closeCompletedTags(charsReader, numberOfTagsToClose, currentHtmlLine);
+
+ if (shouldClosePendingTags(charsReader)) {
+ closeCurrentSyntaxTags(charsReader, currentHtmlLine);
+ }
+
+ Collection<String> tagsToOpen = getTagsToOpen(charsReader.getCurrentIndex(), decorationDataHolder);
+ openNewTags(charsReader, tagsToOpen, currentHtmlLine);
+
+ if (shouldAppendCharToHtmlOutput(charsReader)) {
+ char currentChar = (char) charsReader.getCurrentValue();
+ currentHtmlLine.append(normalize(currentChar));
+ }
+ }
+
+ closeCurrentSyntaxTags(charsReader, currentHtmlLine);
+
+ if (shouldStartNewLine(charsReader)) {
+ addLine(decoratedHtmlLines, currentHtmlLine.toString(), currentLine, from, to);
+ currentLine++;
+ addLine(decoratedHtmlLines, "", currentLine, from, to);
+ currentLine++;
+ } else if (currentHtmlLine.length() > 0) {
+ addLine(decoratedHtmlLines, currentHtmlLine.toString(), currentLine, from, to);
+ currentLine++;
+ }
+
+ } catch (IOException exception) {
+ String errorMsg = "An exception occurred while highlighting the syntax of one of the project's files";
+ LoggerFactory.getLogger(HtmlTextDecorator.class).error(errorMsg);
+ throw new IllegalStateException(errorMsg, exception);
+ } finally {
+ Closeables.closeQuietly(stringBuffer);
+ }
+
+ return decoratedHtmlLines;
+ }
+
+ private void addLine(List<String> decoratedHtmlLines, String line, int currentLine, @Nullable Integer from, @Nullable Integer to) {
+ if ((from == null || currentLine >= from)
+ && (to == null || to >= currentLine)) {
+ decoratedHtmlLines.add(line);
+ }
+ }
+
+ private char[] normalize(char currentChar) {
+ char[] normalizedChars;
+ if (currentChar == HTML_OPENING) {
+ normalizedChars = ENCODED_HTML_OPENING.toCharArray();
+ } else if (currentChar == HTML_CLOSING) {
+ normalizedChars = ENCODED_HTML_CLOSING.toCharArray();
+ } else if (currentChar == AMPERSAND) {
+ normalizedChars = ENCODED_AMPERSAND.toCharArray();
+ } else {
+ normalizedChars = new char[]{currentChar};
+ }
+ return normalizedChars;
+ }
+
+ private boolean shouldAppendCharToHtmlOutput(CharactersReader charsReader) {
+ return charsReader.getCurrentValue() != CR_END_OF_LINE && charsReader.getCurrentValue() != LF_END_OF_LINE;
+ }
+
+ private int getNumberOfTagsToClose(int currentIndex, DecorationDataHolder dataHolder) {
+ int numberOfTagsToClose = 0;
+
+ while (currentIndex == dataHolder.getCurrentClosingTagOffset()) {
+ numberOfTagsToClose++;
+ dataHolder.nextClosingTagOffset();
+ }
+ return numberOfTagsToClose;
+ }
+
+ private Collection<String> getTagsToOpen(int currentIndex, DecorationDataHolder dataHolder) {
+ Collection<String> tagsToOpen = Lists.newArrayList();
+ while (dataHolder.getCurrentOpeningTagEntry() != null && currentIndex == dataHolder.getCurrentOpeningTagEntry().getStartOffset()) {
+ tagsToOpen.add(dataHolder.getCurrentOpeningTagEntry().getCssClass());
+ dataHolder.nextOpeningTagEntry();
+ }
+ return tagsToOpen;
+ }
+
+ private boolean shouldClosePendingTags(CharactersReader charactersReader) {
+ return charactersReader.getCurrentValue() == CR_END_OF_LINE
+ || (charactersReader.getCurrentValue() == LF_END_OF_LINE && charactersReader.getPreviousValue() != CR_END_OF_LINE)
+ || (charactersReader.getCurrentValue() == CharactersReader.END_OF_STREAM && charactersReader.getPreviousValue() != LF_END_OF_LINE);
+ }
+
+ private boolean shouldReopenPendingTags(CharactersReader charactersReader) {
+ return (charactersReader.getPreviousValue() == LF_END_OF_LINE && charactersReader.getCurrentValue() != LF_END_OF_LINE)
+ || (charactersReader.getPreviousValue() == CR_END_OF_LINE && charactersReader.getCurrentValue() != CR_END_OF_LINE
+ && charactersReader.getCurrentValue() != LF_END_OF_LINE);
+ }
+
+ private boolean shouldStartNewLine(CharactersReader charactersReader) {
+ return charactersReader.getPreviousValue() == LF_END_OF_LINE
+ || (charactersReader.getPreviousValue() == CR_END_OF_LINE && charactersReader.getCurrentValue() != LF_END_OF_LINE);
+ }
+
+ private void closeCompletedTags(CharactersReader charactersReader, int numberOfTagsToClose,
+ StringBuilder decoratedText) {
+ for (int i = 0; i < numberOfTagsToClose; i++) {
+ injectClosingHtml(decoratedText);
+ charactersReader.removeLastOpenTag();
+ }
+ }
+
+ private void openNewTags(CharactersReader charactersReader, Collection<String> tagsToOpen,
+ StringBuilder decoratedText) {
+ for (String tagToOpen : tagsToOpen) {
+ injectOpeningHtmlForRule(tagToOpen, decoratedText);
+ charactersReader.registerOpenTag(tagToOpen);
+ }
+ }
+
+ private void closeCurrentSyntaxTags(CharactersReader charactersReader, StringBuilder decoratedText) {
+ for (int i = 0; i < charactersReader.getOpenTags().size(); i++) {
+ injectClosingHtml(decoratedText);
+ }
+ }
+
+ private void reopenCurrentSyntaxTags(CharactersReader charactersReader, StringBuilder decoratedText) {
+ for (String tags : charactersReader.getOpenTags()) {
+ injectOpeningHtmlForRule(tags, decoratedText);
+ }
+ }
+
+ private void injectOpeningHtmlForRule(String textType, StringBuilder decoratedText) {
+ decoratedText.append("<span class=\"").append(textType).append("\">");
+ }
+
+ private void injectClosingHtml(StringBuilder decoratedText) {
+ decoratedText.append("</span>");
+ }
+}
--- /dev/null
+/*
+ * 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.server.source;
+
+class OpeningHtmlTag {
+
+ private final int startOffset;
+ private final String cssClass;
+
+ OpeningHtmlTag(int startOffset, String cssClass) {
+ this.startOffset = startOffset;
+ this.cssClass = cssClass;
+ }
+
+ int getStartOffset() {
+ return startOffset;
+ }
+
+ String getCssClass() {
+ return cssClass;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ return compareTo((OpeningHtmlTag) o);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = startOffset;
+ 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;
+ }
+}
package org.sonar.server.source;
import org.sonar.api.ServerComponent;
+import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.web.UserRole;
import org.sonar.core.measure.db.MeasureDataDao;
import org.sonar.core.measure.db.MeasureDataDto;
import org.sonar.core.resource.ResourceDao;
import org.sonar.core.resource.ResourceDto;
-import org.sonar.core.source.HtmlSourceDecorator;
import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.ui.CodeColorizers;
import org.sonar.server.user.UserSession;
import javax.annotation.CheckForNull;
public class SourceService implements ServerComponent {
+
private final HtmlSourceDecorator sourceDecorator;
+
+ /**
+ * Old service to colorize code
+ */
+ private final CodeColorizers codeColorizers;
+
private final ResourceDao resourceDao;
private final MeasureDataDao measureDataDao;
- public SourceService(HtmlSourceDecorator sourceDecorator, ResourceDao resourceDao, MeasureDataDao measureDataDao) {
+ public SourceService(HtmlSourceDecorator sourceDecorator, CodeColorizers codeColorizers, ResourceDao resourceDao, MeasureDataDao measureDataDao) {
this.sourceDecorator = sourceDecorator;
+ this.codeColorizers = codeColorizers;
this.resourceDao = resourceDao;
this.measureDataDao = measureDataDao;
}
return sourceDecorator.getDecoratedSourceAsHtml(componentKey, from, to);
}
- // TODO move this in another service
@CheckForNull
- public String findDataFromComponent(String componentKey, String metricKey){
+ public String getScmAuthorData(String componentKey) {
+ return findDataFromComponent(componentKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY);
+ }
+
+ @CheckForNull
+ public String getScmDateData(String componentKey) {
+ return findDataFromComponent(componentKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY);
+ }
+
+ @CheckForNull
+ private String findDataFromComponent(String componentKey, String metricKey) {
MeasureDataDto data = measureDataDao.findByComponentKeyAndMetricKey(componentKey, metricKey);
if (data != null) {
return data.getText();
package org.sonar.server.source.ws;
import com.google.common.base.Splitter;
-import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.RequestHandler;
import org.sonar.api.server.ws.Response;
throw new NotFoundException("Component : " + componentKey + " has no source.");
}
- String scmAuthorData = sourceService.findDataFromComponent(componentKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY);
- String scmDataData = sourceService.findDataFromComponent(componentKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY);
+ String scmAuthorData = sourceService.getScmAuthorData(componentKey);
+ String scmDataData = sourceService.getScmDateData(componentKey);
int from = fromParam != null ? fromParam : 1;
int to = toParam != null ? toParam : sourceHtml.size() + from;
import org.apache.commons.lang.StringEscapeUtils;
import org.sonar.api.ServerComponent;
-import org.sonar.core.source.HtmlSourceDecorator;
import org.sonar.markdown.Markdown;
+import org.sonar.server.source.HtmlSourceDecorator;
import java.util.List;
end
panel.html_lines=[]
- html_source_lines = snapshot.highlighted_source_lines || snapshot.source.syntax_highlighted_lines()
+ html_source_lines = Internal.text.highlightedSourceLines(snapshot.id) || snapshot.source.syntax_highlighted_lines()
line_range=sanitize_range(options[:line_range], 1..html_source_lines.length)
html_source_lines.each_with_index do |source, index|
end
end
- def highlighted_source_lines
- Internal.text.highlightedSourceLines(id)
- end
-
def has_source
SnapshotSource.count('id', :conditions => "snapshot_id = #{id}") > 0
end
--- /dev/null
+/*
+ * 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.server.source;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class DecorationDataHolderTest {
+
+ private static final String SAMPLE_SYNTAX_HIGHLIGHTING_RULES = "0,8,k;0,52,cppd;54,67,a;69,75,k;106,130,cppd;114,130,k;";
+ private static final String SAMPLE_SYMBOLS_REFERENCES = "80,85,80,90,140;";
+
+ private DecorationDataHolder decorationDataHolder;
+
+ @Before
+ public void setUpHighlightingContext() {
+ decorationDataHolder = new DecorationDataHolder();
+ decorationDataHolder.loadSyntaxHighlightingData(SAMPLE_SYNTAX_HIGHLIGHTING_RULES);
+ decorationDataHolder.loadSymbolReferences(SAMPLE_SYMBOLS_REFERENCES);
+ }
+
+ @Test
+ public void should_extract_lower_bounds_from_serialized_rules() throws Exception {
+
+ List<OpeningHtmlTag> openingTagsEntries = decorationDataHolder.getOpeningTagsEntries();
+
+ assertThat(openingTagsEntries.get(0)).isEqualTo(new OpeningHtmlTag(0, "k"));
+ assertThat(openingTagsEntries.get(1)).isEqualTo(new OpeningHtmlTag(0, "cppd"));
+ assertThat(openingTagsEntries.get(2)).isEqualTo(new OpeningHtmlTag(54, "a"));
+ assertThat(openingTagsEntries.get(3)).isEqualTo(new OpeningHtmlTag(69, "k"));
+ assertThat(openingTagsEntries.get(4)).isEqualTo(new OpeningHtmlTag(80, "sym-80 sym"));
+ assertThat(openingTagsEntries.get(5)).isEqualTo(new OpeningHtmlTag(90, "sym-80 sym"));
+ assertThat(openingTagsEntries.get(6)).isEqualTo(new OpeningHtmlTag(106, "cppd"));
+ assertThat(openingTagsEntries.get(7)).isEqualTo(new OpeningHtmlTag(114, "k"));
+ assertThat(openingTagsEntries.get(8)).isEqualTo(new OpeningHtmlTag(140, "sym-80 sym"));
+ }
+
+ @Test
+ public void should_extract_upper_bounds_from_serialized_rules() throws Exception {
+
+ List<Integer> offsets = decorationDataHolder.getClosingTagsOffsets();
+
+ assertThat(offsets.get(0)).isEqualTo(8);
+ assertThat(offsets.get(1)).isEqualTo(52);
+ assertThat(offsets.get(2)).isEqualTo(67);
+ assertThat(offsets.get(3)).isEqualTo(75);
+ assertThat(offsets.get(4)).isEqualTo(85);
+ assertThat(offsets.get(5)).isEqualTo(95);
+ assertThat(offsets.get(6)).isEqualTo(130);
+ assertThat(offsets.get(7)).isEqualTo(130);
+ assertThat(offsets.get(8)).isEqualTo(145);
+ }
+}
--- /dev/null
+/*
+ * 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.server.source;
+
+import com.google.common.collect.Lists;
+import org.apache.ibatis.session.SqlSession;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.core.persistence.AbstractDaoTestCase;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.source.db.SnapshotDataDao;
+import org.sonar.core.source.db.SnapshotSourceDao;
+
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+
+public class HtmlSourceDecoratorTest extends AbstractDaoTestCase {
+
+
+ HtmlSourceDecorator sourceDecorator;
+
+ @Before
+ public void setUpDatasets() {
+ setupData("shared");
+
+ SnapshotSourceDao snapshotSourceDao = new SnapshotSourceDao(getMyBatis());
+ SnapshotDataDao snapshotDataDao = new SnapshotDataDao(getMyBatis());
+ sourceDecorator = new HtmlSourceDecorator(getMyBatis(), snapshotSourceDao, snapshotDataDao);
+ }
+
+ @Test
+ public void highlight_syntax_with_html() throws Exception {
+ List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml(11L);
+
+ assertThat(decoratedSource).containsExactly(
+ "<span class=\"cppd\">/*</span>",
+ "<span class=\"cppd\"> * Header</span>",
+ "<span class=\"cppd\"> */</span>",
+ "",
+ "<span class=\"k\">public </span><span class=\"k\">class </span>HelloWorld {",
+ "}"
+ );
+ }
+
+ @Test
+ public void highlight_syntax_with_html_from_component() throws Exception {
+ List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:Dispatcher", null, null);
+
+ assertThat(decoratedSource).containsExactly(
+ "<span class=\"cppd\">/*</span>",
+ "<span class=\"cppd\"> * Header</span>",
+ "<span class=\"cppd\"> */</span>",
+ "",
+ "<span class=\"k\">public </span><span class=\"k\">class </span>HelloWorld {",
+ "}"
+ );
+ }
+
+ @Test
+ public void highlight_syntax_with_html_from_component_on_given_lines() throws Exception {
+ assertThat(sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:Dispatcher", null, 2)).hasSize(2);
+ assertThat(sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:Dispatcher", 2, null)).hasSize(5);
+ assertThat(sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:Dispatcher", 1, 2)).hasSize(2);
+ }
+
+ @Test
+ public void mark_symbols_with_html() throws Exception {
+ List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml(12L);
+
+ assertThat(decoratedSource).containsExactly(
+ "/*",
+ " * Header",
+ " */",
+ "",
+ "public class <span class=\"sym-31 sym\">HelloWorld</span> {",
+ "}"
+ );
+ }
+
+ @Test
+ public void mark_symbols_with_html_from_component() throws Exception {
+ List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:VelocityManager", null, null);
+
+ assertThat(decoratedSource).containsExactly(
+ "/*",
+ " * Header",
+ " */",
+ "",
+ "public class <span class=\"sym-31 sym\">HelloWorld</span> {",
+ "}"
+ );
+ }
+
+ @Test
+ public void decorate_source_with_multiple_decoration_strategies() throws Exception {
+ List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml(13L);
+
+ assertThat(decoratedSource).containsExactly(
+ "<span class=\"cppd\">/*</span>",
+ "<span class=\"cppd\"> * Header</span>",
+ "<span class=\"cppd\"> */</span>",
+ "",
+ "<span class=\"k\">public </span><span class=\"k\">class </span><span class=\"sym-31 sym\">HelloWorld</span> {",
+ " <span class=\"k\">public</span> <span class=\"k\">void</span> <span class=\"sym-58 sym\">foo</span>() {",
+ " }",
+ " <span class=\"k\">public</span> <span class=\"k\">void</span> <span class=\"sym-84 sym\">bar</span>() {",
+ " <span class=\"sym-58 sym\">foo</span>();",
+ " }",
+ "}"
+ );
+ }
+
+ @Test
+ public void decorate_source_with_multiple_decoration_strategies_from_component() throws Exception {
+ List<String> decoratedSource = sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:DebuggingInterceptor", null, null);
+
+ assertThat(decoratedSource).containsExactly(
+ "<span class=\"cppd\">/*</span>",
+ "<span class=\"cppd\"> * Header</span>",
+ "<span class=\"cppd\"> */</span>",
+ "",
+ "<span class=\"k\">public </span><span class=\"k\">class </span><span class=\"sym-31 sym\">HelloWorld</span> {",
+ " <span class=\"k\">public</span> <span class=\"k\">void</span> <span class=\"sym-58 sym\">foo</span>() {",
+ " }",
+ " <span class=\"k\">public</span> <span class=\"k\">void</span> <span class=\"sym-84 sym\">bar</span>() {",
+ " <span class=\"sym-58 sym\">foo</span>();",
+ " }",
+ "}"
+ );
+ }
+
+ @Test
+ public void should_not_query_sources_if_no_snapshot_data() throws Exception {
+ SnapshotSourceDao snapshotSourceDao = mock(SnapshotSourceDao.class);
+ SnapshotDataDao snapshotDataDao = mock(SnapshotDataDao.class);
+
+ HtmlSourceDecorator sourceDecorator = new HtmlSourceDecorator(mock(MyBatis.class), snapshotSourceDao, snapshotDataDao);
+
+ sourceDecorator.getDecoratedSourceAsHtml(14L);
+
+ verify(snapshotDataDao, times(1)).selectSnapshotData(14L, Lists.newArrayList("highlight_syntax", "symbol"));
+ verify(snapshotSourceDao, times(0)).selectSnapshotSource(14L);
+ }
+
+ @Test
+ public void should_not_query_sources_if_no_snapshot_data_from_component() throws Exception {
+ SnapshotSourceDao snapshotSourceDao = mock(SnapshotSourceDao.class);
+ SnapshotDataDao snapshotDataDao = mock(SnapshotDataDao.class);
+
+ HtmlSourceDecorator sourceDecorator = new HtmlSourceDecorator(mock(MyBatis.class), snapshotSourceDao, snapshotDataDao);
+
+ sourceDecorator.getDecoratedSourceAsHtml("org.apache.struts:struts:DebuggingInterceptor", null, null);
+
+ verify(snapshotDataDao, times(1)).selectSnapshotDataByComponentKey(eq("org.apache.struts:struts:DebuggingInterceptor"), eq(Lists.newArrayList("highlight_syntax", "symbol")),
+ any(SqlSession.class));
+ verify(snapshotSourceDao, times(0)).selectSnapshotSourceByComponentKey(eq("org.apache.struts:struts:DebuggingInterceptor"),
+ any(SqlSession.class));
+ }
+}
--- /dev/null
+/*
+ * 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.server.source;
+
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.sonar.server.source.HtmlTextDecorator.CR_END_OF_LINE;
+import static org.sonar.server.source.HtmlTextDecorator.LF_END_OF_LINE;
+
+public class HtmlTextDecoratorTest {
+
+ @Test
+ public void should_decorate_simple_character_range() throws Exception {
+
+ String packageDeclaration = "package org.sonar.core.source;";
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,7,k;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(packageDeclaration, decorationData);
+
+ assertThat(htmlOutput).containsOnly("<span class=\"k\">package</span> org.sonar.core.source;");
+ }
+
+ @Test
+ public void should_decorate_multiple_lines_characters_range() throws Exception {
+
+ String firstCommentLine = "/*";
+ String secondCommentLine = " * Test";
+ String thirdCommentLine = " */";
+
+ String blockComment = firstCommentLine + LF_END_OF_LINE
+ + secondCommentLine + LF_END_OF_LINE
+ + thirdCommentLine + LF_END_OF_LINE;
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,14,cppd;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(blockComment, decorationData);
+
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\">" + firstCommentLine + "</span>",
+ "<span class=\"cppd\">" + secondCommentLine + "</span>",
+ "<span class=\"cppd\">" + thirdCommentLine + "</span>",
+ ""
+ );
+ }
+
+ @Test
+ public void should_highlight_multiple_words_in_one_line() throws Exception {
+
+ String classDeclaration = "public class MyClass implements MyInterface {";
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,6,k;7,12,k;21,31,k;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(classDeclaration, decorationData);
+
+ assertThat(htmlOutput).containsOnly(
+ "<span class=\"k\">public</span> " +
+ "<span class=\"k\">class</span> MyClass " +
+ "<span class=\"k\">implements</span> MyInterface {");
+ }
+
+ @Test
+ public void should_allow_multiple_levels_highlighting() throws Exception {
+
+ String javaDocSample =
+ "/**" + LF_END_OF_LINE +
+ " * Creates a FormulaDecorator" + LF_END_OF_LINE +
+ " *" + LF_END_OF_LINE +
+ " * @param metric the metric should have an associated formula" + LF_END_OF_LINE +
+ " * " + LF_END_OF_LINE +
+ " * @throws IllegalArgumentException if no formula is associated to the metric" + LF_END_OF_LINE +
+ " */" + LF_END_OF_LINE;
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,184,cppd;47,53,k;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javaDocSample, decorationData);
+
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\">/**</span>",
+ "<span class=\"cppd\"> * Creates a FormulaDecorator</span>",
+ "<span class=\"cppd\"> *</span>",
+ "<span class=\"cppd\"> * @param <span class=\"k\">metric</span> the metric should have an associated formula</span>",
+ "<span class=\"cppd\"> * </span>",
+ "<span class=\"cppd\"> * @throws IllegalArgumentException if no formula is associated to the metric</span>",
+ "<span class=\"cppd\"> */</span>",
+ ""
+ );
+ }
+
+ @Test
+ public void should_support_crlf_line_breaks() throws Exception {
+
+ String crlfCodeSample =
+ "/**" + CR_END_OF_LINE + LF_END_OF_LINE +
+ "* @return metric generated by the decorator" + CR_END_OF_LINE + LF_END_OF_LINE +
+ "*/" + CR_END_OF_LINE + LF_END_OF_LINE +
+ "@DependedUpon" + CR_END_OF_LINE + LF_END_OF_LINE +
+ "public Metric generatesMetric() {" + CR_END_OF_LINE + LF_END_OF_LINE +
+ " return metric;" + CR_END_OF_LINE + LF_END_OF_LINE +
+ "}" + CR_END_OF_LINE + LF_END_OF_LINE;
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,52,cppd;54,67,a;69,75,k;106,112,k;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(crlfCodeSample, decorationData);
+
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\">/**</span>",
+ "<span class=\"cppd\">* @return metric generated by the decorator</span>",
+ "<span class=\"cppd\">*/</span>",
+ "<span class=\"a\">@DependedUpon</span>",
+ "<span class=\"k\">public</span> Metric generatesMetric() {",
+ " <span class=\"k\">return</span> metric;",
+ "}",
+ ""
+ );
+ }
+
+ @Test
+ public void should_close_tags_at_end_of_file() throws Exception {
+
+ String classDeclarationSample =
+ "/*" + LF_END_OF_LINE +
+ " * Header" + LF_END_OF_LINE +
+ " */" + LF_END_OF_LINE +
+ LF_END_OF_LINE +
+ "public class HelloWorld {" + LF_END_OF_LINE +
+ "}";
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,16,cppd;18,25,k;25,31,k;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(classDeclarationSample, decorationData);
+
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\">/*</span>",
+ "<span class=\"cppd\"> * Header</span>",
+ "<span class=\"cppd\"> */</span>",
+ "",
+ "<span class=\"k\">public </span><span class=\"k\">class </span>HelloWorld {",
+ "}"
+ );
+ }
+
+ @Test
+ public void should_escape_markup_chars() throws Exception {
+
+ String javadocWithHtml =
+ "/**\n" +
+ " * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.\n" +
+ " * \n" +
+ " * This framework can used for instance in order to :\n" +
+ " * <ul>\n" +
+ " * <li>Create a lexer in charge to generate a list of tokens from a character stream</li>\n" +
+ " * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li>\n" +
+ " * <li>Create a javadoc generator</li>\n" +
+ " * <li>...</li>\n" +
+ " * </ul>\n" +
+ " */\n";
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,453,cppd;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javadocWithHtml, decorationData);
+
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\">/**</span>",
+ "<span class=\"cppd\"> * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.</span>",
+ "<span class=\"cppd\"> * </span>",
+ "<span class=\"cppd\"> * This framework can used for instance in order to :</span>",
+ "<span class=\"cppd\"> * <ul></span>",
+ "<span class=\"cppd\"> * <li>Create a lexer in charge to generate a list of tokens from a character stream</li></span>",
+ "<span class=\"cppd\"> * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li></span>",
+ "<span class=\"cppd\"> * <li>Create a javadoc generator</li></span>",
+ "<span class=\"cppd\"> * <li>...</li></span>",
+ "<span class=\"cppd\"> * </ul></span>",
+ "<span class=\"cppd\"> */</span>",
+ "");
+ }
+
+ @Test
+ public void should_escape_ampersand_char() throws Exception {
+
+ String javadocWithAmpersandChar =
+ "/**\n" +
+ " * Definition of a dashboard.\n" +
+ " * <p/>\n" +
+ " * Its name and description can be retrieved using the i18n mechanism, using the keys \"dashboard.<id>.name\" and\n" +
+ " * \"dashboard.<id>.description\".\n" +
+ " *\n" +
+ " * @since 2.13\n" +
+ " */\n";
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,220,cppd;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javadocWithAmpersandChar, decorationData);
+
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\">/**</span>",
+ "<span class=\"cppd\"> * Definition of a dashboard.</span>",
+ "<span class=\"cppd\"> * <p/></span>",
+ "<span class=\"cppd\"> * Its name and description can be retrieved using the i18n mechanism, using the keys \"dashboard.&lt;id&gt;.name\" and</span>",
+ "<span class=\"cppd\"> * \"dashboard.&lt;id&gt;.description\".</span>",
+ "<span class=\"cppd\"> *</span>",
+ "<span class=\"cppd\"> * @since 2.13</span>",
+ "<span class=\"cppd\"> */</span>",
+ "");
+ }
+
+ @Test
+ public void should_support_cr_line_breaks() throws Exception {
+
+ String crCodeSample =
+ "/**" + CR_END_OF_LINE +
+ "* @return metric generated by the decorator" + CR_END_OF_LINE +
+ "*/" + CR_END_OF_LINE +
+ "@DependedUpon" + CR_END_OF_LINE +
+ "public Metric generatesMetric() {" + CR_END_OF_LINE +
+ " return metric;" + CR_END_OF_LINE +
+ "}" + CR_END_OF_LINE;
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,50,cppd;51,64,a;65,71,k;101,107,k;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(crCodeSample, decorationData);
+
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\">/**</span>",
+ "<span class=\"cppd\">* @return metric generated by the decorator</span>",
+ "<span class=\"cppd\">*/</span>",
+ "<span class=\"a\">@DependedUpon</span>",
+ "<span class=\"k\">public</span> Metric generatesMetric() {",
+ " <span class=\"k\">return</span> metric;",
+ "}",
+ ""
+ );
+
+ }
+
+ @Test
+ public void should_support_multiple_empty_lines_at_end_of_file() throws Exception {
+
+ String classDeclarationSample =
+ "/*" + LF_END_OF_LINE +
+ " * Header" + LF_END_OF_LINE +
+ " */" + LF_END_OF_LINE +
+ LF_END_OF_LINE +
+ "public class HelloWorld {" + LF_END_OF_LINE +
+ "}" + LF_END_OF_LINE + LF_END_OF_LINE + LF_END_OF_LINE;
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,16,cppd;18,25,k;25,31,k;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(classDeclarationSample, decorationData);
+
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\">/*</span>",
+ "<span class=\"cppd\"> * Header</span>",
+ "<span class=\"cppd\"> */</span>",
+ "",
+ "<span class=\"k\">public </span><span class=\"k\">class </span>HelloWorld {",
+ "}",
+ "",
+ "",
+ ""
+ );
+ }
+
+ @Test
+ public void begin_from_given_line() throws Exception {
+
+ String javadocWithHtml =
+ "/**\n" +
+ " * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.\n" +
+ " * \n" +
+ " * This framework can used for instance in order to :\n" +
+ " * <ul>\n" +
+ " * <li>Create a lexer in charge to generate a list of tokens from a character stream</li>\n" +
+ " * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li>\n" +
+ " * <li>Create a javadoc generator</li>\n" +
+ " * <li>...</li>\n" +
+ " * </ul>\n" +
+ " */\n";
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,453,cppd;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javadocWithHtml, decorationData, 4, null);
+ assertThat(htmlOutput).hasSize(9);
+
+ // Begin from line 4
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\"> * This framework can used for instance in order to :</span>",
+ "<span class=\"cppd\"> * <ul></span>",
+ "<span class=\"cppd\"> * <li>Create a lexer in charge to generate a list of tokens from a character stream</li></span>",
+ "<span class=\"cppd\"> * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li></span>",
+ "<span class=\"cppd\"> * <li>Create a javadoc generator</li></span>",
+ "<span class=\"cppd\"> * <li>...</li></span>",
+ "<span class=\"cppd\"> * </ul></span>",
+ "<span class=\"cppd\"> */</span>",
+ "");
+ }
+
+ @Test
+ public void end_to_given_line() throws Exception {
+
+ String javadocWithHtml =
+ "/**\n" +
+ " * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.\n" +
+ " * \n" +
+ " * This framework can used for instance in order to :\n" +
+ " * <ul>\n" +
+ " * <li>Create a lexer in charge to generate a list of tokens from a character stream</li>\n" +
+ " * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li>\n" +
+ " * <li>Create a javadoc generator</li>\n" +
+ " * <li>...</li>\n" +
+ " * </ul>\n" +
+ " */\n";
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,453,cppd;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javadocWithHtml, decorationData, null, 4);
+ assertThat(htmlOutput).hasSize(4);
+
+ // End at line 4
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\">/**</span>",
+ "<span class=\"cppd\"> * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.</span>",
+ "<span class=\"cppd\"> * </span>",
+ "<span class=\"cppd\"> * This framework can used for instance in order to :</span>");
+ }
+
+ @Test
+ public void return_code_from_given_lint_given_end_line() throws Exception {
+
+ String javadocWithHtml =
+ "/**\n" +
+ " * Provides a basic framework to sequentially read any kind of character stream in order to feed a generic OUTPUT.\n" +
+ " * \n" +
+ " * This framework can used for instance in order to :\n" +
+ " * <ul>\n" +
+ " * <li>Create a lexer in charge to generate a list of tokens from a character stream</li>\n" +
+ " * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li>\n" +
+ " * <li>Create a javadoc generator</li>\n" +
+ " * <li>...</li>\n" +
+ " * </ul>\n" +
+ " */\n";
+
+ DecorationDataHolder decorationData = new DecorationDataHolder();
+ decorationData.loadSyntaxHighlightingData("0,453,cppd;");
+
+ HtmlTextDecorator htmlTextDecorator = new HtmlTextDecorator();
+ List<String> htmlOutput = htmlTextDecorator.decorateTextWithHtml(javadocWithHtml, decorationData, 4, 8);
+ assertThat(htmlOutput).hasSize(5);
+
+ // Begin from line 4 and finish at line 8
+ assertThat(htmlOutput).containsExactly(
+ "<span class=\"cppd\"> * This framework can used for instance in order to :</span>",
+ "<span class=\"cppd\"> * <ul></span>",
+ "<span class=\"cppd\"> * <li>Create a lexer in charge to generate a list of tokens from a character stream</li></span>",
+ "<span class=\"cppd\"> * <li>Create a source code syntax highligther in charge to decorate a source code with HTML tags</li></span>",
+ "<span class=\"cppd\"> * <li>Create a javadoc generator</li></span>"
+ );
+ }
+}
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.web.UserRole;
import org.sonar.core.measure.db.MeasureDataDao;
import org.sonar.core.resource.ResourceDao;
import org.sonar.core.resource.ResourceDto;
-import org.sonar.core.source.HtmlSourceDecorator;
import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.ui.CodeColorizers;
import org.sonar.server.user.MockUserSession;
import static org.fest.assertions.Assertions.assertThat;
@Mock
HtmlSourceDecorator sourceDecorator;
+ @Mock
+ CodeColorizers codeColorizers;
+
@Mock
ResourceDao resourceDao;
@Before
public void setUp() throws Exception {
- service = new SourceService(sourceDecorator, resourceDao, measureDataDao);
+ service = new SourceService(sourceDecorator, codeColorizers, resourceDao, measureDataDao);
}
@Test
}
@Test
- public void find_data_from_component() throws Exception {
+ public void get_scm_author_data() throws Exception {
+ String componentKey = "org.sonar.sample:Sample";
+ service.getScmAuthorData(componentKey);
+ verify(measureDataDao).findByComponentKeyAndMetricKey(componentKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY);
+ }
+
+ @Test
+ public void not_get_scm_author_data_if_no_data() throws Exception {
+ String componentKey = "org.sonar.sample:Sample";
+ when(measureDataDao.findByComponentKeyAndMetricKey(eq(componentKey), anyString())).thenReturn(null);
+ assertThat(service.getScmAuthorData(componentKey)).isNull();
+ }
+
+ @Test
+ public void get_scm_date_data() throws Exception {
String componentKey = "org.sonar.sample:Sample";
- service.findDataFromComponent(componentKey, "metric_key");
- verify(measureDataDao).findByComponentKeyAndMetricKey(componentKey, "metric_key");
+ service.getScmDateData(componentKey);
+ verify(measureDataDao).findByComponentKeyAndMetricKey(componentKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY);
}
@Test
- public void not_find_data_from_component_if_no_data() throws Exception {
+ public void not_get_scm_date_data_if_no_data() throws Exception {
String componentKey = "org.sonar.sample:Sample";
- when(measureDataDao.findByComponentKeyAndMetricKey(componentKey, "metric_key")).thenReturn(null);
- assertThat(service.findDataFromComponent(componentKey, "metric_key")).isNull();
+ when(measureDataDao.findByComponentKeyAndMetricKey(eq(componentKey), anyString())).thenReturn(null);
+ assertThat(service.getScmDateData(componentKey)).isNull();
}
}
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.server.ws.WsTester;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.source.SourceService;
"public class <span class=\"sym-31 sym\">HelloWorld</span> {}"
));
- when(sourceService.findDataFromComponent(componentKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY)).thenReturn("1=julien;");
- when(sourceService.findDataFromComponent(componentKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY)).thenReturn("1=2013-03-13T16:22:31+0100;");
+ when(sourceService.getScmAuthorData(componentKey)).thenReturn("1=julien;");
+ when(sourceService.getScmDateData(componentKey)).thenReturn("1=2013-03-13T16:22:31+0100;");
WsTester.TestRequest request = tester.newRequest("show").setParam("key", componentKey);
request.execute().assertJson(getClass(), "show_source_with_scm.json");
"",
"public class <span class=\"sym-31 sym\">HelloWorld</span> {"
));
- when(sourceService.findDataFromComponent(componentKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY))
+ when(sourceService.getScmAuthorData(componentKey))
.thenReturn("1=julien;2=simon;3=julien;4=simon;5=jean;6=julien");
- when(sourceService.findDataFromComponent(componentKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY))
+ when(sourceService.getScmDateData(componentKey))
.thenReturn("1=2013-03-13T16:22:31+0100;2=2013-03-14T16:22:31+0100;3=2013-03-13T16:22:31+0100;4=2013-03-14T16:22:31+0100;5=2013-03-15T16:22:31+0100;6=2013-03-13T16:22:31+0100;");
WsTester.TestRequest request = tester.newRequest("show").setParam("key", componentKey).setParam("from", "3").setParam("to", "5");
"",
"public class <span class=\"sym-31 sym\">HelloWorld</span> {"
));
- when(sourceService.findDataFromComponent(componentKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY))
+ when(sourceService.getScmAuthorData(componentKey))
.thenReturn("1=julien;2=julien;3=simon");
- when(sourceService.findDataFromComponent(componentKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY))
+ when(sourceService.getScmDateData(componentKey))
.thenReturn("1=2013-03-13T16:22:31+0100;2=2013-03-13T16:22:31+0100;3=2013-03-14T16:22:31+0100;");
WsTester.TestRequest request = tester.newRequest("show").setParam("key", componentKey);
request.execute().assertJson(getClass(), "show_source_with_scm_without_repeating_same_lines.json");
package org.sonar.server.text;
import org.junit.Test;
-import org.sonar.core.source.HtmlSourceDecorator;
+import org.sonar.server.source.HtmlSourceDecorator;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.*;
--- /dev/null
+<dataset>
+
+ <projects id="1" kee="org.apache.struts:struts:Dispatcher" enabled="[true]"/>
+ <projects id="2" kee="org.apache.struts:struts:VelocityManager" enabled="[true]"/>
+ <projects id="3" kee="org.apache.struts:struts:DebuggingInterceptor" enabled="[true]"/>
+
+ <snapshots id="11" project_id="1" islast="[true]" />
+ <snapshots id="12" project_id="2" islast="[true]" />
+ <snapshots id="13" project_id="3" islast="[true]" />
+ <snapshots id="14" project_id="3" islast="[true]" />
+
+ <snapshot_data id="101" resource_id="1" snapshot_id="11" snapshot_data="0,16,cppd;18,25,k;25,31,k;" data_type="highlight_syntax" />
+ <snapshot_data id="102" resource_id="2" snapshot_id="12" snapshot_data="31,41,31;" data_type="symbol" />
+ <snapshot_data id="103" resource_id="3" snapshot_id="13" snapshot_data="0,16,cppd;18,25,k;25,31,k;46,52,k;53,57,k;72,78,k;79,83,k;" data_type="highlight_syntax" />
+ <snapshot_data id="104" resource_id="3" snapshot_id="13" snapshot_data="31,41,31;58,61,58,96;84,87,84;" data_type="symbol" />
+
+ <snapshot_sources id="101" snapshot_id="11" data="/* * Header */ public class HelloWorld { }" />
+ <snapshot_sources id="102" snapshot_id="12" data="/* * Header */ public class HelloWorld { }" />
+ <snapshot_sources id="103" snapshot_id="13" data="/* * Header */ public class HelloWorld { public void foo() { } public void bar() { foo(); } }" />
+</dataset>