001/**
002 * www.jcoverage.com
003 * Copyright (C)2003 jcoverage ltd.
004 *
005 * This file is part of jcoverage.
006 *
007 * jcoverage is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published
009 * by the Free Software Foundation; either version 2 of the License,
010 * or (at your option) any later version.
011 *
012 * jcoverage is distributed in the hope that it will be useful, but
013 * WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with jcoverage; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
020 * USA
021 *
022 */
023package com.jcoverage.coverage.reporting.html;
024
025import java.io.*;
026import java.util.*;
027
028import org.apache.log4j.Logger;
029
030import com.jcoverage.coverage.reporting.collation.JavaFilePage;
031import com.jcoverage.reporting.html.HtmlFormatHelper;
032import com.jcoverage.reporting.html.Writable;
033
034/**
035 *
036 */
037public class SourceTable implements Writable {
038  
039  static Logger logger=Logger.getLogger(SourceTable.class);
040  
041  File sourceFile;
042  JavaFilePage page;
043
044  public SourceTable(File sourceFile,JavaFilePage page) {
045    this.sourceFile=sourceFile;
046    this.page=page;
047  }
048
049  public void writeTo(PrintWriter writer) {
050
051    writer.println("<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" width=\"85%\">");
052    writer.print("<tr>");
053    writer.print("<th>Line</th>");
054    writer.print("<th>Hits</th>");
055    writer.print("<th class=\"remainder\">Source</th>");
056    writer.println("</tr>");
057
058    int lineNo=1;             // Java source code lines are 1-based
059
060    try {
061      BufferedReader source=new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile)));
062
063      Iterator coverage=page.getSourceFileLineCoverageSet().iterator();
064
065      while(coverage.hasNext()) {
066        Map.Entry entry=(Map.Entry)coverage.next();
067        int nextLineNo=((Integer)entry.getKey()).intValue();
068
069        while (lineNo<nextLineNo) {
070          String line=source.readLine();
071          if (line==null) {
072            break;
073          }
074          writeRow(writer,page,lineNo,0,indentSource(line));
075          lineNo++;
076        }
077            
078        if (lineNo==nextLineNo) {
079          String line=source.readLine();
080          if (line==null) {
081            // As above
082            break;
083          }
084          writeRow(writer,page,nextLineNo,((Long)entry.getValue()).longValue(),indentSource(line));
085          lineNo++;
086        }
087
088      }
089        
090      while (true) {
091        String remainder=source.readLine();
092        if (remainder==null) {
093          // We have come to the end of the file.
094          break;
095        }
096        writeRow(writer,page,lineNo,0,indentSource(remainder));
097        lineNo++;
098      }
099
100    } catch (Exception ex) {
101      writer.println("<p>Failed to read java source file: ");
102      writer.println("<pre>");
103      writer.println(ex.toString());
104      writer.println("</pre>");
105    }
106
107    writer.println("</table>");
108  }
109  
110  private void writeRow(PrintWriter writer,JavaFilePage page,int lineNo,long hits,String line) {
111    String cssClass=(lineNo%2==0?"yin":"yang");
112    boolean validLine=page.getValidSourceLines().contains(new Integer(lineNo));
113    if (validLine && hits==0) {
114      cssClass="highlight";
115    }
116
117    writer.print("<tr class=\""+cssClass+"\">");
118
119    writeCell(writer,String.valueOf(lineNo),"lineno");
120
121    if (validLine) {
122      writeCell(writer,String.valueOf(hits),"hits");
123    } else {
124      writeCell(writer,"&nbsp;","hits");
125    }
126
127    writeCell(writer,"<tt>"+line+"</tt>","code");
128    writer.println("</tr>");
129  }
130
131  void writeCell(PrintWriter writer,String content,String cssClass) {
132    writer.print("<td class=\""+cssClass+"\">"+content+"</td>");
133  }
134
135  /**
136   * @return a String that has leading spaces converted into non-breaking spaces.
137   */
138  static String indentSource(String line) {
139    StringBuffer sb=new StringBuffer(HtmlFormatHelper.replaceCharacterEntities(HtmlFormatHelper.untabify(line,HtmlFormatHelper.TAB_WIDTH)));
140    StringBuffer newsb=new StringBuffer();
141    int i=0;
142    while(i<sb.length() && sb.charAt(i)==' ') {
143      i++;
144      newsb.append("&nbsp;");
145    }
146    newsb.append(sb.substring(i));
147
148    // For CSS rendering reasons, we always like to have something in the table cell, even if it's just a space.
149    if (newsb.length()==0) {
150      newsb.append("&nbsp;");
151    }
152
153    return newsb.toString();
154  }
155
156}