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.api.profiles;
021
022 import com.google.common.base.Predicate;
023 import com.google.common.collect.Iterables;
024 import com.google.common.collect.Lists;
025 import org.apache.commons.collections.CollectionUtils;
026 import org.apache.commons.collections.Transformer;
027 import org.apache.commons.lang.StringUtils;
028 import org.apache.commons.lang.builder.EqualsBuilder;
029 import org.apache.commons.lang.builder.HashCodeBuilder;
030 import org.sonar.api.rules.ActiveRule;
031 import org.sonar.api.rules.Rule;
032 import org.sonar.api.rules.RulePriority;
033 import org.sonar.api.utils.MessageException;
034
035 import javax.annotation.CheckForNull;
036 import javax.persistence.*;
037
038 import java.util.ArrayList;
039 import java.util.List;
040
041 /**
042 * This class is badly named. It should be "QualityProfile". Indeed it does not relate only to rules but to metric thresholds too.
043 */
044 @Entity
045 @Table(name = "rules_profiles")
046 public class RulesProfile implements Cloneable {
047
048 /**
049 * Name of the default profile "Sonar Way"
050 * @deprecated in 4.2. Use your own constant.
051 */
052 @Deprecated
053 public static final String SONAR_WAY_NAME = "Sonar way";
054
055 /**
056 * Name of the default java profile "Sonar way with Findbugs"
057 * @deprecated in 4.2. Use your own constant.
058 */
059 @Deprecated
060 public static final String SONAR_WAY_FINDBUGS_NAME = "Sonar way with Findbugs";
061
062 /**
063 * Name of the default java profile "Sun checks"
064 * @deprecated in 4.2. Use your own constant.
065 */
066 @Deprecated
067 public static final String SUN_CONVENTIONS_NAME = "Sun checks";
068
069 @Id
070 @Column(name = "id")
071 @GeneratedValue
072 private Integer id;
073
074 @Column(name = "name", updatable = true, nullable = false)
075 private String name;
076
077 @Column(name = "version", updatable = true, nullable = false)
078 private int version = 1;
079
080 @Transient
081 private Boolean defaultProfile = Boolean.FALSE;
082
083 @Column(name = "used_profile", updatable = true, nullable = false)
084 private Boolean used = Boolean.FALSE;
085
086 @Column(name = "language", updatable = true, nullable = false, length = 20)
087 private String language;
088
089 @Column(name = "parent_name", updatable = true, nullable = true)
090 private String parentName;
091
092 @OneToMany(mappedBy = "rulesProfile", fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE})
093 private List<ActiveRule> activeRules = Lists.newArrayList();
094
095 /**
096 * @deprecated use the factory method create()
097 */
098 @Deprecated
099 public RulesProfile() {
100 }
101
102 /**
103 * @deprecated since 2.3. Use the factory method create()
104 */
105 @Deprecated
106 public RulesProfile(String name, String language) {
107 this.name = name;
108 this.language = language;
109 this.activeRules = Lists.newArrayList();
110 }
111
112 /**
113 * @deprecated since 2.3. Use the factory method create()
114 */
115 @Deprecated
116 public RulesProfile(String name, String language, boolean defaultProfile, /* kept for backward-compatibility */boolean provided) {
117 this(name, language);
118 this.defaultProfile = defaultProfile;
119 }
120
121 public Integer getId() {
122 return id;
123 }
124
125 /**
126 * @return the profile name, unique by language.
127 */
128 public String getName() {
129 return name;
130 }
131
132 /**
133 * Set the profile name.
134 */
135 public RulesProfile setName(String s) {
136 this.name = s;
137 return this;
138 }
139
140 public int getVersion() {
141 return version;
142 }
143
144 public RulesProfile setVersion(int version) {
145 this.version = version;
146 return this;
147 }
148
149 public Boolean getUsed() {
150 return used;
151 }
152
153 public RulesProfile setUsed(Boolean used) {
154 this.used = used;
155 return this;
156 }
157
158 /**
159 * @return the list of active rules
160 */
161 public List<ActiveRule> getActiveRules() {
162 return getActiveRules(false);
163 }
164
165 /**
166 * @return the list of active rules
167 */
168 public List<ActiveRule> getActiveRules(boolean acceptDisabledRules) {
169 if (acceptDisabledRules) {
170 return activeRules;
171 }
172 List<ActiveRule> result = Lists.newArrayList();
173 for (ActiveRule activeRule : activeRules) {
174 if (activeRule.isEnabled()) {
175 result.add(activeRule);
176 }
177 }
178 return result;
179 }
180
181 public RulesProfile removeActiveRule(ActiveRule activeRule) {
182 activeRules.remove(activeRule);
183 return this;
184 }
185
186 public RulesProfile addActiveRule(ActiveRule activeRule) {
187 activeRules.add(activeRule);
188 return this;
189 }
190
191 /**
192 * Set the list of active rules
193 */
194 public void setActiveRules(List<ActiveRule> activeRules) {
195 this.activeRules = activeRules;
196 }
197
198 /**
199 * @return whether this is the default profile for the language
200 */
201 public Boolean getDefaultProfile() {
202 return defaultProfile;
203 }
204
205 /**
206 * Set whether this is the default profile for the language. The default profile is used when none is explicitly defined when auditing a
207 * project.
208 */
209 public void setDefaultProfile(Boolean b) {
210 this.defaultProfile = b;
211 }
212
213 /**
214 * @deprecated since 3.3 not replaced
215 */
216 @Deprecated
217 public Boolean getProvided() {
218 return false;
219 }
220
221 /**
222 * @deprecated since 3.3 not replaced
223 */
224 @Deprecated
225 public void setProvided(Boolean b) {
226 }
227
228 /**
229 * @deprecated since 3.3. Always return true.
230 */
231 @Deprecated
232 public Boolean getEnabled() {
233 return Boolean.TRUE;
234 }
235
236 /**
237 * @deprecated since 3.3. Always return true.
238 */
239 @Deprecated
240 public boolean isEnabled() {
241 return true;
242 }
243
244 /**
245 * @deprecated since 3.3.
246 */
247 @Deprecated
248 public RulesProfile setEnabled(Boolean b) {
249 throw new UnsupportedOperationException("The field RulesProfile#enabled is not supported since 3.3.");
250 }
251
252 /**
253 * @return the profile language
254 */
255 public String getLanguage() {
256 return language;
257 }
258
259 /**
260 * Set the profile language
261 */
262 public RulesProfile setLanguage(String s) {
263 this.language = s;
264 return this;
265 }
266
267 /**
268 * For internal use only.
269 *
270 * @since 2.5
271 */
272 @CheckForNull
273 public String getParentName() {
274 return parentName;
275 }
276
277 /**
278 * For internal use only.
279 *
280 * @since 2.5
281 */
282 public void setParentName(String parentName) {
283 this.parentName = parentName;
284 }
285
286 /**
287 * Note: disabled rules are excluded.
288 *
289 * @return the list of active rules for a given severity
290 */
291 public List<ActiveRule> getActiveRules(RulePriority severity) {
292 List<ActiveRule> result = Lists.newArrayList();
293 for (ActiveRule activeRule : activeRules) {
294 if (activeRule.getSeverity().equals(severity) && activeRule.isEnabled()) {
295 result.add(activeRule);
296 }
297 }
298 return result;
299 }
300
301 /**
302 * @deprecated since 2.3 use {@link #getActiveRulesByRepository(String)} instead.
303 */
304 @Deprecated
305 public List<ActiveRule> getActiveRulesByPlugin(String repositoryKey) {
306 return getActiveRulesByRepository(repositoryKey);
307 }
308
309 /**
310 * Get the active rules of a specific repository.
311 * Only enabled rules are selected. Disabled rules are excluded.
312 */
313 public List<ActiveRule> getActiveRulesByRepository(String repositoryKey) {
314 List<ActiveRule> result = Lists.newArrayList();
315 for (ActiveRule activeRule : activeRules) {
316 if (repositoryKey.equals(activeRule.getRepositoryKey()) && activeRule.isEnabled()) {
317 result.add(activeRule);
318 }
319 }
320 return result;
321 }
322
323 /**
324 * Note: disabled rules are excluded.
325 *
326 * @return an active rule from a plugin key and a rule key if the rule is activated, null otherwise
327 */
328 @CheckForNull
329 public ActiveRule getActiveRule(String repositoryKey, String ruleKey) {
330 for (ActiveRule activeRule : activeRules) {
331 if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getRuleKey(), ruleKey) && activeRule.isEnabled()) {
332 return activeRule;
333 }
334 }
335 return null;
336 }
337
338 /**
339 * Note: disabled rules are excluded.
340 */
341 @CheckForNull
342 public ActiveRule getActiveRuleByConfigKey(String repositoryKey, String configKey) {
343 for (ActiveRule activeRule : activeRules) {
344 if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getConfigKey(), configKey) && activeRule.isEnabled()) {
345 return activeRule;
346 }
347 }
348 return null;
349 }
350
351 /**
352 * Note: disabled rules are excluded.
353 */
354 @CheckForNull
355 public ActiveRule getActiveRule(Rule rule) {
356 return getActiveRule(rule.getRepositoryKey(), rule.getKey());
357 }
358
359 /**
360 * @param optionalSeverity if null, then the default rule severity is used
361 */
362 public ActiveRule activateRule(final Rule rule, RulePriority optionalSeverity) {
363 if (Iterables.any(activeRules, new Predicate<ActiveRule>() {
364 @Override
365 public boolean apply(ActiveRule input) {
366 return input.getRule().equals(rule);
367 }
368 })) {
369 throw MessageException.of(String.format(
370 "The definition of the profile '%s' (language '%s') contains multiple occurrences of the '%s:%s' rule. The plugin which declares this profile should fix this.",
371 getName(), getLanguage(), rule.getRepositoryKey(), rule.getKey()));
372 }
373 ActiveRule activeRule = new ActiveRule();
374 activeRule.setRule(rule);
375 activeRule.setRulesProfile(this);
376 activeRule.setSeverity(optionalSeverity == null ? rule.getSeverity() : optionalSeverity);
377 activeRules.add(activeRule);
378 return activeRule;
379 }
380
381 @Override
382 public boolean equals(Object obj) {
383 if (!(obj instanceof RulesProfile)) {
384 return false;
385 }
386 if (this == obj) {
387 return true;
388 }
389 RulesProfile other = (RulesProfile) obj;
390 return new EqualsBuilder().append(language, other.getLanguage()).append(name, other.getName()).isEquals();
391 }
392
393 @Override
394 public int hashCode() {
395 return new HashCodeBuilder(17, 37).append(language).append(name).toHashCode();
396 }
397
398 @Override
399 public Object clone() {
400 RulesProfile clone = RulesProfile.create(getName(), getLanguage());
401 clone.setDefaultProfile(getDefaultProfile());
402 clone.setParentName(getParentName());
403 if (activeRules != null && !activeRules.isEmpty()) {
404 clone.setActiveRules(new ArrayList<ActiveRule>(CollectionUtils.collect(activeRules, new Transformer() {
405 public Object transform(Object input) {
406 return ((ActiveRule) input).clone();
407 }
408 })));
409 }
410 return clone;
411 }
412
413 @Override
414 public String toString() {
415 return new StringBuilder().append("[name=").append(name).append(",language=").append(language).append("]").toString();
416 }
417
418 public static RulesProfile create(String name, String language) {
419 return new RulesProfile().setName(name).setLanguage(language);
420 }
421
422 public static RulesProfile create() {
423 return new RulesProfile();
424 }
425 }