Package net.sf.jasperreports.engine.base

Source Code of net.sf.jasperreports.engine.base.JRVirtualPrintPage

/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2005 - 2009 Works, Inc. All rights reserved.
* http://www.works.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
*/

/*
* Licensed to Jaspersoft Corporation under a Contributer Agreement
*/
package net.sf.jasperreports.engine.base;

import java.awt.Graphics2D;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import net.sf.jasperreports.engine.JRConstants;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPrintFrame;
import net.sf.jasperreports.engine.JRPrintImage;
import net.sf.jasperreports.engine.JRPrintPage;
import net.sf.jasperreports.engine.JRRenderable;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRVirtualizable;
import net.sf.jasperreports.engine.JRVirtualizationHelper;
import net.sf.jasperreports.engine.JRVirtualizer;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.fill.JRTemplateElement;
import net.sf.jasperreports.engine.fill.JRTemplatePrintElement;
import net.sf.jasperreports.engine.fill.JRVirtualizationContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* A print page that can be virtualized to free heap memory.
*
* @author John Bindel
* @version $Id: JRVirtualPrintPage.java 3717 2010-04-09 10:01:33Z teodord $
*/
public class JRVirtualPrintPage implements JRPrintPage, JRVirtualizable, Serializable
{
  protected static final Log log = LogFactory.getLog(JRVirtualPrintPage.class);
 
  /**
   * Identity objects are those that we want to replace when we devirtualize
   * data. If object A was virtualized, and it is referenced outside the
   * virtualized data, then we want to replace those references with object
   * A', which is the version of the object that has been devirtualized. For
   * example the Serialization mechanism creates a new version of the
   * TextElement we want to be filled, but the bound object map references the
   * original object A until we replace it with the new version A'.
   */
  public static class ObjectIDPair implements Serializable {
    /**
     *
     */
    private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;

    private final Object o;

    private final int id;

    public ObjectIDPair(Object o) {
      this.o = o;
      this.id = System.identityHashCode(o);
    }

    /**
     * Gets the object.
     */
    public Object getObject() {
      return o;
    }

    /**
     * Gets the identity of the object. The identity is the current object's
     * identity hash code before we deserialize, but when we have
     * deserialized it, the identity is that of the object that was
     * serialized, not that of the newly deserialized object.
     */
    public int getIdentity() {
      return id;
    }
  }

  /**
   * Classes that want to deal with the identity data should implement this.
   * The JRBaseFiller needs to do this.
   */
  public static interface IdentityDataProvider {
    /**
     * Get identity data that the provider later want to handle when the
     * virtual object is paged in.
     */
    ObjectIDPair[] getIdentityData(JRVirtualPrintPage page);

    /**
     * Handle the identity data as necessary.
     */
    void setIdentityData(JRVirtualPrintPage page,
        ObjectIDPair[] identityData);
  }

  private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;

  private static final Random random = new Random(System.currentTimeMillis());

  private static short counter = 1;
 
 
  protected List elements = new ArrayList();

  /**
   * A unique identifier that is useful for serialization and deserialization
   * to some persistence mechanism.
   */
  private String uid;

  /**
   * The object that does the virtualization work.
   */
  private transient JRVirtualizer virtualizer;

  /**
   * The filler object which has our identity data.
   */
  private transient IdentityDataProvider[] identityProviders;

  protected JRVirtualizationContext virtualizationContext;
 
  /**
   * Constructs a virtualizable page.
   */
  public JRVirtualPrintPage(JasperPrint printObject, JRVirtualizer virtualizer, JRVirtualizationContext virtualizationContext) {
    super();
   
    this.virtualizationContext = virtualizationContext;

    this.uid = makeUID(printObject);
    this.virtualizer = virtualizer;
    this.identityProviders = null;
    if (virtualizer != null) {
      virtualizer.registerObject(this);
    }
  }
 
 
  /**
   * Make some unique identifier for this object.
   */
  private static String makeUID(JasperPrint printObject) {
    synchronized (random) {
      return Integer.toString(System.identityHashCode(printObject)) + "_"
          + (printObject.getPages().size()) + "_"
          + Integer.toString(counter++) + "_"
          + Integer.toString(random.nextInt());
    }
  }

