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.HashMap;
023    import java.util.Map;
024    import java.util.Set;
025    import java.util.StringTokenizer;
026    
027    import org.apache.commons.io.IOUtils;
028    import org.sonar.squid.graph.Edge;
029    import org.sonar.squid.graph.EdgeUsage;
030    import org.sonar.squid.graph.Node;
031    import org.sonar.squid.measures.Measurable;
032    import org.sonar.squid.measures.Metric;
033    
034    public abstract class SourceCode extends TreeNode<SourceCode> implements Node, Measurable, Comparable<SourceCode> {
035    
036      private final String        name;
037      private Map<Metric, Double> measures     = new HashMap<Metric, Double>();
038      private final String        key;
039      private Map<Node, Edge>     dependedUpon = new HashMap<Node, Edge>();
040      private Map<Node, Edge>     dependsUpon  = new HashMap<Node, Edge>();
041      private int                 startAtLine  = -1;
042      private int                 endAtLine    = -1;
043    
044      public SourceCode(String key) {
045        this(key, null);
046      }
047    
048      public SourceCode(String key, String name) {
049        this.name = name;
050        this.key = key;
051        initializeMeasures();
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      @Override
067      public boolean equals(Object obj) {
068        if (!(obj instanceof SourceCode)) {
069          return false;
070        }
071        return getKey().equals(((SourceCode) obj).getKey());
072      }
073    
074      @Override
075      public int hashCode() {
076        return key.hashCode();
077      }
078    
079      @Override
080      public String toString() {
081        StringBuilder sb = new StringBuilder(512);
082        sb.append(this.getClass().getSimpleName()).append(" : ").append(getKey()).append(IOUtils.LINE_SEPARATOR);
083        for (SourceCode child : getChildren()) {
084          String childTree = child.toString();
085          StringTokenizer tokenizer = new StringTokenizer(childTree, IOUtils.LINE_SEPARATOR);
086          while (tokenizer.hasMoreTokens()) {
087            sb.append("-").append(tokenizer.nextToken()).append(IOUtils.LINE_SEPARATOR);
088          }
089        }
090        return sb.toString();
091      }
092    
093      public final void computeMeasures() {
094        for (SourceCode child : getChildren()) {
095          child.computeMeasures();
096        }
097        for (Metric metric : Metric.values()) {
098          if (!metric.aggregateIfThereIsAlreadyAValue() && getDouble(metric) != 0) {
099            continue;
100          }
101          for (SourceCode child : getChildren()) {
102            if (!metric.isCalculatedMetric() && metric.isThereAggregationFormula()) {
103              add(metric, child);
104            }
105          }
106    
107        }
108        updateMeasuresAfterConsolidation();
109      }
110    
111      public void createEdgeWith(Node to, EdgeUsage link) {
112        createEdgeWith(to, link, (Edge) null);
113      }
114    
115      public void createEdgeWith(Node to, EdgeUsage link, Edge rootEdge) {
116        if (equals(to)) {
117          throw new IllegalStateException("You can't create an edge from one node to itself. SquidUnit : " + getKey());
118        }
119        if (getEdgeTo(to) != null) {
120          throw new IllegalStateException("Those two nodes are already linked. From : " + getKey() + ", To : " + to.getKey());
121        }
122        Edge edge = new Edge(this, to, link, null);
123        edge.addRootEdge(rootEdge);
124    
125        dependsUpon.put(to, edge);
126        ((SourceCode) to).dependedUpon.put(this, edge);
127      }
128    
129      public Edge getEdgeTo(Node to) {
130        return dependsUpon.get(to);
131      }
132    
133      public Edge getEdgeFrom(Node from) {
134        return dependedUpon.get(from);
135      }
136    
137      public EdgeUsage getUsageOf(Node to) {
138        Edge relation = dependsUpon.get(to);
139        if (relation == null) {
140          return EdgeUsage.NO_LINK;
141        }
142        return relation.getUsage();
143      }
144    
145      public EdgeUsage getUsageBy(Node from) {
146        Edge edge = dependedUpon.get(from);
147        if (edge == null) {
148          return EdgeUsage.NO_LINK;
149        }
150        return edge.getUsage();
151      }
152    
153      public Set<Node> dependedUpon() {
154        return dependedUpon.keySet();
155      }
156    
157      public Set<Node> dependsUpon() {
158        return dependsUpon.keySet();
159      }
160    
161      public boolean isType(Class<? extends SourceCode> resourceType) {
162        return this.getClass() == resourceType;
163      }
164    
165      public int getInt(Metric metric) {
166        return getMeasure(metric).intValue();
167      }
168    
169      public double getDouble(Metric metric) {
170        return getMeasure(metric).doubleValue();
171      }
172    
173      public void add(Metric metric, SourceCode child) {
174        add(metric, child.getMeasure(metric));
175      }
176    
177      public void add(Metric metric, double value) {
178        setMeasure(metric, getMeasure(metric) + value);
179      }
180    
181      private Double getMeasure(Metric metric) {
182        if (metric.isCalculatedMetric()) {
183          return metric.getCalculatedMetricFormula().calculate(this);
184        }
185        if (measures.get(metric) == null) {
186          return (double) 0;
187        }
188        return measures.get(metric);
189      }
190    
191      public void setMeasure(Metric metric, double measure) {
192        if (metric.isCalculatedMetric()) {
193          throw new IllegalStateException("It's not allowed to set the value of a calculated metric : " + metric.name());
194        }
195        measures.put(metric, measure);
196      }
197    
198      public void setMeasure(Metric metric, int measure) {
199        setMeasure(metric, (double) measure);
200      }
201    
202      protected abstract void initializeMeasures();
203    
204      protected abstract void updateMeasuresAfterConsolidation();
205    
206      public void setStartAtLine(int startAtLine) {
207        this.startAtLine = startAtLine;
208        this.endAtLine = startAtLine;
209      }
210    
211      public void setEndAtLine(int endAtLine) {
212        this.endAtLine = endAtLine;
213      }
214    
215      public int getStartAtLine() {
216        return startAtLine;
217      }
218    
219      public int getEndAtLine() {
220        return endAtLine;
221      }
222    }