001: /*
002: * ============================================================================
003: * GNU Lesser General Public License
004: * ============================================================================
005: *
006: * JasperReports - Free Java report-generating library.
007: * Copyright (C) 2005 Works, Inc. http://www.works.com/
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
022: *
023: * Works, Inc.
024: * 6034 West Courtyard Drive
025: * Suite 210
026: * Austin, TX 78730-5032
027: * USA
028: * http://www.works.com/
029: */
030:
031: /*
032: * Licensed to JasperSoft Corporation under a Contributer Agreement
033: */
034: package net.sf.jasperreports.engine.base;
035:
036: import java.awt.Graphics2D;
037: import java.awt.geom.Dimension2D;
038: import java.awt.geom.Rectangle2D;
039: import java.io.ByteArrayInputStream;
040: import java.io.ByteArrayOutputStream;
041: import java.io.IOException;
042: import java.io.ObjectInputStream;
043: import java.io.ObjectOutputStream;
044: import java.io.Serializable;
045: import java.util.ArrayList;
046: import java.util.HashMap;
047: import java.util.HashSet;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Map;
051: import java.util.Random;
052: import java.util.Set;
053:
054: import org.apache.commons.logging.Log;
055: import org.apache.commons.logging.LogFactory;
056:
057: import net.sf.jasperreports.engine.JRConstants;
058: import net.sf.jasperreports.engine.JRException;
059: import net.sf.jasperreports.engine.JRPrintElement;
060: import net.sf.jasperreports.engine.JRPrintFrame;
061: import net.sf.jasperreports.engine.JRPrintImage;
062: import net.sf.jasperreports.engine.JRPrintPage;
063: import net.sf.jasperreports.engine.JRRenderable;
064: import net.sf.jasperreports.engine.JRRuntimeException;
065: import net.sf.jasperreports.engine.JRVirtualizable;
066: import net.sf.jasperreports.engine.JRVirtualizationHelper;
067: import net.sf.jasperreports.engine.JRVirtualizer;
068: import net.sf.jasperreports.engine.JasperPrint;
069: import net.sf.jasperreports.engine.fill.JRTemplateElement;
070: import net.sf.jasperreports.engine.fill.JRTemplatePrintElement;
071: import net.sf.jasperreports.engine.fill.JRVirtualizationContext;
072:
073: /**
074: * A print page that can be virtualized to free heap memory.
075: *
076: * @author John Bindel
077: * @version $Id: JRVirtualPrintPage.java 1333 2006-07-11 11:31:34Z lucianc $
078: */
079: public class JRVirtualPrintPage implements JRPrintPage,
080: JRVirtualizable, Serializable {
081: protected static final Log log = LogFactory
082: .getLog(JRVirtualPrintPage.class);
083:
084: /**
085: * Identity objects are those that we want to replace when we devirtualize
086: * data. If object A was virtualized, and it is referenced outside the
087: * virtualized data, then we want to replace those references with object
088: * A', which is the version of the object that has been devirtualized. For
089: * example the Serialization mechanism creates a new version of the
090: * TextElement we want to be filled, but the bound object map references the
091: * original object A until we replace it with the new version A'.
092: */
093: public static class ObjectIDPair implements Serializable {
094: /**
095: *
096: */
097: private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;
098:
099: private final Object o;
100:
101: private final int id;
102:
103: public ObjectIDPair(Object o) {
104: this .o = o;
105: this .id = System.identityHashCode(o);
106: }
107:
108: /**
109: * Gets the object.
110: */
111: public Object getObject() {
112: return o;
113: }
114:
115: /**
116: * Gets the identity of the object. The identity is the current object's
117: * identity hash code before we deserialize, but when we have
118: * deserialized it, the identity is that of the object that was
119: * serialized, not that of the newly deserialized object.
120: */
121: public int getIdentity() {
122: return id;
123: }
124: }
125:
126: /**
127: * Classes that want to deal with the identity data should implement this.
128: * The JRBaseFiller needs to do this.
129: */
130: public static interface IdentityDataProvider {
131: /**
132: * Get identity data that the provider later want to handle when the
133: * virtual object is paged in.
134: */
135: ObjectIDPair[] getIdentityData(JRVirtualPrintPage page);
136:
137: /**
138: * Handle the identity data as necessary.
139: */
140: void setIdentityData(JRVirtualPrintPage page,
141: ObjectIDPair[] identityData);
142: }
143:
144: private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;
145:
146: private static final Random random = new Random(System
147: .currentTimeMillis());
148:
149: private static short counter = 1;
150:
151: protected List elements = new ArrayList();
152:
153: /**
154: * A unique identifier that is useful for serialization and deserialization
155: * to some persistence mechanism.
156: */
157: private String uid;
158:
159: /**
160: * The object that does the virtualization work.
161: */
162: private transient JRVirtualizer virtualizer;
163:
164: /**
165: * The filler object which has our identity data.
166: */
167: private transient IdentityDataProvider[] identityProviders;
168:
169: protected JRVirtualizationContext virtualizationContext;
170:
171: /**
172: * Constructs a virtualizable page.
173: */
174: public JRVirtualPrintPage(JasperPrint printObject,
175: JRVirtualizer virtualizer,
176: JRVirtualizationContext virtualizationContext) {
177: super ();
178:
179: this .virtualizationContext = virtualizationContext;
180:
181: this .uid = makeUID(printObject);
182: this .virtualizer = virtualizer;
183: this .identityProviders = null;
184: if (virtualizer != null) {
185: virtualizer.registerObject(this );
186: }
187: }
188:
189: /**
190: * Make some unique identifier for this object.
191: */
192: private static String makeUID(JasperPrint printObject) {
193: synchronized (random) {
194: return Integer.toString(System
195: .identityHashCode(printObject))
196: + "_"
197: + (printObject.getPages().size())
198: + "_"
199: + Integer.toString(counter++)
200: + "_"
201: + Integer.toString(random.nextInt());
202: }
203: }
204:
205: public final String getUID() {
206: return this .uid;
207: }
208:
209: public void setVirtualData(Object o) {
210: elements = (List) o;
211: }
212:
213: public Object getVirtualData() {
214: return elements;
215: }
216:
217: public void removeVirtualData() {
218: elements = null;
219: }
220:
221: public void setIdentityData(Object o) {
222: if (identityProviders != null) {
223: for (int i = 0; i < identityProviders.length; ++i) {
224: identityProviders[i].setIdentityData(this ,
225: (ObjectIDPair[]) o);
226: }
227: }
228: }
229:
230: public Object getIdentityData() {
231: ObjectIDPair[] data;
232: if (identityProviders != null) {
233: if (identityProviders.length == 1) {
234: data = identityProviders[0].getIdentityData(this );
235: } else if (identityProviders.length > 1) {
236: Set list = new HashSet();
237: for (int i = 0; i < identityProviders.length; ++i) {
238: ObjectIDPair[] pairs = identityProviders[i]
239: .getIdentityData(this );
240: if (pairs != null) {
241: for (int j = 0; j < pairs.length; ++j) {
242: list.add(pairs[j]);
243: }
244: }
245: }
246: data = (ObjectIDPair[]) list
247: .toArray(new ObjectIDPair[list.size()]);
248: } else {
249: data = null;
250: }
251: } else {
252: data = null;
253: }
254:
255: return data;
256: }
257:
258: public boolean isVirtualized() {
259: return elements == null;
260: }
261:
262: /**
263: * Sets the virtualizer.
264: */
265: public void setVirtualizer(JRVirtualizer virtualizer) {
266: this .virtualizer = virtualizer;
267: }
268:
269: /**
270: * Gets the virtualizer.
271: */
272: public JRVirtualizer getVirtualizer() {
273: return this .virtualizer;
274: }
275:
276: public void addIdentityDataProvider(IdentityDataProvider p) {
277: if (identityProviders == null) {
278: identityProviders = new IdentityDataProvider[] { p };
279: } else {
280: IdentityDataProvider[] newList = new IdentityDataProvider[identityProviders.length + 1];
281: System.arraycopy(identityProviders, 0, newList, 0,
282: identityProviders.length);
283: newList[identityProviders.length] = p;
284: identityProviders = newList;
285: }
286: }
287:
288: public void removeIdentityDataProvider(IdentityDataProvider p) {
289: if (identityProviders != null) {
290: int idx;
291: for (idx = 0; idx < identityProviders.length; ++idx) {
292: if (identityProviders[idx] == p) {
293: IdentityDataProvider[] newList = new IdentityDataProvider[identityProviders.length - 1];
294: System.arraycopy(identityProviders, 0, newList, 0,
295: idx);
296: int remaining = identityProviders.length - idx - 1;
297: if (remaining > 0) {
298: System.arraycopy(identityProviders, idx + 1,
299: newList, idx, remaining);
300: }
301: identityProviders = newList;
302: break;
303: }
304: }
305: }
306: }
307:
308: public List getElements() {
309: ensureVirtualData();
310: return elements;
311: }
312:
313: protected void ensureVirtualData() {
314: if (this .virtualizer != null) {
315: this .virtualizer.requestData(this );
316: }
317: }
318:
319: public void setElements(List elements) {
320: cleanVirtualData();
321: this .elements = elements;
322: cacheInContext(this .elements);
323: }
324:
325: protected void cleanVirtualData() {
326: if (this .virtualizer != null) {
327: this .virtualizer.clearData(this );
328: }
329: }
330:
331: public void addElement(JRPrintElement element) {
332: ensureVirtualData();
333: elements.add(element);
334: cacheInContext(element);
335: }
336:
337: /**
338: * Dummy image renderer that only stores the ID of a cached renderer.
339: * When a page gets serialized, all image renderers that are cached in the
340: * virtualization context are replaced with dummy renderers that only store the ID.
341: * When a page gets deserialized, the original renderers are restored from the
342: * virtualization context based on the ID.
343: */
344: protected static class JRIdHolderRenderer implements JRRenderable,
345: Serializable {
346: private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;
347:
348: protected final String id;
349:
350: protected JRIdHolderRenderer(JRRenderable renderer) {
351: this .id = renderer.getId();
352: }
353:
354: public String getId() {
355: return id;
356: }
357:
358: public byte getType() {
359: return TYPE_IMAGE;
360: }
361:
362: public byte getImageType() {
363: return IMAGE_TYPE_UNKNOWN;
364: }
365:
366: public Dimension2D getDimension() throws JRException {
367: return null;
368: }
369:
370: public byte[] getImageData() throws JRException {
371: return null;
372: }
373:
374: public void render(Graphics2D grx, Rectangle2D rectanle)
375: throws JRException {
376: }
377: }
378:
379: protected static class JRIdHolderTemplateElement extends
380: JRTemplateElement {
381: private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;
382:
383: protected JRIdHolderTemplateElement(String id) {
384: super (id);
385: }
386: }
387:
388: private void readObject(java.io.ObjectInputStream in)
389: throws IOException, ClassNotFoundException {
390: uid = (String) in.readObject();
391: virtualizationContext = (JRVirtualizationContext) in
392: .readObject();
393:
394: int length = in.readInt();
395: byte[] buffer = new byte[length];
396: in.readFully(buffer);
397: ByteArrayInputStream inputStream = new ByteArrayInputStream(
398: buffer, 0, buffer.length);
399: ObjectInputStream elementsStream = new ObjectInputStream(
400: inputStream);
401: elements = (List) elementsStream.readObject();
402: afterInternalization();
403:
404: setThreadVirtualizer();
405: }
406:
407: private void writeObject(java.io.ObjectOutputStream out)
408: throws IOException {
409: ensureVirtualData();
410: beforeExternalization();
411:
412: try {
413: out.writeObject(uid);
414: out.writeObject(virtualizationContext);
415:
416: ByteArrayOutputStream bout = new ByteArrayOutputStream();
417: ObjectOutputStream stream = new ObjectOutputStream(bout);
418: stream.writeObject(elements);
419: stream.flush();
420:
421: byte[] bytes = bout.toByteArray();
422: out.writeInt(bytes.length);
423: out.write(bytes);
424: } finally {
425: afterExternalization();
426: }
427: }
428:
429: private void setThreadVirtualizer() {
430: JRVirtualizer threadVirtualizer = JRVirtualizationHelper
431: .getThreadVirtualizer();
432: if (threadVirtualizer != null) {
433: virtualizer = threadVirtualizer;
434: virtualizer.registerObject(this );
435: }
436: }
437:
438: protected void finalize() {
439: if (virtualizer != null) {
440: virtualizer.deregisterObject(this );
441: }
442: }
443:
444: /**
445: * Returns all the elements on the page, including the ones placed inside
446: * {@link JRPrintFrame frames}.
447: *
448: * @return all the elements on the page
449: */
450: protected List getDeepElements() {
451: List deepElements = new ArrayList(elements.size());
452: collectDeepElements(elements, deepElements);
453: return deepElements;
454: }
455:
456: protected void collectDeepElements(List elementsList,
457: List deepElements) {
458: for (Iterator it = elementsList.iterator(); it.hasNext();) {
459: JRPrintElement element = (JRPrintElement) it.next();
460: deepElements.add(element);
461:
462: if (element instanceof JRPrintFrame) {
463: JRPrintFrame frame = (JRPrintFrame) element;
464: collectDeepElements(frame.getElements(), deepElements);
465: }
466: }
467: }
468:
469: public void beforeExternalization() {
470: setElementsExternalData();
471: }
472:
473: protected void setElementsExternalData() {
474: traverseDeepElements(new ExternalizationElementVisitor());
475: }
476:
477: protected void setExternalizationRenderer(JRPrintImage image) {
478: JRRenderable renderer = image.getRenderer();
479: if (renderer != null
480: && virtualizationContext.hasCachedRenderer(renderer
481: .getId())) {
482: image.setRenderer(new JRIdHolderRenderer(renderer));
483: }
484: }
485:
486: protected void cacheInContext(List elementList) {
487: if (elementList != null && !elementList.isEmpty()) {
488: for (Iterator it = elementList.iterator(); it.hasNext();) {
489: JRPrintElement element = (JRPrintElement) it.next();
490: cacheInContext(element);
491: }
492: }
493: }
494:
495: protected void cacheInContext(JRPrintElement element) {
496: if (element instanceof JRTemplatePrintElement) {
497: JRTemplatePrintElement templateElement = (JRTemplatePrintElement) element;
498: JRTemplateElement template = templateElement.getTemplate();
499: if (template != null) {
500: virtualizationContext.cacheTemplate(template);
501: }
502: }
503:
504: if (element instanceof JRPrintFrame) {
505: JRPrintFrame frame = (JRPrintFrame) element;
506: cacheInContext(frame.getElements());
507: }
508: }
509:
510: public void afterInternalization() {
511: restoreElementsData();
512: }
513:
514: protected void restoreElementsData() {
515: traverseDeepElements(new InternalizationElementVisitor());
516: }
517:
518: public JRVirtualizationContext getContext() {
519: return virtualizationContext;
520: }
521:
522: public void afterExternalization() {
523: restoreElementsData();
524: }
525:
526: /**
527: * Traverses all the elements on the page, including the ones placed inside
528: * {@link JRPrintFrame frames}.
529: *
530: * @param visitor element visitor
531: */
532: protected void traverseDeepElements(ElementVisitor visitor) {
533: traverseDeepElements(visitor, elements);
534: }
535:
536: protected void traverseDeepElements(ElementVisitor visitor,
537: List elementsList) {
538: for (Iterator it = elementsList.iterator(); it.hasNext();) {
539: JRPrintElement element = (JRPrintElement) it.next();
540: visitor.visitElement(element);
541:
542: if (element instanceof JRPrintFrame) {
543: JRPrintFrame frame = (JRPrintFrame) element;
544: traverseDeepElements(visitor, frame.getElements());
545: }
546: }
547: }
548:
549: protected static interface ElementVisitor {
550: void visitElement(JRPrintElement element);
551: }
552:
553: protected class ExternalizationElementVisitor implements
554: ElementVisitor {
555: private final Map idTemplates = new HashMap();
556:
557: public void visitElement(JRPrintElement element) {
558: // replacing element template with dummy template that only stores the template ID
559: if (element instanceof JRTemplatePrintElement) {
560: setExternalizationTemplate((JRTemplatePrintElement) element);
561: }
562:
563: // replacing image renderer cached in the virtualization context
564: // with dummy renderer that only stores the renderer ID
565: if (element instanceof JRPrintImage) {
566: setExternalizationRenderer((JRPrintImage) element);
567: }
568: }
569:
570: protected void setExternalizationTemplate(
571: JRTemplatePrintElement templateElement) {
572: JRTemplateElement template = templateElement.getTemplate();
573: if (template != null) {
574: if (virtualizationContext.hasCachedTemplate(template
575: .getId())) {
576: String templateId = template.getId();
577: JRIdHolderTemplateElement idTemplate = (JRIdHolderTemplateElement) idTemplates
578: .get(templateId);
579: if (idTemplate == null) {
580: idTemplate = new JRIdHolderTemplateElement(
581: templateId);
582: idTemplates.put(templateId, idTemplate);
583: }
584: templateElement.setTemplate(idTemplate);
585: } else {
586: if (log.isDebugEnabled()) {
587: log
588: .debug("Template "
589: + template
590: + " having id "
591: + template.getId()
592: + " not found in virtualization context cache");
593: }
594: }
595: }
596: }
597: }
598:
599: protected class InternalizationElementVisitor implements
600: ElementVisitor {
601:
602: public void visitElement(JRPrintElement element) {
603: if (element instanceof JRTemplatePrintElement) {
604: // restore the cached element template from the virtualization context
605: restoreTemplate((JRTemplatePrintElement) element);
606: }
607:
608: if (element instanceof JRPrintImage) {
609: // restore the cached image rendere from the virtualization context
610: restoreRenderer((JRPrintImage) element);
611: }
612: }
613:
614: protected void restoreTemplate(JRTemplatePrintElement element) {
615: JRTemplateElement template = element.getTemplate();
616: if (template != null
617: && template instanceof JRIdHolderTemplateElement) {
618: JRTemplateElement cachedTemplate = virtualizationContext
619: .getCachedTemplate(template.getId());
620: if (cachedTemplate == null) {
621: throw new JRRuntimeException("Template "
622: + template.getId()
623: + " not found in virtualization context.");
624: }
625:
626: element.setTemplate(cachedTemplate);
627: }
628: }
629:
630: protected void restoreRenderer(JRPrintImage image) {
631: JRRenderable renderer = image.getRenderer();
632: if (renderer != null
633: && renderer instanceof JRIdHolderRenderer) {
634: JRRenderable cachedRenderer = virtualizationContext
635: .getCachedRenderer(renderer.getId());
636: if (cachedRenderer == null) {
637: throw new JRRuntimeException("Renderer "
638: + renderer.getId()
639: + " not found in virtualization context.");
640: }
641: image.setRenderer(cachedRenderer);
642: }
643: }
644: }
645: }
|