Saturday, July 28, 2012

ANSI C: N Queens Problem


Introduction

The n queens puzzle is the problem of placing n chess queens on an n*n chessboard so that no two queens attack each other.

The Program

queens_ori.c


#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

/**
 * The n queens puzzle is the problem of
 * placing n chess queens on an n×n chessboard
 * so that no two queens attack each other. 
 */

// define some values to shorten the code

// status message and params
// printf(statmsg, statparam); will become
// printf("there are %d queens in a %d x %d chessboard\n\n answer(s): \n\n", number_of_queens, number_of_queens, number_of_queens);
#define statmsg "there are %d queens in a %d x %d chessboard\n\n answer(s): \n\n"
#define statparam number_of_queens, number_of_queens, number_of_queens
// answer message and params
#define ansmsg "put col[%d] queen to row %d\n"
#define ansparam outIdx, col[outIdx]
int number_of_queens;  // n queens in nxn chessboard
                       // note: don't too large (ex, over 25)
int *col;  // chessboard
FILE *outptr; // output file
int cnt; // result count

// function to solve this problem
void queens( int currentCol );
// function to determing whether the status is valid
bool promising( int currentCol );

int main()
{

  cnt = 0;
  
  printf("please enter the number of queens:\n");
  scanf("%d", &number_of_queens);
  // col[0] not used, start from col[1]
  col = (int*) malloc ((number_of_queens+1)*sizeof(int));
  outptr = fopen("QueenSol.txt", "w" );

  printf(statmsg, statparam);
  fprintf(outptr, statmsg, statparam);
  // call function to solve problem
  // pass 0 into it but it will then start from 1
  queens( 0 );

  if (cnt == 0) {
     printf(" no result\n\n");
     fprintf(outptr, " no result\n\n");
  }
  fclose( outptr );
  free(col);

  system("PAUSE");
  return 0;
}

void queens( int currentCol )
{
 int row;    // row index to test
 int outIdx; // index for output result
 if( promising(currentCol) )  // if valid
 {
   if( currentCol == number_of_queens )  // output if at latest col
   {
     cnt++;
     for( outIdx = 1; outIdx <= number_of_queens; outIdx++ )
     {
       printf(ansmsg, ansparam);
       fprintf(outptr, ansmsg, ansparam);
     }
     printf("\n\n");
     fprintf(outptr, "\n\n");
   }

   // call function recursively if not latest col
   else
   {
     for(row = 1; row <= number_of_queens; row++ )  // test next col from row 1 to row n
     {
       col[currentCol + 1] = row;
       queens( currentCol + 1 );
     }
   }

 }
}

// check whether current stats is valid
bool promising( int currentCol )
{
  int idx = 1; // loop index
  bool isValid = true; // is valid? default to true


  // test whether previous queens will attack current queen
  while( (idx < currentCol) && isValid )
  {
    // found invalid status
    // in the same row
    // or diagonal
    if( (col[currentCol] == col[idx])
        || (abs( col[currentCol] - col[idx]) == currentCol - idx) )
    {
        isValid = false;  // set to invalid, stop loop
    }
    idx++;  // increase index and contiune

  }
  return isValid;
}



The Result




Reference
http://en.wikipedia.org/wiki/Eight_queens_puzzle


Download
The files are at github
https://github.com/benbai123/C_Cplusplus_Practice/tree/master/C_Algorithm/N_Quenes

Thursday, July 26, 2012

ZK: Mask page manually



Introduction

Sometimes we may need to load something for a while and the page may seems ugly before it loaded (e.g., we are loading something for fancy style). We can mask the page to hide the ugly page if needed.

The Page

mask_page_manually.zul

<zk>
    <script type="text/javascript"><![CDATA[
        // mask a while
        zAu.cmd0.showBusy('Loading...');
        // fake loading some data
        setTimeout(function () {
            some = {};
            some.loaded = {};
            some.loaded.data = 'data';
        }, 2000);
        // clear mask after data loaded
        var timer = setInterval(function () {
            // log, can be removed
            zk.log(' checking... ');
            if (some && some.loaded && some.loaded.data) {
                zAu.cmd0.clearBusy(null);
                clearInterval(timer);
            }
        }, 500);
    ]]></script>
    <window border="normal" title="test win">
        test
    </window>
</zk>



The Result
View the demo flash on line
http://screencast.com/t/jNxbQoyffH0

You can find the flash file at github:
https://github.com/benbai123/ZK_Practice/blob/c0fe761cdcb5487d113b01c153676bf5cbf74bac/Components/demos/mask_page_manually.swf?raw=true

Download
The file is at github
https://github.com/benbai123/ZK_Practice/blob/c0fe761cdcb5487d113b01c153676bf5cbf74bac/Components/projects/Components_Practice/WebContent/mask_page_manually.zul

Reading and writing text files in Java

Introduction