  /**
   * Make a new identifier for an object.
   *
   * @return the new identifier
   */
  private static String makeUID(JRVirtualPrintPage page) {
    synchronized (random) {
      return Integer.toString(System.identityHashCode(page)) + "_"
          + Integer.toString(counter++) + "_"
          + Integer.toString(random.nextInt());
    }
  }

  public final String getUID() {
    return this.uid;
  }

  public void setVirtualData(Object o)
  {
    elements = (List) o;
  }
 
  public Object getVirtualData()
  {
    return elements;
  }

  public void removeVirtualData() {
    elements = null;
  }

  public void setIdentityData(Object o) {
    if (identityProviders != null) {
      for (int i = 0; i < identityProviders.length; ++i) {
        identityProviders[i].setIdentityData(this, (ObjectIDPair[]) o);
      }
    }
  }

  public Object getIdentityData() {
    ObjectIDPair[] data;
    if (identityProviders != null) {
      if (identityProviders.length == 1) {
        data = identityProviders[0].getIdentityData(this);
      } else if (identityProviders.length > 1) {
        Set list = new HashSet();
        for (int i = 0; i < identityProviders.length; ++i) {
          ObjectIDPair[] pairs = identityProviders[i]
              .getIdentityData(this);
          if (pairs != null) {
            for (int j = 0; j < pairs.length; ++j) {
              list.add(pairs[j]);
            }
          }
        }
        data = (ObjectIDPair[]) list.toArray(new ObjectIDPair[list
            .size()]);
      } else {
        data = null;
      }
    } else {
      data = null;
    }

    return data;
  }

  public boolean isVirtualized() {
    return elements == null;
  }

  /**
   * Sets the virtualizer.
   */
  public void setVirtualizer(JRVirtualizer virtualizer) {
    this.virtualizer = virtualizer;
  }

  /**
   * Gets the virtualizer.
   */
  public JRVirtualizer getVirtualizer() {
    return this.virtualizer;
  }

  public void addIdentityDataProvider(IdentityDataProvider p) {
    if (identityProviders == null) {
      identityProviders = new IdentityDataProvider[] { p };
    } else {
      IdentityDataProvider[] newList = new IdentityDataProvider[identityProviders.length + 1];
      System.arraycopy(identityProviders, 0, newList, 0,
          identityProviders.length);
      newList[identityProviders.length] = p;
      identityProviders = newList;
    }
  }

  public void removeIdentityDataProvider(IdentityDataProvider p) {
    if (identityProviders != null) {
      int idx;
      for (idx = 0; idx < identityProviders.length; ++idx) {
        if (identityProviders[idx] == p) {
          IdentityDataProvider[] newList = new IdentityDataProvider[identityProviders.length - 1];
          System.arraycopy(identityProviders, 0, newList, 0, idx);
          int remaining = identityProviders.length - idx - 1;
          if (remaining > 0) {
            System.arraycopy(identityProviders, idx + 1, newList,
                idx, remaining);
          }
          identityProviders = newList;
          break;
        }
      }
    }
  }

  public List getElements()
  {
    ensureVirtualData();
    return elements;
  }

  protected void ensureVirtualData()
  {
    if (this.virtualizer != null)
    {     
      this.virtualizer.requestData(this);
    }
  }

  public void setElements(List elements) {
    cleanVirtualData();
    this.elements = elements;
    cacheInContext(this.elements);
  }

  protected void cleanVirtualData()
  {
    if (this.virtualizer != null)
    {
      this.virtualizer.clearData(this);
    }
  }

 
  public void addElement(JRPrintElement element)
  {
    ensureVirtualData();
    elements.add(element);
    cacheInContext(element);
  }
 
 
  /**
   * Dummy image renderer that only stores the ID of a cached renderer.
   * When a page gets serialized, all image renderers that are cached in the
   * virtualization context are replaced with dummy renderers that only store the ID.
   * When a page gets deserialized, the original renderers are restored from the
   * virtualization context based on the ID.
   */
  protected static class JRIdHolderRenderer implements JRRenderable, Serializable
  {
    private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;
   
    protected final String id;
   
    protected JRIdHolderRenderer(JRRenderable renderer)
    {
      this.id = renderer.getId();
    }

    public String getId()
    {
      return id;
    }

    public byte getType()
    {
      return TYPE_IMAGE;
    }

    public byte getImageType()
    {
      return IMAGE_TYPE_UNKNOWN;
    }

