December 02, 2006

About me

 

My name is Anh Quan Nguyen, from Saigon, Vietnam.
I am currently living in Frankfurt, Germany.

I like to blog about Web 2.0, JasperReports, Portal/Portlet, JSR168, DWR, Dojo, Struts... and so on.

A little bit about my background. In March 2003, I graduated Bachelor in Electronics, Hochiminh City University of Technology. Then I got a position as Electronic Engineer in Sony. Tivi production made me tired. I decided to change my career. In September 2004, I came to Germany to take the Master Course in Communication and Media Engineering. Here I also do not know exactly my real way, become a telecom. engineer or a software developer. After a while of listening to my heart, I know I have no other choice than to become a programmer. It's quite a little bit late, but it's better than going to the wrong way for the rest of my life.

Thank you for visiting my website. And if you are professional programmer, please help me with your knowledge.

Nice to know you all !

Read more : 0 responses : Saturday, December 02, 2006

OutOfMemory - How to avoid it ?

 

Sometimes, you encounter the OutOfMemoryException because JasperReports tries to fill data which are bigger than memory allocated to JVM.
Don't be panic. JasperReports can split a big report to smaller segments and store these segment in a temporary directory in your drive.

What you have to do is to set the built-in parameter REPORT_VIRTUALIZER
Take a look at the code below:

//Create connection to database server
connection = ConnectionPool.getConnection();

//Create a cache at C:\Temp

JRFileVirtualizer fileVirtualizer = new JRFileVirtualizer(3, "c:\\Temp");

//Set parameter REPORT_VIRTUALIZER
HashMap parameterMap = new HashMap();
parameterMap.put(JRParameter.REPORT_VIRTUALIZER, fileVirtualizer);

//Fill data as usual
JasperFillManager.fillReportToFile("MyReport.jasper", parameterMap,connection);



Good luck !

Read more : 0 responses : Saturday, December 02, 2006

Struts and XDoclet

 

Type your summary here.

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

Portlet development with XDoclet, ANT

 

Type your summary here.

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

Struts based porlet in Liferay Portal

 

Type your summary here.

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

Struts resources (eBooks, articles, tutorials)

 

Type your summary here.

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

Struts development in MyEclipse

 

Hướng dẫn cách sử dụng MyEclipse để phát triển Struts application

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

HelloWorld Struts Application

 

Chương trình cơ bản sử dụng Struts

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

Struts and JasperReports

 

Not finished!

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

JasperReports Resources (eBooks, articles, tutorials)

 

eBooks:
1.
2.
3.
4.
5.
6.
7.
Articles:

Tutorials:



Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

iReport Tips

 

Một số tip khi sử dụng iReport.

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

Jasper Report Printing from Servlet

 

In this post I will show you how to write a simple Servlet to view PDF reports on web browser.

The source code is quite simple and self exlained.
Take a look !










package com.db.misportal.servlet;



import java.io.IOException;



import javax.servlet.ServletConfig;

import javax.servlet.ServletContext;

import javax.servlet.ServletException;

import javax.servlet.ServletOutputStream;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.db.report.jasper.custom.common.AbstractReport;

import com.db.report.jasper.custom.consumptionreport.ConsumptionReport;

import com.db.report.jasper.custom.trackingreport.TrackingReport;


/**

* @web.servlet name = "JasperReportsBrowserServlet" description = "Display PDF

* reports on the browser.The default file name is

* <productcode>.pdf"

*

* @web.servlet-mapping url-pattern = "/JasperReportsBrowserServlet/*"

*

* @author anhquan.nguyen@gmail.com

*

*/


public class JasperReportsBrowserServlet extends HttpServlet {



private static final long serialVersionUID = 1L;

static Logger _log = Logger.getLogger(JasperReportsBrowserServlet.class);

String strProductCode;

String strReportDate;

String strReportHome;

String strProductCategory;

String strReportType;

String strReportMonth;

String strReportYear;

String strReportMode;

public void init(ServletConfig config) throws ServletException {

super.init(config);

_log.info("init JasperReportsBrowserServlet...");

// String initial = config.getInitParameter("initial");

ServletContext context = this.getServletConfig().getServletContext();

strReportHome = context.getRealPath("/reports");

_log.debug("Report Home : " + strReportHome);

_log.info("init completed!");

}



protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

process(request, response);

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

process(request, response);

}