Just simple log

Program fragment

Read file

where the "data.txt" is under project root

            File srcFile = new File( "data.txt" );
            BufferedReader br =
                new BufferedReader(new InputStreamReader(
                    new FileInputStream(srcFile), "UTF-8"));
            while( br.ready() ) {
                System.out.println(br.readLine());
            }
            br.close();


Write text to file

            File destFile = new File("dest.txt");
            if (!destFile.exists()) {
                destFile.createNewFile();
            }
            BufferedWriter bw =
                new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(destFile), "UTF-8"));
            bw.write("test\n");
            bw.write("test test");
            bw.flush();
            bw.close();

Reference
http://docs.oracle.com/javase/tutorial/i18n/text/stream.html

Sunday, July 22, 2012

ANSI C: Hamiltonian cycle


Introduction

A Hamiltonian path (or traceable path) is a path in an undirected graph that visits each vertex exactly once. A Hamiltonian cycle (or Hamiltonian circuit) is a Hamiltonian path that is a cycle.

In this post, we will implement an ANSI C program that will display all Hamiltonian cycle of a given graph array.

The Program

hamiltonian.c

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

/**
 * A Hamiltonian path (or traceable path)
 * is a path in an undirected graph that
 * visits each vertex exactly once.
 * A Hamiltonian cycle (or Hamiltonian circuit) is
 * a Hamiltonian path that is a cycle.
 * From wiki: http://en.wikipedia.org/wiki/Hamiltonian_path
 */

// the number of nodes in the test graphic
int node_amount = 12;
// the number of Hamiltonian cycles
int hamiltonian_cycle_amount = 0;

// Graph Representation with nodes
// not use [0][x] and [x][0]
int graph_array[13][13] = {
                 0,0,0,0,0,0,0,0,0,0,0,0,0,
                 0,0,1,0,0,1,0,0,0,0,0,0,0,
                 0,1,0,1,0,0,0,1,1,0,0,0,0,
                 0,0,1,0,1,0,0,0,1,0,0,0,0,
                 0,0,0,1,0,1,0,0,0,1,0,0,0,
                 0,1,0,0,0,0,1,0,0,0,1,0,0,
                 0,0,0,0,0,1,0,1,0,0,0,1,0,
                 0,0,1,0,0,0,1,0,1,0,0,0,0,
                 0,0,1,1,0,0,0,1,0,1,0,0,0,
                 0,0,0,0,1,0,0,0,1,0,1,0,1,
                 0,0,0,0,0,1,0,0,0,0,0,1,0,
                 0,0,0,0,0,0,1,0,0,0,1,0,1,
                 0,1,0,0,0,0,0,0,0,1,0,1,0
                };

// the array store the result through recursive
int result_array[13] = {0};

/**
 * The function that find all Hamiltonian cycle
 * and output to console
 * param node_index: int, nth node in path
 */
void hamiltonian ( int node_index );
/**
 * The function that check whether current node
 * is valid in Hamiltonian cycle.
 */
bool promising ( int node_index );

int main()
{
  printf("\n");
  result_array[1] = 1;  // start from first node
  hamiltonian( 1 );  // start to find all Hamiltonian cycles recursively

  if( hamiltonian_cycle_amount == 0 )  // no Hamiltonian cycles found
   printf("\nThere is no Hamiltonian cycles in this graph\n\n");

  printf("\n");

  system("PAUSE");
  return 0;
}

void hamiltonian ( int node_index )
{
  int j, k;
  if( promising(node_index) )  // current node is valid in a Hamiltonian cycles
  {
     if( node_index == (node_amount) )  // at latest node
     {  hamiltonian_cycle_amount = 1;  // increase hamiltonian_cycle_amount
        printf(" v%d", result_array[1]);  // print out the result
        for( k = 2;k <= node_amount;k ++ )
          printf(" → v%d", result_array[k]);
        printf("\n\n");
     }
     else  // not latest node
     {
        for( j = 2;j <= node_amount;j ++ )  // recursively scan from second node to latest node
        {
           result_array[node_index+1] = j; // store result
           hamiltonian( node_index+1 );  // call function recursively
        }
     }
  }

}

bool promising ( int node_index )
{
  int j, k;
  bool isValid = true; // default to valid

  j = 1;

  if (node_index == 1) // first node, valid
    isValid =  true;
  if( (node_index == (node_amount)) && (!graph_array[result_array[node_index]][result_array[1]]) )
     isValid =  false;  // latest node but not connected to first node, invalid

  else if( ( node_index > 1 ) && (!graph_array[result_array[node_index-1]][result_array[node_index]]) )
     isValid =  false;  // not connected between current node and previous node, invalid

  else
  {
    while( (j < node_index) && isValid)  // check all previous nodes
    {
       if( result_array[node_index] == result_array [j] )
         isValid = false;  // current point already in path, invalid
       j++;
    }
  }
  return isValid;  // return whether current node is valid
}

