Browse Source

Move out all statistics/debug related code into an inner Statistics class.

Patch submitted by Adrian Cumiskey (fop-dev AT cumiskey DOT com)


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@530682 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-0_94
Vincent Hennebert 17 years ago
parent
commit
69ebbadae6
1 changed files with 153 additions and 122 deletions
  1. 153
    122
      src/java/org/apache/fop/area/AreaTreeHandler.java

+ 153
- 122
src/java/org/apache/fop/area/AreaTreeHandler.java View File

@@ -53,36 +53,23 @@ import org.apache.fop.fo.extensions.destination.Destination;

/**
* Area tree handler for formatting objects.
*
* Concepts:
* The area tree is to be as small as possible. With minimal classes
* and data to fully represent an area tree for formatting objects.
* The area tree needs to be simple to render and follow the spec
* closely.
* This area tree has the concept of page sequences.
* Wherever possible information is discarded or optimized to
* keep memory use low. The data is also organized to make it
* possible for renderers to minimize their output.
* A page can be saved if not fully resolved and once rendered
* a page contains only size and id reference information.
* The area tree pages are organized in a model that depends on the
*
* Concepts: The area tree is to be as small as possible. With minimal classes
* and data to fully represent an area tree for formatting objects. The area
* tree needs to be simple to render and follow the spec closely. This area tree
* has the concept of page sequences. Wherever possible information is discarded
* or optimized to keep memory use low. The data is also organized to make it
* possible for renderers to minimize their output. A page can be saved if not
* fully resolved and once rendered a page contains only size and id reference
* information. The area tree pages are organized in a model that depends on the
* type of renderer.
*/
public class AreaTreeHandler extends FOEventHandler {

private static Log log = LogFactory.getLog(AreaTreeHandler.class);

// show statistics after document complete?
private boolean outputStatistics;

// for statistics gathering
private Runtime runtime;

// heap memory allocated (for statistics)
private long initialMemory;
/** debug statistics */
private Statistics statistics = null;

// time used in rendering (for statistics)
private long startTime;
private static Log log = LogFactory.getLog(AreaTreeHandler.class);

// the LayoutManager maker
private LayoutManagerMaker lmMaker;
@@ -93,66 +80,68 @@ public class AreaTreeHandler extends FOEventHandler {
// The fo:root node of the document
private Root rootFObj;

// HashMap of ID's whose area is located on one or more consecutive
// PageViewports. Each ID has an arraylist of PageViewports that
// HashMap of ID's whose area is located on one or more consecutive
// PageViewports. Each ID has an arraylist of PageViewports that
// form the defined area of this ID
private Map idLocations = new HashMap();

// idref's whose target PageViewports have yet to be identified
// Each idref has a HashSet of Resolvable objects containing that idref
private Map unresolvedIDRefs = new HashMap();
private Set unfinishedIDs = new HashSet();

private Set alreadyResolvedIDs = new HashSet();

// The formatting results to be handed back to the caller.
// The formatting results to be handed back to the caller.
private FormattingResults results = new FormattingResults();

private PageSequenceLayoutManager prevPageSeqLM;

private int idGen = 0;
/**
* Constructor.
*
* @param userAgent FOUserAgent object for process
* @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
* @param outputFormat the MIME type of the output format to use (ex.
* "application/pdf").
* @param stream OutputStream
* @throws FOPException if the RenderPagesModel cannot be created
*/
public AreaTreeHandler (FOUserAgent userAgent, String outputFormat,
OutputStream stream) throws FOPException {
public AreaTreeHandler(FOUserAgent userAgent, String outputFormat,
OutputStream stream) throws FOPException {
super(userAgent);

setupModel(userAgent, outputFormat, stream);
lmMaker = userAgent.getFactory().getLayoutManagerMakerOverride();
if (lmMaker == null) {
lmMaker = new LayoutManagerMapping();
}

outputStatistics = log.isDebugEnabled();

if (outputStatistics) {
runtime = Runtime.getRuntime();
if (log.isDebugEnabled()) {
statistics = new Statistics();
}
}

/**
* Sets up the AreaTreeModel instance for use by the AreaTreeHandler.
*
* @param userAgent FOUserAgent object for process
* @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
* @param outputFormat the MIME type of the output format to use (ex.
* "application/pdf").
* @param stream OutputStream
* @throws FOPException if the RenderPagesModel cannot be created
*/
protected void setupModel(FOUserAgent userAgent, String outputFormat,
protected void setupModel(FOUserAgent userAgent, String outputFormat,
OutputStream stream) throws FOPException {
model = new RenderPagesModel(userAgent, outputFormat, fontInfo,
stream);
model = new RenderPagesModel(userAgent, outputFormat, fontInfo, stream);
}
/**
* Get the area tree model for this area tree.
*
*
* @return AreaTreeModel the model being used for this area tree
*/
public AreaTreeModel getAreaTreeModel() {
@@ -161,17 +150,19 @@ public class AreaTreeHandler extends FOEventHandler {

/**
* Get the LayoutManager maker for this area tree.
*
* @return LayoutManagerMaker the LayoutManager maker being used for this area tree
*
* @return LayoutManagerMaker the LayoutManager maker being used for this
* area tree
*/
public LayoutManagerMaker getLayoutManagerMaker() {
return lmMaker;
}

/**
* Tie a PageViewport with an ID found on a child area of the PV.
* Note that an area with a given ID may be on more than one PV, hence
* an ID may have more than one PV associated with it.
* Tie a PageViewport with an ID found on a child area of the PV. Note that
* an area with a given ID may be on more than one PV, hence an ID may have
* more than one PV associated with it.
*
* @param id the property ID of the area
* @param pv a page viewport that contains the area with this ID
*/
@@ -185,22 +176,23 @@ public class AreaTreeHandler extends FOEventHandler {
idLocations.put(id, pvList);
pvList.add(pv);

/*
* See if this ID is in the unresolved idref list, if so
* resolve Resolvable objects tied to it.
/*
* See if this ID is in the unresolved idref list, if so resolve
* Resolvable objects tied to it.
*/
if (!unfinishedIDs.contains(id)) {
tryIDResolution(id, pv, pvList);
tryIDResolution(id, pv, pvList);
}
} else {
pvList.add(pv);
}
}
/**
* This method tie an ID to the areaTreeHandler until this one is
* ready to be processed. This is used in page-number-citation-last processing so we know
* when an id can be resolved.
* This method tie an ID to the areaTreeHandler until this one is ready to
* be processed. This is used in page-number-citation-last processing so we
* know when an id can be resolved.
*
* @param id the id of the object being processed
*/
public void signalPendingID(String id) {
@@ -209,23 +201,25 @@ public class AreaTreeHandler extends FOEventHandler {
}
unfinishedIDs.add(id);
}
/**
* Signals that all areas for the formatting object with the given ID have been generated.
* This is used to determine when page-number-citation-last ref-ids can be resolved.
* Signals that all areas for the formatting object with the given ID have
* been generated. This is used to determine when page-number-citation-last
* ref-ids can be resolved.
*
* @param id the id of the formatting object which was just finished
*/
public void signalIDProcessed(String id) {
if (log.isDebugEnabled()) {
log.debug("signalIDProcessed(" + id + ")");
}
alreadyResolvedIDs.add(id);
if (!unfinishedIDs.contains(id)) {
return;
}
unfinishedIDs.remove(id);
List pvList = (List) idLocations.get(id);
Set todo = (Set) unresolvedIDRefs.get(id);
if (todo != null) {
@@ -236,9 +230,10 @@ public class AreaTreeHandler extends FOEventHandler {
unresolvedIDRefs.remove(id);
}
}
/**
* Check if an ID has already been resolved
*
* @param id the id to check
* @return true if the ID has been resolved
*/
@@ -248,6 +243,7 @@ public class AreaTreeHandler extends FOEventHandler {

/**
* Tries to resolve all unresolved ID references on the given page.
*
* @param id ID to resolve
* @param pv page viewport whose ID refs to resolve
* @param List of PageViewports
@@ -270,6 +266,7 @@ public class AreaTreeHandler extends FOEventHandler {

/**
* Tries to resolve all unresolved ID references on the given page.
*
* @param pv page viewport whose ID refs to resolve
*/
public void tryIDResolution(PageViewport pv) {
@@ -283,9 +280,10 @@ public class AreaTreeHandler extends FOEventHandler {
}
}
}
/**
* Get the list of page viewports that have an area with a given id.
*
* @param id the id to lookup
* @return the list of PageViewports
*/
@@ -294,8 +292,8 @@ public class AreaTreeHandler extends FOEventHandler {
}

/**
* Get information about the rendered output, like
* number of pages created.
* Get information about the rendered output, like number of pages created.
*
* @return the results structure
*/
public FormattingResults getResults() {
@@ -304,6 +302,7 @@ public class AreaTreeHandler extends FOEventHandler {

/**
* Add an Resolvable object with an unresolved idref
*
* @param idref the idref whose target id has not yet been located
* @param res the Resolvable object needing the idref to be resolved
*/
@@ -318,16 +317,16 @@ public class AreaTreeHandler extends FOEventHandler {
}

/**
* Prepare AreaTreeHandler for document processing
* This is called from FOTreeBuilder.startDocument()
*
* @throws SAXException if there is an error
* Prepare AreaTreeHandler for document processing This is called from
* FOTreeBuilder.startDocument()
*
* @throws SAXException
* if there is an error
*/
public void startDocument() throws SAXException {
//Initialize statistics
if (outputStatistics) {
initialMemory = runtime.totalMemory() - runtime.freeMemory();
startTime = System.currentTimeMillis();
// Initialize statistics
if (statistics != null) {
statistics.start();
}
}

@@ -344,40 +343,39 @@ public class AreaTreeHandler extends FOEventHandler {

/**
* @see org.apache.fop.fo.FOEventHandler
* @param pageSequence is the pageSequence being started
* */
* @param pageSequence
* is the pageSequence being started
*/
public void startPageSequence(PageSequence pageSequence) {
rootFObj = pageSequence.getRoot();
finishPrevPageSequence(pageSequence.getInitialPageNumber());
pageSequence.initPageNumber();
//extension attachments from fo:root
// extension attachments from fo:root
wrapAndAddExtensionAttachments(rootFObj.getExtensionAttachments());
//extension attachments from fo:declarations
// extension attachments from fo:declarations
if (rootFObj.getDeclarations() != null) {
wrapAndAddExtensionAttachments(rootFObj.getDeclarations().getExtensionAttachments());
}
}
private void wrapAndAddExtensionAttachments(List list) {
Iterator i = list.iterator();
while (i.hasNext()) {
ExtensionAttachment attachment = (ExtensionAttachment)i.next();
ExtensionAttachment attachment = (ExtensionAttachment) i.next();
addOffDocumentItem(new OffDocumentExtensionAttachment(attachment));
}
}
/**
* End the PageSequence.
* The PageSequence formats Pages and adds them to the AreaTree.
* The area tree then handles what happens with the pages.
*
* End the PageSequence. The PageSequence formats Pages and adds them to the
* AreaTree. The area tree then handles what happens with the pages.
*
* @param pageSequence the page sequence ending
*/
public void endPageSequence(PageSequence pageSequence) {

if (outputStatistics) {
long memoryNow = runtime.totalMemory() - runtime.freeMemory();
log.debug("Current heap size: " + (memoryNow / 1024L) + "Kb");
if (statistics != null) {
statistics.end();
}

// If no main flow, nothing to layout!
@@ -388,28 +386,28 @@ public class AreaTreeHandler extends FOEventHandler {
pageSLM.activateLayout();
// preserve the current PageSequenceLayoutManger for the
// force-page-count check at the beginning of the next PageSequence
prevPageSeqLM = pageSLM;
prevPageSeqLM = pageSLM;
}
}

/**
* Called by the PageSequenceLayoutManager when it is finished with a page-sequence.
* Called by the PageSequenceLayoutManager when it is finished with a
* page-sequence.
*
* @param pageSequence the page-sequence just finished
* @param pageCount The number of pages generated for the page-sequence
*/
public void notifyPageSequenceFinished(PageSequence pageSequence,
int pageCount) {
this.results.haveFormattedPageSequence(pageSequence,
pageCount);
int pageCount) {
this.results.haveFormattedPageSequence(pageSequence, pageCount);
if (log.isDebugEnabled()) {
log.debug("Last page-sequence produced "
+ pageCount + " pages.");
log.debug("Last page-sequence produced " + pageCount + " pages.");
}
}

/**
* End the document.
*
*
* @throws SAXException if there is some error
*/
public void endDocument() throws SAXException {
@@ -418,8 +416,8 @@ public class AreaTreeHandler extends FOEventHandler {
// process fox:destination elements
ArrayList destinationList = rootFObj.getDestinationList();
if (destinationList != null) {
while(destinationList.size() > 0) {
Destination destination = (Destination)destinationList.remove(0);
while (destinationList.size() > 0) {
Destination destination = (Destination) destinationList.remove(0);
DestinationData destinationData = new DestinationData(destination);
addOffDocumentItem(destinationData);
}
@@ -430,35 +428,23 @@ public class AreaTreeHandler extends FOEventHandler {
BookmarkData data = new BookmarkData(bookmarkTree);
addOffDocumentItem(data);
if (!data.isResolved()) {
//bookmarks did not fully resolve, add anyway. (hacky? yeah)
// bookmarks did not fully resolve, add anyway. (hacky? yeah)
model.handleOffDocumentItem(data);
}
}

model.endDocument();

if (outputStatistics) {
long memoryNow = runtime.totalMemory() - runtime.freeMemory();
long memoryUsed = (memoryNow - initialMemory) / 1024L;
long timeUsed = System.currentTimeMillis() - startTime;
int pageCount = rootFObj.getTotalPagesGenerated();
log.debug("Initial heap size: " + (initialMemory / 1024L) + "Kb");
log.debug("Current heap size: " + (memoryNow / 1024L) + "Kb");
log.debug("Total memory used: " + memoryUsed + "Kb");
log.debug("Total time used: " + timeUsed + "ms");
log.debug("Pages rendered: " + pageCount);
if (pageCount > 0) {
long perPage = (timeUsed / pageCount);
long ppm = (timeUsed != 0 ? Math.round(60000 * pageCount / (double)timeUsed) : -1);
log.debug("Avg render time: " + perPage + "ms/page (" + ppm + "pages/min)");
}
if (statistics != null) {
statistics.logResults();
}
}

/**
* Add a OffDocumentItem to the area tree model
* This checks if the OffDocumentItem is resolvable and attempts
* to resolve or add the resolvable ids for later resolution.
* Add a OffDocumentItem to the area tree model. This checks if the
* OffDocumentItem is resolvable and attempts to resolve or add the
* resolvable ids for later resolution.
*
* @param odi the OffDocumentItem to add.
*/
private void addOffDocumentItem(OffDocumentItem odi) {
@@ -469,8 +455,8 @@ public class AreaTreeHandler extends FOEventHandler {
if (idLocations.containsKey(ids[count])) {
res.resolveIDRef(ids[count], (List) idLocations.get(ids[count]));
} else {
log.warn(odi.getName() + ": Unresolved id reference \""
+ ids[count] + "\" found.");
log.warn(odi.getName() + ": Unresolved id reference \""
+ ids[count] + "\" found.");
addUnresolvedIDRef(ids[count], res);
}
}
@@ -482,15 +468,60 @@ public class AreaTreeHandler extends FOEventHandler {
model.handleOffDocumentItem(odi);
}
}
/**
* Generates and returns a unique key for a page viewport.
*
* @return the generated key.
*/
public String generatePageViewportKey() {
this.idGen++;
return "P" + this.idGen;
}
}

/**
* Gather statistics when log is debug
*/
private final class Statistics {
// for statistics gathering
private Runtime runtime;

// heap memory allocated (for statistics)
private long initialMemory;

// time used in rendering (for statistics)
private long startTime;

private Statistics() {
runtime = Runtime.getRuntime();
}

public void start() {
initialMemory = runtime.totalMemory() - runtime.freeMemory();
startTime = System.currentTimeMillis();
}

public void end() {
long memoryNow = runtime.totalMemory() - runtime.freeMemory();
log.debug("Current heap size: " + (memoryNow / 1024L) + "KB");
}

public void logResults() {
long memoryNow = runtime.totalMemory() - runtime.freeMemory();
long memoryUsed = (memoryNow - initialMemory) / 1024L;
long timeUsed = System.currentTimeMillis() - startTime;
int pageCount = rootFObj.getTotalPagesGenerated();
log.debug("Initial heap size: " + (initialMemory / 1024L) + "KB");
log.debug("Current heap size: " + (memoryNow / 1024L) + "KB");
log.debug("Total memory used: " + memoryUsed + "KB");
log.debug("Total time used: " + timeUsed + "ms");
log.debug("Pages rendered: " + pageCount);
if (pageCount > 0) {
long perPage = (timeUsed / pageCount);
long ppm = (timeUsed != 0 ? Math.round(60000 * pageCount
/ (double) timeUsed) : -1);
log.debug("Avg render time: " + perPage + "ms/page (" + ppm + "pages/min)");
}
}
}
}

Loading…
Cancel
Save