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
025package net.sourceforge.cobertura.coveragedata;
026
027import java.util.Iterator;
028import java.util.SortedSet;
029import java.util.TreeSet;
030
031import net.sourceforge.cobertura.util.StringUtil;
032
033public 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}