IDEMPIERE-146 Performance: Report Engine always read all data into memory
This commit is contained in:
parent
e0e7d05167
commit
809f57fe77
|
@ -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
|
||||
;
|
||||
|
|
@ -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
|
||||
;
|
||||
|
|
@ -19,6 +19,7 @@ package org.compiere.print;
|
|||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
@ -32,6 +33,8 @@ import javax.xml.transform.dom.DOMSource;
|
|||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
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.DisplayType;
|
||||
import org.compiere.util.Trace;
|
||||
|
@ -67,6 +70,7 @@ public class PrintData implements Serializable
|
|||
throw new IllegalArgumentException("Name cannot be null");
|
||||
m_ctx = ctx;
|
||||
m_name = name;
|
||||
m_matrix = new SerializableMatrixImpl<Serializable>(name);
|
||||
} // PrintData
|
||||
|
||||
/**
|
||||
|
@ -80,21 +84,18 @@ public class PrintData implements Serializable
|
|||
if (name == null)
|
||||
throw new IllegalArgumentException("Name cannot be null");
|
||||
m_ctx = ctx;
|
||||
m_name = name;
|
||||
m_name = name;
|
||||
m_matrix = new SerializableMatrixImpl<Serializable>(name);
|
||||
if (nodes != null)
|
||||
m_nodes = nodes;
|
||||
addRow(false, 0, nodes);
|
||||
} // PrintData
|
||||
|
||||
private SerializableMatrix<Serializable> m_matrix;
|
||||
|
||||
/** Context */
|
||||
private Properties m_ctx;
|
||||
/** Data Structure 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 */
|
||||
private ArrayList<Integer> m_functionRows = new ArrayList<Integer>();
|
||||
|
||||
|
@ -207,7 +208,7 @@ public class PrintData implements Serializable
|
|||
public String toString()
|
||||
{
|
||||
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)
|
||||
sb.append(",TableName=").append(m_TableName);
|
||||
sb.append("]");
|
||||
|
@ -222,9 +223,7 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public boolean isEmpty()
|
||||
{
|
||||
if (m_nodes == null)
|
||||
return true;
|
||||
return m_nodes.size() == 0;
|
||||
return m_matrix.getRowCount() == 0 || m_matrix.getRowData().isEmpty();
|
||||
} // isEmpty
|
||||
|
||||
/**
|
||||
|
@ -233,24 +232,27 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public int getNodeCount()
|
||||
{
|
||||
if (m_nodes == null)
|
||||
if (m_matrix.getRowCount() == 0)
|
||||
return 0;
|
||||
return m_nodes.size();
|
||||
return m_matrix.getRowData().size();
|
||||
} // getNodeCount
|
||||
|
||||
|
||||
public void addRow (boolean functionRow, int levelNo)
|
||||
{
|
||||
addRow(functionRow, levelNo, new ArrayList<Serializable>());
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Add Row
|
||||
* @param functionRow true if function row
|
||||
* @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_row = m_rows.size();
|
||||
m_rows.add (m_nodes);
|
||||
m_matrix.addRow(nodes);
|
||||
if (functionRow)
|
||||
m_functionRows.add(new Integer(m_row));
|
||||
m_functionRows.add(new Integer(m_matrix.getRowIndex()));
|
||||
if (m_hasLevelNo && levelNo != 0)
|
||||
addNode(new PrintDataElement(LEVEL_NO, new Integer(levelNo), DisplayType.Integer, null));
|
||||
} // addRow
|
||||
|
@ -262,11 +264,7 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public boolean setRowIndex (int row)
|
||||
{
|
||||
if (row < 0 || row >= m_rows.size())
|
||||
return false;
|
||||
m_row = row;
|
||||
m_nodes = m_rows.get(m_row);
|
||||
return true;
|
||||
return m_matrix.setRowIndex(row);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -275,7 +273,7 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public boolean setRowNext()
|
||||
{
|
||||
return setRowIndex(m_row+1);
|
||||
return m_matrix.setRowNext();
|
||||
} // setRowNext
|
||||
|
||||
/**
|
||||
|
@ -284,7 +282,7 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public int getRowCount()
|
||||
{
|
||||
return m_rows.size();
|
||||
return m_matrix.getRowCount();
|
||||
} // getRowCount
|
||||
|
||||
/**
|
||||
|
@ -293,7 +291,7 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public int getRowIndex()
|
||||
{
|
||||
return m_row;
|
||||
return m_matrix.getRowCount();
|
||||
} // getRowIndex
|
||||
|
||||
/**
|
||||
|
@ -312,7 +310,7 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public boolean isFunctionRow ()
|
||||
{
|
||||
return m_functionRows.contains(new Integer(m_row));
|
||||
return m_functionRows.contains(new Integer(m_matrix.getRowIndex()));
|
||||
} // isFunctionRow
|
||||
|
||||
/**
|
||||
|
@ -322,11 +320,12 @@ public class PrintData implements Serializable
|
|||
public boolean isPageBreak ()
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
PrintDataElement pde = (PrintDataElement)o;
|
||||
|
@ -362,12 +361,13 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public int getLineLevelNo ()
|
||||
{
|
||||
if (m_nodes == null || !m_hasLevelNo)
|
||||
List<Serializable> nodes = m_matrix.getRowData();
|
||||
if (nodes == null || !m_hasLevelNo)
|
||||
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)
|
||||
{
|
||||
PrintDataElement pde = (PrintDataElement)o;
|
||||
|
@ -391,9 +391,12 @@ public class PrintData implements Serializable
|
|||
{
|
||||
if (parent == null)
|
||||
throw new IllegalArgumentException("Parent cannot be null");
|
||||
if (m_nodes == null)
|
||||
addRow(false, 0);
|
||||
m_nodes.add (parent);
|
||||
List<Serializable> nodes = m_matrix.getRowData();
|
||||
if (nodes == null) {
|
||||
nodes = new ArrayList<Serializable>();
|
||||
addRow(false, 0, nodes);
|
||||
}
|
||||
nodes.add (parent);
|
||||
} // addNode
|
||||
|
||||
/**
|
||||
|
@ -404,9 +407,12 @@ public class PrintData implements Serializable
|
|||
{
|
||||
if (node == null)
|
||||
throw new IllegalArgumentException("Node cannot be null");
|
||||
if (m_nodes == null)
|
||||
addRow(false, 0);
|
||||
m_nodes.add (node);
|
||||
List<Serializable> nodes = m_matrix.getRowData();
|
||||
if (nodes == null) {
|
||||
nodes = new ArrayList<Serializable>();
|
||||
addRow(false, 0, nodes);
|
||||
}
|
||||
nodes.add (node);
|
||||
} // addNode
|
||||
|
||||
/**
|
||||
|
@ -416,9 +422,10 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
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 m_nodes.get(index);
|
||||
return nodes.get(index);
|
||||
} // getNode
|
||||
|
||||
/**
|
||||
|
@ -431,7 +438,8 @@ public class PrintData implements Serializable
|
|||
int index = getIndex (name);
|
||||
if (index < 0)
|
||||
return null;
|
||||
return m_nodes.get(index);
|
||||
List<Serializable> nodes = m_matrix.getRowData();
|
||||
return nodes.get(index);
|
||||
} // getNode
|
||||
|
||||
/**
|
||||
|
@ -444,7 +452,8 @@ public class PrintData implements Serializable
|
|||
int index = getIndex (AD_Column_ID.intValue());
|
||||
if (index < 0)
|
||||
return null;
|
||||
return m_nodes.get(index);
|
||||
List<Serializable> nodes = m_matrix.getRowData();
|
||||
return nodes.get(index);
|
||||
} // getNode
|
||||
|
||||
/**
|
||||
|
@ -453,11 +462,12 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public PrintDataElement getPKey()
|
||||
{
|
||||
if (m_nodes == null)
|
||||
List<Serializable> nodes = m_matrix.getRowData();
|
||||
if (nodes == 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)
|
||||
{
|
||||
PrintDataElement pde = (PrintDataElement)o;
|
||||
|
@ -475,11 +485,12 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public int getIndex (String columnName)
|
||||
{
|
||||
if (m_nodes == null)
|
||||
List<Serializable> nodes = m_matrix.getRowData();
|
||||
if (nodes == null)
|
||||
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 (columnName.equals(((PrintDataElement)o).getColumnName()))
|
||||
|
@ -540,7 +551,7 @@ public class PrintData implements Serializable
|
|||
*/
|
||||
public void dumpCurrentRow()
|
||||
{
|
||||
dumpRow(this, m_row);
|
||||
dumpRow(this, m_matrix.getRowIndex());
|
||||
} // dump
|
||||
|
||||
/**
|
||||
|
@ -769,7 +780,7 @@ public class PrintData implements Serializable
|
|||
PrintData pdx = new PrintData(new Properties(), "test2");
|
||||
pdx.addNode(new PrintDataElement("test2element1-1","testvalue11",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-2","testvalue22",0,null));
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.compiere.print.layout;
|
|||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 2D Dimesnion Implementation
|
||||
|
@ -25,8 +26,13 @@ import java.awt.geom.Dimension2D;
|
|||
* @author Jorg Janke
|
||||
* @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
|
||||
*/
|
||||
|
|
|
@ -19,6 +19,12 @@ package org.compiere.print.layout;
|
|||
import java.awt.Graphics;
|
||||
import java.awt.Rectangle;
|
||||
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.StringReader;
|
||||
import java.util.logging.Level;
|
||||
|
@ -41,8 +47,13 @@ import org.compiere.util.CLogger;
|
|||
* @author Jorg Janke
|
||||
* @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
|
||||
* @param html html string
|
||||
|
@ -74,6 +85,10 @@ public class HTMLRenderer extends View
|
|||
/** Logger */
|
||||
private static CLogger log = CLogger.getCLogger(HTMLRenderer.class);
|
||||
|
||||
public HTMLRenderer() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Constructor
|
||||
* @param f factory
|
||||
|
@ -85,14 +100,18 @@ public class HTMLRenderer extends View
|
|||
m_factory = f;
|
||||
m_view = v;
|
||||
m_view.setParent(this);
|
||||
m_element = m_view.getElement();
|
||||
// initially layout to the preferred size
|
||||
setSize(m_view.getPreferredSpan(X_AXIS), m_view.getPreferredSpan(Y_AXIS));
|
||||
} // HTMLRenderer
|
||||
|
||||
private int m_width;
|
||||
private View m_view;
|
||||
private ViewFactory m_factory;
|
||||
private View m_view;
|
||||
private ViewFactory m_factory;
|
||||
private Element m_element;
|
||||
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)
|
||||
{
|
||||
this.m_width = (int) width;
|
||||
this.m_viewWidth = width;
|
||||
this.m_viewHeight = height;
|
||||
m_view.setSize(width, height);
|
||||
}
|
||||
|
||||
|
@ -396,4 +417,25 @@ public class HTMLRenderer extends View
|
|||
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
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.awt.print.PrinterJob;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Serializable;
|
||||
import java.net.URL;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
|
@ -61,6 +62,8 @@ import org.compiere.print.MPrintPaper;
|
|||
import org.compiere.print.MPrintTableFormat;
|
||||
import org.compiere.print.PrintData;
|
||||
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.DB;
|
||||
import org.compiere.util.DisplayType;
|
||||
|
@ -1647,8 +1650,7 @@ public class LayoutEngine implements Pageable, Printable, Doc
|
|||
|
||||
// The Data
|
||||
int rows = printData.getRowCount();
|
||||
// System.out.println("Rows=" + rows);
|
||||
Object[][] data = new Object [rows][columnCount];
|
||||
SerializableMatrix<Serializable> elements = new SerializableMatrixImpl<Serializable>(m_PrintInfo.getName());
|
||||
KeyNamePair[] pk = new KeyNamePair[rows];
|
||||
String pkColumnName = null;
|
||||
ArrayList<Integer> functionRows = new ArrayList<Integer>();
|
||||
|
@ -1657,7 +1659,7 @@ public class LayoutEngine implements Pageable, Printable, Doc
|
|||
// for all rows
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
// System.out.println("row=" + row);
|
||||
ArrayList<Serializable> columns = new ArrayList<Serializable>();
|
||||
printData.setRowIndex(row);
|
||||
if (printData.isFunctionRow())
|
||||
{
|
||||
|
@ -1690,33 +1692,33 @@ public class LayoutEngine implements Pageable, Printable, Doc
|
|||
}
|
||||
}
|
||||
// for all columns
|
||||
col = 0;
|
||||
for (int c = 0; c < format.getItemCount(); c++)
|
||||
{
|
||||
Serializable columnElement = null;
|
||||
MPrintFormatItem item = format.getItem(c);
|
||||
Object dataElement = null;
|
||||
Serializable dataElement = null;
|
||||
if (item.isPrinted()) // Text Columns
|
||||
{
|
||||
if (item.isTypeImage())
|
||||
{
|
||||
if (item.isImageField())
|
||||
data[row][col] = createImageElement (item, printData);
|
||||
columnElement = createImageElement (item, printData);
|
||||
else if (item.isImageIsAttached())
|
||||
data[row][col] = ImageElement.get (item.get_ID());
|
||||
columnElement = ImageElement.get (item.get_ID());
|
||||
else
|
||||
data[row][col] = ImageElement.get (item.getImageURL());
|
||||
if (data[row][col] != null)
|
||||
((PrintElement)data[row][col]).layout(item.getMaxWidth(), item.getMaxHeight(), false, item.getFieldAlignmentType());
|
||||
columnElement = ImageElement.get (item.getImageURL());
|
||||
if (columnElement != null)
|
||||
((PrintElement)columnElement).layout(item.getMaxWidth(), item.getMaxHeight(), false, item.getFieldAlignmentType());
|
||||
}
|
||||
else if (item.isBarcode())
|
||||
{
|
||||
data[row][col] = createBarcodeElement(item, printData);
|
||||
if (data[row][col] != null)
|
||||
((PrintElement)data[row][col]).layout(item.getMaxWidth(), item.getMaxHeight(), false, item.getFieldAlignmentType());
|
||||
columnElement = createBarcodeElement(item, printData);
|
||||
if (columnElement != null)
|
||||
((PrintElement)columnElement).layout(item.getMaxWidth(), item.getMaxHeight(), false, item.getFieldAlignmentType());
|
||||
}
|
||||
else if (item.isTypeText() )
|
||||
{
|
||||
data[row][col] = item.getPrintName(format.getLanguage());
|
||||
columnElement = item.getPrintName(format.getLanguage());
|
||||
}
|
||||
else if (item.isTypeField())
|
||||
{
|
||||
|
@ -1729,22 +1731,22 @@ public class LayoutEngine implements Pageable, Printable, Doc
|
|||
{
|
||||
PrintDataElement pde = (PrintDataElement)obj;
|
||||
if (pde.isID() || pde.isYesNo())
|
||||
dataElement = pde.getValue();
|
||||
dataElement = (Serializable) pde.getValue();
|
||||
else
|
||||
dataElement = pde.getValueDisplay(format.getLanguage());
|
||||
}
|
||||
}
|
||||
else
|
||||
log.log(Level.SEVERE, "Element not PrintDataElement " + obj.getClass());
|
||||
// System.out.println(" row=" + row + ",col=" + col + " - " + item.getAD_Column_ID() + " => " + dataElement);
|
||||
data[row][col] = dataElement;
|
||||
columnElement = dataElement;
|
||||
}
|
||||
else // item.isTypeBox() or isTypePrintFormat()
|
||||
{
|
||||
log.warning("Unsupported: " + (item.isTypeBox() ? "Box" : "PrintFormat") + " in Table: " + item);
|
||||
}
|
||||
col++;
|
||||
columns.add(columnElement);
|
||||
} // printed
|
||||
} // for all columns
|
||||
elements.addRow(columns);
|
||||
|
||||
PrintDataElement pde = printData.getPKey();
|
||||
if (pde != null) // for FunctionRows
|
||||
|
@ -1761,7 +1763,7 @@ public class LayoutEngine implements Pageable, Printable, Doc
|
|||
TableElement table = new TableElement(columnHeader,
|
||||
columnMaxWidth, columnMaxHeight, columnJustification,
|
||||
fixedWidth, functionRows, multiLineHeader,
|
||||
data, pk, pkColumnName,
|
||||
elements, pk, pkColumnName,
|
||||
pageNoStart, firstPage, nextPages, repeatedColumns, additionalLines,
|
||||
rowColFont, rowColColor, rowColBackground,
|
||||
tf, pageBreak, colSuppressRepeats);
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.awt.Rectangle;
|
|||
import java.awt.Toolkit;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.image.ImageObserver;
|
||||
import java.io.Serializable;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
@ -38,8 +39,14 @@ import org.compiere.util.CLogger;
|
|||
* @author Jorg Janke
|
||||
* @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
|
||||
*/
|
||||
|
|
|
@ -30,11 +30,13 @@ import java.awt.font.TextLayout;
|
|||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.Serializable;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.AttributedString;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -45,6 +47,8 @@ import net.sourceforge.barbecue.output.OutputException;
|
|||
import org.compiere.model.MQuery;
|
||||
import org.compiere.print.MPrintFormatItem;
|
||||
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.NamePair;
|
||||
import org.compiere.util.Util;
|
||||
|
@ -111,13 +115,16 @@ public class TableElement extends PrintElement
|
|||
public TableElement (ValueNamePair[] columnHeader,
|
||||
int[] columnMaxWidth, int[] columnMaxHeight, String[] columnJustification,
|
||||
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,
|
||||
HashMap<Point,Font> rowColFont, HashMap<Point,Color> rowColColor, HashMap<Point,Color> rowColBackground,
|
||||
MPrintTableFormat tFormat, ArrayList<Integer> pageBreak, boolean[] colSuppressRepeats)
|
||||
{
|
||||
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_columnHeader = columnHeader;
|
||||
m_columnMaxWidth = columnMaxWidth;
|
||||
|
@ -191,7 +198,7 @@ public class TableElement extends PrintElement
|
|||
/** List of Function Rows */
|
||||
private ArrayList<Integer> m_functionRows;
|
||||
/** The Data */
|
||||
private Object[][] m_data;
|
||||
private SerializableMatrix<Serializable> m_data;
|
||||
/** Primary Keys */
|
||||
private KeyNamePair[] m_pk;
|
||||
/** Primary Key Column Name */
|
||||
|
@ -274,16 +281,16 @@ public class TableElement extends PrintElement
|
|||
return true;
|
||||
|
||||
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)
|
||||
float dynMxColumnWidth = m_firstPage.width / 2;
|
||||
|
||||
// Width caolculation
|
||||
int rows = m_data.length;
|
||||
// Width calculation
|
||||
int rows = m_data.getRowCount();
|
||||
int cols = m_columnHeader.length;
|
||||
// Data Sizes and Header Sizes
|
||||
Dimension2DImpl[][] dataSizes = new Dimension2DImpl[rows][cols];
|
||||
SerializableMatrix<Dimension2DImpl> dataSizes = new SerializableMatrixImpl<Dimension2DImpl>("TableElementDimensions");
|
||||
Dimension2DImpl[] headerSizes = new Dimension2DImpl[cols];
|
||||
FontRenderContext frc = new FontRenderContext(null, true, true);
|
||||
|
||||
|
@ -300,48 +307,62 @@ public class TableElement extends PrintElement
|
|||
float colWidth = 0;
|
||||
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)
|
||||
{
|
||||
dataSizes[row][dataCol] = new Dimension2DImpl();
|
||||
dimensions.set(dataCol, new Dimension2DImpl());
|
||||
continue;
|
||||
}
|
||||
String string = dataItem.toString();
|
||||
if (string.length() == 0)
|
||||
{
|
||||
dataSizes[row][dataCol] = new Dimension2DImpl();
|
||||
dimensions.set(dataCol, new Dimension2DImpl());
|
||||
continue;
|
||||
}
|
||||
Font font = getFont(row, dataCol);
|
||||
|
||||
// Print below existing column = (col != dataCol)
|
||||
addPrintLines(row, col, dataItem);
|
||||
dataSizes[row][dataCol] = new Dimension2DImpl(); // don't print
|
||||
dimensions.set(dataCol, new Dimension2DImpl());
|
||||
|
||||
if (dataItem instanceof Boolean)
|
||||
{
|
||||
dataSizes[row][col].addBelow(LayoutEngine.IMAGE_SIZE);
|
||||
dimensions.get(col).addBelow(LayoutEngine.IMAGE_SIZE);
|
||||
continue;
|
||||
}
|
||||
else if (dataItem instanceof ImageElement)
|
||||
{
|
||||
dataSizes[row][col].addBelow(
|
||||
dimensions.get(col).addBelow(
|
||||
new Dimension((int)((ImageElement)dataItem).getWidth(),
|
||||
(int)((ImageElement)dataItem).getHeight()));
|
||||
// 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)
|
||||
colWidth = width;
|
||||
continue;
|
||||
}
|
||||
else if (dataItem instanceof BarcodeElement)
|
||||
{
|
||||
dataSizes[row][col].addBelow(
|
||||
dimensions.get(col).addBelow(
|
||||
new Dimension((int)((BarcodeElement)dataItem).getWidth(),
|
||||
(int)((BarcodeElement)dataItem).getHeight()));
|
||||
// Check if the overflow is allowed - teo_sarca, [ 1673590 ]
|
||||
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)
|
||||
colWidth = width;
|
||||
}
|
||||
|
@ -362,12 +383,12 @@ public class TableElement extends PrintElement
|
|||
m_columnMaxWidth[col] = (int)Math.ceil(dynMxColumnWidth);
|
||||
else if (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);
|
||||
}
|
||||
dataSizes[row][col].addBelow(width, height);
|
||||
dimensions.get(col).addBelow(width, height);
|
||||
}
|
||||
// Width limitations
|
||||
if (m_columnMaxWidth[col] != 0 && m_columnMaxWidth[col] != -1)
|
||||
|
@ -385,7 +406,7 @@ public class TableElement extends PrintElement
|
|||
height = renderer.getHeight();
|
||||
renderer.setAllocation((int)colWidth, (int)height);
|
||||
// log.finest( "calculateSize HTML - " + renderer.getAllocation());
|
||||
m_data[row][dataCol] = renderer; // replace for printing
|
||||
m_data.getRowData().set(dataCol, renderer);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -415,14 +436,17 @@ public class TableElement extends PrintElement
|
|||
}
|
||||
if (m_fixedWidth[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)
|
||||
m_rowColDrillDown.put(new Point(row, col), (NamePair)dataItem);
|
||||
//
|
||||
log.finest("Col=" + col + ", row=" + row
|
||||
+ " => " + dataSizes[row][col] + " - ColWidth=" + colWidth);
|
||||
if (log.isLoggable(Level.FINEST))
|
||||
{
|
||||
log.finest("Col=" + col + ", row=" + row
|
||||
+ " => " + dimensions.get(col) + " - ColWidth=" + colWidth);
|
||||
}
|
||||
} // for all data rows
|
||||
|
||||
// Column Width for Header
|
||||
|
@ -532,10 +556,12 @@ public class TableElement extends PrintElement
|
|||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
float rowHeight = 0f;
|
||||
dataSizes.setRowIndex(row);
|
||||
List<Dimension2DImpl> dimensions = dataSizes.getRowData();
|
||||
for (int col = 0; col < cols; col++)
|
||||
{
|
||||
if (dataSizes[row][col].height > rowHeight) // max
|
||||
rowHeight = (float)dataSizes[row][col].height;
|
||||
if (dimensions.get(col).height > rowHeight) // max
|
||||
rowHeight = (float)dimensions.get(col).height;
|
||||
} // for all columns
|
||||
rowHeight += m_tFormat.getLineStroke().floatValue() + (2*V_GAP);
|
||||
m_rowHeights.add(new Float(rowHeight));
|
||||
|
@ -1080,7 +1106,7 @@ public class TableElement extends PrintElement
|
|||
return -1; // above
|
||||
//
|
||||
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())
|
||||
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();
|
||||
//
|
||||
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())
|
||||
nextPageRow = ((Integer)m_firstRowOnPage.get(pageYindex+1)).intValue();
|
||||
if (DEBUG_PRINT)
|
||||
|
@ -1557,7 +1583,7 @@ public class TableElement extends PrintElement
|
|||
curX += m_tFormat.getVLineStroke().floatValue();
|
||||
|
||||
// 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
|
||||
|
@ -1608,30 +1634,34 @@ public class TableElement extends PrintElement
|
|||
* @param col col
|
||||
* @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)
|
||||
m_printRows.add(null);
|
||||
ArrayList<ArrayList<Object>> columns = m_printRows.get(row);
|
||||
while (m_printRows.getRowCount() <= row)
|
||||
m_printRows.addRow(null);
|
||||
m_printRows.setRowIndex(row);
|
||||
List<ArrayList<Serializable>> columns = m_printRows.getRowData();
|
||||
if (columns == null)
|
||||
columns = new ArrayList<ArrayList<Object>>(m_columnHeader.length);
|
||||
columns = new ArrayList<ArrayList<Serializable>>(m_columnHeader.length);
|
||||
while (columns.size() <= col)
|
||||
columns.add(null);
|
||||
//
|
||||
ArrayList<Object> coordinate = columns.get(col);
|
||||
ArrayList<Serializable> coordinate = columns.get(col);
|
||||
if (coordinate == null)
|
||||
coordinate = new ArrayList<Object>();
|
||||
coordinate = new ArrayList<Serializable>();
|
||||
coordinate.add(data);
|
||||
//
|
||||
columns.set(col, coordinate);
|
||||
m_printRows.set(row, columns);
|
||||
log.finest("row=" + row + ", col=" + col
|
||||
+ " - Rows=" + m_printRows.size() + ", Cols=" + columns.size()
|
||||
+ " - " + data);
|
||||
m_printRows.setRowData(columns);
|
||||
if (log.isLoggable(Level.FINEST))
|
||||
{
|
||||
log.finest("row=" + row + ", col=" + col
|
||||
+ " - Rows=" + m_printRows.getRowCount() + ", Cols=" + columns.size()
|
||||
+ " - " + data);
|
||||
}
|
||||
} // addAdditionalLines
|
||||
|
||||
/** 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
|
||||
|
@ -1651,12 +1681,15 @@ public class TableElement extends PrintElement
|
|||
*/
|
||||
private Object[] getPrintItems (int row, int col)
|
||||
{
|
||||
ArrayList<ArrayList<Object>> columns = null;
|
||||
if (m_printRows.size() > row)
|
||||
columns = m_printRows.get(row);
|
||||
List<ArrayList<Serializable>> columns = null;
|
||||
if (m_printRows.getRowCount() > row)
|
||||
{
|
||||
m_printRows.setRowIndex(row);
|
||||
columns = m_printRows.getRowData();
|
||||
}
|
||||
if (columns == null)
|
||||
return new Object[]{};
|
||||
ArrayList<Object> coordinate = null;
|
||||
ArrayList<Serializable> coordinate = null;
|
||||
if (columns.size() > col)
|
||||
coordinate = columns.get(col);
|
||||
if (coordinate == null)
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -69,6 +69,7 @@ import org.adempiere.util.IProcessUI;
|
|||
import org.compiere.model.MAttachment;
|
||||
import org.compiere.model.MAttachmentEntry;
|
||||
import org.compiere.model.MProcess;
|
||||
import org.compiere.model.MSysConfig;
|
||||
import org.compiere.model.PrintInfo;
|
||||
import org.compiere.model.X_AD_PInstance_Para;
|
||||
import org.compiere.print.MPrintFormat;
|
||||
|
@ -103,6 +104,8 @@ import org.compiere.utils.DigestOfFile;
|
|||
*/
|
||||
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 */
|
||||
private static CLogger log = CLogger.getCLogger(ReportStarter.class);
|
||||
private static File REPORT_HOME = null;
|
||||
|
@ -515,12 +518,14 @@ public class ReportStarter implements ProcessCall, ClientProcess
|
|||
}
|
||||
|
||||
Connection conn = null;
|
||||
JRSwapFileVirtualizer virtualizer = null;
|
||||
int maxPages = MSysConfig.getIntValue(JASPER_SWAP_MAX_PAGES, DEFAULT_SWAP_MAX_PAGES);
|
||||
try {
|
||||
conn = getConnection();
|
||||
|
||||
String swapPath = System.getProperty("java.io.tmpdir");
|
||||
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);
|
||||
JRProperties.setProperty("net.sf.jasperreports.awt.ignore.missing.font", true);
|
||||
|
||||
|
@ -593,11 +598,15 @@ public class ReportStarter implements ProcessCall, ClientProcess
|
|||
} catch (JRException e) {
|
||||
log.severe("ReportStarter.startProcess: Can not run report - "+ e.getMessage());
|
||||
} finally {
|
||||
if (conn != null)
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
}
|
||||
if (virtualizer != null) {
|
||||
virtualizer.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue