001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 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     */
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    import java.util.Collections;
032    import java.util.List;
033    
034    /**
035     * @since 2.3
036     */
037    @Entity
038    @Table(name = "characteristics")
039    public 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    }