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.reporting;
024
025import java.util.*;
026
027import org.apache.log4j.Logger;
028
029/**
030 * This class provides a skeletal implementation of a {@link Page}.
031 * Subclasses should override the {@link
032 * #instantiateLineForCategory} method to return Line
033 * implementation instances on demand.
034 */
035public abstract class AbstractPage implements Page {
036  
037  static Logger logger=Logger.getLogger(AbstractPage.class);
038  
039  String label;
040  Report report;
041  List categories=new ArrayList();
042  Line masterLine;
043  Map lineSetsByCategory=new HashMap();
044
045  int state=Report.READY;
046
047  protected AbstractPage(String label) {
048    this.label=label;
049  }
050
051  Set getLineSetForCategory(LineCategory category) {
052    if (state==Report.CLOSED) {
053      throw new IllegalStateException("Report is closed, cannot add new lines.");
054    }
055    if (category==null) {
056      throw new IllegalArgumentException("Category cannot be null");
057    }
058    if (!categories.contains(category)) {
059      throw new IllegalArgumentException("This page (type "+getClass().getName()+") cannot handle category: "+category);
060    }
061    Set set=(Set)lineSetsByCategory.get(category);
062    if(set==null) {
063      set=new HashSet();
064      lineSetsByCategory.put(category,set);
065    }
066    return set;
067  }
068
069  /**
070   * @return a new line instance, null if no lines match the given category.
071   */
072  public Line createLine(LineCategory category) {
073    Set set=getLineSetForCategory(category);
074    Line line=instantiateLineForCategory(category);
075    if (line!=null) {
076      line.setCategory(category);
077      line.setOwner(this);
078      line.setReport(report);
079      set.add(line);
080    }
081    return line;
082  }
083
084  public void addLineReference(Line line,LineCategory category) {
085    getLineSetForCategory(category).add(line);    
086  }
087
088  public Line lookupLineByField(LineCategory category,Column column,Object value) {
089    Set lines=(Set)lineSetsByCategory.get(category);
090    if (lines==null) {
091      return null;
092    }
093    for(Iterator it=lines.iterator();it.hasNext();) {
094      Line line=(Line)it.next();
095      Object field=line.getField(column);
096      if (field!=null && field.equals(value)) {
097        return line;
098      }
099    }
100    return null;
101  }
102
103  /**
104   * 
105   */
106  public Set getLines(LineCategory category) {
107    Set result=(Set)lineSetsByCategory.get(category);
108    return result!=null?result:Collections.EMPTY_SET;
109
110  }
111
112  /**
113   * Subclasses should override this method to return {@link
114   * Line} implementation instances on demand.
115   * @param category guarenteed not to be null
116   */
117  protected Line instantiateLineForCategory(LineCategory category) {
118    return null;
119  }
120
121  /**
122   * Subclasses should implemented this method to return the
123   * categories of {@link Line line}s that are applicable to this
124   * report, or else make exclusive use of the {@link #addCategory}
125   * method.
126   */
127  public LineCategory[] getCategories() {
128    return (LineCategory[])categories.toArray(new LineCategory[categories.size()]);
129  }
130
131  public void addCategory(LineCategory category) {
132    categories.add(category);
133  }
134
135  public void setMasterLine(Line masterLine) {
136    this.masterLine=masterLine;
137  }
138
139  public Line getMasterLine() {
140    return masterLine;
141  }
142
143  public void setReport(Report report) {
144    this.report=report;
145  }
146
147  /**
148   * Call this method to indicate that no further lines will be
149   * created for this report and it can be considered immutable from
150   * the point-of-view of formatting.
151   */
152  public void close() throws ReportingException {
153    if(state==Report.READY) {
154      state=Report.CLOSED;
155      // TODO: Ensure report items are closed
156      for(Iterator it=lineSetsByCategory.values().iterator();it.hasNext();) {
157        close((Set)it.next());
158      }
159      if (report.getCollator()!=null) {
160        report.getCollator().pageClosed(this);
161      }
162    } else if (state==Report.CLOSED) {
163      throw new IllegalStateException("Aleady closed");
164    } else {
165      throw new IllegalStateException("Page in unknown state: "+state);
166    }
167  }
168
169  public boolean isClosed() {
170    return state==Report.CLOSED;
171  }
172
173  public String getLabel() {
174    return label;
175  }
176
177  void close(Set set) throws ReportingException {
178    for(Iterator it=set.iterator();it.hasNext();) {
179      Line line=(Line)it.next();
180      if(!line.isClosed()) {
181        try {
182          line.close();
183        } catch(ReportingException ex) {
184          throw new ReportingException("Failed to close line "+line,ex);
185        }
186      }
187    }
188  }
189  
190}