001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2013 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 */
020package org.sonar.api.qualitymodel;
021
022import com.google.common.collect.Lists;
023import org.apache.commons.lang.StringUtils;
024import org.apache.commons.lang.builder.ToStringBuilder;
025import org.apache.commons.lang.builder.ToStringStyle;
026import org.hibernate.annotations.Sort;
027import org.hibernate.annotations.SortType;
028import org.sonar.api.rules.Rule;
029
030import javax.persistence.*;
031
032import java.util.Collections;
033import java.util.List;
034
035/**
036 * @since 2.3
037 */
038@Entity
039@Table(name = "characteristics")
040public final class Characteristic implements Comparable<Characteristic> {
041
042  public static final int ROOT_DEPTH = 1;
043
044  @Id
045  @Column(name = "id")
046  @GeneratedValue
047  private Integer id;
048
049  @Column(name = "kee", nullable = true, length = 100)
050  private String key;
051
052  @Column(name = "name", nullable = true, length = 100)
053  private String name;
054
055  @Column(name = "depth")
056  private int depth = ROOT_DEPTH;
057
058  @Column(name = "characteristic_order")
059  private int order = 0;
060
061  @ManyToOne(fetch = FetchType.EAGER)
062  @JoinColumn(name = "quality_model_id")
063  private Model model;
064
065  @ManyToOne(fetch = FetchType.EAGER)
066  @JoinColumn(name = "rule_id")
067  private Rule rule;
068
069  @Column(name = "description", nullable = true, length = 4000)
070  private String description;
071
072  @Column(name = "enabled", updatable = true, nullable = true)
073  private Boolean enabled = Boolean.TRUE;
074
075  @ManyToMany
076  @JoinTable(
077      name = "characteristic_edges",
078      joinColumns = @JoinColumn(name = "child_id", referencedColumnName = "id"),
079      inverseJoinColumns = @JoinColumn(name = "parent_id",
080          referencedColumnName = "id")
081  )
082  private List<Characteristic> parents = Lists.newArrayList();
083
084  @Sort(type = SortType.NATURAL)
085  @ManyToMany(mappedBy = "parents", cascade = CascadeType.ALL)
086  private List<Characteristic> children = Lists.newArrayList();
087
088  @OneToMany(mappedBy = "characteristic", fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE})
089  private List<CharacteristicProperty> properties = Lists.newArrayList();
090
091  Characteristic() {
092  }
093
094  public Integer getId() {
095    return id;
096  }
097
098  Characteristic setId(Integer id) {
099    this.id = id;
100    return this;
101  }
102
103  public String getKey() {
104    return key;
105  }
106
107  public Characteristic setKey(String s) {
108    this.key = StringUtils.trimToNull(s);
109    return this;
110  }
111
112  public String getName() {
113    return name;
114  }
115
116  public Characteristic setName(String s) {
117    return setName(s, false);
118  }
119
120  public Characteristic setName(String s, boolean asKey) {
121    this.name = StringUtils.trimToNull(s);
122    if (asKey) {
123      this.key = StringUtils.upperCase(this.name);
124      this.key = StringUtils.replaceChars(this.key, ' ', '_');
125    }
126    return this;
127  }
128
129  public Model getModel() {
130    return model;
131  }
132
133  Characteristic setModel(Model model) {
134    this.model = model;
135    return this;
136  }
137
138  public Rule getRule() {
139    return rule;
140  }
141
142  public boolean hasRule() {
143    return rule != null;
144  }
145
146  public Characteristic setRule(Rule r) {
147    this.rule = r;
148    return this;
149  }
150
151  public Boolean getEnabled() {
152    return enabled;
153  }
154
155  public Characteristic setEnabled(Boolean b) {
156    this.enabled = b;
157    return this;
158  }
159
160  public Characteristic addChildren(Characteristic... list) {
161    if (list != null) {
162      for (Characteristic c : list) {
163        addChild(c);
164      }
165    }
166    return this;
167  }
168
169  public Characteristic addChild(Characteristic child) {
170    propagateDepth(child, depth + 1);
171    child.addParents(this);
172    child.setModel(model);
173    children.add(child);
174    return this;
175  }
176
177  Characteristic removeChild(Characteristic child) {
178    children.remove(child);
179    return this;
180  }
181
182  private static void propagateDepth(Characteristic characteristic, int depth) {
183    characteristic.setDepth(depth);
184    for (Characteristic child : characteristic.getChildren()) {
185      propagateDepth(child, depth + 1);
186    }
187  }
188
189  Characteristic addParents(Characteristic... pc) {
190    if (pc != null) {
191      Collections.addAll(this.parents, pc);
192    }
193    return this;
194  }
195
196  public List<Characteristic> getParents() {
197    return parents;
198  }
199
200  public Characteristic getParent(String name) {
201    for (Characteristic parent : parents) {
202      if (StringUtils.equals(parent.getName(), name)) {
203        return parent;
204      }
205    }
206    return null;
207  }
208
209  /**
210   * Enabled children sorted by insertion order
211   */
212  public List<Characteristic> getChildren() {
213    return getChildren(true);
214  }
215
216  /**
217   * Enabled children sorted by insertion order
218   */
219  public List<Characteristic> getChildren(boolean onlyEnabled) {
220    if (onlyEnabled) {
221      return children;
222    }
223    List<Characteristic> result = Lists.newArrayList();
224    for (Characteristic child : children) {
225      if (child.getEnabled()) {
226        result.add(child);
227      }
228    }
229    return result;
230  }
231
232  public Characteristic getChild(String name) {
233    for (Characteristic child : children) {
234      if (StringUtils.equals(child.getName(), name)) {
235        return child;
236      }
237    }
238    return null;
239  }
240
241  public int getDepth() {
242    return depth;
243  }
244
245  public boolean isRoot() {
246    return depth == ROOT_DEPTH;
247  }
248
249  Characteristic setDepth(int i) {
250    this.depth = i;
251    return this;
252  }
253
254  public int getOrder() {
255    return order;
256  }
257
258  Characteristic setOrder(int i) {
259    this.order = i;
260    return this;
261  }
262
263  public String getDescription() {
264    return description;
265  }
266
267  public Characteristic setDescription(String s) {
268    this.description = s;
269    return this;
270  }
271
272  public CharacteristicProperty setProperty(String key, String value) {
273    return addProperty(CharacteristicProperty.create(key).setTextValue(value));
274  }
275
276  public CharacteristicProperty setProperty(String key, Double value) {
277    return addProperty(CharacteristicProperty.create(key).setValue(value));
278  }
279
280  public CharacteristicProperty addProperty(CharacteristicProperty property) {
281    property.setCharacteristic(this);
282    properties.add(property);
283    return property;
284  }
285
286  public CharacteristicProperty getProperty(String key) {
287    for (CharacteristicProperty property : properties) {
288      if (StringUtils.equals(key, property.getKey())) {
289        return property;
290      }
291    }
292    return null;
293  }
294
295  public String getPropertyTextValue(String key, String defaultValue) {
296    CharacteristicProperty property = getProperty(key);
297    String value = property != null ? property.getTextValue() : null;
298    return StringUtils.defaultIfEmpty(value, defaultValue);
299  }
300
301  public Double getPropertyValue(String key, Double defaultValue) {
302    CharacteristicProperty property = getProperty(key);
303    Double value = property != null ? property.getValue() : null;
304    return value == null ? defaultValue : value;
305  }
306
307  public List<CharacteristicProperty> getProperties() {
308    return properties;
309  }
310
311  @Override
312  public boolean equals(Object o) {
313    if (this == o) {
314      return true;
315    }
316    if (o == null || getClass() != o.getClass()) {
317      return false;
318    }
319
320    Characteristic that = (Characteristic) o;
321    if (key != null ? !key.equals(that.key) : that.key != null) {
322      return false;
323    }
324    if (rule != null ? !rule.equals(that.rule) : that.rule != null) {
325      return false;
326    }
327    return true;
328  }
329
330  @Override
331  public int hashCode() {
332    int result = key != null ? key.hashCode() : 0;
333    result = 31 * result + (rule != null ? rule.hashCode() : 0);
334    return result;
335  }
336
337  @Override
338  public String toString() {
339    return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
340        .append("key", key)
341        .append("name", name)
342        .append("rule", rule)
343        .append("description", description)
344        .toString();
345  }
346
347  public int compareTo(Characteristic o) {
348    if (equals(o)) {
349      return 0;
350    }
351    return order - o.order;
352  }
353
354  public static Characteristic create() {
355    return new Characteristic();
356  }
357
358  public static Characteristic createByName(String name) {
359    return new Characteristic().setName(name, true);
360  }
361
362  public static Characteristic createByKey(String key, String name) {
363    return new Characteristic().setKey(key).setName(name, false);
364  }
365
366  public static Characteristic createByRule(Rule rule) {
367    return new Characteristic().setRule(rule);
368  }
369}