protected void process(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

ServletOutputStream servletOutputStream = response.getOutputStream();

/* Get parameters from form */

strProductCode = request.getParameter("productCode");

strProductCategory = request.getParameter("productCategory");

strReportType = request.getParameter("reportType");

strReportMonth = request.getParameter("reportMonth");

strReportYear = request.getParameter("reportYear");

strReportMode = request.getParameter("reportMode");

strReportDate = strReportMonth + "/" + strReportYear;



AbstractReport report = null;

String strReportHome = "N:\\thesis\\workspace\\misportal\\WebRoot\\reports";



if ("consumption_report".equals(strReportType)) {

String strPDFFileName = strProductCode + ".pdf";

String strContentHeader = strProductCode;

report = new ConsumptionReport();

report.setParams(strProductCode, strReportDate);

if ("single".equals(strReportMode)) {

report.setPath("consumptionreportsingle", strReportHome

+ "\\consumption\\in", strReportHome

+ "\\consumption\\out");

} else // all

{

strPDFFileName = "Consumption" + ".pdf";

strContentHeader = "Consumption";

report.setPath("consumptionreportall", strReportHome

+ "\\consumption\\in", strReportHome

+ "\\consumption\\out");

}

exportToPDFStream(response, servletOutputStream, report,

strPDFFileName, strContentHeader);

} else if ("tracking_report".equals(strReportType)) {

if ("single".equals(strReportMode)) {

report = new TrackingReport();

report.setParams(strProductCode, strReportDate);



report.setPath("tracking", strReportHome

+ "\\trackingreport\\in", strReportHome

+ "\\trackingreport\\out");



report.setSubReports("tracking_part", 1, 10);

String strPDFFileName = strProductCode + ".pdf";

String strContentHeader = strProductCode;



exportToPDFStream(response, servletOutputStream, report,

strPDFFileName, strContentHeader);

} else {

report = new TrackingReport();

report.setParams(strProductCode, strReportDate);



report.setPath("tracking", strReportHome

+ "\\trackingreport\\in", strReportHome

+ "\\trackingreport\\out");



report.setSubReports("tracking_part", 1, 10);

String strPDFFileName = strProductCode + ".pdf";

String strContentHeader = strProductCode;

exportToPDFStream(response, servletOutputStream, report,

strPDFFileName, strContentHeader);

}

}



servletOutputStream.close();

}



/**

* Export to PDF Stream

*

* @param response

* @param servletOutputStream

* @param report

* @param strPDFFileName

* Default file name

* @param strContentHeader

* Content header description

* @throws IOException

*/

private void exportToPDFStream(HttpServletResponse response,

ServletOutputStream servletOutputStream, AbstractReport report,

String strPDFFileName, String strContentHeader) throws IOException {



byte[] bytes = report.getPDFBytes();

_log.debug("Bytes length : " + bytes.length);

response.setContentType("application/pdf");

response.setHeader("Content-Description", strContentHeader);

response.setHeader("Content-transfer-encoding", "binary");

response.setHeader("Content-Disposition", "attachment; filename="

+ strPDFFileName);

response.setContentLength(bytes.length);

servletOutputStream.write(bytes, 0, bytes.length);

servletOutputStream.flush();

_log.debug("close outputstream");

}

}




That's all !

Read more : 1 responses : Saturday, December 02, 2006

Create more readable report with conditional Formatting

 

Khi tạo report, bạn sẽ thấy nếu sử dụng conditional formatting thì sẽ tạo được những report rất đẹp. Ví dụ, đối với field kiểu boolean, thay vì hiện giá trị true/false, bạn có thể xuất dấu check (dạng file image). Làm việc đó như thế nào? Thực sự cũng không quá phức tạp.

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

How to use SubReports

 

Để tạo một report phức tạp, bạn không thể sử dụng một file jrxml đơn lẻ được. Trong trường hợp này, để đơn giản, bạn có thể chia main report thành các subreport để dễ quản lý.

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

Dynamic datasource & embedded SQL

 

Trong JasperReport, bạn có thể nhúng lệnh SQL trong file jrxml hoặc có thể truyền data bằng datasource. Cách nào hay hơn ? Lần này tôi sẽ so sánh ưu và nhược điểm của hai phương pháp này.

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

Create better quality Chart in PDF format

 

Bạn sẽ để ý rằng khi tạo Chart trong JasperReports và export ra PDF, Chart nhận được rất xấu, đặc biệt là các ký tự và số (đôi khi mờ không thấy chữ). Tại sao? Đó là do PdfExporter của JasperReports convert Chart ra dạng pixel, sau đó mới in ra PDF. Mà đương nhiên là các chữ số dạng pixel thì không thể rõ bằng các chữ số dạng text được rồi. Bạn không thể dùng Select Tool trong Acrobat Reader để chọn các chữ số trong Chart, mà chỉ có thể select chart như một hình ảnh. Bài viết này sẽ hướng dẫn bạn các vượt qua khó khăn này.

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

Force PageBreak in JasperReports

 

Trong MS Access, bạn có thể dễ dàng "ép" report nhảy qua trang mới. Nhưng việc này không dễ dàng tí nào trong JasperReports. Bài viết này hướng dẫn bạn vượt qua hạn chế này của Jasper

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

How to use Crosstab

 

Crosstab hay còn gọi là PivotTable trong Excel và Access. Làm thế nào để tạo Crosstab trong JasperReports. Bài viết này sẽ hướng dẫn chi tiết cách tạo Crosstab.

Type the rest of your post here.

Read more : 0 responses : Saturday, December 02, 2006

How to use ChartCustomizer

 

JasperReports use jFreeChart as a charting library. However many features in jFreeChart are not yet supported in JasperReports. With the help of customizer, you can fully control jFreeChart.

I took me quite a while to learn how to customize the jFreeChart in JasperReports.For example, we cannot change the size of bars in BarChart, or we cannot add a complicated dynamic subtitle.
Like scriptlets, Customizer is automatically invoked during the filling process.
Your own customizer class must implement net.sf.jasperreports.engine.JRChartCustomizer interface. This interface has only one method customize(org.jfree.chart.JFreeChart chart, JRChart jasperChart).

Here is one example:
















ConsumptionChartCustomizer.java




package com.db.report.jasper.custom.consumptionreport.style;



import java.awt.BasicStroke;

import java.awt.Color;

import java.awt.Font;

import java.sql.Connection;

import java.sql.SQLException;



import net.sf.jasperreports.engine.JRAbstractChartCustomizer;

import net.sf.jasperreports.engine.JRChart;



import org.apache.log4j.Logger;

import org.jfree.chart.JFreeChart;

import org.jfree.chart.axis.CategoryAxis;

import org.jfree.chart.axis.NumberAxis;

import org.jfree.chart.block.BlockBorder;

import org.jfree.chart.plot.CategoryPlot;

import org.jfree.chart.renderer.category.StackedBarRenderer;

import org.jfree.chart.title.LegendTitle;

import org.jfree.chart.title.TextTitle;

import org.jfree.chart.title.Title;

import org.jfree.data.category.DefaultCategoryDataset;

import org.jfree.ui.HorizontalAlignment;

import org.jfree.ui.RectangleEdge;

import org.jfree.ui.RectangleInsets;



import com.db.misportal.util.ConnectionPool;

import com.db.report.jasper.custom.consumptionreport.dataset.ODBCDataset;



public class ConsumptionChartCustomizer extends JRAbstractChartCustomizer {

 /** Default Constanst */

 protected static final java.awt.Font HEADER_FONT = new java.awt.Font(

   "Arial", Font.BOLD + Font.ITALIC, 14);



 protected static final Color HEADER_COLOR = new Color(0x000x000xDE);



