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