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