001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube 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 * SonarQube 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 com.google.common.collect.Sets;
024import org.sonar.api.batch.fs.InputFile;
025import org.sonar.api.batch.fs.internal.DefaultInputFile;
026import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
027import org.sonar.api.batch.sensor.highlighting.TypeOfText;
028import org.sonar.api.batch.sensor.internal.DefaultStorable;
029import org.sonar.api.batch.sensor.internal.SensorStorage;
030
031import javax.annotation.Nullable;
032
033import java.util.Comparator;
034import java.util.Iterator;
035import java.util.Set;
036
037public class DefaultHighlighting extends DefaultStorable implements NewHighlighting {
038
039  private InputFile inputFile;
040  private Set<SyntaxHighlightingRule> syntaxHighlightingRuleSet;
041
042  public DefaultHighlighting() {
043    this(null);
044  }
045
046  public DefaultHighlighting(@Nullable SensorStorage storage) {
047    super(storage);
048    syntaxHighlightingRuleSet = Sets.newTreeSet(new Comparator<SyntaxHighlightingRule>() {
049      @Override
050      public int compare(SyntaxHighlightingRule left, SyntaxHighlightingRule right) {
051        int result = left.getStartPosition() - right.getStartPosition();
052        if (result == 0) {
053          result = right.getEndPosition() - left.getEndPosition();
054        }
055        return result;
056      }
057    });
058  }
059
060  public Set<SyntaxHighlightingRule> getSyntaxHighlightingRuleSet() {
061    return syntaxHighlightingRuleSet;
062  }
063
064  private void checkOverlappingBoudaries() {
065    if (syntaxHighlightingRuleSet.size() > 1) {
066      Iterator<SyntaxHighlightingRule> it = syntaxHighlightingRuleSet.iterator();
067      SyntaxHighlightingRule previous = it.next();
068      while (it.hasNext()) {
069        SyntaxHighlightingRule current = it.next();
070        if (previous.getEndPosition() > current.getStartPosition() && !(previous.getEndPosition() >= current.getEndPosition())) {
071          String errorMsg = String.format("Cannot register highlighting rule for characters from %s to %s as it " +
072            "overlaps at least one existing rule", current.getStartPosition(), current.getEndPosition());
073          throw new IllegalStateException(errorMsg);
074        }
075        previous = current;
076      }
077    }
078  }
079
080  @Override
081  public DefaultHighlighting onFile(InputFile inputFile) {
082    Preconditions.checkNotNull(inputFile, "file can't be null");
083    this.inputFile = inputFile;
084    return this;
085  }
086
087  public InputFile inputFile() {
088    return inputFile;
089  }
090
091  @Override
092  public DefaultHighlighting highlight(int startOffset, int endOffset, TypeOfText typeOfText) {
093    Preconditions.checkState(inputFile != null, "Call onFile() first");
094    int maxValidOffset = ((DefaultInputFile) inputFile).lastValidOffset();
095    checkOffset(startOffset, maxValidOffset, "startOffset");
096    checkOffset(endOffset, maxValidOffset, "endOffset");
097    Preconditions.checkArgument(startOffset < endOffset, "startOffset (" + startOffset + ") should be < endOffset (" + endOffset + ") for file " + inputFile + ".");
098    SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(startOffset, endOffset,
099      typeOfText);
100    this.syntaxHighlightingRuleSet.add(syntaxHighlightingRule);
101    return this;
102  }
103
104  private void checkOffset(int offset, int maxValidOffset, String label) {
105    Preconditions.checkArgument(offset >= 0 && offset <= maxValidOffset, "Invalid " + label + " " + offset + ". Should be >= 0 and <= " + maxValidOffset
106      + " for file " + inputFile);
107  }
108
109  @Override
110  protected void doSave() {
111    Preconditions.checkState(inputFile != null, "Call onFile() first");
112    checkOverlappingBoudaries();
113    storage.store(this);
114  }
115}