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    Preconditions.checkState(inputFile != null, "Call onFile() first");
080    TextRange newRange;
081    try {
082      Preconditions.checkArgument(startOffset < endOffset, "start offset should be strictly before end offset");
083      newRange = inputFile.newRange(startOffset, endOffset);
084    } catch (Exception e) {
085      throw new IllegalArgumentException("Unable to highlight file " + inputFile + " from offset " + startOffset + " to offset " + endOffset, e);
086    }
087    SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(newRange, typeOfText);
088    this.syntaxHighlightingRules.add(syntaxHighlightingRule);
089    return this;
090  }
091
092  @Override
093  protected void doSave() {
094    Preconditions.checkState(inputFile != null, "Call onFile() first");
095    // Sort rules to avoid variation during consecutive runs
096    Collections.sort(syntaxHighlightingRules, new Comparator<SyntaxHighlightingRule>() {
097      @Override
098      public int compare(SyntaxHighlightingRule left, SyntaxHighlightingRule right) {
099        int result = left.range().start().compareTo(right.range().start());
100        if (result == 0) {
101          result = right.range().end().compareTo(left.range().end());
102        }
103        return result;
104      }
105    });
106    checkOverlappingBoudaries();
107    storage.store(this);
108  }
109}