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