001/*
002 * SonarQube
003 * Copyright (C) 2009-2016 SonarSource SA
004 * mailto:contact AT sonarsource DOT com
005 *
006 * This program is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.api.batch.sensor.highlighting.internal;
021
022import com.google.common.base.Preconditions;
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.Comparator;
026import java.util.Iterator;
027import java.util.List;
028import org.sonar.api.batch.fs.InputFile;
029import org.sonar.api.batch.fs.TextRange;
030import org.sonar.api.batch.fs.internal.DefaultInputFile;
031import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
032import org.sonar.api.batch.sensor.highlighting.TypeOfText;
033import org.sonar.api.batch.sensor.internal.DefaultStorable;
034import org.sonar.api.batch.sensor.internal.SensorStorage;
035
036public class DefaultHighlighting extends DefaultStorable implements NewHighlighting {
037
038  private final List<SyntaxHighlightingRule> syntaxHighlightingRules;
039  private DefaultInputFile inputFile;
040
041  public DefaultHighlighting(SensorStorage storage) {
042    super(storage);
043    syntaxHighlightingRules = new ArrayList<>();
044  }
045
046  public List<SyntaxHighlightingRule> getSyntaxHighlightingRuleSet() {
047    return syntaxHighlightingRules;
048  }
049
050  private void checkOverlappingBoudaries() {
051    if (syntaxHighlightingRules.size() > 1) {
052      Iterator<SyntaxHighlightingRule> it = syntaxHighlightingRules.iterator();
053      SyntaxHighlightingRule previous = it.next();
054      while (it.hasNext()) {
055        SyntaxHighlightingRule current = it.next();
056        if (previous.range().end().compareTo(current.range().start()) > 0 && (previous.range().end().compareTo(current.range().end()) < 0)) {
057          String errorMsg = String.format("Cannot register highlighting rule for characters at %s as it " +
058            "overlaps at least one existing rule", current.range());
059          throw new IllegalStateException(errorMsg);
060        }
061        previous = current;
062      }
063    }
064  }
065
066  @Override
067  public DefaultHighlighting onFile(InputFile inputFile) {
068    Preconditions.checkNotNull(inputFile, "file can't be null");
069    this.inputFile = (DefaultInputFile) inputFile;
070    return this;
071  }
072
073  public InputFile inputFile() {
074    return inputFile;
075  }
076
077  @Override
078  public DefaultHighlighting highlight(int startOffset, int endOffset, TypeOfText typeOfText) {
079    checkInputFileNotNull();
080    TextRange newRange;
081    try {
082      newRange = inputFile.newRange(startOffset, endOffset);
083    } catch (Exception e) {
084      throw new IllegalArgumentException("Unable to highlight file " + inputFile, e);
085    }
086    return highlight(newRange, typeOfText);
087  }
088
089  @Override
090  public DefaultHighlighting highlight(int startLine, int startLineOffset, int endLine, int endLineOffset, TypeOfText typeOfText) {
091    checkInputFileNotNull();
092    TextRange newRange;
093    try {
094      newRange = inputFile.newRange(startLine, startLineOffset, endLine, endLineOffset);
095    } catch (Exception e) {
096      throw new IllegalArgumentException("Unable to highlight file " + inputFile, e);
097    }
098    return highlight(newRange, typeOfText);
099  }
100
101  @Override
102  public DefaultHighlighting highlight(TextRange range, TypeOfText typeOfText) {
103    SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(range, typeOfText);
104    this.syntaxHighlightingRules.add(syntaxHighlightingRule);
105    return this;
106  }
107
108  @Override
109  protected void doSave() {
110    checkInputFileNotNull();
111    // Sort rules to avoid variation during consecutive runs
112    Collections.sort(syntaxHighlightingRules, new Comparator<SyntaxHighlightingRule>() {
113      @Override
114      public int compare(SyntaxHighlightingRule left, SyntaxHighlightingRule right) {
115        int result = left.range().start().compareTo(right.range().start());
116        if (result == 0) {
117          result = right.range().end().compareTo(left.range().end());
118        }
119        return result;
120      }
121    });
122    checkOverlappingBoudaries();
123    storage.store(this);
124  }
125
126  private void checkInputFileNotNull() {
127    Preconditions.checkState(inputFile != null, "Call onFile() first");
128  }
129}