    public Dimension2D getDimension() throws JRException
    {
      return null;
    }

    public byte[] getImageData() throws JRException
    {
      return null;
    }

    public void render(Graphics2D grx, Rectangle2D rectanle) throws JRException
    {
    }
  }
 
 
  protected static class JRIdHolderTemplateElement extends JRTemplateElement
  {
    private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;
   
    protected JRIdHolderTemplateElement(String id)
    {
      super(id);
    }
  }
 
  private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
  {
    // we do not use the original ID as the source object might still be
    // alive in the JVM
    String oldUid = (String) in.readObject();
    uid = makeUID(this);
    if (log.isDebugEnabled())
    {
      log.debug("Original uid " + oldUid + "; new uid " + uid);
    }
   
    virtualizationContext = (JRVirtualizationContext) in.readObject();
   
    int length = in.readInt();
    byte[] buffer = new byte[length];
    in.readFully(buffer);
    ByteArrayInputStream inputStream = new ByteArrayInputStream(buffer, 0, buffer.length);
    ObjectInputStream elementsStream = new ObjectInputStream(inputStream);
    elements = (List) elementsStream.readObject();
    afterInternalization();
   
    setThreadVirtualizer();
  }

 
  private void writeObject(java.io.ObjectOutputStream out) throws IOException
  {
    ensureVirtualData();
    beforeExternalization();
   
    try
    {
      // maybe we should no longer serialize the id, as a new one is
      // generated on deserialization
      out.writeObject(uid);
      out.writeObject(virtualizationContext);

      ByteArrayOutputStream bout = new ByteArrayOutputStream();
      ObjectOutputStream stream = new ObjectOutputStream(bout);
      stream.writeObject(elements);
      stream.flush();

      byte[] bytes = bout.toByteArray();
      out.writeInt(bytes.length);
      out.write(bytes);
    }
    finally
    {
      afterExternalization();
    }
  }

 
  private void setThreadVirtualizer()
  {
    JRVirtualizer threadVirtualizer = JRVirtualizationHelper.getThreadVirtualizer();
    if (threadVirtualizer != null)
    {
      virtualizer = threadVirtualizer;
      virtualizer.registerObject(this);
    }
  }


  protected void finalize() throws Throwable //NOSONAR
  {
    if (virtualizer != null)
    {
      virtualizer.deregisterObject(this);
    }
   
    super.finalize();
  }
 
 
  /**
   * Returns all the elements on the page, including the ones placed inside
   * {@link JRPrintFrame frames}.
   *
   * @return all the elements on the page
   */
  protected List getDeepElements()
  {
    List deepElements = new ArrayList(elements.size());
    collectDeepElements(elements, deepElements);
    return deepElements;
  }

  protected void collectDeepElements(List elementsList, List deepElements)
  {
    for (Iterator it = elementsList.iterator(); it.hasNext();)
    {
      JRPrintElement element = (JRPrintElement) it.next();
      deepElements.add(element);
     
      if (element instanceof JRPrintFrame)
      {
        JRPrintFrame frame = (JRPrintFrame) element;
        collectDeepElements(frame.getElements(), deepElements);
      }
    }
  }


  public void beforeExternalization()
  {
    setElementsExternalData();
  }


  protected void setElementsExternalData()
  {
    traverseDeepElements(new ExternalizationElementVisitor());
  }


  protected void setExternalizationRenderer(JRPrintImage image)
  {
    JRRenderable renderer = image.getRenderer();
    if (renderer != null && virtualizationContext.hasCachedRenderer(renderer.getId()))
    {
      image.setRenderer(new JRIdHolderRenderer(renderer));
    }
  }


  protected void cacheInContext(List elementList)
  {
    if (elementList != null && !elementList.isEmpty())
    {
      for (Iterator it = elementList.iterator(); it.hasNext();)
      {
        JRPrintElement element = (JRPrintElement) it.next();
        cacheInContext(element);
      }
    }
  }

 
  protected void cacheInContext(JRPrintElement element)
  {
    if (element instanceof JRTemplatePrintElement)
    {
      JRTemplatePrintElement templateElement = (JRTemplatePrintElement) element;
      JRTemplateElement template = templateElement.getTemplate();
      if (template != null)
      {
        virtualizationContext.cacheTemplate(template);
      }
    }
   
    if (element instanceof JRPrintFrame)
    {
      JRPrintFrame frame = (JRPrintFrame) element;
      cacheInContext(frame.getElements());
    }
  }

