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