IDEMPIERE-146 Performance: Report Engine always read all data into memory

This commit is contained in:
Heng Sin Low 2012-07-01 09:27:13 +08:00
parent e0e7d05167
commit 809f57fe77
13 changed files with 862 additions and 124 deletions

View File

@ -0,0 +1,16 @@
-- Jun 30, 2012 9:04:20 AM MYT
-- IDEMPIERE-146 Performance: Report Engine always read all data into memory
INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_SysConfig_UU,ConfigurationLevel,Value,Description,EntityType,AD_Client_ID,Created,Updated,AD_Org_ID,CreatedBy,UpdatedBy,IsActive,Name) VALUES (200010,'794a49c8-c127-4e9c-bb3f-7be83f42aa48','S','2000','Use swap file when report data exceed the number of rows define here. Enter a value of zero or less if you don''t want to use swap file for all reports regarding the number of rows involved','D',0,TO_DATE('2012-06-30 09:04:18','YYYY-MM-DD HH24:MI:SS'),TO_DATE('2012-06-30 09:04:18','YYYY-MM-DD HH24:MI:SS'),0,100,100,'Y','REPORT_SWAP_MAX_ROWS')
;
-- Jun 30, 2012 9:09:35 AM MYT
-- IDEMPIERE-146 Performance: Report Engine always read all data into memory
INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_SysConfig_UU,ConfigurationLevel,Value,Description,EntityType,AD_Client_ID,Created,Updated,AD_Org_ID,CreatedBy,UpdatedBy,IsActive,Name) VALUES (200011,'2f885a17-b05f-4744-a617-20e8449c22df','S','100','Max number of pages to keep in memory when rendering a jasper report','D',0,TO_DATE('2012-06-30 09:09:34','YYYY-MM-DD HH24:MI:SS'),TO_DATE('2012-06-30 09:09:34','YYYY-MM-DD HH24:MI:SS'),0,100,100,'Y','JASPER_REPORT_SWAP_MAX_PAGES')
;
UPDATE AD_System
SET LastMigrationScriptApplied='848_IDEMPIERE-146.sql'
WHERE LastMigrationScriptApplied<'848_IDEMPIERE-146.sql'
OR LastMigrationScriptApplied IS NULL
;

View File

@ -0,0 +1,16 @@
-- Jun 30, 2012 9:04:20 AM MYT
-- IDEMPIERE-146 Performance: Report Engine always read all data into memory
INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_SysConfig_UU,ConfigurationLevel,Value,Description,EntityType,AD_Client_ID,Created,Updated,AD_Org_ID,CreatedBy,UpdatedBy,IsActive,Name) VALUES (200010,'794a49c8-c127-4e9c-bb3f-7be83f42aa48','S','2000','Use swap file when report data exceed the number of rows define here. Enter a value of zero or less if you don''t want to use swap file for all reports regarding the number of rows involved','D',0,TO_TIMESTAMP('2012-06-30 09:04:18','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2012-06-30 09:04:18','YYYY-MM-DD HH24:MI:SS'),0,100,100,'Y','REPORT_SWAP_MAX_ROWS')
;
-- Jun 30, 2012 9:09:35 AM MYT
-- IDEMPIERE-146 Performance: Report Engine always read all data into memory
INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_SysConfig_UU,ConfigurationLevel,Value,Description,EntityType,AD_Client_ID,Created,Updated,AD_Org_ID,CreatedBy,UpdatedBy,IsActive,Name) VALUES (200011,'2f885a17-b05f-4744-a617-20e8449c22df','S','100','Max number of pages to keep in memory when rendering a jasper report','D',0,TO_TIMESTAMP('2012-06-30 09:09:34','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2012-06-30 09:09:34','YYYY-MM-DD HH24:MI:SS'),0,100,100,'Y','JASPER_REPORT_SWAP_MAX_PAGES')
;
UPDATE AD_System
SET LastMigrationScriptApplied='848_IDEMPIERE-146.sql'
WHERE LastMigrationScriptApplied<'848_IDEMPIERE-146.sql'
OR LastMigrationScriptApplied IS NULL
;

View File

