From 69ebbadae6522167b0b3b958222f9aeb365eb37b Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Fri, 20 Apr 2007 08:04:55 +0000 Subject: [PATCH] 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 --- .../org/apache/fop/area/AreaTreeHandler.java | 275 ++++++++++-------- 1 file changed, 153 insertions(+), 122 deletions(-) diff --git a/src/java/org/apache/fop/area/AreaTreeHandler.java b/src/java/org/apache/fop/area/AreaTreeHandler.java index e7f6effe0..d818a0d83 100644 --- a/src/java/org/apache/fop/area/AreaTreeHandler.java +++ b/src/java/org/apache/fop/area/AreaTreeHandler.java @@ -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)"); + } + } + } +} -- 2.39.5