001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2009 SonarSource SA
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.squid.api;
021    
022    import java.util.Set;
023    import java.util.SortedSet;
024    import java.util.TreeSet;
025    
026    import org.sonar.squid.measures.Measurable;
027    import org.sonar.squid.measures.Measures;
028    import org.sonar.squid.measures.Metric;
029    
030    public abstract class SourceCode implements Measurable, Comparable<SourceCode> {
031    
032      private final String name;
033      private Measures measures = new Measures();
034      private final String key;
035      private int startAtLine = -1;
036      private int endAtLine = -1;
037      private SourceCode parent;
038      private SortedSet<SourceCode> children;
039      private SourceCodeIndexer indexer;
040    
041      public SourceCode(String key) {
042        this(key, null);
043      }
044    
045      public SourceCode(String key, String name) {
046        this.key = key;
047        this.name = name;
048      }
049    
050      public String getKey() {
051        return key;
052      }
053    
054      public int compareTo(SourceCode resource) {
055        return key.compareTo(resource.getKey());
056      }
057    
058      public String getName() {
059        return name;
060      }
061    
062      public final void setSourceCodeIndexer(SourceCodeIndexer indexer) {
063        this.indexer = indexer;
064      }
065    
066      private void index(SourceCode sourceCode) {
067        if (indexer != null) {
068          indexer.index(sourceCode);
069        }
070      }
071    
072      @Override
073      public boolean equals(Object obj) {
074        return (obj instanceof SourceCode) && key.equals(((SourceCode) obj).key);
075      }
076    
077      @Override
078      public int hashCode() {
079        return key.hashCode();
080      }
081    
082      @Override
083      public String toString() {
084        return getKey();
085      }
086    
087      public final void computeMeasures() {
088        if (hasChildren()) {
089          for (SourceCode child : getChildren()) {
090            child.computeMeasures();
091          }
092        }
093        for (Metric metric : Metric.values()) {
094          if (!metric.aggregateIfThereIsAlreadyAValue() && getDouble(metric) != 0) {
095            continue;
096          }
097          if (hasChildren()) {
098            for (SourceCode child : getChildren()) {
099              if (!metric.isCalculatedMetric() && metric.isThereAggregationFormula()) {
100                add(metric, child);
101              }
102            }
103          }
104        }
105      }
106    
107      public boolean isType(Class<? extends SourceCode> resourceType) {
108        return this.getClass() == resourceType;
109      }
110    
111      /**
112       * {@inheritDoc}
113       */
114      public int getInt(Metric metric) {
115        return (int) getMeasure(metric);
116      }
117    
118      /**
119       * {@inheritDoc}
120       */
121      public double getDouble(Metric metric) {
122        return getMeasure(metric);
123      }
124    
125      public void add(Metric metric, SourceCode child) {
126        add(metric, child.getMeasure(metric));
127      }
128    
129      public void add(Metric metric, double value) {
130        setMeasure(metric, getMeasure(metric) + value);
131      }
132    
133      public void addData(Metric metric, Object data) {
134        measures.setData(metric, data);
135      }
136    
137      public Object getData(Metric metric) {
138        return measures.getData(metric);
139      }
140    
141      private double getMeasure(Metric metric) {
142        if (metric.isCalculatedMetric()) {
143          return metric.getCalculatedMetricFormula().calculate(this);
144        }
145        return measures.getValue(metric);
146      }
147    
148      /**
149       * {@inheritDoc}
150       */
151      public void setMeasure(Metric metric, double measure) {
152        if (metric.isCalculatedMetric()) {
153          throw new IllegalStateException("It's not allowed to set the value of a calculated metric : " + metric.name());
154        }
155        measures.setValue(metric, measure);
156      }
157    
158      /**
159       * {@inheritDoc}
160       */
161      public void setMeasure(Metric metric, int measure) {
162        setMeasure(metric, (double) measure);
163      }
164    
165      public void removeMeasure(Metric metric) {
166        measures.removeMeasure(metric);
167      }
168    
169      public void setStartAtLine(int startAtLine) {
170        this.startAtLine = startAtLine;
171        this.endAtLine = startAtLine;
172      }
173    
174      public void setEndAtLine(int endAtLine) {
175        this.endAtLine = endAtLine;
176      }
177    
178      public int getStartAtLine() {
179        return startAtLine;
180      }
181    
182      public int getEndAtLine() {
183        return endAtLine;
184      }
185    
186      public SourceCode addChild(SourceCode sourceCode) {
187        if (children == null) {
188          children = new TreeSet<SourceCode>();
189        }
190        sourceCode.setParent(this);
191        if (!children.contains(sourceCode)) {
192          children.add(sourceCode);
193          index(sourceCode);
194        }
195        return this;
196      }
197    
198      public SourceCode getParent(Class<? extends SourceCode> sourceCode) {
199        if (parent == null) {
200          return null;
201        } else if (parent.getClass().equals(sourceCode)) {
202          return parent;
203        } else {
204          return parent.getParent(sourceCode);
205        }
206      }
207    
208      public SourceCode getFirstChild() {
209        return !children.isEmpty() ? children.first() : null;
210      }
211    
212      public SourceCode getLastChild() {
213        return !children.isEmpty() ? children.last() : null;
214      }
215    
216      private void setParent(SourceCode parent) {
217        this.parent = parent;
218      }
219    
220      public SourceCode getParent() {
221        return parent;
222      }
223    
224      public Set<SourceCode> getChildren() {
225        return children;
226      }
227    
228      public boolean hasChild(SourceCode squidUnit) {
229        if (!hasChildren()) {
230          return false;
231        }
232        if (children.contains(squidUnit)) {
233          return true;
234        }
235        for (SourceCode child : children) {
236          if (child.hasChild(squidUnit)) {
237            return true;
238          }
239        }
240        return false;
241      }
242    
243      private boolean hasChildren() {
244        return children != null && children.size() != 0;
245      }
246    
247      public boolean hasAmongParents(SourceCode expectedParent) {
248        if (parent == null) {
249          return false;
250        }
251        return parent.equals(expectedParent) || parent.hasAmongParents(expectedParent);
252      }
253    }