The Result


Reference
http://en.wikipedia.org/wiki/Hamiltonian_path

Download
The files at github
https://github.com/benbai123/C_Cplusplus_Practice/tree/master/C_Algorithm/Hamiltonian_Cycle

Sunday, July 15, 2012

ANSI C: Coloring Problem


Introduction

The Coloring Problem is coloring the vertices of a graph such that no two adjacent vertices share the same color, in this post, we will try to implement it in ANSI C.

The Program

Coloring_Problem.c

#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>

/**
 * Graph coloring
 * Coloring the vertices of a graph such that
 * no two adjacent vertices share the same color;
 * Wiki: http://en.wikipedia.org/wiki/Graph_coloring
 */

FILE *res;

// The 2-D array denotes a graph adjacent status,
// For example, [a, b] are adjacent vertices,
// [a, d] are also adjacent vertices.
// the index starts from 1,
// [0][x] and [x][0] are not considered
                           // #,a,b,c,d,e,f
int _adjacentMatrix [7][7] = {0,0,0,0,0,0,0, // not use
               0,0,1,0,1,0,0, // a
               0,1,0,1,0,1,0, // b
               0,0,1,0,0,0,1, // c
               0,1,0,0,0,1,0, // d 
               0,0,1,0,1,0,1, // e
               0,0,0,1,0,1,0  // f
              };

int _verticeAmount = 6;  // six points

// three colors
char _colors[4][20] = { "NULL",
                      "RED",
                      "GREEN",
                      "WHITE"
                      };

// temp store the result
int _result[7] = {0};

void m_coloring( int i );
bool promising( int i );

int main()
{
  res = fopen("result.txt", "w");
  printf("there are %d vertices, the color combinations are as below: \n\n", _verticeAmount);
  fprintf(res, "there are %d vertices, the color combinations are as below: \n\n", _verticeAmount);
  m_coloring( 0 );
  fclose( res );



      system("PAUSE");
      return 0;
}

/**
 * The coloring function that will recursively create different combinations
 * then call promising function to test whether the combination is valid.
 *
 * Three actions:
 *         Output the result if
 *         the current combination is valid and reach the latest vertice.
 *         
 *         Continue add color - vertice to extend combination if
 *         the current combination is valid but not reach the latest vertice.
 *
 *         Terminate the recursive to skip any combination
 *         that starts with the current combination if
 *         the current combination is not valid.
 *
 */
void m_coloring ( int i )
{
  int color; // color index for test
  int j; // index for output result

  if( promising(i) )  // coloring success
  {
    if( i == _verticeAmount ) // is latest vertice
    {
      for( j = 1;j <= _verticeAmount;j ++ )  // output the result
      {
        if (j > 1) {
          printf(",");
          fprintf(res, ", ");
        }
        printf("%s", _colors[_result[j]]);
        fprintf(res, "%s", _colors[_result[j]]);
      }
      printf("\n\n");
      fprintf(res, "\n\n");

    }

    else  // not latest vertice
    {
       for( color = 1;color <= 3;color ++ )  // test each color
       {
          _result[i+1] = color;  // set color to next vertice
          m_coloring(i+1); // recursive to continue combination
       }
    }
  }
}

/**
 * check whether has adjacent virtice share color with current virtice
 * return bool
 * true: no adjacent vertice share the same color, can 
 * continue this combination
 * false: has adjacent vertice share the same color,
 * this combination should be terminated
 */
bool promising( int currentVerticeIndex )
{
   int j = 1; // start from first virtice
   bool sw = true;  // default to success (no two adjacent vertices share the same color)

   while( (j < currentVerticeIndex) && sw)  // scan all previous vertices
   {
      // if found an adjacent vertice share the same color
      if( _adjacentMatrix[currentVerticeIndex][j] && (_result[currentVerticeIndex] == _result[j])) {
        sw = false;  // set to fail (has two adjacent vertices share the same color)
        break;
      }
      j++;
   }

   return sw;  // return the result (success or fail)
}


The promising function will return whether current combination is valid.

The m_coloring function that will recursively create different combinations then call promising function to test whether the combination is valid. It will do three different actions with respect to the different status and the result of promising function:

1. Output the result if the current combination is valid and reach the latest vertice.

2. Continue add color - vertice to extend combination if the current combination is valid but not reach the latest vertice.

3. Terminate the recursive to skip any combination that starts with the current combination if the current combination is not valid.

The Result



Reference
Wiki
http://en.wikipedia.org/wiki/Graph_coloring


Download
Files at github
https://github.com/benbai123/C_Cplusplus_Practice/tree/master/C_Algorithm/Coloring_Problem

Saturday, July 14, 2012

ZK: Modify tooltiop on gantt chart


Introduction

