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.symbol.internal; 021 022import com.google.common.base.Preconditions; 023import java.util.Collection; 024import java.util.LinkedHashMap; 025import java.util.Map; 026import java.util.Set; 027import java.util.TreeSet; 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.internal.DefaultStorable; 032import org.sonar.api.batch.sensor.internal.SensorStorage; 033import org.sonar.api.batch.sensor.symbol.NewSymbol; 034import org.sonar.api.batch.sensor.symbol.NewSymbolTable; 035 036import static java.util.Objects.requireNonNull; 037 038public class DefaultSymbolTable extends DefaultStorable implements NewSymbolTable { 039 040 private final Map<TextRange, Set<TextRange>> referencesBySymbol; 041 private DefaultInputFile inputFile; 042 043 public DefaultSymbolTable(SensorStorage storage) { 044 super(storage); 045 referencesBySymbol = new LinkedHashMap<>(); 046 } 047 048 public Map<TextRange, Set<TextRange>> getReferencesBySymbol() { 049 return referencesBySymbol; 050 } 051 052 @Override 053 public DefaultSymbolTable onFile(InputFile inputFile) { 054 requireNonNull(inputFile, "file can't be null"); 055 this.inputFile = (DefaultInputFile) inputFile; 056 return this; 057 } 058 059 public InputFile inputFile() { 060 return inputFile; 061 } 062 063 @Override 064 public NewSymbol newSymbol(int startLine, int startLineOffset, int endLine, int endLineOffset) { 065 checkInputFileNotNull(); 066 TextRange declarationRange; 067 try { 068 declarationRange = inputFile.newRange(startLine, startLineOffset, endLine, endLineOffset); 069 } catch (Exception e) { 070 throw new IllegalArgumentException("Unable to create symbol on file " + inputFile, e); 071 } 072 return newSymbol(declarationRange); 073 } 074 075 @Override 076 public NewSymbol newSymbol(int startOffset, int endOffset) { 077 checkInputFileNotNull(); 078 TextRange declarationRange; 079 try { 080 declarationRange = inputFile.newRange(startOffset, endOffset); 081 } catch (Exception e) { 082 throw new IllegalArgumentException("Unable to create symbol on file " + inputFile, e); 083 } 084 return newSymbol(declarationRange); 085 } 086 087 @Override 088 public NewSymbol newSymbol(TextRange range) { 089 checkInputFileNotNull(); 090 TreeSet<TextRange> references = new TreeSet<>((o1, o2) -> o1.start().compareTo(o2.start())); 091 referencesBySymbol.put(range, references); 092 return new DefaultSymbol(inputFile, range, references); 093 } 094 095 private static class DefaultSymbol implements NewSymbol { 096 097 private final Collection<TextRange> references; 098 private final DefaultInputFile inputFile; 099 private final TextRange declaration; 100 101 public DefaultSymbol(DefaultInputFile inputFile, TextRange declaration, Collection<TextRange> references) { 102 this.inputFile = inputFile; 103 this.declaration = declaration; 104 this.references = references; 105 } 106 107 @Override 108 public NewSymbol newReference(int startOffset, int endOffset) { 109 TextRange referenceRange; 110 try { 111 referenceRange = inputFile.newRange(startOffset, endOffset); 112 } catch (Exception e) { 113 throw new IllegalArgumentException("Unable to create symbol reference on file " + inputFile, e); 114 } 115 return newReference(referenceRange); 116 } 117 118 @Override 119 public NewSymbol newReference(int startLine, int startLineOffset, int endLine, int endLineOffset) { 120 TextRange referenceRange; 121 try { 122 referenceRange = inputFile.newRange(startLine, startLineOffset, endLine, endLineOffset); 123 } catch (Exception e) { 124 throw new IllegalArgumentException("Unable to create symbol reference on file " + inputFile, e); 125 } 126 return newReference(referenceRange); 127 } 128 129 @Override 130 public NewSymbol newReference(TextRange range) { 131 requireNonNull(range, "Provided range is null"); 132 Preconditions.checkArgument(!declaration.overlap(range), "Overlapping symbol declaration and reference for symbol at %s", declaration); 133 references.add(range); 134 return this; 135 } 136 137 } 138 139 @Override 140 protected void doSave() { 141 checkInputFileNotNull(); 142 storage.store(this); 143 } 144 145 private void checkInputFileNotNull() { 146 Preconditions.checkState(inputFile != null, "Call onFile() first"); 147 } 148}