001/* 002 * SonarQube 003 * Copyright (C) 2009-2017 SonarSource SA 004 * mailto:info 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.Iterator; 026import java.util.List; 027import org.sonar.api.batch.fs.InputFile; 028import org.sonar.api.batch.fs.TextRange; 029import org.sonar.api.batch.fs.internal.DefaultInputFile; 030import org.sonar.api.batch.sensor.highlighting.NewHighlighting; 031import org.sonar.api.batch.sensor.highlighting.TypeOfText; 032import org.sonar.api.batch.sensor.internal.DefaultStorable; 033import org.sonar.api.batch.sensor.internal.SensorStorage; 034 035import static java.util.Objects.requireNonNull; 036 037public class DefaultHighlighting extends DefaultStorable implements NewHighlighting { 038 039 private final List<SyntaxHighlightingRule> syntaxHighlightingRules; 040 private DefaultInputFile inputFile; 041 042 public DefaultHighlighting(SensorStorage storage) { 043 super(storage); 044 syntaxHighlightingRules = new ArrayList<>(); 045 } 046 047 public List<SyntaxHighlightingRule> getSyntaxHighlightingRuleSet() { 048 return syntaxHighlightingRules; 049 } 050 051 private void checkOverlappingBoudaries() { 052 if (syntaxHighlightingRules.size() > 1) { 053 Iterator<SyntaxHighlightingRule> it = syntaxHighlightingRules.iterator(); 054 SyntaxHighlightingRule previous = it.next(); 055 while (it.hasNext()) { 056 SyntaxHighlightingRule current = it.next(); 057 if (previous.range().end().compareTo(current.range().start()) > 0 && (previous.range().end().compareTo(current.range().end()) < 0)) { 058 String errorMsg = String.format("Cannot register highlighting rule for characters at %s as it " + 059 "overlaps at least one existing rule", current.range()); 060 throw new IllegalStateException(errorMsg); 061 } 062 previous = current; 063 } 064 } 065 } 066 067 @Override 068 public DefaultHighlighting onFile(InputFile inputFile) { 069 requireNonNull(inputFile, "file can't be null"); 070 this.inputFile = (DefaultInputFile) inputFile; 071 return this; 072 } 073 074 public InputFile inputFile() { 075 return inputFile; 076 } 077 078 @Override 079 public DefaultHighlighting highlight(int startOffset, int endOffset, TypeOfText typeOfText) { 080 checkInputFileNotNull(); 081 TextRange newRange; 082 try { 083 newRange = inputFile.newRange(startOffset, endOffset); 084 } catch (Exception e) { 085 throw new IllegalArgumentException("Unable to highlight file " + inputFile, e); 086 } 087 return highlight(newRange, typeOfText); 088 } 089 090 @Override 091 public DefaultHighlighting highlight(int startLine, int startLineOffset, int endLine, int endLineOffset, TypeOfText typeOfText) { 092 checkInputFileNotNull(); 093 TextRange newRange; 094 try { 095 newRange = inputFile.newRange(startLine, startLineOffset, endLine, endLineOffset); 096 } catch (Exception e) { 097 throw new IllegalArgumentException("Unable to highlight file " + inputFile, e); 098 } 099 return highlight(newRange, typeOfText); 100 } 101 102 @Override 103 public DefaultHighlighting highlight(TextRange range, TypeOfText typeOfText) { 104 SyntaxHighlightingRule syntaxHighlightingRule = SyntaxHighlightingRule.create(range, typeOfText); 105 this.syntaxHighlightingRules.add(syntaxHighlightingRule); 106 return this; 107 } 108 109 @Override 110 protected void doSave() { 111 checkInputFileNotNull(); 112 // Sort rules to avoid variation during consecutive runs 113 Collections.sort(syntaxHighlightingRules, (left, right) -> { 114 int result = left.range().start().compareTo(right.range().start()); 115 if (result == 0) { 116 result = right.range().end().compareTo(left.range().end()); 117 } 118 return result; 119 }); 120 checkOverlappingBoudaries(); 121 storage.store(this); 122 } 123 124 private void checkInputFileNotNull() { 125 Preconditions.checkState(inputFile != null, "Call onFile() first"); 126 } 127}