Chart is a component of ZK which can draw various kind of chart, in gantt chart it will show some default tooltip on the 'bar'.

This post is about how to change the tooltip of bars to customized value.

The ZUL Page

modify_tooltip_on_gantt_chart.zul

<zk xmlns:w="client">
    <!-- prepare model -->
    <zscript><![CDATA[
        import org.zkoss.zul.GanttModel.GanttTask;
             
            public Date date(int year, int month, int day) {
                final java.util.Calendar calendar = java.util.Calendar.getInstance();
                calendar.set(year, month-1, day);
                final Date result = calendar.getTime();
                return result;
            }
            //series, task (task description, start, end, complete percentage)
            GanttModel ganttmodel = new GanttModel();
            ganttmodel.addValue("Scheduled", new GanttTask("Write Proposal", date(2008,4,1), date(2008,4,5), 0.1));
            ganttmodel.addValue("Scheduled", new GanttTask("Obtain Approval", date(2008,4,9), date(2008,4,9), 0.0));
            ganttmodel.addValue("Scheduled", new GanttTask("Requirements Analysis", date(2008,4,10), date(2008,5,5), 0.0));
            ganttmodel.addValue("Scheduled", new GanttTask("Design Phase", date(2008,5,6), date(2008,5,30), 0.0));
            ganttmodel.addValue("Scheduled", new GanttTask("Design Signoff", date(2008,6,2), date(2008,6,2), 0.0));
            ganttmodel.addValue("Scheduled", new GanttTask("Alpha Implementation", date(2008,6,3), date(2008,7,31), 0.2));
            ganttmodel.addValue("Scheduled", new GanttTask("Design Review", date(2008,8,1), date(2008,8,8), 0.0));
            ganttmodel.addValue("Scheduled", new GanttTask("Revised Design Signoff", date(2008,8,10), date(2008,8,10), 0.0));
            ganttmodel.addValue("Scheduled", new GanttTask("Beta Implementation", date(2008,8,12), date(2008,9,12), 0.0));
            ganttmodel.addValue("Scheduled", new GanttTask("Testing", date(2008,9,13), date(2008,10,31), 0.0));
            ganttmodel.addValue("Scheduled", new GanttTask("Final Implementation", date(2008,11,1), date(2008,11,15), 0.0));
            ganttmodel.addValue("Scheduled", new GanttTask("Signoff", date(2008,11,28), date(2008,11,30), 0.0));
             
            ganttmodel.addValue("Actual", new GanttTask("Write Proposal", date(2008,4,1), date(2008,4,3), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Obtain Approval", date(2008,4,9), date(2008,4,9), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Requirements Analysis", date(2008,4,10), date(2008,5,15), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Design Phase", date(2008,5,15), date(2008,6,17), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Design Signoff", date(2008,6,30), date(2008,6,30), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Alpha Implementation", date(2008,7,1), date(2008,9,12), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Design Review", date(2008,9,12), date(2008,9,22), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Revised Design Signoff", date(2008,9,25), date(2008,9,27), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Beta Implementation", date(2008,8,12), date(2008,9,12), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Testing", date(2008,10,31), date(2008,11,17), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Final Implementation", date(2008,11,18), date(2008,12,5), 0.0));
            ganttmodel.addValue("Actual", new GanttTask("Signoff", date(2008,12,10), date(2008,12,11), 0.0));
    ]]></zscript>
    <chart id="gantt" title="Gantt Chart" width="700" height="400"
        model="${ganttmodel}"
        type="gantt" threeD="false" fgAlpha="128" dateFormat="yyyy/MM/dd" >
        <!-- override bind_ function to update the tooltip -->
        <attribute w:name="bind_"><![CDATA[
            function () {
                this.$bind_();
                var areas = jq('area'),
                    len = areas.length;
                if (len > 0) {
                    for (var i = 0; i < len; i++) {
                        var area = areas[i],
                            title;
                        // original format is 'percentage, start ~ end'
                        // extract the 'start' and 'end' to create new tooltip
                        if ((title = area.title) && title.indexOf(',') >= 0 && title.indexOf('~') >= 0) {
                            var range = title.split(',')[1].split('~'),
                                start = range[0].replace(/^\s\s*/, '').replace(/\s\s*$/, ''),
                                end = range[1].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    
                            // update title
                            area.title = 'Range: From ' + start + ' to ' + end;
                        }
                    }
                }
            }
    ]]></attribute>
    </chart>
</zk>


The Result



Reference
http://books.zkoss.org/wiki/ZK_Component_Reference/Diagrams_and_Reports/Chart


Download
https://github.com/benbai123/ZK_Practice/blob/master/Components/projects/Components_Practice/WebContent/modify_tooltip_on_gantt_chart.zul

Friday, July 13, 2012

ZK Pivottable: Display Data in ZK Pivottable


Introduction

