001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube 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     * SonarQube 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     */
020    package org.sonar.batch.scan2;
021    
022    import org.sonar.api.batch.sensor.measure.Measure;
023    import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder;
024    
025    import com.google.common.base.Objects;
026    import com.google.common.base.Preconditions;
027    import com.google.common.collect.ImmutableMap;
028    import com.google.common.collect.Maps;
029    import org.sonar.api.batch.fs.InputFile;
030    import org.sonar.api.batch.measure.MetricFinder;
031    import org.sonar.api.measures.FileLinesContext;
032    import org.sonar.api.utils.KeyValueFormat;
033    import org.sonar.api.utils.KeyValueFormat.Converter;
034    import org.sonar.core.component.ComponentKeys;
035    
036    import java.util.Map;
037    
038    public class DefaultFileLinesContext implements FileLinesContext {
039    
040      private final AnalyzerMeasureCache measureCache;
041      private final InputFile inputFile;
042    
043      /**
044       * metric key -> line -> value
045       */
046      private final Map<String, Map<Integer, Object>> map = Maps.newHashMap();
047      private String projectKey;
048      private MetricFinder metricFinder;
049    
050      public DefaultFileLinesContext(MetricFinder metricFinder, AnalyzerMeasureCache measureCache, String projectKey, InputFile inputFile) {
051        this.metricFinder = metricFinder;
052        this.projectKey = projectKey;
053        Preconditions.checkNotNull(measureCache);
054        this.measureCache = measureCache;
055        this.inputFile = inputFile;
056      }
057    
058      public void setIntValue(String metricKey, int line, int value) {
059        Preconditions.checkNotNull(metricKey);
060        Preconditions.checkArgument(line > 0);
061    
062        setValue(metricKey, line, value);
063      }
064    
065      public Integer getIntValue(String metricKey, int line) {
066        Preconditions.checkNotNull(metricKey);
067        Preconditions.checkArgument(line > 0);
068    
069        Map lines = map.get(metricKey);
070        if (lines == null) {
071          // not in memory, so load
072          lines = loadData(metricKey, KeyValueFormat.newIntegerConverter());
073          map.put(metricKey, lines);
074        }
075        return (Integer) lines.get(line);
076      }
077    
078      public void setStringValue(String metricKey, int line, String value) {
079        Preconditions.checkNotNull(metricKey);
080        Preconditions.checkArgument(line > 0);
081        Preconditions.checkNotNull(value);
082    
083        setValue(metricKey, line, value);
084      }
085    
086      public String getStringValue(String metricKey, int line) {
087        Preconditions.checkNotNull(metricKey);
088        Preconditions.checkArgument(line > 0);
089    
090        Map lines = map.get(metricKey);
091        if (lines == null) {
092          // not in memory, so load
093          lines = loadData(metricKey, KeyValueFormat.newStringConverter());
094          map.put(metricKey, lines);
095        }
096        return (String) lines.get(line);
097      }
098    
099      private Map<Integer, Object> getOrCreateLines(String metricKey) {
100        Map<Integer, Object> lines = map.get(metricKey);
101        if (lines == null) {
102          lines = Maps.newHashMap();
103          map.put(metricKey, lines);
104        }
105        return lines;
106      }
107    
108      private void setValue(String metricKey, int line, Object value) {
109        getOrCreateLines(metricKey).put(line, value);
110      }
111    
112      public void save() {
113        for (Map.Entry<String, Map<Integer, Object>> entry : map.entrySet()) {
114          String metricKey = entry.getKey();
115          org.sonar.api.batch.measure.Metric<String> metric = metricFinder.findByKey(metricKey);
116          if (metric == null) {
117            throw new IllegalStateException("Unable to find metric with key: " + metricKey);
118          }
119          Map<Integer, Object> lines = entry.getValue();
120          if (shouldSave(lines)) {
121            String data = KeyValueFormat.format(lines);
122            measureCache.put(projectKey, ComponentKeys.createEffectiveKey(projectKey, inputFile), new DefaultMeasureBuilder<String>()
123              .forMetric(metric)
124              .onFile(inputFile)
125              .withValue(data)
126              .build());
127            entry.setValue(ImmutableMap.copyOf(lines));
128          }
129        }
130      }
131    
132      private Map loadData(String metricKey, Converter converter) {
133        Measure measure = measureCache.byMetric(projectKey, ComponentKeys.createEffectiveKey(projectKey, inputFile), metricKey);
134        if (measure == null) {
135          // no such measure
136          return ImmutableMap.of();
137        }
138        return ImmutableMap.copyOf(KeyValueFormat.parse((String) measure.value(), KeyValueFormat.newIntegerConverter(), converter));
139      }
140    
141      /**
142       * Checks that measure was not saved.
143       *
144       * @see #loadData(String, Converter)
145       * @see #save()
146       */
147      private boolean shouldSave(Map<Integer, Object> lines) {
148        return !(lines instanceof ImmutableMap);
149      }
150    
151      @Override
152      public String toString() {
153        return Objects.toStringHelper(this)
154          .add("map", map)
155          .toString();
156      }
157    
158    }