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     */
020    package org.sonar.api.qualitymodel;
021    
022    import com.google.common.collect.Lists;
023    import org.apache.commons.lang.StringUtils;
024    import org.apache.commons.lang.builder.ToStringBuilder;
025    import org.apache.commons.lang.builder.ToStringStyle;
026    import org.hibernate.annotations.Sort;
027    import org.hibernate.annotations.SortType;
028    import org.sonar.api.rules.Rule;
029    
030    import javax.persistence.*;
031    
032    import java.util.Collections;
033    import java.util.List;
034    
035    /**
036     * @since 2.3
037     */
038    @Entity
039    @Table(name = "characteristics")
040    public 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    }