@ -19,6 +19,7 @@ package org.compiere.print;
import java.io.File; import java.io.File;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
@ -32,6 +33,8 @@ import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamResult;
import org.compiere.Adempiere; import org.compiere.Adempiere;
import org.compiere.print.util.SerializableMatrix;
import org.compiere.print.util.SerializableMatrixImpl;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
import org.compiere.util.DisplayType; import org.compiere.util.DisplayType;
import org.compiere.util.Trace; import org.compiere.util.Trace;
@ -67,6 +70,7 @@ public class PrintData implements Serializable
throw new IllegalArgumentException("Name cannot be null"); throw new IllegalArgumentException("Name cannot be null");
m_ctx = ctx; m_ctx = ctx;
m_name = name; m_name = name;
m_matrix = new SerializableMatrixImpl<Serializable>(name);
} // PrintData } // PrintData
/** /**
@ -81,20 +85,17 @@ public class PrintData implements Serializable
throw new IllegalArgumentException("Name cannot be null"); throw new IllegalArgumentException("Name cannot be null");
m_ctx = ctx; m_ctx = ctx;
m_name = name; m_name = name;
m_matrix = new SerializableMatrixImpl<Serializable>(name);
if (nodes != null) if (nodes != null)
m_nodes = nodes; addRow(false, 0, nodes);
} // PrintData } // PrintData
private SerializableMatrix<Serializable> m_matrix;
/** Context */ /** Context */
private Properties m_ctx; private Properties m_ctx;
/** Data Structure Name */ /** Data Structure Name */
private String m_name; private String m_name;
/** Data Structure rows */
private ArrayList<ArrayList<Serializable>> m_rows = new ArrayList<ArrayList<Serializable>>();
/** Current Row Data Structure elements */
private ArrayList<Serializable> m_nodes = null;
/** Current Row */
private int m_row = -1;
/** List of Function Rows */ /** List of Function Rows */
private ArrayList<Integer> m_functionRows = new ArrayList<Integer>(); private ArrayList<Integer> m_functionRows = new ArrayList<Integer>();
@ -207,7 +208,7 @@ public class PrintData implements Serializable
public String toString() public String toString()
{ {
StringBuffer sb = new StringBuffer("PrintData["); StringBuffer sb = new StringBuffer("PrintData[");
sb.append(m_name).append(",Rows=").append(m_rows.size()); sb.append(m_name).append(",Rows=").append(m_matrix.getRowCount());
if (m_TableName != null) if (m_TableName != null)
sb.append(",TableName=").append(m_TableName); sb.append(",TableName=").append(m_TableName);
sb.append("]"); sb.append("]");
@ -222,9 +223,7 @@ public class PrintData implements Serializable
*/ */
public boolean isEmpty() public boolean isEmpty()
{ {
if (m_nodes == null) return m_matrix.getRowCount() == 0 || m_matrix.getRowData().isEmpty();
return true;
return m_nodes.size() == 0;
} // isEmpty } // isEmpty
/** /**
@ -233,24 +232,27 @@ public class PrintData implements Serializable
*/ */
public int getNodeCount() public int getNodeCount()
{ {
if (m_nodes == null) if (m_matrix.getRowCount() == 0)
return 0; return 0;
return m_nodes.size(); return m_matrix.getRowData().size();
} // getNodeCount } // getNodeCount
public void addRow (boolean functionRow, int levelNo)
{
addRow(functionRow, levelNo, new ArrayList<Serializable>());
}
/************************************************************************** /**************************************************************************
* Add Row * Add Row
* @param functionRow true if function row * @param functionRow true if function row
* @param levelNo Line detail Level Number 0=Normal * @param levelNo Line detail Level Number 0=Normal
*/ */
public void addRow (boolean functionRow, int levelNo) public void addRow (boolean functionRow, int levelNo, List<Serializable> nodes)
{ {
m_nodes = new ArrayList<Serializable>(); m_matrix.addRow(nodes);
m_row = m_rows.size();
m_rows.add (m_nodes);
if (functionRow) if (functionRow)
m_functionRows.add(new Integer(m_row)); m_functionRows.add(new Integer(m_matrix.getRowIndex()));
if (m_hasLevelNo && levelNo != 0) if (m_hasLevelNo && levelNo != 0)
addNode(new PrintDataElement(LEVEL_NO, new Integer(levelNo), DisplayType.Integer, null)); addNode(new PrintDataElement(LEVEL_NO, new Integer(levelNo), DisplayType.Integer, null));
} // addRow } // addRow
@ -262,11 +264,7 @@ public class PrintData implements Serializable
*/ */
public boolean setRowIndex (int row) public boolean setRowIndex (int row)
{ {
if (row < 0 || row >= m_rows.size()) return m_matrix.setRowIndex(row);
return false;
m_row = row;
m_nodes = m_rows.get(m_row);
return true;
} }
/** /**
@ -275,7 +273,7 @@ public class PrintData implements Serializable
*/ */
public boolean setRowNext() public boolean setRowNext()
{ {
return setRowIndex(m_row+1); return m_matrix.setRowNext();
} // setRowNext } // setRowNext
/** /**
@ -284,7 +282,7 @@ public class PrintData implements Serializable
*/ */
public int getRowCount() public int getRowCount()
{ {
return m_rows.size(); return m_matrix.getRowCount();
} // getRowCount } // getRowCount
/** /**
@ -293,7 +291,7 @@ public class PrintData implements Serializable
*/ */
public int getRowIndex() public int getRowIndex()
{ {
return m_row; return m_matrix.getRowCount();
} // getRowIndex } // getRowIndex
/** /**
@ -312,7 +310,7 @@ public class PrintData implements Serializable
*/ */
public boolean isFunctionRow () public boolean isFunctionRow ()
{ {
return m_functionRows.contains(new Integer(m_row)); return m_functionRows.contains(new Integer(m_matrix.getRowIndex()));
} // isFunctionRow } // isFunctionRow
/** /**
@ -322,11 +320,12 @@ public class PrintData implements Serializable
public boolean isPageBreak () public boolean isPageBreak ()
{ {
// page break requires function and meta data // page break requires function and meta data
if (isFunctionRow() && m_nodes != null) List<Serializable> nodes = m_matrix.getRowData();
if (isFunctionRow() && nodes != null)
{ {
for (int i = 0; i < m_nodes.size(); i++) for (int i = 0; i < nodes.size(); i++)
{ {
Object o = m_nodes.get(i); Object o = nodes.get(i);
if (o instanceof PrintDataElement) if (o instanceof PrintDataElement)
{ {
PrintDataElement pde = (PrintDataElement)o; PrintDataElement pde = (PrintDataElement)o;
@ -362,12 +361,13 @@ public class PrintData implements Serializable
*/ */
public int getLineLevelNo () public int getLineLevelNo ()
{ {
if (m_nodes == null || !m_hasLevelNo) List<Serializable> nodes = m_matrix.getRowData();
if (nodes == null || !m_hasLevelNo)
return 0; return 0;
for (int i = 0; i < m_nodes.size(); i++) for (int i = 0; i < nodes.size(); i++)
{ {
Object o = m_nodes.get (i); Object o = nodes.get (i);
if (o instanceof PrintDataElement) if (o instanceof PrintDataElement)
{ {
PrintDataElement pde = (PrintDataElement)o; PrintDataElement pde = (PrintDataElement)o;
@ -391,9 +391,12 @@ public class PrintData implements Serializable
{ {
if (parent == null) if (parent == null)
throw new IllegalArgumentException("Parent cannot be null"); throw new IllegalArgumentException("Parent cannot be null");
if (m_nodes == null) List<Serializable> nodes = m_matrix.getRowData();
addRow(false, 0); if (nodes == null) {
m_nodes.add (parent); nodes = new ArrayList<Serializable>();
addRow(false, 0, nodes);
}
nodes.add (parent);
} // addNode } // addNode
/** /**
@ -404,9 +407,12 @@ public class PrintData implements Serializable
{ {
if (node == null) if (node == null)
throw new IllegalArgumentException("Node cannot be null"); throw new IllegalArgumentException("Node cannot be null");
if (m_nodes == null) List<Serializable> nodes = m_matrix.getRowData();
addRow(false, 0); if (nodes == null) {
m_nodes.add (node); nodes = new ArrayList<Serializable>();
addRow(false, 0, nodes);
}
nodes.add (node);
} // addNode } // addNode
/** /**
@ -416,9 +422,10 @@ public class PrintData implements Serializable
*/ */
public Object getNode (int index) public Object getNode (int index)
{ {
if (m_nodes == null || index < 0 || index >= m_nodes.size()) List<Serializable> nodes = m_matrix.getRowData();
if (nodes == null || index < 0 || index >= nodes.size())
return null; return null;
return m_nodes.get(index); return nodes.get(index);
} // getNode } // getNode
/** /**
@ -431,7 +438,8 @@ public class PrintData implements Serializable
int index = getIndex (name); int index = getIndex (name);
if (index < 0) if (index < 0)
return null; return null;
return m_nodes.get(index); List<Serializable> nodes = m_matrix.getRowData();
return nodes.get(index);
} // getNode } // getNode
/** /**
@ -444,7 +452,8 @@ public class PrintData implements Serializable
int index = getIndex (AD_Column_ID.intValue()); int index = getIndex (AD_Column_ID.intValue());
if (index < 0) if (index < 0)
return null; return null;
return m_nodes.get(index); List<Serializable> nodes = m_matrix.getRowData();
return nodes.get(index);
} // getNode } // getNode
/** /**
@ -453,11 +462,12 @@ public class PrintData implements Serializable
*/ */
public PrintDataElement getPKey() public PrintDataElement getPKey()
{ {
if (m_nodes == null) List<Serializable> nodes = m_matrix.getRowData();
if (nodes == null)
return null; return null;
for (int i = 0; i < m_nodes.size(); i++) for (int i = 0; i < nodes.size(); i++)
{ {
Object o = m_nodes.get(i); Object o = nodes.get(i);
if (o instanceof PrintDataElement) if (o instanceof PrintDataElement)
{ {
PrintDataElement pde = (PrintDataElement)o; PrintDataElement pde = (PrintDataElement)o;
@ -475,11 +485,12 @@ public class PrintData implements Serializable
*/ */
public int getIndex (String columnName) public int getIndex (String columnName)
{ {
if (m_nodes == null) List<Serializable> nodes = m_matrix.getRowData();
if (nodes == null)
return -1; return -1;
for (int i = 0; i < m_nodes.size(); i++) for (int i = 0; i < nodes.size(); i++)
{ {
Object o = m_nodes.get(i); Object o = nodes.get(i);
if (o instanceof PrintDataElement) if (o instanceof PrintDataElement)
{ {
if (columnName.equals(((PrintDataElement)o).getColumnName())) if (columnName.equals(((PrintDataElement)o).getColumnName()))
@ -540,7 +551,7 @@ public class PrintData implements Serializable
*/ */
public void dumpCurrentRow() public void dumpCurrentRow()
{ {
dumpRow(this, m_row); dumpRow(this, m_matrix.getRowIndex());
} // dump } // dump
/** /**
@ -769,7 +780,7 @@ public class PrintData implements Serializable
PrintData pdx = new PrintData(new Properties(), "test2"); PrintData pdx = new PrintData(new Properties(), "test2");
pdx.addNode(new PrintDataElement("test2element1-1","testvalue11",0,null)); pdx.addNode(new PrintDataElement("test2element1-1","testvalue11",0,null));
pdx.addNode(new PrintDataElement("test2element1-2","testvalue12",0,null)); pdx.addNode(new PrintDataElement("test2element1-2","testvalue12",0,null));
pdx.addRow(false, 0); pdx.addRow(false, 0, new ArrayList<Serializable>());
pdx.addNode(new PrintDataElement("test2element2-1","testvalue21",0,null)); pdx.addNode(new PrintDataElement("test2element2-1","testvalue21",0,null));
pdx.addNode(new PrintDataElement("test2element2-2","testvalue22",0,null)); pdx.addNode(new PrintDataElement("test2element2-2","testvalue22",0,null));

View File

@ -18,6 +18,7 @@ package org.compiere.print.layout;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.geom.Dimension2D; import java.awt.geom.Dimension2D;
import java.io.Serializable;
/** /**
* 2D Dimesnion Implementation * 2D Dimesnion Implementation
@ -25,8 +26,13 @@ import java.awt.geom.Dimension2D;
* @author Jorg Janke * @author Jorg Janke
* @version $Id: Dimension2DImpl.java,v 1.3 2006/07/30 00:53:02 jjanke Exp $ * @version $Id: Dimension2DImpl.java,v 1.3 2006/07/30 00:53:02 jjanke Exp $
*/ */
public class Dimension2DImpl extends Dimension2D public class Dimension2DImpl extends Dimension2D implements Serializable
{ {
/**
* generated serial id
*/
private static final long serialVersionUID = -6718670551461826020L;
/** /**
* Constructor 0/0 * Constructor 0/0
*/ */

View File

@ -19,6 +19,12 @@ package org.compiere.print.layout;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Shape; import java.awt.Shape;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.util.logging.Level; import java.util.logging.Level;
@ -41,8 +47,13 @@ import org.compiere.util.CLogger;
* @author Jorg Janke * @author Jorg Janke
* @version $Id: HTMLRenderer.java,v 1.3 2006/07/30 00:53:02 jjanke Exp $ * @version $Id: HTMLRenderer.java,v 1.3 2006/07/30 00:53:02 jjanke Exp $
*/ */
public class HTMLRenderer extends View public class HTMLRenderer extends View implements Externalizable
{ {
/**
* generated serial id
*/
private static final long serialVersionUID = 7180048200607805705L;
/** /**
* Get View from HTML String * Get View from HTML String
* @param html html string * @param html html string
@ -74,6 +85,10 @@ public class HTMLRenderer extends View
/** Logger */ /** Logger */
private static CLogger log = CLogger.getCLogger(HTMLRenderer.class); private static CLogger log = CLogger.getCLogger(HTMLRenderer.class);
public HTMLRenderer() {
super(null);
}
/************************************************************************** /**************************************************************************
* Constructor * Constructor
* @param f factory * @param f factory
@ -85,14 +100,18 @@ public class HTMLRenderer extends View
m_factory = f; m_factory = f;
m_view = v; m_view = v;
m_view.setParent(this); m_view.setParent(this);
m_element = m_view.getElement();
// initially layout to the preferred size // initially layout to the preferred size
setSize(m_view.getPreferredSpan(X_AXIS), m_view.getPreferredSpan(Y_AXIS)); setSize(m_view.getPreferredSpan(X_AXIS), m_view.getPreferredSpan(Y_AXIS));
} // HTMLRenderer } // HTMLRenderer
private int m_width; private int m_width;
private View m_view; private View m_view;
private ViewFactory m_factory; private ViewFactory m_factory;
private Element m_element;
private Rectangle m_allocation; private Rectangle m_allocation;
private float m_viewWidth;
private float m_viewHeight;
/** /**
@ -378,6 +397,8 @@ public class HTMLRenderer extends View
public void setSize(float width, float height) public void setSize(float width, float height)
{ {
this.m_width = (int) width; this.m_width = (int) width;
this.m_viewWidth = width;
this.m_viewHeight = height;
m_view.setSize(width, height); m_view.setSize(width, height);
} }
@ -396,4 +417,25 @@ public class HTMLRenderer extends View
return m_factory; return m_factory;
} }
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(m_element);
out.writeObject(m_allocation);
out.writeFloat(m_viewWidth);
out.writeFloat(m_viewHeight);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
m_element = (Element) in.readObject();
m_allocation = (Rectangle) in.readObject();
HTMLEditorKit kit = new HTMLEditorKit();
m_factory = kit.getViewFactory();
m_view = m_factory.create(m_element);
m_view.setParent(this);
float width = in.readFloat();
float height = in.readFloat();
setSize(width, height);
}
} // HTMLRenderer } // HTMLRenderer

View File

@ -34,6 +34,7 @@ import java.awt.print.PrinterJob;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.io.Serializable;
import java.net.URL; import java.net.URL;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
@ -61,6 +62,8 @@ import org.compiere.print.MPrintPaper;
import org.compiere.print.MPrintTableFormat; import org.compiere.print.MPrintTableFormat;
import org.compiere.print.PrintData; import org.compiere.print.PrintData;
import org.compiere.print.PrintDataElement; import org.compiere.print.PrintDataElement;
import org.compiere.print.util.SerializableMatrix;
import org.compiere.print.util.SerializableMatrixImpl;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
import org.compiere.util.DB; import org.compiere.util.DB;
import org.compiere.util.DisplayType; import org.compiere.util.DisplayType;
@ -1647,8 +1650,7 @@ public class LayoutEngine implements Pageable, Printable, Doc
// The Data // The Data
int rows = printData.getRowCount(); int rows = printData.getRowCount();
// System.out.println("Rows=" + rows); SerializableMatrix<Serializable> elements = new SerializableMatrixImpl<Serializable>(m_PrintInfo.getName());
Object[][] data = new Object [rows][columnCount];
KeyNamePair[] pk = new KeyNamePair[rows]; KeyNamePair[] pk = new KeyNamePair[rows];
String pkColumnName = null; String pkColumnName = null;
ArrayList<Integer> functionRows = new ArrayList<Integer>(); ArrayList<Integer> functionRows = new ArrayList<Integer>();
@ -1657,7 +1659,7 @@ public class LayoutEngine implements Pageable, Printable, Doc
// for all rows // for all rows
for (int row = 0; row < rows; row++) for (int row = 0; row < rows; row++)
{ {
// System.out.println("row=" + row); ArrayList<Serializable> columns = new ArrayList<Serializable>();
printData.setRowIndex(row); printData.setRowIndex(row);
if (printData.isFunctionRow()) if (printData.isFunctionRow())
{ {
@ -1690,33 +1692,33 @@ public class LayoutEngine implements Pageable, Printable, Doc
} }
} }
// for all columns // for all columns
col = 0;
for (int c = 0; c < format.getItemCount(); c++) for (int c = 0; c < format.getItemCount(); c++)
{ {
Serializable columnElement = null;
MPrintFormatItem item = format.getItem(c); MPrintFormatItem item = format.getItem(c);
Object dataElement = null; Serializable dataElement = null;
if (item.isPrinted()) // Text Columns if (item.isPrinted()) // Text Columns
{ {
if (item.isTypeImage()) if (item.isTypeImage())
{ {
if (item.isImageField()) if (item.isImageField())
data[row][col] = createImageElement (item, printData); columnElement = createImageElement (item, printData);
else if (item.isImageIsAttached()) else if (item.isImageIsAttached())
data[row][col] = ImageElement.get (item.get_ID()); columnElement = ImageElement.get (item.get_ID());
else else
data[row][col] = ImageElement.get (item.getImageURL()); columnElement = ImageElement.get (item.getImageURL());
if (data[row][col] != null) if (columnElement != null)
((PrintElement)data[row][col]).layout(item.getMaxWidth(), item.getMaxHeight(), false, item.getFieldAlignmentType()); ((PrintElement)columnElement).layout(item.getMaxWidth(), item.getMaxHeight(), false, item.getFieldAlignmentType());
} }
else if (item.isBarcode()) else if (item.isBarcode())
{ {
data[row][col] = createBarcodeElement(item, printData); columnElement = createBarcodeElement(item, printData);
if (data[row][col] != null) if (columnElement != null)
((PrintElement)data[row][col]).layout(item.getMaxWidth(), item.getMaxHeight(), false, item.getFieldAlignmentType()); ((PrintElement)columnElement).layout(item.getMaxWidth(), item.getMaxHeight(), false, item.getFieldAlignmentType());
} }
else if (item.isTypeText() ) else if (item.isTypeText() )
{ {
data[row][col] = item.getPrintName(format.getLanguage()); columnElement = item.getPrintName(format.getLanguage());
} }
else if (item.isTypeField()) else if (item.isTypeField())
{ {
@ -1729,22 +1731,22 @@ public class LayoutEngine implements Pageable, Printable, Doc
{ {
PrintDataElement pde = (PrintDataElement)obj; PrintDataElement pde = (PrintDataElement)obj;
if (pde.isID() || pde.isYesNo()) if (pde.isID() || pde.isYesNo())
dataElement = pde.getValue(); dataElement = (Serializable) pde.getValue();
else else
dataElement = pde.getValueDisplay(format.getLanguage()); dataElement = pde.getValueDisplay(format.getLanguage());
} }
else else
log.log(Level.SEVERE, "Element not PrintDataElement " + obj.getClass()); log.log(Level.SEVERE, "Element not PrintDataElement " + obj.getClass());
// System.out.println(" row=" + row + ",col=" + col + " - " + item.getAD_Column_ID() + " => " + dataElement); columnElement = dataElement;
data[row][col] = dataElement;
} }
else // item.isTypeBox() or isTypePrintFormat() else // item.isTypeBox() or isTypePrintFormat()
{ {
log.warning("Unsupported: " + (item.isTypeBox() ? "Box" : "PrintFormat") + " in Table: " + item); log.warning("Unsupported: " + (item.isTypeBox() ? "Box" : "PrintFormat") + " in Table: " + item);
} }
col++; columns.add(columnElement);
} // printed } // printed
} // for all columns } // for all columns
elements.addRow(columns);
PrintDataElement pde = printData.getPKey(); PrintDataElement pde = printData.getPKey();
if (pde != null) // for FunctionRows if (pde != null) // for FunctionRows
@ -1761,7 +1763,7 @@ public class LayoutEngine implements Pageable, Printable, Doc
TableElement table = new TableElement(columnHeader, TableElement table = new TableElement(columnHeader,
columnMaxWidth, columnMaxHeight, columnJustification, columnMaxWidth, columnMaxHeight, columnJustification,
fixedWidth, functionRows, multiLineHeader, fixedWidth, functionRows, multiLineHeader,
data, pk, pkColumnName, elements, pk, pkColumnName,
pageNoStart, firstPage, nextPages, repeatedColumns, additionalLines, pageNoStart, firstPage, nextPages, repeatedColumns, additionalLines,
rowColFont, rowColColor, rowColBackground, rowColFont, rowColColor, rowColBackground,
tf, pageBreak, colSuppressRepeats); tf, pageBreak, colSuppressRepeats);

View File

@ -24,6 +24,7 @@ import java.awt.Rectangle;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.image.ImageObserver; import java.awt.image.ImageObserver;
import java.io.Serializable;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
@ -38,8 +39,14 @@ import org.compiere.util.CLogger;
* @author Jorg Janke * @author Jorg Janke
* @version $Id: PrintElement.java,v 1.2 2006/07/30 00:53:02 jjanke Exp $ * @version $Id: PrintElement.java,v 1.2 2006/07/30 00:53:02 jjanke Exp $
*/ */
public abstract class PrintElement implements ImageObserver public abstract class PrintElement implements ImageObserver, Serializable
{ {
/**
* generated serial id
*/
private static final long serialVersionUID = 5894090289966933777L;
/** /**
* Constructor * Constructor
*/ */

View File

@ -30,11 +30,13 @@ import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.text.AttributedCharacterIterator; import java.text.AttributedCharacterIterator;
import java.text.AttributedString; import java.text.AttributedString;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -45,6 +47,8 @@ import net.sourceforge.barbecue.output.OutputException;
import org.compiere.model.MQuery; import org.compiere.model.MQuery;
import org.compiere.print.MPrintFormatItem; import org.compiere.print.MPrintFormatItem;
import org.compiere.print.MPrintTableFormat; import org.compiere.print.MPrintTableFormat;
import org.compiere.print.util.SerializableMatrix;
import org.compiere.print.util.SerializableMatrixImpl;
import org.compiere.util.KeyNamePair; import org.compiere.util.KeyNamePair;
import org.compiere.util.NamePair; import org.compiere.util.NamePair;
import org.compiere.util.Util; import org.compiere.util.Util;
@ -111,13 +115,16 @@ public class TableElement extends PrintElement
public TableElement (ValueNamePair[] columnHeader, public TableElement (ValueNamePair[] columnHeader,
int[] columnMaxWidth, int[] columnMaxHeight, String[] columnJustification, int[] columnMaxWidth, int[] columnMaxHeight, String[] columnJustification,
boolean[] fixedWidth, ArrayList<Integer> functionRows, boolean multiLineHeader, boolean[] fixedWidth, ArrayList<Integer> functionRows, boolean multiLineHeader,
Object[][] data, KeyNamePair[] pk, String pkColumnName, SerializableMatrix<Serializable> data, KeyNamePair[] pk, String pkColumnName,
int pageNoStart, Rectangle firstPage, Rectangle nextPages, int repeatedColumns, HashMap<Integer,Integer> additionalLines, int pageNoStart, Rectangle firstPage, Rectangle nextPages, int repeatedColumns, HashMap<Integer,Integer> additionalLines,
HashMap<Point,Font> rowColFont, HashMap<Point,Color> rowColColor, HashMap<Point,Color> rowColBackground, HashMap<Point,Font> rowColFont, HashMap<Point,Color> rowColColor, HashMap<Point,Color> rowColBackground,
MPrintTableFormat tFormat, ArrayList<Integer> pageBreak, boolean[] colSuppressRepeats) MPrintTableFormat tFormat, ArrayList<Integer> pageBreak, boolean[] colSuppressRepeats)
{ {
super(); super();
log.fine("Cols=" + columnHeader.length + ", Rows=" + data.length); if (log.isLoggable(Level.FINE))
{
log.fine("Cols=" + columnHeader.length + ", Rows=" + data.getRowCount());
}
m_colSuppressRepeats = colSuppressRepeats; m_colSuppressRepeats = colSuppressRepeats;
m_columnHeader = columnHeader; m_columnHeader = columnHeader;
m_columnMaxWidth = columnMaxWidth; m_columnMaxWidth = columnMaxWidth;
@ -191,7 +198,7 @@ public class TableElement extends PrintElement
/** List of Function Rows */ /** List of Function Rows */
private ArrayList<Integer> m_functionRows; private ArrayList<Integer> m_functionRows;
/** The Data */ /** The Data */
private Object[][] m_data; private SerializableMatrix<Serializable> m_data;
/** Primary Keys */ /** Primary Keys */
private KeyNamePair[] m_pk; private KeyNamePair[] m_pk;
/** Primary Key Column Name */ /** Primary Key Column Name */
@ -274,16 +281,16 @@ public class TableElement extends PrintElement
return true; return true;
p_width = 0; p_width = 0;
m_printRows = new ArrayList<ArrayList<ArrayList<Object>>>(m_data.length); // reset m_printRows = new SerializableMatrixImpl<ArrayList<Serializable>>("TableElementPrintRows"); // reset
// Max Column Width = 50% of available width (used if maxWidth not set) // Max Column Width = 50% of available width (used if maxWidth not set)
float dynMxColumnWidth = m_firstPage.width / 2; float dynMxColumnWidth = m_firstPage.width / 2;
// Width caolculation // Width calculation
int rows = m_data.length; int rows = m_data.getRowCount();
int cols = m_columnHeader.length; int cols = m_columnHeader.length;
// Data Sizes and Header Sizes // Data Sizes and Header Sizes
Dimension2DImpl[][] dataSizes = new Dimension2DImpl[rows][cols]; SerializableMatrix<Dimension2DImpl> dataSizes = new SerializableMatrixImpl<Dimension2DImpl>("TableElementDimensions");
Dimension2DImpl[] headerSizes = new Dimension2DImpl[cols]; Dimension2DImpl[] headerSizes = new Dimension2DImpl[cols];
FontRenderContext frc = new FontRenderContext(null, true, true); FontRenderContext frc = new FontRenderContext(null, true, true);
@ -300,48 +307,62 @@ public class TableElement extends PrintElement
float colWidth = 0; float colWidth = 0;
for (int row = 0; row < rows; row++) for (int row = 0; row < rows; row++)
{ {
Object dataItem = m_data[row][dataCol]; m_data.setRowIndex(row);
if (dataSizes.getRowCount() <= row)
{
dataSizes.addRow(new ArrayList<Dimension2DImpl>());
}
else
{
dataSizes.setRowIndex(row);
}
List<Dimension2DImpl> dimensions = dataSizes.getRowData();
if (dimensions.size() <= dataCol)
{
dimensions.add(null);
}
Serializable dataItem = m_data.getRowData().get(dataCol);
if (dataItem == null) if (dataItem == null)
{ {
dataSizes[row][dataCol] = new Dimension2DImpl(); dimensions.set(dataCol, new Dimension2DImpl());
continue; continue;
} }
String string = dataItem.toString(); String string = dataItem.toString();
if (string.length() == 0) if (string.length() == 0)
{ {
dataSizes[row][dataCol] = new Dimension2DImpl(); dimensions.set(dataCol, new Dimension2DImpl());
continue; continue;
} }
Font font = getFont(row, dataCol); Font font = getFont(row, dataCol);
// Print below existing column = (col != dataCol) // Print below existing column = (col != dataCol)
addPrintLines(row, col, dataItem); addPrintLines(row, col, dataItem);
dataSizes[row][dataCol] = new Dimension2DImpl(); // don't print dimensions.set(dataCol, new Dimension2DImpl());
if (dataItem instanceof Boolean) if (dataItem instanceof Boolean)
{ {
dataSizes[row][col].addBelow(LayoutEngine.IMAGE_SIZE); dimensions.get(col).addBelow(LayoutEngine.IMAGE_SIZE);
continue; continue;
} }
else if (dataItem instanceof ImageElement) else if (dataItem instanceof ImageElement)
{ {
dataSizes[row][col].addBelow( dimensions.get(col).addBelow(
new Dimension((int)((ImageElement)dataItem).getWidth(), new Dimension((int)((ImageElement)dataItem).getWidth(),
(int)((ImageElement)dataItem).getHeight())); (int)((ImageElement)dataItem).getHeight()));
// Adjust the column width - teo_sarca, [ 1673620 ] // Adjust the column width - teo_sarca, [ 1673620 ]
float width = (float)Math.ceil(dataSizes[row][col].getWidth()); float width = (float)Math.ceil(dimensions.get(col).getWidth());
if (colWidth < width) if (colWidth < width)
colWidth = width; colWidth = width;
continue; continue;
} }
else if (dataItem instanceof BarcodeElement) else if (dataItem instanceof BarcodeElement)
{ {
dataSizes[row][col].addBelow( dimensions.get(col).addBelow(
new Dimension((int)((BarcodeElement)dataItem).getWidth(), new Dimension((int)((BarcodeElement)dataItem).getWidth(),
(int)((BarcodeElement)dataItem).getHeight())); (int)((BarcodeElement)dataItem).getHeight()));
// Check if the overflow is allowed - teo_sarca, [ 1673590 ] // Check if the overflow is allowed - teo_sarca, [ 1673590 ]
if (!((BarcodeElement)dataItem).isAllowOverflow()) { if (!((BarcodeElement)dataItem).isAllowOverflow()) {
float width = (float)Math.ceil(dataSizes[row][col].getWidth()); float width = (float)Math.ceil(dimensions.get(col).getWidth());
if (colWidth < width) if (colWidth < width)
colWidth = width; colWidth = width;
} }
@ -362,12 +383,12 @@ public class TableElement extends PrintElement
m_columnMaxWidth[col] = (int)Math.ceil(dynMxColumnWidth); m_columnMaxWidth[col] = (int)Math.ceil(dynMxColumnWidth);
else if (colWidth < width) else if (colWidth < width)
colWidth = width; colWidth = width;
if (dataSizes[row][col] == null) if (dimensions.get(col) == null)
{ {
dataSizes[row][col] = new Dimension2DImpl(); dimensions.set(col, new Dimension2DImpl());
log.log(Level.WARNING, "No Size for r=" + row + ",c=" + col); log.log(Level.WARNING, "No Size for r=" + row + ",c=" + col);
} }
dataSizes[row][col].addBelow(width, height); dimensions.get(col).addBelow(width, height);
} }
// Width limitations // Width limitations
if (m_columnMaxWidth[col] != 0 && m_columnMaxWidth[col] != -1) if (m_columnMaxWidth[col] != 0 && m_columnMaxWidth[col] != -1)
@ -385,7 +406,7 @@ public class TableElement extends PrintElement
height = renderer.getHeight(); height = renderer.getHeight();
renderer.setAllocation((int)colWidth, (int)height); renderer.setAllocation((int)colWidth, (int)height);
// log.finest( "calculateSize HTML - " + renderer.getAllocation()); // log.finest( "calculateSize HTML - " + renderer.getAllocation());
m_data[row][dataCol] = renderer; // replace for printing m_data.getRowData().set(dataCol, renderer);
} }
else else
{ {
@ -415,14 +436,17 @@ public class TableElement extends PrintElement
} }
if (m_fixedWidth[col]) if (m_fixedWidth[col])
colWidth = Math.abs(m_columnMaxWidth[col]); colWidth = Math.abs(m_columnMaxWidth[col]);
dataSizes[row][col].addBelow(colWidth, height); dimensions.get(col).addBelow(colWidth, height);
} }
dataSizes[row][col].roundUp(); dimensions.get(col).roundUp();
if (dataItem instanceof NamePair) if (dataItem instanceof NamePair)
m_rowColDrillDown.put(new Point(row, col), (NamePair)dataItem); m_rowColDrillDown.put(new Point(row, col), (NamePair)dataItem);
// //
log.finest("Col=" + col + ", row=" + row if (log.isLoggable(Level.FINEST))
+ " => " + dataSizes[row][col] + " - ColWidth=" + colWidth); {
log.finest("Col=" + col + ", row=" + row
+ " => " + dimensions.get(col) + " - ColWidth=" + colWidth);
}
} // for all data rows } // for all data rows
// Column Width for Header // Column Width for Header
@ -532,10 +556,12 @@ public class TableElement extends PrintElement
for (int row = 0; row < rows; row++) for (int row = 0; row < rows; row++)
{ {
float rowHeight = 0f; float rowHeight = 0f;
dataSizes.setRowIndex(row);
List<Dimension2DImpl> dimensions = dataSizes.getRowData();
for (int col = 0; col < cols; col++) for (int col = 0; col < cols; col++)
{ {
if (dataSizes[row][col].height > rowHeight) // max if (dimensions.get(col).height > rowHeight) // max
rowHeight = (float)dataSizes[row][col].height; rowHeight = (float)dimensions.get(col).height;
} // for all columns } // for all columns
rowHeight += m_tFormat.getLineStroke().floatValue() + (2*V_GAP); rowHeight += m_tFormat.getLineStroke().floatValue() + (2*V_GAP);
m_rowHeights.add(new Float(rowHeight)); m_rowHeights.add(new Float(rowHeight));
@ -1080,7 +1106,7 @@ public class TableElement extends PrintElement
return -1; // above return -1; // above
// //
int firstRow = ((Integer)m_firstRowOnPage.get(pageYindex)).intValue(); int firstRow = ((Integer)m_firstRowOnPage.get(pageYindex)).intValue();
int nextPageRow = m_data.length; // no of rows int nextPageRow = m_data.getRowCount(); // no of rows
if (pageYindex+1 < m_firstRowOnPage.size()) if (pageYindex+1 < m_firstRowOnPage.size())
nextPageRow = ((Integer)m_firstRowOnPage.get(pageYindex+1)).intValue(); nextPageRow = ((Integer)m_firstRowOnPage.get(pageYindex+1)).intValue();
// //
@ -1162,7 +1188,7 @@ public class TableElement extends PrintElement
nextPageColumn = ((Integer)m_firstColumnOnPage.get(pageXindex+1)).intValue(); nextPageColumn = ((Integer)m_firstColumnOnPage.get(pageXindex+1)).intValue();
// //
int firstRow = ((Integer)m_firstRowOnPage.get(pageYindex)).intValue(); int firstRow = ((Integer)m_firstRowOnPage.get(pageYindex)).intValue();
int nextPageRow = m_data.length; // no of rows int nextPageRow = m_data.getRowCount(); // no of rows
if (pageYindex+1 < m_firstRowOnPage.size()) if (pageYindex+1 < m_firstRowOnPage.size())
nextPageRow = ((Integer)m_firstRowOnPage.get(pageYindex+1)).intValue(); nextPageRow = ((Integer)m_firstRowOnPage.get(pageYindex+1)).intValue();
if (DEBUG_PRINT) if (DEBUG_PRINT)
@ -1557,7 +1583,7 @@ public class TableElement extends PrintElement
curX += m_tFormat.getVLineStroke().floatValue(); curX += m_tFormat.getVLineStroke().floatValue();
// X end line // X end line
if (row == m_data.length-1) // last Line if (row == m_data.getRowCount()-1) // last Line
{ {
/** /**
* Bug fix - Bottom line was always displayed whether or not header lines was set to be visible * Bug fix - Bottom line was always displayed whether or not header lines was set to be visible
@ -1608,30 +1634,34 @@ public class TableElement extends PrintElement
* @param col col * @param col col
* @param data data * @param data data
*/ */
private void addPrintLines (int row, int col, Object data) private void addPrintLines (int row, int col, Serializable data)
{ {
while (m_printRows.size() <= row) while (m_printRows.getRowCount() <= row)
m_printRows.add(null); m_printRows.addRow(null);
ArrayList<ArrayList<Object>> columns = m_printRows.get(row); m_printRows.setRowIndex(row);
List<ArrayList<Serializable>> columns = m_printRows.getRowData();
if (columns == null) if (columns == null)
columns = new ArrayList<ArrayList<Object>>(m_columnHeader.length); columns = new ArrayList<ArrayList<Serializable>>(m_columnHeader.length);
while (columns.size() <= col) while (columns.size() <= col)
columns.add(null); columns.add(null);
// //
ArrayList<Object> coordinate = columns.get(col); ArrayList<Serializable> coordinate = columns.get(col);
if (coordinate == null) if (coordinate == null)
coordinate = new ArrayList<Object>(); coordinate = new ArrayList<Serializable>();
coordinate.add(data); coordinate.add(data);
// //
columns.set(col, coordinate); columns.set(col, coordinate);
m_printRows.set(row, columns); m_printRows.setRowData(columns);
log.finest("row=" + row + ", col=" + col if (log.isLoggable(Level.FINEST))
+ " - Rows=" + m_printRows.size() + ", Cols=" + columns.size() {
+ " - " + data); log.finest("row=" + row + ", col=" + col
+ " - Rows=" + m_printRows.getRowCount() + ", Cols=" + columns.size()
+ " - " + data);
}
} // addAdditionalLines } // addAdditionalLines
/** Print Data */ /** Print Data */
private ArrayList<ArrayList<ArrayList<Object>>> m_printRows = new ArrayList<ArrayList<ArrayList<Object>>>(); private SerializableMatrix<ArrayList<Serializable>> m_printRows = new SerializableMatrixImpl<ArrayList<Serializable>>("PrintRows");
/** /**
* Insert empty Row after current Row * Insert empty Row after current Row
@ -1651,12 +1681,15 @@ public class TableElement extends PrintElement
*/ */
private Object[] getPrintItems (int row, int col) private Object[] getPrintItems (int row, int col)
{ {
ArrayList<ArrayList<Object>> columns = null; List<ArrayList<Serializable>> columns = null;
if (m_printRows.size() > row) if (m_printRows.getRowCount() > row)
columns = m_printRows.get(row); {
m_printRows.setRowIndex(row);
columns = m_printRows.getRowData();
}
if (columns == null) if (columns == null)
return new Object[]{}; return new Object[]{};
ArrayList<Object> coordinate = null; ArrayList<Serializable> coordinate = null;
if (columns.size() > col) if (columns.size() > col)
coordinate = columns.get(col); coordinate = columns.get(col);
if (coordinate == null) if (coordinate == null)

View File

@ -0,0 +1,46 @@
/******************************************************************************
* Copyright (C) 2012 Heng Sin Low *
* Copyright (C) 2012 Trek Global *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.compiere.print.util;
import java.io.Serializable;
import java.util.List;
public interface SerializableMatrix<T extends Serializable> {
public void addRow(List<T> data);
public boolean setRowIndex(int row);
/**
* Set Row Index to next
* @return true if success
*/
public boolean setRowNext(); // setRowNext
/**
* Get Row Count
* @return row count
*/
public int getRowCount(); // getRowCount
/**
* Get Current Row Index
* @return row index
*/
public int getRowIndex(); // getRowIndex
public List<T> getRowData();
public void setRowData(List<T> data);
}

View File

@ -0,0 +1,250 @@
/******************************************************************************
* Copyright (C) 2012 Heng Sin Low *
* Copyright (C) 2012 Trek Global *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.compiere.print.util;
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.List;
import java.util.logging.Level;
import org.compiere.model.MSysConfig;
import org.compiere.util.CLogger;
/**
*
* @author hengsin
*
*/
public class SerializableMatrixImpl<T extends Serializable> implements SerializableMatrix<T> {
/** default 4k block size **/
private static final int DEFAULT_BLOCK_SIZE = 4*1024;
/** Default to start swapping after 2k row **/
private static final int DEFAULT_SWAP_MAX_ROWS = 2000;
/** set to 0 or smaller to disable swap file usage **/
private static final String REPORT_SWAP_MAX_ROWS = "REPORT_SWAP_MAX_ROWS";
private static final CLogger log = CLogger.getCLogger(SerializableMatrixImpl.class);
/** Data Structure rows */
private ArrayList<List<T>> m_rows = new ArrayList<List<T>>();
/** Current Row */
private int m_currentRow = -1;
private int m_pageSize = 0;
private int m_size = 0;
private Page currentPage = null;
private List<SwapFileSegment> segments = new ArrayList<SwapFileSegment>();
private List<Page> pages = new ArrayList<Page>();
private SwapFile swapFile;
private String prefix;
public SerializableMatrixImpl(String name) {
this.prefix = name;
int pageSize = MSysConfig.getIntValue(REPORT_SWAP_MAX_ROWS, DEFAULT_SWAP_MAX_ROWS);
if (pageSize <= 0) {
m_pageSize = Integer.MAX_VALUE;
} else {
m_pageSize = pageSize;
}
}
/* (non-Javadoc)
* @see org.compiere.print.util.SerializableDataTable#addRow(java.util.ArrayList)
*/
@Override
public void addRow(List<T> data) {
m_size++;
if (currentPage == null) {
currentPage = new Page();
pages.add(currentPage);
currentPage.first = 0;
currentPage.last = 0;
currentPage.size = 1;
currentPage.pageNo = pages.size() - 1;
m_rows.add(data);
m_currentRow = 0;
} else {
Page lastPage = pages.get(pages.size() - 1);
if (lastPage.size == m_pageSize) {
pageout(currentPage);
currentPage = new Page();
pages.add(currentPage);
currentPage.pageNo = pages.size() - 1;
currentPage.first = lastPage.last + 1;
currentPage.last = currentPage.first;
currentPage.size = 1;
m_rows = new ArrayList<List<T>>();
m_rows.add(data);
m_currentRow = currentPage.first;
} else {
if (currentPage.pageNo != lastPage.pageNo) {
pageout(currentPage);
pagein(lastPage.pageNo);
}
m_rows.add(data);
currentPage.last++;
currentPage.size++;
m_currentRow = currentPage.last;
}
}
}
/* (non-Javadoc)
* @see org.compiere.print.util.SerializableDataTable#setRowIndex(int)
*/
@Override
public boolean setRowIndex (int row) {
if (row < 0 || row >= m_size)
return false;
if (row >= currentPage.first && row <= currentPage.last) {
m_currentRow = row;
return true;
} else {
Page tmp = currentPage;
for(Page page : pages) {
if (row >= page.first && row <= page.last) {
currentPage = page;
pageout(tmp);
pagein(currentPage.pageNo);
m_currentRow = row;
return true;
}
}
return false;
}
}
/* (non-Javadoc)
* @see org.compiere.print.util.SerializableDataTable#setRowNext()
*/
@Override
public boolean setRowNext()
{
return setRowIndex(m_currentRow+1);
} // setRowNext
/* (non-Javadoc)
* @see org.compiere.print.util.SerializableDataTable#getRowCount()
*/
@Override
public int getRowCount()
{
return m_size;
} // getRowCount
/* (non-Javadoc)
* @see org.compiere.print.util.SerializableDataTable#getRowIndex()
*/
@Override
public int getRowIndex()
{
return m_currentRow;
} // getRowIndex
/* (non-Javadoc)
* @see org.compiere.print.util.SerializableDataTable#getRowData()
*/
@Override
public List<T> getRowData()
{
return m_rows.isEmpty() ? null : m_rows.get(m_currentRow - currentPage.first);
}
@Override
public void setRowData(List<T> data) {
if (currentPage != null) {
int index = m_currentRow - currentPage.first;
if (index < m_rows.size()) {
m_rows.set(index, data);
}
}
}
private void pageout(Page currentPage) {
ByteArrayOutputStream bas = new ByteArrayOutputStream();
try {
ObjectOutputStream ous = new ObjectOutputStream(bas);
ous.writeObject(m_rows);
if (swapFile == null) {
swapFile = new SwapFile(makePrefix(prefix), DEFAULT_BLOCK_SIZE, 2);
}
swapFile.open();
SwapFileSegment segment = swapFile.write(bas.toByteArray());
if (currentPage.pageNo < segments.size())
segments.set(currentPage.pageNo, segment);
else
segments.add(segment);
} catch (IOException e) {
log.log(Level.SEVERE, e.getLocalizedMessage(), e);
} finally {
if (swapFile != null)
swapFile.close();
}
}
private void pagein(int index) {
SwapFileSegment segment = segments.get(index);
try {
swapFile.open();
byte[] data = swapFile.read(segment);
swapFile.free(segment);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
ArrayList<List<T>> rows = (ArrayList<List<T>>) ois.readObject();
this.m_rows = rows;
currentPage = pages.get(index);
m_currentRow = currentPage.first;
} catch (IOException e) {
log.log(Level.SEVERE, e.getLocalizedMessage(), e);
} catch (ClassNotFoundException e) {
log.log(Level.SEVERE, e.getLocalizedMessage(), e);
} finally {
swapFile.close();
}
}
private String makePrefix(String name) {
StringBuffer prefix = new StringBuffer();
char[] nameArray = name.toCharArray();
for (char ch : nameArray) {
if (Character.isLetterOrDigit(ch)) {
prefix.append(ch);
} else {
prefix.append("_");
}
}
return prefix.toString();
}
class Page {
protected int pageNo;
protected int first;
protected int last;
protected int size;
}
}

View File

@ -0,0 +1,260 @@
/******************************************************************************
* Copyright (C) 2012 Heng Sin Low *
* Copyright (C) 2012 Trek Global *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.compiere.print.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.LinkedList;
import java.util.logging.Level;
import org.compiere.util.CLogger;
/**
*
* @author hengsin
*
*/
public class SwapFile
{
private static final CLogger log = CLogger.getCLogger(SwapFile.class);
private final File swapFile;
private RandomAccessFile randomAccessFile;
private final int blockSize;
private final int minBlockToGrow;
private final LinkedList<Long> freeBlocks;
/**
* Creates a swap file.
*
* The file name is generated automatically.
*
* @param prefix the swap file prefix
* @param blockSize the size of the blocks allocated by the swap file
* @param minBlockToGrow the minimum number of blocks by which the swap file grows when full
*/
public SwapFile(String prefix, int blockSize, int minBlockToGrow)
{
try
{
swapFile = File.createTempFile(prefix, ".swap");
if (log.isLoggable(Level.INFO))
{
log.info("Creating swap file " + swapFile.getPath());
}
swapFile.deleteOnExit();
this.blockSize = blockSize;
this.minBlockToGrow = minBlockToGrow;
freeBlocks = new LinkedList<Long>();
}
catch (FileNotFoundException e)
{
throw new RuntimeException(e);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
/**
* open for read write
*/
public synchronized void open() {
try {
randomAccessFile = new RandomAccessFile(swapFile, "rw");
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* close and release file handle
*/
public synchronized void close() {
if (randomAccessFile != null) {
try {
randomAccessFile.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* Allocates an area in the swap file and writes data in it.
*
* @param data the data for which to allocate an area in the file
* @return a handle to the allocated area
* @throws IOException
*/
public SwapFileSegment write(byte[] data) throws IOException
{
verifyOpen();
int blockCount = (data.length - 1) / blockSize + 1;
long[] offsets = allocateFreeBlocks(blockCount);
int lastBlockSize = (data.length - 1) % blockSize + 1;
SwapFileSegment segment = new SwapFileSegment(offsets, lastBlockSize);
for (int i = 0; i < blockCount; ++i)
{
int dataSize = i < blockCount - 1 ? blockSize : lastBlockSize;
int dataOffset = i * blockSize;
write(data, dataSize, dataOffset, offsets[i]);
}
return segment;
}
private synchronized void write(byte[] data, int dataSize, int dataOffset, long fileOffset) throws IOException
{
randomAccessFile.seek(fileOffset);
randomAccessFile.write(data, dataOffset, dataSize);
}
/**
* Reads all the data from an allocated area.
*
* @param segment the allocated segment
* @return the whole data saved in an allocated area
* @throws IOException
*/
public byte[] read(SwapFileSegment segment) throws IOException
{
verifyOpen();
long[] offsets = segment.getOffsets();
int totalLength = (offsets.length - 1) * blockSize + segment.getLastBlockSize();
byte[] data = new byte[totalLength];
for (int i = 0; i < offsets.length; ++i)
{
int dataOffset = i * blockSize;
int dataLength = i < offsets.length - 1 ? blockSize : segment.getLastBlockSize();
read(data, dataOffset, dataLength, offsets[i]);
}
return data;
}
private synchronized void read(byte[] data, int dataOffset, int dataLength, long fileOffset) throws IOException
{
randomAccessFile.seek(fileOffset);
randomAccessFile.readFully(data, dataOffset, dataLength);
}
/**
* Frees an allocated area.
*
* @param segment the allocated segment
*/
public void free(SwapFileSegment segment)
{
verifyOpen();
freeBlocks(segment.getOffsets());
}
private void verifyOpen() {
if (randomAccessFile == null) {
throw new RuntimeException("Swap file not open for read write access");
}
}
/**
* Closes and deletes the swap file.
*/
public void dispose()
{
synchronized (this)
{
if (swapFile.exists())
{
if (log.isLoggable(Level.INFO))
{
log.info("Disposing swap file " + swapFile.getPath());
}
try
{
close();
}
catch (Exception e)
{
log.warning("Not able to close swap file " + swapFile.getPath());
}
if (!swapFile.delete())
{
log.warning("Not able to delete swap file " + swapFile.getPath());
}
}
}
}
protected void finalize() throws Throwable //NOSONAR
{
dispose();
super.finalize();
}
private synchronized long[] allocateFreeBlocks(int blockCount) throws IOException
{
int growCount = blockCount - freeBlocks.size();
if (growCount > 0)
{
if (growCount < minBlockToGrow)
{
growCount = minBlockToGrow;
}
long length = randomAccessFile.length();
long newLength = length + growCount * blockSize;
if (log.isLoggable(Level.INFO))
{
log.info("Growing swap file " + swapFile.getPath() + " with " + growCount + " blocks x " + blockSize + " bytes to size " + newLength);
}
randomAccessFile.setLength(newLength);
for (int i = 0; i < growCount; ++i)
{
freeBlocks.addLast(length + i * blockSize);
}
}
long[] offsets = new long[blockCount];
for (int i = 0; i < blockCount; i++)
{
offsets[i] = freeBlocks.pollFirst();
}
return offsets;
}
private synchronized void freeBlocks(long []offsets)
{
for (int i = offsets.length - 1; i >= 0; --i)
{
freeBlocks.addFirst(offsets[i]);
}
}
}

View File

@ -0,0 +1,40 @@
/******************************************************************************
* Copyright (C) 2012 Heng Sin Low *
* Copyright (C) 2012 Trek Global *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program 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 General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.compiere.print.util;
/**
*
* @author hengsin
*
*/
public class SwapFileSegment {
private final long[] offsets;
private final int lastBlockSize;
public SwapFileSegment(long[] offsets, int lastSize)
{
this.offsets = offsets;
this.lastBlockSize = lastSize;
}
public long[] getOffsets()
{
return offsets;
}
public int getLastBlockSize()
{
return lastBlockSize;
}
}

View File

@ -69,6 +69,7 @@ import org.adempiere.util.IProcessUI;
import org.compiere.model.MAttachment; import org.compiere.model.MAttachment;
import org.compiere.model.MAttachmentEntry; import org.compiere.model.MAttachmentEntry;
import org.compiere.model.MProcess; import org.compiere.model.MProcess;
import org.compiere.model.MSysConfig;
import org.compiere.model.PrintInfo; import org.compiere.model.PrintInfo;
import org.compiere.model.X_AD_PInstance_Para; import org.compiere.model.X_AD_PInstance_Para;
import org.compiere.print.MPrintFormat; import org.compiere.print.MPrintFormat;
@ -103,6 +104,8 @@ import org.compiere.utils.DigestOfFile;
*/ */
public class ReportStarter implements ProcessCall, ClientProcess public class ReportStarter implements ProcessCall, ClientProcess
{ {
private static final int DEFAULT_SWAP_MAX_PAGES = 100;
private static final String JASPER_SWAP_MAX_PAGES = "JASPER_REPORT_SWAP_MAX_PAGES";
/** Logger */ /** Logger */
private static CLogger log = CLogger.getCLogger(ReportStarter.class); private static CLogger log = CLogger.getCLogger(ReportStarter.class);
private static File REPORT_HOME = null; private static File REPORT_HOME = null;
@ -515,12 +518,14 @@ public class ReportStarter implements ProcessCall, ClientProcess
} }
Connection conn = null; Connection conn = null;
JRSwapFileVirtualizer virtualizer = null;
int maxPages = MSysConfig.getIntValue(JASPER_SWAP_MAX_PAGES, DEFAULT_SWAP_MAX_PAGES);
try { try {
conn = getConnection(); conn = getConnection();
String swapPath = System.getProperty("java.io.tmpdir"); String swapPath = System.getProperty("java.io.tmpdir");
JRSwapFile swapFile = new JRSwapFile(swapPath, 1024, 1024); JRSwapFile swapFile = new JRSwapFile(swapPath, 1024, 1024);
JRSwapFileVirtualizer virtualizer = new JRSwapFileVirtualizer(2, swapFile, true); virtualizer = new JRSwapFileVirtualizer(maxPages, swapFile, true);
params.put(JRParameter.REPORT_VIRTUALIZER, virtualizer); params.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
JRProperties.setProperty("net.sf.jasperreports.awt.ignore.missing.font", true); JRProperties.setProperty("net.sf.jasperreports.awt.ignore.missing.font", true);
@ -593,11 +598,15 @@ public class ReportStarter implements ProcessCall, ClientProcess
} catch (JRException e) { } catch (JRException e) {
log.severe("ReportStarter.startProcess: Can not run report - "+ e.getMessage()); log.severe("ReportStarter.startProcess: Can not run report - "+ e.getMessage());
} finally { } finally {
if (conn != null) if (conn != null) {
try { try {
conn.close(); conn.close();
} catch (SQLException e) { } catch (SQLException e) {
} }
}
if (virtualizer != null) {
virtualizer.cleanup();
}
} }
} }