  public void afterInternalization()
  {
    restoreElementsData();
  }


  protected void restoreElementsData()
  {
    traverseDeepElements(new InternalizationElementVisitor());
  }


  public JRVirtualizationContext getContext()
  {
    return virtualizationContext;
  }


  public void afterExternalization()
  {
    restoreElementsData();
  }
 
 
  /**
   * Traverses all the elements on the page, including the ones placed inside
   * {@link JRPrintFrame frames}.
   *
   * @param visitor element visitor
   */
  protected void traverseDeepElements(ElementVisitor visitor)
  {
    traverseDeepElements(visitor, elements);
  }

  protected void traverseDeepElements(ElementVisitor visitor, List elementsList)
  {
    for (Iterator it = elementsList.iterator(); it.hasNext();)
    {
      JRPrintElement element = (JRPrintElement) it.next();
      visitor.visitElement(element);
     
      if (element instanceof JRPrintFrame)
      {
        JRPrintFrame frame = (JRPrintFrame) element;
        traverseDeepElements(visitor, frame.getElements());
      }
    }
  }
 
  protected static interface ElementVisitor
  {
    void visitElement(JRPrintElement element);
  }
 
 
  protected class ExternalizationElementVisitor implements ElementVisitor
  {
    private final Map idTemplates = new HashMap();

    public void visitElement(JRPrintElement element)
    {
      // replacing element template with dummy template that only stores the template ID
      if (element instanceof JRTemplatePrintElement)
      {
        setExternalizationTemplate((JRTemplatePrintElement) element);
      }
     
      // replacing image renderer cached in the virtualization context
      // with dummy renderer that only stores the renderer ID
      if (element instanceof JRPrintImage)
      {
        setExternalizationRenderer((JRPrintImage) element);
      }
    }

    protected void setExternalizationTemplate(JRTemplatePrintElement templateElement)
    {
      JRTemplateElement template = templateElement.getTemplate();
      if (template != null)
      {
        if (virtualizationContext.hasCachedTemplate(template.getId()))
        {
          String templateId = template.getId();
          JRIdHolderTemplateElement idTemplate = (JRIdHolderTemplateElement) idTemplates.get(templateId);
          if (idTemplate == null)
          {
            idTemplate = new JRIdHolderTemplateElement(templateId);
            idTemplates.put(templateId, idTemplate);
          }
          templateElement.setTemplate(idTemplate);
        }
        else
        {
          if (log.isDebugEnabled())
          {
            log.debug("Template " + template + " having id " + template.getId() + " not found in virtualization context cache");
          }
        }
      }
    }
  }
 
  protected class InternalizationElementVisitor implements ElementVisitor
  {

    public void visitElement(JRPrintElement element)
    {     
      if (element instanceof JRTemplatePrintElement)
      {
        // restore the cached element template from the virtualization context
        restoreTemplate((JRTemplatePrintElement) element);
      }
     
      if (element instanceof JRPrintImage)
      {
        // restore the cached image rendere from the virtualization context
        restoreRenderer((JRPrintImage) element);
      }
    }

    protected void restoreTemplate(JRTemplatePrintElement element)
    {
      JRTemplateElement template = element.getTemplate();
      if (template != null && template instanceof JRIdHolderTemplateElement)
      {
        JRTemplateElement cachedTemplate = virtualizationContext.getCachedTemplate(template.getId());
        if (cachedTemplate == null)
        {
          throw new JRRuntimeException("Template " + template.getId() + " not found in virtualization context.");
        }

        element.setTemplate(cachedTemplate);
      }
    }

    protected void restoreRenderer(JRPrintImage image)
    {
      JRRenderable renderer = image.getRenderer();
      if (renderer != null && renderer instanceof JRIdHolderRenderer)
      {
        JRRenderable cachedRenderer = virtualizationContext.getCachedRenderer(renderer.getId());
        if (cachedRenderer == null)
        {
          throw new JRRuntimeException("Renderer " + renderer.getId() + " not found in virtualization context.");
        }
        image.setRenderer(cachedRenderer);
      }
    }   
  }
}
TOP

Related Classes of net.sf.jasperreports.engine.base.JRVirtualPrintPage

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.