 protected static final float BAR_WIDTH = 0.03f;



 protected static final java.awt.Font TITLE_FONT = new java.awt.Font(

   "Arial", Font.BOLD, 8);



 protected static final Color TITLE_COLOR = new Color(0x000x00128);



 protected static final java.awt.Font TICKET_FONT = new java.awt.Font(

   "Arial", Font.PLAIN, 8);



 protected static final java.awt.Font LEGEND_FONT = new java.awt.Font(

   "Arial", Font.PLAIN, 8);



 protected static final java.awt.Color GRID_COLOR = new java.awt.Color(00,

   0);



 private final static String[] DEFAULT_MONTH_ABBREVIATION = "J""F""M",

   "A"" M"" J"" J "" A"" S""O""N""D" };



 protected static final String DEFAULT_TITLE = "Default Title";



 protected static final String DEFAULT_SUB_TITLE = null;



 protected static final String DEFAULT_DOMAIN_AXIS_LABEL = null;



 protected static final String DEFAULT_RANGE_AXIS_LABEL = null;



 protected static final RectangleEdge DEFAULT_LEGEND_POSITION = RectangleEdge.TOP;



 protected JFreeChart chart;



 private Title subTitle = null;



 private boolean hasSubTitle = false;



 private boolean hasLegend = false;



 protected static final RectangleEdge DEFAULT_SUBTITLE_POSITION = RectangleEdge.BOTTOM;



 protected static final HorizontalAlignment DEFAULT_SUBTITLE_HORIZONTAL_ALIGNMENT = HorizontalAlignment.LEFT;



 public void customize(JFreeChart chart, JRChart jrChart) {



  _log.info("customize jFreeChart ...");

  this.chart = chart;



  /* Set Chart Title */

  try {

   TextTitle title = chart.getTitle();

   title.setHorizontalAlignment(HorizontalAlignment.LEFT);

  catch (NullPointerException e) {

  }



  /* Chart Properties */

  chart.setBackgroundPaint(Color.white);

  chart.setBorderPaint(new Color(192192192));

  chart.setBorderVisible(true);

  chart.setBorderStroke(new BasicStroke(0.001f));

  chart.setPadding(new RectangleInsets(1111))// top,left,bottom,right



  /* Legend Properties */

  hasLegend = true;

  try {

   LegendTitle legend = chart.getLegend();

   legend.setItemFont(LEGEND_FONT);

   legend.setBorder(BlockBorder.NONE);

   legend.setPosition(DEFAULT_LEGEND_POSITION);

  catch (NullPointerException e) {

   hasLegend = false;

  }



  /* Chart subtitle */

  try {

   int subTitleIndex = 0;

   if (hasLegend) {

    subTitleIndex = 1;

   else {

    subTitleIndex = 0;

   }



   hasSubTitle = true;

   subTitle = chart.getSubtitle(subTitleIndex);

   setSubTitlePosition(DEFAULT_SUBTITLE_POSITION);

   setSubTitleHorizontalAlignment(DEFAULT_SUBTITLE_HORIZONTAL_ALIGNMENT);

  catch (IllegalArgumentException e) {

   hasSubTitle = false;

   subTitle = null;

   _log.debug("This chart has no subtitle.");

  }



  /* Axis: Ticket Label + Line */

  try {

   CategoryPlot plot = chart.getCategoryPlot();

   NumberAxis numberAxis = (NumberAxisplot.getRangeAxis();

   numberAxis.setTickLabelFont(TICKET_FONT);

   numberAxis.setTickLabelPaint(Color.black);

   numberAxis.setAxisLinePaint(Color.black);

   numberAxis.setAxisLineStroke(new BasicStroke(0.001f));

   numberAxis.setTickMarkPaint(GRID_COLOR);

   numberAxis.setTickMarkStroke(new BasicStroke(0.001f));

   numberAxis.setTickMarkOutsideLength(3f);



   CategoryAxis categoryAxis = (CategoryAxisplot.getDomainAxis();

   categoryAxis.setTickLabelFont(TICKET_FONT);

   categoryAxis.setTickLabelPaint(Color.black);

   categoryAxis.setAxisLinePaint(Color.black);

   categoryAxis.setAxisLineStroke(new BasicStroke(0.001f));

   categoryAxis.setAxisLineVisible(false);

   categoryAxis.setTickMarkPaint(GRID_COLOR);

   categoryAxis.setTickMarkStroke(new BasicStroke(0.001f));

   categoryAxis.setTickMarkOutsideLength(3f);

   categoryAxis.setTickMarkInsideLength(1f);



   // plot.setBackgroundPaint(Color.gray);

   /* Grid */

   // plot.setRangeGridlinesVisible(false);

   plot.setRangeGridlinePaint(GRID_COLOR);

   plot.setRangeGridlineStroke(new BasicStroke(0.001f));

   plot.clearRangeMarkers();



   /* Outline */

   plot.setOutlinePaint(null);



   /* Unit */

   numberAxis

     .setStandardTickUnits(NumberAxis.createIntegerTickUnits());



   StackedBarRenderer renderer = (StackedBarRendererplot

     .getRenderer();

   renderer.setMaximumBarWidth(BAR_WIDTH);

   renderer.setDrawBarOutline(true);

   renderer.setOutlinePaint(Color.black);

   renderer.setOutlineStroke(new BasicStroke(0.00001f));



   /* Series Color - dont need it any more - we set color in iReport */

   // renderer.setSeriesPaint(0, new Color(153, 153, 255));

   // renderer.setSeriesPaint(1, new Color(153, 51, 102));

   // renderer.setSeriesPaint(2, new Color(255, 255, 204));

   // renderer.setSeriesPaint(3, new Color(204, 255, 255));

  catch (NullPointerException e) {

  }

 }



 /**

  * Set query with default month abbreviation

  

  @param query

  */

 protected void setQuery(String query) {



  this.setQuery(query, DEFAULT_MONTH_ABBREVIATION);

 }



 /**

  * Set Chart Query : execute query and set plot dataset

  

  @param query

  @param month_abbreviation

  */

 protected void setQuery(String query, String[] month_abbreviation) {

  Connection conn = null;

  DefaultCategoryDataset dataset = null;



  try {

   conn = ConnectionPool.getConnection();

   dataset = new ODBCDataset(conn, query);

  catch (SQLException e) {

   e.printStackTrace();

  catch (com.db.report.jasper.custom.common.dataset.NullDatasetException e) {

   _log.debug("Empty dataset...Hide chart");

   this.hide();



  catch (ClassNotFoundException e) {

   e.printStackTrace();

  }



  CategoryPlot plot = chart.getCategoryPlot();

  plot.setDataset(dataset);

 }



 /**

  * Set chart title

  

  @param strTitle

  */

 public void setTitle(String strTitle) {

  TextTitle title = chart.getTitle();



  if (title == null) {

   chart.setTitle(strTitle);

  else {

   title.setText(strTitle);

  }

 }



 /**

  * Set Legend Position

  

  @param position

  */

 public void setLegendPosition(RectangleEdge position) {

  try {

   LegendTitle legend = chart.getLegend();

   legend.setPosition(position);

  catch (NullPointerException e) {

  }

 }



 /**

  * Make chart invisible

  

  */

 public void hide() {

  CategoryPlot plot = chart.getCategoryPlot();

  NumberAxis numberAxis = (NumberAxisplot.getRangeAxis();

  numberAxis.setVisible(false);

  CategoryAxis categoryAxis = (CategoryAxisplot.getDomainAxis();

  categoryAxis.setVisible(false);

  plot.setRangeGridlinesVisible(false);

 }



 /**

  * Set Subtitle Position

  

  @param position

  */

 public void setSubTitlePosition(RectangleEdge position) {

  if (hasSubTitle && subTitle != null) {

   subTitle.setPosition(position);

  }

 }



 /**

  * Set Subtitle text

  

  @param text

  */

 public void setSubTitleText(String text) {

  if (hasSubTitle && subTitle != null) {

   ((TextTitlesubTitle).setText(text);

  }

 }



 /**

  * Set Subtitle Horizontal Aligment (LEFT,RIGHT,CENTER)

  

  @param align

  */

 public void setSubTitleHorizontalAlignment(HorizontalAlignment align) {



  if (hasSubTitle && subTitle != null) {

   subTitle.setHorizontalAlignment(align);

  }

 }



 /** Logger */

 static Logger _log = Logger.getLogger(ConsumptionChartCustomizer.class);

}






That's so far for today!

Read more : 15 responses : Saturday, December 02, 2006

How to use Scriptlets

 

Scriptlets allow you to execute snippets of Java code (or other language) during the filling process.

Your custom Scriptlet class must extend either JRDefaultScriptlet or JRAbstractScriptlet, and override these methods:
beforeReportInit,
afterReportInit,
beforePageInit
...

The name of the methods also show when the methods will be invoked.
Here is an example.
















TrackingReportScriptlet.java




package com.db.report.jasper.custom.trackingreport;



import java.sql.Connection;

import java.sql.SQLException;



import net.sf.jasperreports.engine.JRDefaultScriptlet;

import org.apache.log4j.Logger;



import com.db.misportal.util.ConnectionPool;

import com.db.report.jasper.util.DateTool;



import net.sf.jasperreports.engine.JRScriptletException;



public class TrackingReportScriptlet extends JRDefaultScriptlet {

 public void beforeReportInit() throws JRScriptletException

 {

  //Invoked before the report is initilized

 }

 

 public void afterReportInit() throws JRScriptletException

 {

  //Invoked after the report is initilized 

 }



 public void beforePageInit() throws JRScriptletException

 {

  // Invoked before each page is initilized

 }



 public void afterPageInit() throws JRScriptletException

 {

  // Invoked after each page is initilized 

 }



 public void beforeColumnInit() throws JRScriptletException

 {

  // Invoked before each column is initilized

 }

 public void afterColumnInit() throws JRScriptletException

 {

  // Invoked after each column is initilized

 }



 public void beforeGroupInit(String groupNamethrows JRScriptletException

 {

  // Invoked before the group named "groupName" is initilized

  if ("group1".equals(groupName))

  {

   // do something

  }

  else if ("group2".equals(groupName))

  {

   // do something else

  }

 }



 public void afterGroupInit(String groupNamethrows JRScriptletException

 {

  // Invoked after the group named "groupName" is initilized

  /*

  if ("group1".equals(groupName))

  {

   // do something

  }

  else if ("group2".equals(groupName))

  {

   // do something else

  }

  */

 }

 

 public void beforeDetailEval() throws JRScriptletException

 {

  // Invoked before the detail section is evaluated.

 }



 public void afterDetailEval() throws JRScriptletException

 {

  // Invoked after the detail section is evaluated.  

  String strReportDate = (String)getParameterValue("PARAM_REPORT_DATE");

  String strReportDateLastYear = DateTool.getLastYear(strReportDate);

  _log.debug("last year = "+(String)getVariableValue("VAR_REPORT_DATE_LAST_YEAR"));

  setVariableValue("VAR_REPORT_DATE_LAST_YEAR",strReportDateLastYear);

  _log.debug("last year = "+strReportDateLastYear);

 }



 private static Logger _log = Logger.getLogger(TrackingReportScriptlet.class);

}









After writting the scriptlet class, you need to set the scriptletClass atribute to the fully qualified name of the scriptlet class. For example,

< jasperReport
name="tracking_part5"
columnCount="1"
printOrder="Vertical"
orientation="Landscape"
pageWidth="782"
pageHeight="595"
columnWidth="782"
columnSpacing="0"
leftMargin="0"
rightMargin="0"
topMargin="0"
bottomMargin="0"
whenNoDataType="NoPages"
scriptletClass="com.db.report.jasper.custom.trackingreport.TrackingReportScriptlet" isTitleNewPage="false"
isSummaryNewPage="false">


That's all !

Read more : 1 responses : Saturday, December 02, 2006