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
021package org.sonar.squid.api;
022
023import org.sonar.squid.measures.Measurable;
024import org.sonar.squid.measures.Measures;
025import org.sonar.squid.measures.Metric;
026import org.sonar.squid.measures.MetricDef;
027
028import java.util.HashSet;
029import java.util.Set;
030import java.util.SortedSet;
031import java.util.TreeSet;
032
033public 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}