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 }