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 void addClassData(ClassData classData)
053            {
054                    lock.lock();
055                    try
056                    {
057                            if (children.containsKey(classData.getBaseName()))
058                                    throw new IllegalArgumentException("Source file " + this.name
059                                                    + " already contains a class with the name "
060                                                    + classData.getBaseName());
061            
062                            // Each key is a class basename, stored as an String object.
063                            // Each value is information about the class, stored as a ClassData object.
064                            children.put(classData.getBaseName(), classData);
065                    }
066                    finally
067                    {
068                            lock.unlock();
069                    }
070            }
071    
072            /**
073             * This is required because we implement Comparable.
074             */
075            public int compareTo(Object o)
076            {
077                    if (!o.getClass().equals(SourceFileData.class))
078                            return Integer.MAX_VALUE;
079                    return this.name.compareTo(((SourceFileData)o).name);
080            }
081    
082            public boolean contains(String name)
083            {
084                    lock.lock();
085                    try
086                    {
087                            return this.children.containsKey(name);
088                    }
089                    finally
090                    {
091                            lock.unlock();
092                    }
093            }
094    
095            public boolean containsInstrumentationInfo()
096            {
097                    lock.lock();
098                    try
099                    {
100                            // Return false if any of our child ClassData's does not
101                            // contain instrumentation info
102                            Iterator iter = this.children.values().iterator();
103                            while (iter.hasNext())
104                            {
105                                    ClassData classData = (ClassData)iter.next();
106                                    if (!classData.containsInstrumentationInfo())
107                                            return false;
108                            }
109                    }
110                    finally
111                    {
112                            lock.unlock();
113                    }
114                    return true;
115            }
116    
117            /**
118             * Returns true if the given object is an instance of the
119             * SourceFileData class, and it contains the same data as this
120             * class.
121             */
122            public boolean equals(Object obj)
123            {
124                    if (this == obj)
125                            return true;
126                    if ((obj == null) || !(obj.getClass().equals(this.getClass())))
127                            return false;
128    
129                    SourceFileData sourceFileData = (SourceFileData)obj;
130                    getBothLocks(sourceFileData);
131                    try
132                    {
133                            return super.equals(obj)
134                                            && this.name.equals(sourceFileData.name);
135                    }
136                    finally
137                    {
138                            lock.unlock();
139                            sourceFileData.lock.unlock();
140                    }
141            }
142    
143            public String getBaseName()
144            {
145                    String fullNameWithoutExtension;
146                    int lastDot = this.name.lastIndexOf('.');
147                    if (lastDot == -1)
148                    {
149                            fullNameWithoutExtension = this.name;
150                    }
151                    else
152                    {
153                            fullNameWithoutExtension = this.name.substring(0, lastDot);
154                    }
155    
156                    int lastSlash = fullNameWithoutExtension.lastIndexOf('/');
157                    if (lastSlash == -1)
158                    {
159                            return fullNameWithoutExtension;
160                    }
161                    return fullNameWithoutExtension.substring(lastSlash + 1);
162            }
163    
164            public SortedSet getClasses()
165            {
166                    lock.lock();
167                    try
168                    {
169                            return new TreeSet(this.children.values());
170                    }
171                    finally
172                    {
173                            lock.unlock();
174                    }
175            }
176    
177            public LineData getLineCoverage(int lineNumber)
178            {
179                    lock.lock();
180                    try
181                    {
182                            Iterator iter = this.children.values().iterator();
183                            while (iter.hasNext())
184                            {
185                                    ClassData classData = (ClassData)iter.next();
186                                    if (classData.isValidSourceLineNumber(lineNumber))
187                                            return classData.getLineCoverage(lineNumber);
188                            }
189                    }
190                    finally
191                    {
192                            lock.unlock();
193                    }
194                    return null;
195            }
196    
197            public String getName()
198            {
199                    return this.name;
200            }
201    
202            /**
203             * @return The name of this source file without the file extension
204             *         in the format
205             *         "net.sourceforge.cobertura.coveragedata.SourceFileData"
206             */
207            public String getNormalizedName()
208            {
209                    String fullNameWithoutExtension;
210                    int lastDot = this.name.lastIndexOf('.');
211                    if (lastDot == -1)
212                    {
213                            fullNameWithoutExtension = this.name;
214                    }
215                    else
216                    {
217                            fullNameWithoutExtension = this.name.substring(0, lastDot);
218                    }
219    
220                    return StringUtil.replaceAll(fullNameWithoutExtension, "/", ".");
221            }
222    
223            /**
224             * @return The name of the package that this source file is in.
225             *         In the format "net.sourceforge.cobertura.coveragedata"
226             */
227            public String getPackageName()
228            {
229                    int lastSlash = this.name.lastIndexOf('/');
230                    if (lastSlash == -1)
231                    {
232                            return null;
233                    }
234                    return StringUtil.replaceAll(this.name.substring(0, lastSlash), "/",
235                                    ".");
236            }
237    
238            public int hashCode()
239            {
240                    return this.name.hashCode();
241            }
242    
243            public boolean isValidSourceLineNumber(int lineNumber)
244            {
245                    lock.lock();
246                    try
247                    {
248                            Iterator iter = this.children.values().iterator();
249                            while (iter.hasNext())
250                            {
251                                    ClassData classData = (ClassData)iter.next();
252                                    if (classData.isValidSourceLineNumber(lineNumber))
253                                            return true;
254                            }
255                    }
256                    finally
257                    {
258                            lock.unlock();
259                    }
260                    return false;
261            }
262    
263    }