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