001    /*
002     * Cobertura - http://cobertura.sourceforge.net/
003     *
004     * Copyright (C) 2003 jcoverage ltd.
005     * Copyright (C) 2005 Mark Doliner
006     * Copyright (C) 2005 Jeremy Thomerson
007     * Copyright (C) 2006 Jiri Mares
008     *
009     * Cobertura is free software; you can redistribute it and/or modify
010     * it under the terms of the GNU General Public License as published
011     * by the Free Software Foundation; either version 2 of the License,
012     * or (at your option) any later version.
013     *
014     * Cobertura is distributed in the hope that it will be useful, but
015     * WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     * General Public License for more details.
018     *
019     * You should have received a copy of the GNU General Public License
020     * along with Cobertura; if not, write to the Free Software
021     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
022     * USA
023     */
024    
025    package net.sourceforge.cobertura.coveragedata;
026    
027    import java.util.Iterator;
028    import java.util.SortedSet;
029    import java.util.TreeSet;
030    
031    import net.sourceforge.cobertura.util.StringUtil;
032    
033    public class SourceFileData extends CoverageDataContainer
034                    implements Comparable, HasBeenInstrumented
035    {
036    
037            private static final long serialVersionUID = 3;
038    
039            private String name;
040    
041       /**
042        * @param name In the format, "net/sourceforge/cobertura/coveragedata/SourceFileData.java"
043        */
044            public SourceFileData(String name)
045            {
046                    if (name == null)
047                            throw new IllegalArgumentException(
048                                    "Source file name must be specified.");
049                    this.name = name;
050            }
051    
052            public synchronized void addClassData(ClassData classData)
053            {
054                    if (children.containsKey(classData.getBaseName()))
055                            throw new IllegalArgumentException("Source file " + this.name
056                                            + " already contains a class with the name "
057                                            + classData.getBaseName());
058    
059                    // Each key is a class basename, stored as an String object.
060                    // Each value is information about the class, stored as a ClassData object.
061                    children.put(classData.getBaseName(), classData);
062            }
063    
064            /**
065             * This is required because we implement Comparable.
066             */
067            public int compareTo(Object o)
068            {
069                    if (!o.getClass().equals(SourceFileData.class))
070                            return Integer.MAX_VALUE;
071                    return this.name.compareTo(((SourceFileData)o).name);
072            }
073    
074            public boolean contains(String name)
075            {
076                    return this.children.containsKey(name);
077            }
078    
079            public boolean containsInstrumentationInfo()
080            {
081                    // Return false if any of our child ClassData's does not
082                    // contain instrumentation info
083                    Iterator iter = this.children.values().iterator();
084                    while (iter.hasNext())
085                    {
086                            ClassData classData = (ClassData)iter.next();
087                            if (!classData.containsInstrumentationInfo())
088                                    return false;
089                    }
090                    return true;
091            }
092    
093            /**
094             * Returns true if the given object is an instance of the
095             * SourceFileData class, and it contains the same data as this
096             * class.
097             */
098            public boolean equals(Object obj)
099            {
100                    if (this == obj)
101                            return true;
102                    if ((obj == null) || !(obj.getClass().equals(this.getClass())))
103                            return false;
104    
105                    SourceFileData sourceFileData = (SourceFileData)obj;
106                    return super.equals(obj)
107                                    && this.name.equals(sourceFileData.name);
108            }
109    
110            public String getBaseName()
111            {
112                    String fullNameWithoutExtension;
113                    int lastDot = this.name.lastIndexOf('.');
114                    if (lastDot == -1)
115                    {
116                            fullNameWithoutExtension = this.name;
117                    }
118                    else
119                    {
120                            fullNameWithoutExtension = this.name.substring(0, lastDot);
121                    }
122    
123                    int lastSlash = fullNameWithoutExtension.lastIndexOf('/');
124                    if (lastSlash == -1)
125                    {
126                            return fullNameWithoutExtension;
127                    }
128                    return fullNameWithoutExtension.substring(lastSlash + 1);
129            }
130    
131            public SortedSet getClasses()
132            {
133                    return new TreeSet(this.children.values());
134            }
135    
136            public LineData getLineCoverage(int lineNumber)
137            {
138                    Iterator iter = this.children.values().iterator();
139                    while (iter.hasNext())
140                    {
141                            ClassData classData = (ClassData)iter.next();
142                            if (classData.isValidSourceLineNumber(lineNumber))
143                                    return classData.getLineCoverage(lineNumber);
144                    }
145                    return null;
146            }
147    
148            public String getName()
149            {
150                    return this.name;
151            }
152    
153            /**
154             * @return The name of this source file without the file extension
155             *         in the format
156             *         "net.sourceforge.cobertura.coveragedata.SourceFileData"
157             */
158            public String getNormalizedName()
159            {
160                    String fullNameWithoutExtension;
161                    int lastDot = this.name.lastIndexOf('.');
162                    if (lastDot == -1)
163                    {
164                            fullNameWithoutExtension = this.name;
165                    }
166                    else
167                    {
168                            fullNameWithoutExtension = this.name.substring(0, lastDot);
169                    }
170    
171                    return StringUtil.replaceAll(fullNameWithoutExtension, "/", ".");
172            }
173    
174            /**
175             * @return The name of the package that this source file is in.
176             *         In the format "net.sourceforge.cobertura.coveragedata"
177             */
178            public String getPackageName()
179            {
180                    int lastSlash = this.name.lastIndexOf('/');
181                    if (lastSlash == -1)
182                    {
183                            return null;
184                    }
185                    return StringUtil.replaceAll(this.name.substring(0, lastSlash), "/",
186                                    ".");
187            }
188    
189            public int hashCode()
190            {
191                    return this.name.hashCode();
192            }
193    
194            public boolean isValidSourceLineNumber(int lineNumber)
195            {
196                    Iterator iter = this.children.values().iterator();
197                    while (iter.hasNext())
198                    {
199                            ClassData classData = (ClassData)iter.next();
200                            if (classData.isValidSourceLineNumber(lineNumber))
201                                    return true;
202                    }
203                    return false;
204            }
205    
206    }