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.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; public class JRVirtualPrintPage implements JRPrintPage, JRVirtualizable, Serializable { protected static final Log log = LogFactory.getLog(JRVirtualPrintPage.class); private static final long serialVersionUID = 10200L; public static class ObjectIDPair implements Serializable { private static final long serialVersionUID = 10200L; private final Object o; private final int id; public ObjectIDPair(Object o) { this.o = o; this.id = System.identityHashCode(o); } public Object getObject() { return this.o; } public int getIdentity() { return this.id; } } private static final Random random = new Random(System.currentTimeMillis()); private static short counter = 1; protected List elements = new ArrayList(); private String uid; private transient JRVirtualizer virtualizer; private transient IdentityDataProvider[] identityProviders; protected JRVirtualizationContext virtualizationContext; public JRVirtualPrintPage(JasperPrint printObject, JRVirtualizer virtualizer, JRVirtualizationContext virtualizationContext) { this.virtualizationContext = virtualizationContext; this.uid = makeUID(printObject); this.virtualizer = virtualizer; this.identityProviders = null; if (virtualizer != null) virtualizer.registerObject(this); } private static String makeUID(JasperPrint printObject) { synchronized (random) { counter = (short)(counter + 1); return Integer.toString(System.identityHashCode(printObject)) + "_" + printObject.getPages().size() + "_" + Integer.toString(counter) + "_" + Integer.toString(random.nextInt()); } } public final String getUID() { return this.uid; } public void setVirtualData(Object o) { this.elements = (List)o; } public Object getVirtualData() { return this.elements; } public void removeVirtualData() { this.elements = null; } public void setIdentityData(Object o) { if (this.identityProviders != null) for (int i = 0; i < this.identityProviders.length; i++) this.identityProviders[i].setIdentityData(this, (ObjectIDPair[])o); } public Object getIdentityData() { ObjectIDPair[] data; if (this.identityProviders != null) { if (this.identityProviders.length == 1) { data = this.identityProviders[0].getIdentityData(this); } else if (this.identityProviders.length > 1) { Set list = new HashSet(); for (int i = 0; i < this.identityProviders.length; i++) { ObjectIDPair[] pairs = this.identityProviders[i].getIdentityData(this); if (pairs != null) for (int j = 0; j < pairs.length; j++) list.add(pairs[j]); } data = list.toArray(new ObjectIDPair[list.size()]); } else { data = null; } } else { data = null; } return data; } public boolean isVirtualized() { return (this.elements == null); } public void setVirtualizer(JRVirtualizer virtualizer) { this.virtualizer = virtualizer; } public JRVirtualizer getVirtualizer() { return this.virtualizer; } public void addIdentityDataProvider(IdentityDataProvider p) { if (this.identityProviders == null) { this.identityProviders = new IdentityDataProvider[] { p }; } else { IdentityDataProvider[] newList = new IdentityDataProvider[this.identityProviders.length + 1]; System.arraycopy(this.identityProviders, 0, newList, 0, this.identityProviders.length); newList[this.identityProviders.length] = p; this.identityProviders = newList; } } public void removeIdentityDataProvider(IdentityDataProvider p) { if (this.identityProviders != null) for (int idx = 0; idx < this.identityProviders.length; idx++) { if (this.identityProviders[idx] == p) { IdentityDataProvider[] newList = new IdentityDataProvider[this.identityProviders.length - 1]; System.arraycopy(this.identityProviders, 0, newList, 0, idx); int remaining = this.identityProviders.length - idx - 1; if (remaining > 0) System.arraycopy(this.identityProviders, idx + 1, newList, idx, remaining); this.identityProviders = newList; break; } } } public List getElements() { ensureVirtualData(); return this.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(); this.elements.add(element); cacheInContext(element); } public static interface IdentityDataProvider { JRVirtualPrintPage.ObjectIDPair[] getIdentityData(JRVirtualPrintPage param1JRVirtualPrintPage); void setIdentityData(JRVirtualPrintPage param1JRVirtualPrintPage, JRVirtualPrintPage.ObjectIDPair[] param1ArrayOfObjectIDPair); } protected static class JRIdHolderRenderer implements JRRenderable, Serializable { private static final long serialVersionUID = 10200L; protected final String id; protected JRIdHolderRenderer(JRRenderable renderer) { this.id = renderer.getId(); } public String getId() { return this.id; } public byte getType() { return 0; } public byte getImageType() { return 0; } 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 = 10200L; protected JRIdHolderTemplateElement(String id) { super(id); } } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { this.uid = (String)in.readObject(); this.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); this.elements = (List)elementsStream.readObject(); afterInternalization(); setThreadVirtualizer(); } private void writeObject(ObjectOutputStream out) throws IOException { ensureVirtualData(); beforeExternalization(); try { out.writeObject(this.uid); out.writeObject(this.virtualizationContext); ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream stream = new ObjectOutputStream(bout); stream.writeObject(this.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) { this.virtualizer = threadVirtualizer; this.virtualizer.registerObject(this); } } protected void finalize() { if (this.virtualizer != null) this.virtualizer.deregisterObject(this); } protected List getDeepElements() { List deepElements = new ArrayList(this.elements.size()); collectDeepElements(this.elements, deepElements); return deepElements; } protected void collectDeepElements(List elementsList, List deepElements) { for (Iterator it = elementsList.iterator(); it.hasNext(); ) { JRPrintElement element = 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 && this.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 = it.next(); cacheInContext(element); } } protected void cacheInContext(JRPrintElement element) { if (element instanceof JRTemplatePrintElement) { JRTemplatePrintElement templateElement = (JRTemplatePrintElement)element; JRTemplateElement template = templateElement.getTemplate(); if (template != null) this.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 this.virtualizationContext; } public void afterExternalization() { restoreElementsData(); } protected void traverseDeepElements(ElementVisitor visitor) { traverseDeepElements(visitor, this.elements); } protected void traverseDeepElements(ElementVisitor visitor, List elementsList) { for (Iterator it = elementsList.iterator(); it.hasNext(); ) { JRPrintElement element = it.next(); visitor.visitElement(element); if (element instanceof JRPrintFrame) { JRPrintFrame frame = (JRPrintFrame)element; traverseDeepElements(visitor, frame.getElements()); } } } protected static interface ElementVisitor { void visitElement(JRPrintElement param1JRPrintElement); } protected class ExternalizationElementVisitor implements ElementVisitor { private final Map idTemplates = new HashMap(); private final JRVirtualPrintPage this$0; public void visitElement(JRPrintElement element) { if (element instanceof JRTemplatePrintElement) setExternalizationTemplate((JRTemplatePrintElement)element); if (element instanceof JRPrintImage) JRVirtualPrintPage.this.setExternalizationRenderer((JRPrintImage)element); } protected void setExternalizationTemplate(JRTemplatePrintElement templateElement) { JRTemplateElement template = templateElement.getTemplate(); if (template != null) if (JRVirtualPrintPage.this.virtualizationContext.hasCachedTemplate(template.getId())) { String templateId = template.getId(); JRVirtualPrintPage.JRIdHolderTemplateElement idTemplate = (JRVirtualPrintPage.JRIdHolderTemplateElement)this.idTemplates.get(templateId); if (idTemplate == null) { idTemplate = new JRVirtualPrintPage.JRIdHolderTemplateElement(templateId); this.idTemplates.put(templateId, idTemplate); } templateElement.setTemplate(idTemplate); } else if (JRVirtualPrintPage.log.isDebugEnabled()) { JRVirtualPrintPage.log.debug("Template " + template + " having id " + template.getId() + " not found in virtualization context cache"); } } } protected class InternalizationElementVisitor implements ElementVisitor { private final JRVirtualPrintPage this$0; public void visitElement(JRPrintElement element) { if (element instanceof JRTemplatePrintElement) restoreTemplate((JRTemplatePrintElement)element); if (element instanceof JRPrintImage) restoreRenderer((JRPrintImage)element); } protected void restoreTemplate(JRTemplatePrintElement element) { JRTemplateElement template = element.getTemplate(); if (template != null && template instanceof JRVirtualPrintPage.JRIdHolderTemplateElement) { JRTemplateElement cachedTemplate = JRVirtualPrintPage.this.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 JRVirtualPrintPage.JRIdHolderRenderer) { JRRenderable cachedRenderer = JRVirtualPrintPage.this.virtualizationContext.getCachedRenderer(renderer.getId()); if (cachedRenderer == null) throw new JRRuntimeException("Renderer " + renderer.getId() + " not found in virtualization context."); image.setRenderer(cachedRenderer); } } } }