]> source.dussan.org Git - sonarqube.git/blob
3ccc765c1b29ebe659100238f2660803929478cc
[sonarqube.git] /
1 /*
2  * SonarQube, open source software quality management tool.
3  * Copyright (C) 2008-2014 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
6  * SonarQube is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * SonarQube is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.db.migrations.v501;
21
22 import com.google.common.base.Predicate;
23 import com.google.common.collect.Iterables;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.sonar.api.utils.System2;
27 import org.sonar.core.persistence.Database;
28 import org.sonar.server.db.migrations.BaseDataChange;
29 import org.sonar.server.db.migrations.Select;
30
31 import javax.annotation.CheckForNull;
32 import javax.annotation.Nullable;
33
34 import java.sql.SQLException;
35 import java.util.Date;
36 import java.util.List;
37
38 import static com.google.common.collect.Lists.newArrayList;
39
40 /**
41  * See http://jira.codehaus.org/browse/SONAR-6187
42  *
43  * Add a new Characteristic 'Usability' with 2 sub-characteristics 'Accessibility' and 'Ease of Use'
44  * and add a new sub-characteristic 'Compliance' for all characteristics.
45  *
46  * @since 5.0.1
47  */
48 public class AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration extends BaseDataChange {
49
50   private static final Logger LOGGER = LoggerFactory.getLogger(AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration.class);
51
52   private static final String COMPLIANCE_NAME = "Compliance";
53   private static final String COMPLIANCE_KEY_SUFFIX = "_COMPLIANCE";
54
55   private final System2 system;
56
57   public AddCharacteristicUsabilityAndSubCharacteristicsComplianceMigration(Database db, System2 system) {
58     super(db);
59     this.system = system;
60   }
61
62   @Override
63   public void execute(Context context) throws SQLException {
64     CharacteristicsContext characteristicsContext = new CharacteristicsContext(context, system);
65
66     int usabilityOder = moveCharacteristicsDownToBeAbleToInsertUsability(characteristicsContext);
67     createOrUpdateUsabilityCharacteristicAndItsSubCharacteristic(characteristicsContext, usabilityOder);
68
69     createSubCharacteristic(characteristicsContext, "REUSABILITY" + COMPLIANCE_KEY_SUFFIX, "Reusability " + COMPLIANCE_NAME, "REUSABILITY");
70     createSubCharacteristic(characteristicsContext, "PORTABILITY" + COMPLIANCE_KEY_SUFFIX, "Portability " + COMPLIANCE_NAME, "PORTABILITY");
71     createSubCharacteristic(characteristicsContext, "MAINTAINABILITY" + COMPLIANCE_KEY_SUFFIX, "Maintainability " + COMPLIANCE_NAME, "MAINTAINABILITY");
72     createSubCharacteristic(characteristicsContext, "SECURITY" + COMPLIANCE_KEY_SUFFIX, "Security " + COMPLIANCE_NAME, "SECURITY");
73     createSubCharacteristic(characteristicsContext, "EFFICIENCY" + COMPLIANCE_KEY_SUFFIX, "Efficiency " + COMPLIANCE_NAME, "EFFICIENCY");
74     createSubCharacteristic(characteristicsContext, "CHANGEABILITY" + COMPLIANCE_KEY_SUFFIX, "Changeability " + COMPLIANCE_NAME, "CHANGEABILITY");
75     createSubCharacteristic(characteristicsContext, "RELIABILITY" + COMPLIANCE_KEY_SUFFIX, "Reliability " + COMPLIANCE_NAME, "RELIABILITY");
76     createSubCharacteristic(characteristicsContext, "TESTABILITY" + COMPLIANCE_KEY_SUFFIX, "Testability " + COMPLIANCE_NAME, "TESTABILITY");
77   }
78
79   /**
80    * If the characteristic 'Security' exists, the new characteristic 'Usability' should be inserted just below it,
81    * so every existing characteristics below Security should move down.
82    *
83    * If the characteristic 'Security' does not exists, the new characteristic 'Usability' should be the first one,
84    * so every existing characteristics should move down.
85    *
86    * If the characteristic 'Usability' is already at the right place, nothing will be done.
87    */
88   private int moveCharacteristicsDownToBeAbleToInsertUsability(CharacteristicsContext characteristicsContext) throws SQLException {
89     Characteristic security = characteristicsContext.findCharacteristicByKey("SECURITY");
90     Characteristic usability = characteristicsContext.findCharacteristicByKey("USABILITY");
91
92     int usabilityOder = 1;
93     int indexToStart = 0;
94     if (security != null) {
95       indexToStart = characteristicsContext.characteristics().indexOf(security) + 1;
96       usabilityOder = security.getOrder() + 1;
97     }
98
99     if (usability == null || usability.getOrder() != usabilityOder) {
100       // Move root characteristics one step lower
101       for (int i = indexToStart; i < characteristicsContext.characteristics().size(); i++) {
102         Characteristic characteristic = characteristicsContext.characteristics().get(i);
103         if (characteristic.getParentId() == null) {
104           characteristicsContext.updateCharacteristicOrder(characteristic.getKey(), characteristic.getOrder() + 1);
105         }
106       }
107     }
108     return usabilityOder;
109   }
110
111   private void createOrUpdateUsabilityCharacteristicAndItsSubCharacteristic(CharacteristicsContext characteristicsContext, int newUsabilityOrder)
112     throws SQLException {
113     String usabilityKey = "USABILITY";
114     Characteristic usability = characteristicsContext.findCharacteristicByKey(usabilityKey);
115     if (usability != null) {
116       if (usability.getOrder() != newUsabilityOrder) {
117         usability.setOrder(newUsabilityOrder);
118         characteristicsContext.updateCharacteristicOrder(usability.getKey(), usability.getOrder());
119       }
120     } else {
121       usability = new Characteristic().setKey(usabilityKey).setName("Usability").setOrder(newUsabilityOrder);
122       characteristicsContext.insertCharacteristic(usability);
123     }
124
125     createSubCharacteristic(characteristicsContext, "USABILITY_ACCESSIBILITY", "Accessibility", usabilityKey);
126     createSubCharacteristic(characteristicsContext, "USABILITY_EASE_OF_USE", "Ease of Use", usabilityKey);
127     createSubCharacteristic(characteristicsContext, "USABILITY" + COMPLIANCE_KEY_SUFFIX, "Usability " + COMPLIANCE_NAME, usabilityKey);
128   }
129
130   private void createSubCharacteristic(CharacteristicsContext characteristicsContext,
131     String subCharacteristicKey, String subCharacteristicName, String parentKey) throws SQLException {
132     Characteristic parent = characteristicsContext.findCharacteristicByKey(parentKey);
133     if (parent != null) {
134       Characteristic subCharacteristic = characteristicsContext.findSubCharacteristicByKey(subCharacteristicKey, parent);
135       if (subCharacteristic == null) {
136         characteristicsContext.insertCharacteristic(new Characteristic().setKey(subCharacteristicKey).setName(subCharacteristicName).setParentId(parent.getId()));
137       }
138     }
139     // If the characteristic parent does not exits, the sub-characteristic is not added
140   }
141
142   private static class Characteristic {
143     private Integer id;
144     private String key;
145     private String name;
146     private Integer order;
147     private Integer parentId;
148
149     public Characteristic setId(Integer id) {
150       this.id = id;
151       return this;
152     }
153
154     public Integer getId() {
155       return id;
156     }
157
158     public Characteristic setKey(String key) {
159       this.key = key;
160       return this;
161     }
162
163     public String getKey() {
164       return key;
165     }
166
167     public Characteristic setName(String name) {
168       this.name = name;
169       return this;
170     }
171
172     public String getName() {
173       return name;
174     }
175
176     public Characteristic setOrder(@Nullable Integer order) {
177       this.order = order;
178       return this;
179     }
180
181     @CheckForNull
182     public Integer getOrder() {
183       return order;
184     }
185
186     public Characteristic setParentId(@Nullable Integer parentId) {
187       this.parentId = parentId;
188       return this;
189     }
190
191     @CheckForNull
192     public Integer getParentId() {
193       return parentId;
194     }
195   }
196
197   private static class CharacteristicsContext {
198     Context context;
199     private final System2 system;
200     Date now;
201     List<Characteristic> characteristics;
202
203     public CharacteristicsContext(Context context, System2 system) throws SQLException {
204       this.context = context;
205       this.system = system;
206       init();
207     }
208
209     private void init() throws SQLException {
210       now = new Date(system.now());
211       characteristics = selectEnabledCharacteristics();
212     }
213
214     public List<Characteristic> characteristics() {
215       return characteristics;
216     }
217
218     @CheckForNull
219     public Characteristic findCharacteristicByKey(final String key) {
220       Characteristic characteristic = Iterables.find(characteristics, new Predicate<Characteristic>() {
221         @Override
222         public boolean apply(@Nullable Characteristic input) {
223           return input != null && input.key.equals(key);
224         }
225       }, null);
226       if (characteristic != null) {
227         if (characteristic.getParentId() != null) {
228           throw new IllegalStateException(String.format("'%s' must be a characteristic", characteristic.getName()));
229         }
230       }
231       return characteristic;
232     }
233
234     @CheckForNull
235     public Characteristic findSubCharacteristicByKey(final String key, Characteristic parent) {
236       Characteristic characteristic = Iterables.find(characteristics, new Predicate<Characteristic>() {
237         @Override
238         public boolean apply(@Nullable Characteristic input) {
239           return input != null && input.key.equals(key);
240         }
241       }, null);
242       if (characteristic != null) {
243         Integer parentId = characteristic.getParentId();
244         if (parentId == null) {
245           throw new IllegalStateException(String.format("'%s' must be a sub-characteristic", characteristic.getName()));
246         } else if (!characteristic.getParentId().equals(parent.getId())) {
247           throw new IllegalStateException(String.format("'%s' must be defined under '%s'", characteristic.getName(), parent.getName()));
248         }
249       }
250       return characteristic;
251     }
252
253     private List<Characteristic> selectEnabledCharacteristics() throws SQLException {
254       return context.prepareSelect(
255         "SELECT c.id, c.kee, c.name, c.characteristic_order, c.parent_id FROM characteristics c WHERE c.enabled=? ORDER BY c.characteristic_order")
256         .setBoolean(1, true)
257         .list(new CharacteristicReader());
258     }
259
260     private int selectCharacteristicId(String key) throws SQLException {
261       return context.prepareSelect(
262         "SELECT c.id FROM characteristics c WHERE c.kee = ? AND c.enabled=?")
263         .setString(1, key)
264         .setBoolean(2, true)
265         .get(Select.LONG_READER).intValue();
266     }
267
268     public void insertCharacteristic(Characteristic characteristic) throws SQLException {
269       if (characteristic.getParentId() == null) {
270         LOGGER.info("Insert new characteristic '{}'", characteristic.getKey());
271       } else {
272         LOGGER.info("Insert new sub characteristic '{}'", characteristic.getKey());
273       }
274
275       context.prepareUpsert("INSERT INTO characteristics (kee, name, parent_id, characteristic_order, enabled, created_at) VALUES (?, ?, ?, ?, ?, ?)")
276         .setString(1, characteristic.getKey())
277         .setString(2, characteristic.getName())
278         .setInt(3, characteristic.getParentId())
279         .setInt(4, characteristic.getOrder())
280         .setBoolean(5, true)
281         .setDate(6, now)
282         .execute()
283         .commit();
284       characteristic.setId(selectCharacteristicId(characteristic.getKey()));
285
286       characteristics.add(characteristic);
287     }
288
289     public void updateCharacteristicOrder(String key, Integer order) throws SQLException {
290       LOGGER.info("Update characteristic '{}' order to {}", key, order);
291
292       context.prepareUpsert("UPDATE characteristics SET characteristic_order=?, updated_at=? WHERE kee=?")
293         .setInt(1, order)
294         .setDate(2, now)
295         .setString(3, key)
296         .execute()
297         .commit();
298     }
299
300     private static class CharacteristicReader implements Select.RowReader<Characteristic> {
301       @Override
302       public Characteristic read(Select.Row row) throws SQLException {
303         return new Characteristic()
304           .setId(row.getInt(1))
305           .setKey(row.getString(2))
306           .setName(row.getString(3))
307           .setOrder(row.getInt(4))
308           .setParentId(row.getInt(5));
309       }
310     }
311   }
312 }