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 */
020 package org.sonar.batch;
021
022 import com.google.common.annotations.Beta;
023 import com.google.common.base.Objects;
024 import com.google.common.base.Preconditions;
025 import com.google.common.collect.ImmutableMap;
026 import com.google.common.collect.Maps;
027 import org.sonar.api.batch.SonarIndex;
028 import org.sonar.api.measures.FileLinesContext;
029 import org.sonar.api.measures.Measure;
030 import org.sonar.api.measures.Metric;
031 import org.sonar.api.measures.PersistenceMode;
032 import org.sonar.api.resources.Resource;
033 import org.sonar.api.resources.ResourceUtils;
034 import org.sonar.api.utils.KeyValueFormat;
035 import org.sonar.api.utils.KeyValueFormat.Converter;
036
037 import java.util.Map;
038
039 /**
040 * @since 2.14
041 */
042 @Beta
043 public 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 }