KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > groboutils > codecoverage > v2 > ant > FailOnReportStyle


1 /*
2  * @(#)FailOnReportStyle.java
3  *
4  * Copyright (C) 2004 Matt Albrecht
5  * groboclown@users.sourceforge.net
6  * http://groboutils.sourceforge.net
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  */

26
27 package net.sourceforge.groboutils.codecoverage.v2.ant;
28
29 import java.text.NumberFormat JavaDoc;
30 import java.util.Enumeration JavaDoc;
31 import java.util.StringTokenizer JavaDoc;
32 import java.util.Vector JavaDoc;
33
34 import org.apache.tools.ant.BuildException;
35 import org.apache.tools.ant.Project;
36 import org.w3c.dom.Document JavaDoc;
37 import org.w3c.dom.Element JavaDoc;
38 import org.w3c.dom.NodeList JavaDoc;
39
40
41
42 /**
43  * Checks the coverage report for a subset of classes whose total
44  * coverage percentage isn't up-to-snuff.
45  *
46  * @author Matt Albrecht <a HREF="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
47  * @version $Date: 2004/04/15 05:48:25 $
48  * @since March 15, 2003
49  */

50 public class FailOnReportStyle implements IReportStyle
51 {
52     private Vector JavaDoc includeExcludeSet = new Vector JavaDoc();
53     private String JavaDoc failureProperty = null;
54     private double minPercent = 80.0;
55     
56     private int markCount = 0;
57     private int coverCount = 0;
58     
59     private static final NumberFormat JavaDoc DOUBLE_FORMATTER =
60         NumberFormat.getInstance();
61     static {
62         DOUBLE_FORMATTER.setMaximumFractionDigits( 2 );
63     }
64     
65     
66     public static class IncludeAndExclude
67     {
68         String JavaDoc value = null;
69         boolean module = false;
70         boolean corp = false;
71         boolean isInclude = true;
72         
73         public void setModule( String JavaDoc s )
74         {
75             this.module = true;
76             this.value = s;
77         }
78         
79         public void setClass( String JavaDoc f )
80         {
81             this.corp = true;
82             this.value = f;
83         }
84         
85         
86         protected void check()
87                 throws BuildException
88         {
89             if (this.module && this.corp)
90             {
91                 throw new BuildException(
92                     "Only one of 'module' and 'class' attributes can be "+
93                     "specified in an "+this.getType()+" element." );
94             }
95             if (!this.module && !this.corp)
96             {
97                 throw new BuildException(
98                     "One of 'module' or 'class' attributes must be "+
99                     "specified in an "+this.getType()+" element." );
100             }
101         }
102         
103         protected String JavaDoc getType()
104         {
105             return (this.isInclude ? "include" : "exclude");
106         }
107     }
108     
109     
110     static class ClassFilter
111     {
112         IFilterToken parts[];
113         
114         public ClassFilter( String JavaDoc filter )
115                 throws BuildException
116         {
117             if (filter == null || filter.length() <= 0 ||
118                 filter.charAt(0) == '.' || filter.indexOf( ".." ) >= 0 ||
119                 filter.indexOf( "//" ) >= 0 || filter.indexOf( "/." ) >= 0 ||
120                 filter.indexOf( "./" ) >= 0 ||
121                 filter.charAt(filter.length()-1) == '.' ||
122                 filter.charAt(filter.length()-1) == '/')
123             {
124                 throw new BuildException( "Invalid class filter '"+filter+"'" );
125             }
126             if (filter.charAt(0) == '/')
127             {
128                 if (filter.length() <= 1)
129                 {
130                     throw new BuildException( "Invalid class filter '"+filter+"'" );
131                 }
132                 filter = filter.substring( 1 );
133             }
134             
135             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc( filter, "./", false );
136             Vector JavaDoc v = new Vector JavaDoc();
137             while (st.hasMoreTokens())
138             {
139                 String JavaDoc s = st.nextToken();
140                 if (s.length() <= 0)
141                 {
142                     throw new BuildException(
143                         "Invalid class filter: no values between delimiters."
144                         );
145                 }
146                 int len = s.length();
147                 if (s.charAt(0) == '*')
148                 {
149                     if (len == 1)
150                     {
151                         v.addElement( ANY_TOKEN );
152                     }
153                     else
154                     //if (s.equals( "**" ))
155
if (len == 2 && s.charAt(1) == '*')
156                     {
157                         v.addElement( NULL_TOKEN );
158                     }
159                     else
160                     {
161                         s = s.substring( 1 );
162                         if (s.indexOf( '*' ) >= 0)
163                         {
164                             throw new BuildException(
165                                 "Invalid class filter: only 1 wildcard may "+
166                                 "be present between delimiters." );
167                         }
168                         v.addElement( new BeforeFilterToken( s ) );
169                     }
170                 }
171                 else
172                 if (s.charAt( len - 1) == '*')
173                 {
174                     s = s.substring( 0, len - 1 );
175                     if (s.indexOf( '*' ) >= 0)
176                     {
177                         throw new BuildException(
178                             "Invalid class filter: only 1 wildcard may "+
179                             "be present between delimiters." );
180                     }
181                     v.addElement( new EndFilterToken( s ) );
182                 }
183                 else
184                 {
185                     if (s.indexOf( '*' ) > 0)
186                     {
187                         throw new BuildException(
188                             "Invalid class filter: a wildcard may "+
189                             "only be present before and after a text part." );
190                     }
191                     v.addElement( new ExactFilterToken( s ) );
192                 }
193             }
194             if (v.size() <= 0)
195             {
196                 throw new BuildException(
197                     "Invalid class filter: no delimited elements" );
198             }
199             parts = new IFilterToken[ v.size() ];
200             v.copyInto( parts );
201         }
202         
203         
204         public boolean match( String JavaDoc c )
205         {
206             if (c == null || c.length() <= 0)
207             {
208                 throw new IllegalArgumentException JavaDoc( "no null args." );
209             }
210             
211             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc( c, ".", false );
212             int filterPos = 0;
213             while (st.hasMoreTokens())
214             {
215                 if (filterPos >= this.parts.length)
216                 {
217                     return false;
218                 }
219                 String JavaDoc s = st.nextToken();
220                 
221                 // special handling for the '**' filter
222
if (this.parts[filterPos] == NULL_TOKEN)
223                 {
224                     // if the rest of the filters are '**', early out
225
if (filterPos == this.parts.length - 1)
226                     {
227                         return true;
228                     }
229                     
230                     // this kind of pattern matching isn't greedy.
231
if (this.parts[filterPos+1].match( s ))
232                     {
233                         // we can advance to the filter after the
234
// one just matched
235
filterPos += 2;
236                     }
237                     // else the next filterPos doesn't match this token,
238
// so keep this token for the next match
239
}
240                 else
241                 {
242                     // check if the next filter doesn't match, so we can
243
// early out.
244
if (!this.parts[filterPos++].match( s ))
245                     {
246                         return false;
247                     }
248                 }
249             }
250             
251             // no more tokens, but are there more filters? if so, then
252
// the token didn't match everything
253
return (filterPos >= this.parts.length);
254         }
255     }
256     
257     // uses an anti-pattern: use a "null" token to be used for the "**"
258
// filter.
259
static interface IFilterToken
260     {
261         public boolean match( String JavaDoc part );
262     }
263     
264     private static class BeforeFilterToken implements IFilterToken
265     {
266         private String JavaDoc m;
267         public BeforeFilterToken( String JavaDoc s )
268         {
269             this.m = s;
270         }
271         public boolean match( String JavaDoc part )
272         {
273             return part.endsWith( this.m );
274         }
275     }
276     
277     private static class EndFilterToken implements IFilterToken
278     {
279         private String JavaDoc m;
280         public EndFilterToken( String JavaDoc s )
281         {
282             this.m = s;
283         }
284         public boolean match( String JavaDoc part )
285         {
286             return part.startsWith( this.m );
287         }
288     }
289     
290     private static class ExactFilterToken implements IFilterToken
291     {
292         private String JavaDoc m;
293         public ExactFilterToken( String JavaDoc s )
294         {
295             this.m = s;
296         }
297         public boolean match( String JavaDoc part )
298         {
299             return part.equals( this.m );
300         }
301     }
302     
303     private static class AnyFilterToken implements IFilterToken
304     {
305         public boolean match( String JavaDoc part )
306         {
307             return true;
308         }
309     }
310     private static final IFilterToken ANY_TOKEN = new AnyFilterToken();
311     private static final IFilterToken NULL_TOKEN = new AnyFilterToken();
312     
313     
314     
315     public void setPercentage( double v )
316     {
317         this.minPercent = v;
318     }
319     
320     
321     public void setProperty( String JavaDoc p )
322     {
323         this.failureProperty = p;
324     }
325     
326     
327     public void addInclude( IncludeAndExclude iae )
328     {
329         if (iae != null)
330         {
331             iae.isInclude = true;
332             this.includeExcludeSet.addElement( iae );
333         }
334     }
335     
336     
337     public void addExclude( IncludeAndExclude iae )
338     {
339         if (iae != null)
340         {
341             iae.isInclude = false;
342             this.includeExcludeSet.addElement( iae );
343         }
344     }
345     
346     
347     
348     
349     
350     /**
351      * Called when the task is finished generating all the reports. This
352      * may be useful for styles that join all the reports together.
353      */

354     public void reportComplete( Project project, Vector JavaDoc errorList )
355             throws BuildException
356     {
357         double actual = getActualPercentage();
358         project.log(
359             "Testing for minimal coverage of "+
360             DOUBLE_FORMATTER.format( this.minPercent )+
361             ", and found an actual coverage of "+
362             DOUBLE_FORMATTER.format( actual ), Project.MSG_VERBOSE );
363         if (actual < this.minPercent)
364         {
365             // post an error
366
if (this.failureProperty != null)
367             {
368                 project.setNewProperty( this.failureProperty,
369                     Double.toString( actual ) );
370             }
371             errorList.addElement(
372                 "Did not have sufficient coverage: requires "+
373                 DOUBLE_FORMATTER.format( this.minPercent ) +
374                 ", but found " + DOUBLE_FORMATTER.format( actual ) + "." );
375         }
376     }
377     
378     
379     public void generateReport( Project project, Document JavaDoc doc,
380             String JavaDoc moduleName )
381             throws BuildException
382     {
383         Vector JavaDoc inClass = new Vector JavaDoc();
384         Vector JavaDoc exClass = new Vector JavaDoc();
385         Vector JavaDoc inMod = new Vector JavaDoc();
386         Vector JavaDoc exMod = new Vector JavaDoc();
387         setupFilters( inClass, exClass, inMod, exMod );
388         
389         if (!"all".equalsIgnoreCase( moduleName ) &&
390             !matchModule( moduleName, inMod, exMod ))
391         {
392             return;
393         }
394         
395         
396         NodeList JavaDoc ccList = doc.getElementsByTagName( "classcoverage" );
397         for (int ccIndex = 0; ccIndex < ccList.getLength(); ++ccIndex)
398         {
399             Element JavaDoc ccEl = (Element JavaDoc)ccList.item( ccIndex );
400             
401             NodeList JavaDoc coverList = ccEl.getElementsByTagName( "cover" );
402             for (int coverIndex = 0; coverIndex < coverList.getLength();
403                 ++coverIndex)
404             {
405                 Element JavaDoc coverEl = (Element JavaDoc)coverList.item( coverIndex );
406                 NodeList JavaDoc modList = coverEl.getElementsByTagName(
407                     "modulecover" );
408                 if (modList != null && modList.getLength() > 0)
409                 {
410                     for (int modIndex = 0; modIndex < modList.getLength();
411                         ++modIndex)
412                     {
413                         Element JavaDoc modEl = (Element JavaDoc)modList.item( modIndex );
414                         if (matchModule( modEl.getAttribute( "measure" ),
415                             inMod, exMod ))
416                         {
417                             cover( project, modEl );
418                         }
419                     }
420                 }
421                 else
422                 {
423                     cover( project, coverEl );
424                 }
425             }
426         }
427         /*
428   <classcoverage classname="x.main" classsignature="x.main-2208445997" package="x" sourcefile="main.java">
429     <cover covered="25" display-covered="25" display-percentcovered="86.21" display-total="29" display-weightedpercent="375.01" percentcovered="86.20689655172414" total="29" weightedpercent="375.00766949585847">
430       <modulecover covered="19" display-covered="19" display-percentcovered="86.36" display-total="22" display-weightedpercent="375.66" measure="BytecodeCount" percentcovered="86.36363636363636" total="22" weightedpercent="375.66213314244806"></modulecover>
431       <modulecover covered="6" display-covered="6" display-percentcovered="85.71" display-total="7" display-weightedpercent="374.35" measure="LineCount" percentcovered="85.71428571428571" total="7" weightedpercent="374.3532058492689"></modulecover>
432     </cover>
433         */

434         
435     }
436     
437     
438     protected void cover( Project project, Element JavaDoc el )
439     {
440         String JavaDoc coveredS = el.getAttribute( "covered" );
441         String JavaDoc markedS = el.getAttribute( "total" );
442         try
443         {
444             int cov = Integer.parseInt( coveredS );
445             int mar = Integer.parseInt( markedS );
446             
447             // if any of the values were invalid, then this shouldn't be reached
448
if (cov > 0 && mar > 0)
449             {
450                 this.coverCount += cov;
451                 this.markCount += mar;
452             }
453         }
454         catch (NumberFormatException JavaDoc e)
455         {
456             project.log( "Invalid covered attribute: expected a number.",
457                 Project.MSG_VERBOSE );
458         }
459     }
460     
461     
462     protected double getActualPercentage()
463     {
464         if (this.markCount == 0)
465         {
466             return 100.0;
467         }
468         // else
469
return ((double)this.coverCount * 100.0) / (double)this.markCount;
470     }
471     
472     
473     protected boolean matchModule( String JavaDoc moduleName,
474             Vector JavaDoc inMod, Vector JavaDoc exMod )
475     {
476         // it must pass all inMod and fail all exMod
477
if (!matchModuleInSet( moduleName, inMod ))
478         {
479             return false;
480         }
481         if (matchModuleInSet( moduleName, exMod ))
482         {
483             return false;
484         }
485         return true;
486     }
487     
488     
489     protected boolean matchModuleInSet( String JavaDoc moduleName, Vector JavaDoc set )
490     {
491         Enumeration JavaDoc e = set.elements();
492         while (e.hasMoreElements())
493         {
494             String JavaDoc m = (String JavaDoc)e.nextElement();
495             if ("*".equals(m) || moduleName.equalsIgnoreCase( m ))
496             {
497                 return true;
498             }
499         }
500         return false;
501     }
502     
503     
504     protected boolean matchClass( String JavaDoc className,
505             Vector JavaDoc inClass, Vector JavaDoc exClass )
506     {
507         if (!matchClassInSet( className, inClass ))
508         {
509             return false;
510         }
511         if (matchClassInSet( className, exClass ))
512         {
513             return false;
514         }
515         return true;
516     }
517     
518     
519     protected boolean matchClassInSet( String JavaDoc className, Vector JavaDoc set )
520     {
521         Enumeration JavaDoc e = set.elements();
522         while (e.hasMoreElements())
523         {
524             ClassFilter cf = (ClassFilter)e.nextElement();
525             if (cf.match( className ))
526             {
527                 return true;
528             }
529         }
530         return false;
531     }
532     
533     
534     protected void setupFilters( Vector JavaDoc includeClass, Vector JavaDoc excludeClass,
535             Vector JavaDoc includeModule, Vector JavaDoc excludeModule )
536             throws BuildException
537     {
538         Enumeration JavaDoc e = this.includeExcludeSet.elements();
539         while (e.hasMoreElements())
540         {
541             IncludeAndExclude iae = (IncludeAndExclude)e.nextElement();
542             iae.check();
543             if (iae.module)
544             {
545                 if (iae.isInclude)
546                 {
547                     includeModule.addElement( iae.value );
548                 }
549                 else
550                 {
551                     excludeModule.addElement( iae.value );
552                 }
553             }
554             else
555             // if (iae.corp)
556
{
557                 if (iae.isInclude)
558                 {
559                     includeClass.addElement( new ClassFilter( iae.value ) );
560                 }
561                 else
562                 {
563                     excludeClass.addElement( new ClassFilter( iae.value ) );
564                 }
565             }
566         }
567         
568         // by default, if no includes are specified, then there's an implicit
569
// include **
570
if (includeModule.size() <= 0)
571         {
572             includeModule.addElement( "*" );
573         }
574         if (includeClass.size() <= 0)
575         {
576             includeClass.addElement( new ClassFilter( "**" ) );
577         }
578     }
579 }
580
581
Popular Tags