ZK Pivottable is a ZK addon component that can display data in summarized view as MS-Excel's pivottable. In this post, we will try to display some fake data in pivottable.

The Composer

TestComposer.java

package test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

import org.zkoss.pivot.PivotField;

import org.zkoss.pivot.impl.TabularPivotModel;

import org.zkoss.zk.ui.select.SelectorComposer;

/**
 * Tested with ZK 6.0.1 CE and ZK Pivottable 2.0.0
 *
 */
@SuppressWarnings("rawtypes")
public class TestComposer extends SelectorComposer {
    /**
     * generated serial version UID
     */
    private static final long serialVersionUID = -2897873399288955635L;
    private TabularPivotModel _pivotModel;

    /**
     * Get pivottable's model
     * @return TabularPivotModel the pivottable's model
     * @throws Exception
     */
    public TabularPivotModel getPivotModel () throws Exception {
        if (_pivotModel == null) {
            _pivotModel = new TabularPivotModel(getData(), getColumns());

            // assign rows, the order matches to the level of row node field
            _pivotModel.setFieldType("Row_Level_001", PivotField.Type.ROW);
            _pivotModel.setFieldType("Row_Level_002", PivotField.Type.ROW);
            _pivotModel.setFieldType("Row_Level_003", PivotField.Type.ROW);
            _pivotModel.setFieldType("Row_Level_004", PivotField.Type.ROW);

            // assign columns, the order matches to the level of column node field
            _pivotModel.setFieldType("Column_Level_001", PivotField.Type.COLUMN);
            _pivotModel.setFieldType("Column_Level_002", PivotField.Type.COLUMN);

            // assign datas, the order matches to the order of data field
            _pivotModel.setFieldType("Data_Field_001", PivotField.Type.DATA);
            _pivotModel.setFieldType("Data_Field_002", PivotField.Type.DATA);
            _pivotModel.setFieldType("Data_Field_003", PivotField.Type.DATA);
        }
        return _pivotModel;
    }
    /**
     * prepare the data for pivottable's model
     * The order of object put into data list matches
     * the order of column name's order
     * @return
     * @throws Exception
     */
    public List<List<Object>> getData() throws Exception {
        List<List<Object>> result = new ArrayList<List<Object>>();
        Random r = new Random();

        for (int i = 0; i < 10000; i++) {
            List<Object> data = new ArrayList<Object>();
            data.add("Row_Level_001 - " + (r.nextInt(10) + 1));
            data.add("Row_Level_002 - " + (r.nextInt(10) + 1));
            data.add("Row_Level_003 - " + (r.nextInt(10) + 1));
            data.add("Row_Level_004 - " + (r.nextInt(10) + 1));
            data.add("Column_Level_001 - " + (r.nextInt(10) + 1));
            data.add("Column_Level_002 - " + (r.nextInt(10) + 1));
            data.add(r.nextInt(10000));
            data.add(r.nextDouble() * 10000.0);
            data.add(r.nextInt(100));
            result.add(data);
        }
        return result;
    }
    /**
     * prepare columns name for pivottable's model
     * @return
     */
    public List<String> getColumns() {
        return Arrays.asList(new String[]{
                "Row_Level_001", "Row_Level_002", "Row_Level_003", "Row_Level_004",
                "Column_Level_001", "Column_Level_002",
                "Data_Field_001", "Data_Field_002", "Data_Field_003"
        });
    }
    
    
}


Here only simply add some fake data / columns to pivot model.
The order of data will matches to the order of column automatically.

The ZUL Page

index.zul

<zk>
    <!-- Tested with ZK 6.0.1 CE and ZK Pivottable 2.0.0 -->
    <!-- window, apply a SelectorComposer -->
    <window id="win" xmlns:w="client"
        apply="test.TestComposer">
        <!-- pivottable, get model from window's composer -->
        <pivottable id="pivottable" model="${win$composer.pivotModel}" />
    </window>
</zk>


The Result
View the demo flash on line
http://screencast.com/t/C2CxL4aeNHmZ

You can find the flash file at github:
https://github.com/benbai123/ZK_Practice/blob/master/Components/demos/addon/DisplayDataInPivottable.swf

Reference
http://books.zkoss.org/wiki/ZK_Pivottable_Essentials

Download
The full project is at github
https://github.com/benbai123/ZK_Practice/tree/master/Components/projects/Addon_Practice/PivottableTest/DisplayDataInPivottable

Sunday, July 8, 2012

JSP Custom Tag: Nested Tags


Introduction

