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