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