In the previous post (http://ben-bai.blogspot.tw/2012/07/jsp-custom-tag-body-tag.html), we have implemented a body tag 'fadeoutBlock', in this post we will try the advanced body tag - 'Nested Tags' and implement a tag set 'tabbox, tabpanel'.

Pre-define

1. A tabbox can contain several tabpanels.
2. You can set the 'width' and 'height' of a tabbox.
3. A tabpanel can contains any JSP body content.
4. You can set the 'header' of a tabpanel.

The Program

Tabbox.java

package test.tag.custom;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.TagSupport;

/**
 * Simple tabbox with poor look and feel 
 */
public class Tabbox extends BodyTagSupport {
    private int _panelCnt = 0;
    private int _width = 100;
    private int _height = 100;
    private StringBuilder _headerContent = new StringBuilder("");
    private StringBuilder _bodyContent = new StringBuilder("");

    public void setWidth (int width) {
        _width = width;
    }
    public void setHeight (int height) {
        _height = height;
    }
    public int doStartTag() throws JspException {
        try {
            // output the out most area
            JspWriter out = pageContext.getOut();
            out.print("<div");
            out.print(" style=\"overflow: auto; width: "+_width+"px; height: "+_height+"px; border: 1px solid #CCCCCC;\"");
            out.print(">");
        } catch (Exception e) {
            throw new JspException("Error: IOException while writing to client");
        }

        // evaluate body content and output it directly
        return EVAL_BODY_INCLUDE;
    }

    public int doEndTag() throws JspException {
        try {
            JspWriter out = pageContext.getOut();
            // output the header/body of tabpanels
            out.print("<div>"+_headerContent.toString()+"</div>");
            out.print("<div style=\"margin: 10px; border: 1px solid #3648AE;\">"+_bodyContent.toString()+"</div></div>");
        } catch (Exception ex) {
            throw new JspException(ex.getMessage());
        }
        release();
        // continue evaluate page
        return EVAL_PAGE;
        
    }

    public void release() {
        // have to reset all field values since container may reuse this instance
        _panelCnt = 0;
        _width = 100;
        _height = 100;
        _headerContent.setLength(0);
        _bodyContent.setLength(0);
    }

    public void addHeaderContent (String content) {
        // called by Tabpanel, add header content
        String style = _panelCnt == 0? "background-color: gray;" : "background-color: transparent;";
        style += " margin-right: 5px; border: 1px solid #CCCCCC; border-bottom: 0px; cursor: pointer;";
        _headerContent.append("<span onclick=\""+showMatchedPanel()+"\" style=\""+style+"\">")
            .append(content)
            .append("</span>");
    }
    public void addBodyContent (String content) {
        // called by Tabpanel, add body contents
        String style = _panelCnt == 0? "" : "display: none;";
        _bodyContent.append("<div style=\""+style+"\">")
            .append(content)
            .append("</div>");
    }

    public void increaseCnt () {
        // called by Tabpanel, tell Tabbox the number of tabpanel is increased
        _panelCnt++;
    }

    // its better provided by a .js file
    private String showMatchedPanel () {
        // the javascript that executes while tabpanel's header clicked
        StringBuilder cmd = new StringBuilder();

        cmd.append("var headerContainer = this.parentNode,")
            .append("    headerArray = headerContainer.childNodes,")
            .append("    bodyContainer = headerContainer.nextSibling,")
            .append("    bodyArray = bodyContainer.childNodes,")
            .append("    ele, i, idx;")
            .append("for (i = 0; i < headerArray.length; i++) {")
            .append("    if ((ele = headerArray[i]) == this) {")
            .append("        ele.style.backgroundColor = 'gray';")
            .append("        idx = i;")
            .append("    } else")
            .append("        ele.style.backgroundColor = 'transparent';")
            .append("}")
            .append("for (i = 0; i < bodyArray.length; i++) {")
            .append("    if (i == idx)")
            .append("        bodyArray[i].style.display = 'block';")
            .append("    else")
            .append("        bodyArray[i].style.display = 'none';")
            .append("}");
        return cmd.toString();
    }
}

Tabbox provide the API's for tabpanels to add their header/body content and then output them properly with the appropriate javascript action while header clicked.

Tabpanel.java

package test.tag.custom;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.TagSupport;


/**
 * Simple tabpanel with poor look and feel
 */
public class Tabpanel extends BodyTagSupport {
    private String _header = new String("tab header");

    public void setHeader (String header) {
        _header = header;
    }

    public int doStartTag() throws JspException {
        // denotes evaluate body but do not output it, store it in buffer
        return EVAL_BODY_BUFFERED;
    }
    
    public int doEndTag() throws JspException {
        // find the parent tabbox
        Tabbox parent = (Tabbox)findAncestorWithClass(this, Tabbox.class);
        // get the buffered body content
        String body = getBodyContent().getString();
        // parent should not be null
        if(parent == null)
            throw new JspException("Tabpanel.doStartTag(): " + "No Tabbox ancestor");

        // fix empty body
        if (body == null || body.isEmpty())
            body = "&nbsp;"; // at least a space char
        // fix empty header
        if (_header == null || _header.isEmpty())
            _header = "&nbsp;"; // at least a space char
        // add header content to parent tabbox
        parent.addHeaderContent(_header);
        // add body content to parent tabbox
        parent.addBodyContent(body);
        parent.increaseCnt();
        release();

        return EVAL_PAGE;
        
    }
    
    public void release() {
        // have to reset all field values since container may reuse this instance
        _header = null;
    }
    
}

Tabpanel will store its body content in a buffer then pass it and header to Tabbox to render.

The tag definition

Add the fragment below to the tld file which already created from previous post (http://ben-bai.blogspot.tw/2012/06/jsp-custom-tag-simple-tag.html) then export jar as described in the previous post.


<tag>
    <!-- tag name -->
    <name>tabbox</name>
    <!-- tag class path -->
    <tagclass>test.tag.custom.Tabbox</tagclass>
    <!-- denotes the tag has JSP body content -->
    <bodycontent>JSP</bodycontent>
    <attribute>
        <!-- attribute name -->
        <name>width</name>
        <!-- required or not -->
        <required>false</required>
        <!-- el enable or not (true denotes can be eval at runtime) -->
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
        <!-- attribute name -->
        <name>height</name>
        <!-- required or not -->
        <required>false</required>
        <!-- el enable or not (true denotes can be eval at runtime) -->
        <rtexprvalue>false</rtexprvalue>
    </attribute>
</tag>
<tag>
    <!-- tag name -->
    <name>tabpanel</name>
    <!-- tag class path -->
    <tagclass>test.tag.custom.Tabpanel</tagclass>
    <!-- denotes the tag has JSP body content -->
    <bodycontent>JSP</bodycontent>
    <attribute>
        <!-- attribute name -->
        <name>header</name>
        <!-- required or not -->
        <required>false</required>
        <!-- el enable or not (true denotes can be eval at runtime) -->
        <rtexprvalue>false</rtexprvalue>
    </attribute>
</tag>

Test Page

tabboxTest.jsp

<%@ page isErrorPage="true" language="java"
    contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page isELIgnored ="false" %>
<!-- use the custom taglib with prefix ct -->
<%@taglib prefix="ct" uri="http://test.tag.custom/jsp/impl/taglib"%>
<html>
    <head>
        <meta http-equiv="Content-Type" 
            content="text/html; charset=UTF-8"/>
        <title>EL Math Practice</title>
    </head>
    <body>
        <!-- the tabbox -->
        <ct:tabbox width="500" height="300">
            <!-- child tabpanels -->
            <ct:tabpanel header="Tab 1">
                this is the first panel of the tabbox
                <div style="height: 150px; width: 150px; background-color: red;"></div>
            </ct:tabpanel>
            <ct:tabpanel header="Tab 2">
                <div style="height: 200px; width: 200px; background-color: green;">
                    second panel
                </div>
            </ct:tabpanel>
            <ct:tabpanel header="Tab 3">
                the third panel
            </ct:tabpanel>
        </ct:tabbox>
    </body>
</html>


The Result

View the demo flash on line
http://screencast.com/t/e5QqxXQ1O

You can find the flash file at github:
https://github.com/benbai123/JSP_Servlet_Practice/blob/master/demo_src/JSP/Custom_tag/tabbox_test.swf



Reference
http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/JSPTags.html


Download

The full project is at github
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/CustomTagPractice

Sunday, July 1, 2012

JSP Custom Tag: Body Tag


Introduction

In the previous post (http://ben-bai.blogspot.tw/2012/06/jsp-custom-tag-simple-tag.html) we implement a simple tag 'errMsg' to display some error message, in this post, we will create a body tag 'fadeoutBlock' to display content in a fade-out block.

A body tag is a JSP custom tag that has a body, the only difference between simple-tag and body-tag is body-tag will evaluate its body content but simple-tag will not.

Pre-define
The spec of the fadeoutBlock tag:
1. Can contain any JSP content in its body.
2. Attributes:
    style: the css style as normal html tag's style.
    styleClass: the css class as normal html tag's class.
    duration: the duration of fade-out action in milli seconds.
    step: the value that the opacity will be 'decreased' in each fade-out step.
3. Do fade-out while clicked.

The Program

FadeoutBlock.java

package test.tag.custom;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

/**
 * Fadeout Block JSP Custom Tag, body tag, can contain jsp content body.
 *
 */
public class FadeoutBlock extends TagSupport {

    private static final long serialVersionUID = 3563006227719937104L;
    private String _style = "background-color: CCBBEE;";
    private String _styleClass = null; //--css class
    private Integer _duration = 1000;
    private float _step = 0.1f;

    /**
     * tag attribute setter
     * @param style The style of this element
     */
    public void setStyle(String style){
        if (style != null && !style.isEmpty())
            _style = style;
    }
    /**
     * tag attribute setter
     * @param styleClass The css class of this element
     */
    public void setStyleClass(String styleClass){
        _styleClass = styleClass;
    }
    /**
     * tag attribute setter
     * @param duration The duration of fade-out action
     */
    public void setDuration (Integer duration) {
        if (duration > 0)
            _duration = duration;
    }
    /**
     * tag attribute setter
     * @param step The step value of each fade-out step
     */
    public void setStep (float step) {
        if (step > 0)
            _step = step;
    }
    /**
     * do start tag
     */
    @Override
    public int doStartTag() throws JspException {
        try {
            JspWriter out = pageContext.getOut();
            // output div's start tag, style, class and fadeOut function
            out.print("<div style=\""+_style+"\"");
            if (_styleClass != null)
                out.print(" class=\""+_styleClass+"\"");
            out.print("onclick=\""+fadeOut()+"\">");
        } catch (Exception e) {
            throw new JspException("Error: IOException while writing to client");
        }
        //-- continue process the body content
        return EVAL_BODY_INCLUDE;
    }
    /**
     * do end tag
     */
    @Override
    public int doEndTag() throws JspException {
        try {
            // output div's end tag
            pageContext.getOut().print("</div>");
        }
        catch (IOException ioe) {
            throw new JspException("Error: IOException while writing to client");
        }
        //-- continue process the page
        return EVAL_PAGE;
    }
    /**
     * The fadeOut function, its better provided from a js file.
     * @return String, the fadeOut function
     */
    private String fadeOut () {
        StringBuilder sb = new StringBuilder();
        sb.append("var ele = this, time = "+_duration / (1.0/_step)
                +", eStyle = ele.style, inc = "+_step+", timer, value;")
            .append("if (!eStyle.opacity) eStyle.opacity = 1;")
            .append("if (!ele.fo) ele.fo = setInterval(function () {")
            .append("if (eStyle.opacity > 0) {")
            .append("value = eStyle.opacity; eStyle.opacity -= inc;")
            .append("if (value == eStyle.opacity) value = eStyle.opacity = 0;")
            .append("else value = eStyle.opacity;")
            .append("eStyle.filter = 'alpha(opacity = ' + (value*100) + ')';")
            .append("} else clearInterval(ele.fo);")
            .append("}, time);");
        
        return sb.toString();
    }
}

The tag definition

Add the fragment below to the tld file which already created from previous post (http://ben-bai.blogspot.tw/2012/06/jsp-custom-tag-simple-tag.html) then export jar as described in the previous post.

<!-- fadeoutBlock tag -->
    <tag>
        <!-- tag name -->
        <name>fadeoutBlock</name>
        <!-- tag class path -->
        <tagclass>test.tag.custom.FadeoutBlock</tagclass>
        <!-- denotes the tag has JSP body content -->
        <bodycontent>JSP</bodycontent>
        <!-- attributes -->
        <attribute>
            <!-- attribute name -->
            <name>style</name>
            <!-- required or not -->
            <required>false</required>
            <!-- el enable or not (true denotes can be eval at runtime) -->
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <attribute>
            <!-- attribute name -->
            <name>styleClass</name>
            <!-- required or not -->
            <required>false</required>
            <!-- el enable or not (true denotes can be eval at runtime) -->
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <attribute>
            <!-- attribute name -->
            <name>duration</name>
            <!-- required or not -->
            <required>false</required>
            <!-- el enable or not (true denotes can be eval at runtime) -->
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <attribute>
            <!-- attribute name -->
            <name>step</name>
            <!-- required or not -->
            <required>false</required>
            <!-- el enable or not (true denotes can be eval at runtime) -->
            <rtexprvalue>false</rtexprvalue>
        </attribute>
    </tag>

Test page

fadeoutBlockTest.jsp

<%@ page isErrorPage="true" language="java"
    contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page isELIgnored ="false" %>
<!-- use the custom taglib with prefix ct -->
<%@taglib prefix="ct" uri="http://test.tag.custom/jsp/impl/taglib"%>
<html>
    <head>
        <meta http-equiv="Content-Type" 
            content="text/html; charset=UTF-8"/>
        <title>EL Math Practice</title>
        <style>
            .msg_block {
                position: absolute;
                left: 300px;
                top: 200px;
                width: 300px;
                height: 150px;
            }
        </style>
    </head>
    <body>
        <ct:fadeoutBlock styleClass="msg_block"
            duration="3000" step="0.05">
            <div style="margin: 20px; border: 1px solid #8463AE;">
                test message, click to fade-out
            </div>
        </ct:fadeoutBlock>
    </body>
</html>


The Result

View the demo flash on line
http://screencast.com/t/DAY7MfKaxtSg

You can find the flash file at github:
https://github.com/benbai123/JSP_Servlet_Practice/blob/master/demo_src/JSP/Custom_tag/fadeOutBlock.swf


Reference
http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/JSPTags.html

Download
The full project is at github
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/CustomTagPractice