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;
021    
022    import com.google.common.base.Objects;
023    import com.google.common.base.Preconditions;
024    import com.google.common.collect.ImmutableMap;
025    import com.google.common.collect.Maps;
026    import org.sonar.api.batch.SonarIndex;
027    import org.sonar.api.measures.FileLinesContext;
028    import org.sonar.api.measures.Measure;
029    import org.sonar.api.measures.Metric;
030    import org.sonar.api.measures.PersistenceMode;
031    import org.sonar.api.resources.Resource;
032    import org.sonar.api.resources.ResourceUtils;
033    import org.sonar.api.utils.KeyValueFormat;
034    import org.sonar.api.utils.KeyValueFormat.Converter;
035    
036    import java.util.Map;
037    
038    public class DefaultFileLinesContext implements FileLinesContext {
039    
040      private final SonarIndex index;
041      private final Resource resource;
042    
043      /**
044       * metric key -> line -> value
045       */
046      private final Map<String, Map<Integer, Object>> map = Maps.newHashMap();
047    
048      public DefaultFileLinesContext(SonarIndex index, Resource resource) {
049        Preconditions.checkNotNull(index);
050        Preconditions.checkArgument(ResourceUtils.isFile(resource));
051        this.index = index;
052        this.resource = resource;
053      }
054    
055      public void setIntValue(String metricKey, int line, int value) {
056        Preconditions.checkNotNull(metricKey);
057        Preconditions.checkArgument(line > 0);
058    
059        setValue(metricKey, line, value);
060      }
061    
062      public Integer getIntValue(String metricKey, int line) {
063        Preconditions.checkNotNull(metricKey);
064        Preconditions.checkArgument(line > 0);
065    
066        Map lines = map.get(metricKey);
067        if (lines == null) {
068          // not in memory, so load
069          lines = loadData(metricKey, KeyValueFormat.newIntegerConverter());
070          map.put(metricKey, lines);
071        }
072        return (Integer) lines.get(line);
073      }
074    
075      public void setStringValue(String metricKey, int line, String value) {
076        Preconditions.checkNotNull(metricKey);
077        Preconditions.checkArgument(line > 0);
078        Preconditions.checkNotNull(value);
079    
080        setValue(metricKey, line, value);
081      }
082    
083      public String getStringValue(String metricKey, int line) {
084        Preconditions.checkNotNull(metricKey);
085        Preconditions.checkArgument(line > 0);
086    
087        Map lines = map.get(metricKey);
088        if (lines == null) {
089          // not in memory, so load
090          lines = loadData(metricKey, KeyValueFormat.newStringConverter());
091          map.put(metricKey, lines);
092        }
093        return (String) lines.get(line);
094      }
095    
096      private Map<Integer, Object> getOrCreateLines(String metricKey) {
097        Map<Integer, Object> lines = map.get(metricKey);
098        if (lines == null) {
099          lines = Maps.newHashMap();
100          map.put(metricKey, lines);
101        }
102        return lines;
103      }
104    
105      private void setValue(String metricKey, int line, Object value) {
106        getOrCreateLines(metricKey).put(line, value);
107      }
108    
109      public void save() {
110        for (Map.Entry<String, Map<Integer, Object>> entry : map.entrySet()) {
111          String metricKey = entry.getKey();
112          Map<Integer, Object> lines = entry.getValue();
113          if (shouldSave(lines)) {
114            String data = KeyValueFormat.format(lines);
115            Measure measure = new Measure(metricKey)
116              .setPersistenceMode(PersistenceMode.DATABASE)
117              .setData(data);
118            index.addMeasure(resource, measure);
119            entry.setValue(ImmutableMap.copyOf(lines));
120          }
121        }
122      }
123    
124      private Map loadData(String metricKey, Converter converter) {
125        // FIXME no way to load measure only by key
126        Measure measure = index.getMeasure(resource, new Metric(metricKey));
127        if (measure == null || measure.getData() == null) {
128          // no such measure
129          return ImmutableMap.of();
130        }
131        return ImmutableMap.copyOf(KeyValueFormat.parse(measure.getData(), KeyValueFormat.newIntegerConverter(), converter));
132      }
133    
134      /**
135       * Checks that measure was not saved.
136       *
137       * @see #loadData(String, Converter)
138       * @see #save()
139       */
140      private boolean shouldSave(Map<Integer, Object> lines) {
141        return !(lines instanceof ImmutableMap);
142      }
143    
144      @Override
145      public String toString() {
146        return Objects.toStringHelper(this)
147          .add("map", map)
148          .toString();
149      }
150    
151    }