package yoonforh.bbs;

/**
 * BBSUtil.java
 * Copyright (c) 1997-1998 Yoon Kyung Koo. All rights reserved.
 *
 * contact via <A HREF="mailto:yoonforh@moon.daewoo.co.kr">yoonforh@moon.daewoo.co.kr</A>
 *
 * first release (ver. 0.01) date 1997/10/07
 *
 * @version 1.2a 1998/06/04
 * @author <A HREF="http://moon.daewoo.co.kr/~yoonforh/">Yoon Kyung Koo</A>
 */


import java.io.*;
import java.util.*;
import java.text.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * BBSUtil class
 * This class holds several information about this service-specific path names
 * and several common methods.
 */
class BBSUtil {
	/* these values can be initialized from init parameter of servlet */
	private static String dataDir="C:/bbs_data/";
	private static String masterKey="password";

	/* these values are fixed for convenience */
	private static final String printURL="/servlet/BBSPrint"; // yoonforh.bbs.PrintServlet
	private static final String listURL="/servlet/BBSList"; // yoonforh.bbs.ListServlet
	private static final String editURL="/servlet/BBSEdit"; // yoonforh.bbs.EditServlet
	private static final String editResultURL="/servlet/BBSEditResult"; // yoonforh.bbs.EditResultServlet
	private static final String adminEditURL="/servlet/BBSAdminEdit"; // yoonforh.bbs.AdminEditServlet
	private static final String adminEditChallengeURL="/servlet/BBSAdminEditChallenge"; // yoonforh.bbs.AdminEditChallengeServlet
	private static final String adminEditResultURL="/servlet/BBSAdminEditResult"; // yoonforh.bbs.AdminEditResultServlet
	private static final String deleteURL="/servlet/BBSDelete"; // yoonforh.bbs.DeleteServlet
	private static final String deleteResultURL="/servlet/BBSDeleteResult"; // yoonforh.bbs.DeleteResultServlet
	private static final String postURL="/servlet/BBSPost"; // yoonforh.bbs.PostServlet
	private static final String postResultURL="/servlet/BBSPostResult"; // yoonforh.bbs.PostResultServlet
	private static final String homeURL="/bbs"; // root home directory
	private static final String homeImageURL="/images/home.jpg";
	private static final String docuImageURL="/images/document.gif"; // prefer transparent gif
	private static final String threadImageURL="/images/thread.gif"; // prefer transparent gif
	private static final String bodyStatement="<BODY BACKGROUND=\"/images/back.gif\">";
	private static final String countFile="BBS_counter";

	/**
	 * setInitParameters()
	 * this method set init parameters
	 * @param String dataDir root directory to save data
	 * @param String masterKey master password
	 */
	static void setInitParameters(String dataDir, String masterKey)
	{
		if (dataDir!=null)
			BBSUtil.dataDir=dataDir;
		if (masterKey!=null)
			BBSUtil.masterKey=masterKey;
	}

	/**
	 * getCount()
	 * this method returns current number of data files
	 * @param String category to specify bbs
	 * @return current number of data files(articles)
	 * @exception java.io.IOException
	 */
	private synchronized static int getCount(String category) throws IOException 
	{

		int count;

		RandomAccessFile file=new RandomAccessFile(new File(dataDir+category+"/"+countFile), "rw");
		
		try {
			file.seek(0);    // set file pointer to beginning

			try {
				count=file.readInt();
			} catch (IOException ie) { // case no data
				try {
					file.writeInt(0);
					count=0;
				} catch (IOException ie2) {
					throw ie2;
				}
			}
		} finally {
			file.close();
		}

		return count;
	} // end of getCount()

	/**
	 * addCount()
	 * this method increases current number of data files
	 * @param String category to specify bbs
	 * @exception java.io.IOException
	 */
	private synchronized static void addCount(String category) throws IOException 
	{

		int count;

		RandomAccessFile file=new RandomAccessFile(new File(dataDir+category+"/"+countFile), "rw");
		
		try {
			file.seek(0);    // set file pointer to beginning

			try {
				count=file.readInt();
			} catch (IOException ie) { // case no data
				try {
					file.writeInt(0);
					count=0;
				} catch (IOException ie2) {
					throw ie2;
				}
			}
	
			count++;
	
			file.seek(0);    // set file pointer to beginning
			file.writeInt(count);
   	             
		} finally {
			file.close();
		}
	} // end of addCount()
	
	/**
	 * addNextNumber()
	 * this method add a number to prev article's next numbers
	 * @param String category to specify bbs
	 * @param int prev article number
	 * @param int new article number
	 * @return boolean if succeed then true, else false
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	synchronized static boolean addNextNumber(String category, int prevNo, int newNo)
				throws IOException 
	{
		BBSData data=null;
		String fileName=dataDir+category+"/"+prevNo+".dat";
		try {
			data=readData(category, prevNo);
		} catch (IOException ie) {
			return false;
		}
		if (data==null)
			return false;
		data.nextNumbers.addElement(new Integer(newNo));
		if (!replaceData(category, data)) 
			return false;
		return true;
	} // end of addNextNumber
	
	/**
	 * writeData()
	 * this method writes data object into specified file
	 * @param String category to specify bbs
	 * @param BBSData data file to write into file
	 * @return numbers of current articles
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	synchronized static int writeData(String category, BBSData data)
				throws IOException 
	{

		// serializing
		String fileName=null;
		int count=0;

		count=getCount(category) + 1;
		fileName=dataDir+category+"/"+count+".dat";
		FileOutputStream f_out=new FileOutputStream(fileName);
		ObjectOutput s_out=new ObjectOutputStream(f_out);
		data.articleNo=count;

		try {
			/* update previous data's nextNumbers */
			if (data.prevNo>0)
				addNextNumber(category, data.prevNo, data.articleNo);

			s_out.writeObject(data);
			s_out.flush();
		} finally {
			s_out.close();
			f_out.close();
		}

		addCount(category);
		return count;
	} // end of writeData()

	/**
	 * updateDataWithAuth()
	 * this method writes data object into specified file
	 * @param String category to specify bbs
	 * @param BBSData data file to write into file
	 * @return boolean if succeed then true, else if password mismatch then false
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	private synchronized static boolean updateDataWithAuth(String category, BBSData data)
				throws IOException 
	{
		int count=data.articleNo;
		String fileName=dataDir+category+"/"+count+".dat";
		BBSData oldData=readData(category, count);
		if (!data.passwd.equals(oldData.passwd) && !data.passwd.equals(masterKey)) 
			return false;
		if (data.passwd.equals(masterKey)) // when masterKey used, clear password
			data.passwd=null;
		FileOutputStream f_out=new FileOutputStream(fileName);
		ObjectOutput s_out=new ObjectOutputStream(f_out);
		try {
			s_out.writeObject(data);
			s_out.flush();
		} finally {
			s_out.close();
			f_out.close();
		}

		return true;
	} // end of updateDataWithAuth()

	/**
	 * replaceData()
	 * this method simply overwrites an existing file with specified data
	 * @param String category to specify bbs
	 * @param BBSData data file to write into file
	 * @return boolean if succeed then true, else if password mismatch then false
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	private synchronized static boolean replaceData(String category, BBSData data)
				throws IOException 
	{
		int count=data.articleNo;
		String fileName=dataDir+category+"/"+count+".dat";

		FileOutputStream f_out=new FileOutputStream(fileName);
		ObjectOutput s_out=new ObjectOutputStream(f_out);
		try {
			s_out.writeObject(data);
			s_out.flush();
		} finally {
			s_out.close();
			f_out.close();
		}

		return true;
	} // end of replaceData()

	/**
	 * removeData()
	 * this method deletes data object file
	 * @param String category to specify bbs
	 * @param number article number to delete
	 * @return boolean if succeed to delete file then true, otherwise false
	 */
	private synchronized static boolean removeData(String category, int number)
	{
		String fileName=null;
		BBSData data=null;
		try {
			data=readData(category, number);
		} catch (IOException ie) {}
		if (data == null) 
			return false;
		/* if child data exist ... */
		if (data.nextNumbers !=null && !data.nextNumbers.isEmpty()) {
			for (Enumeration enum = data.nextNumbers.elements(); enum.hasMoreElements(); ) {
				int nextNo=((Integer) enum.nextElement()).intValue(); 
				try {
					BBSData nextData=readData(category, nextNo);
					if (nextData==null)
						continue;
					nextData.prevNo=data.prevNo; // replace next data's prev number with data's prev number
					replaceData(category, nextData);
				} catch (IOException ie) {} // ignore update-next-file error
			}
		}
		/* if parent data exist ... */
		if (data.prevNo>0) {
			try {
				BBSData prevData=readData(category, data.prevNo);
				if (prevData!=null && 
						prevData.nextNumbers!=null && 
						!prevData.nextNumbers.isEmpty() )
					// NOTE: removeElement() finds the object using equals() method
					if (prevData.nextNumbers.removeElement(new Integer(number))) {
						/* add next elements to prev data */
						/* if child data exist ... */
						if (data.nextNumbers !=null && !data.nextNumbers.isEmpty()) {
							for (Enumeration enum = data.nextNumbers.elements();
								enum.hasMoreElements();
							)
							{
								prevData.nextNumbers.addElement((Integer) enum.nextElement()); 
							}
						}
						replaceData(category, prevData);
					}
			} catch (IOException ie) {} // ignore update-prev-file error
		}
		fileName=dataDir+category+"/"+number+".dat";
		File file=new File(fileName);
		return file.delete();
	} // end of removeData()

	/**
	 * getGetData()
	 * this method read data object from POST/GET request
	 * @param HttpServletRequest req stream to read
	 * @param boolean isForUpdate specify if this is for update procedure
	 * @return BBSData object read from stream
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static BBSData getGetData(HttpServletRequest req, boolean isForUpdate) 
		throws ServletException, IOException 
	{
		BBSData data=null;

		if (isForUpdate) {
			String category=req.getParameter("cate");
			int articleNo=-1;
			try {
				articleNo=Integer.parseInt(req.getParameter("number"));
			} catch (NumberFormatException nfe) {}
			data=readData(category, articleNo);
		} else
			data=new BBSData();

		data.name=req.getParameter("NAME_FIELD");
		data.passwd=req.getParameter("PASSWORD_FIELD");
		String string=req.getParameter("WEB_FIELD");
		if (string.equals("")) {
			data.webAddress=null;
		} else {
			try {
				data.webAddress=new URL(string);
			} catch (MalformedURLException me) {
				try {
					data.webAddress=new URL("http://"+string);
				} catch (MalformedURLException me2) {
					data.webAddress=null;
				}
			}
		}
		string=req.getParameter("EMAIL_FIELD");
		if (string.equals("")) {
			data.emailAddress=null;
		} else {
			try {
				data.emailAddress=new URL(req.getParameter("EMAIL_FIELD"));
			} catch (MalformedURLException me) {
				try {
					data.emailAddress=new URL("mailto:"+req.getParameter("EMAIL_FIELD"));
				} catch (MalformedURLException me2) {
					data.emailAddress=null;
				}
			}
		}
		data.title=req.getParameter("TITLE_FIELD");
		data.data=req.getParameter("COMMENT_FIELD");
		data.logDate=new Date(System.currentTimeMillis());

		String replyTo=req.getParameter("replyto");
		try {
			data.prevNo=(replyTo==null?-1:Integer.parseInt(replyTo));
		} catch (NumberFormatException nfe) {}

		return data;

	} // end of getGetData()

	/**
	 * getGetData()
	 * this method read data object from POST/GET request to show article
	 * @param HttpServletRequest req stream to read
	 * @return BBSData object read from stream
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static BBSData getGetData(HttpServletRequest req) 
		throws ServletException, IOException 
	{
		return getGetData(req, false);
	}

	/**
	 * getGetDataForUpdate()
	 * this method read data object from POST/GET request for update
	 * @param HttpServletRequest req stream to read
	 * @return BBSData object read from stream
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static BBSData getGetDataForUpdate(HttpServletRequest req) 
		throws ServletException, IOException 
	{
		return getGetData(req, true);
	}

	/**
	 * getAdminGetData()
	 * this method read data object from POST/GET request for adminer's article edit
	 * @param HttpServletRequest req stream to read
	 * @return BBSData object read from stream
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static BBSData getAdminGetData(HttpServletRequest req) 
		throws ServletException, IOException 
	{
		BBSData data=getGetData(req, true);

		String replyTo=req.getParameter("replyto");
		try {
			data.prevNo=(replyTo==null?-1:Integer.parseInt(replyTo));
		} catch (NumberFormatException nfe) {}

		String nexts=req.getParameter("nexts");
		if (nexts != null) {
			data.nextNumbers.removeAllElements();
			StringTokenizer token = new StringTokenizer(nexts, ",");
			while (token.hasMoreTokens()) {
				try {
					String nextNo = token.nextToken();
					data.nextNumbers.addElement(new Integer(nextNo));
				} catch (NumberFormatException nfe) {}
			}
		} else 
			data.nextNumbers=null;

		return data;
	}

	/**
	 * readData()
	 * this method read data object
	 * @param String category to specify bbs
	 * @param count specify which file (nth article) to read
	 * @return BBSData object read from data file
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static BBSData readData(String category, int count) throws IOException 
	{
		// de-serialize 
		BBSData data=null;
		boolean isAccurateNumber=true;
		if (count<0) { // -1 means last article
			count=getCount(category);
			isAccurateNumber=false;
		}
		String fileName=dataDir+category+"/"+count+".dat";
		FileInputStream f_in=null;

		boolean notFound=true;
		while (notFound) {
			try {
				f_in=new FileInputStream(fileName);
			} catch (FileNotFoundException fe) {
				if (isAccurateNumber)
					return null;
				count-=1;
				if (count<=0)
					return null;
				fileName=dataDir+category+"/"+count+".dat";
				continue;
			}
			notFound=false;
		}
		ObjectInput s_in=new ObjectInputStream(f_in);
		try {
			data=(BBSData) s_in.readObject();
		} catch (ClassNotFoundException cnfe) {
			data=null;
		} finally {
			s_in.close();
			f_in.close();
		}

		return data;

	} // end of readData()

	/**
	 * readPrevArticle()
	 * this method read data objects
	 * @param String category to specify bbs
	 * @param articleNo specify from which file (nth article) to read
	 * @param nItems specify number of file to read
	 * @return Vector of BBSData objects
	 * @see yoonforh.bbs.BBSData
	 */
	private static Vector readPrevArticle(String category, int articleNo, int nItems) 
	{
		BBSData data=null;
		Vector vector = new Vector();
		int count=0;
		for (int i=0; (count<nItems && articleNo-i>=0); i++) {
			try {
				data=readData(category, articleNo-i);
				if (data != null && data.prevNo<0 /* if thread-beginning article */) {
					vector.addElement(data);
					count++;
				}
			} catch (IOException ie) {
			}
		}

		return vector;
	} // end of readPrevArticle()

	/**
	 * readNextArticle()
	 * this method read data objects
	 * @param String category to specify bbs
	 * @param articleNo specify from which file (nth article) to read
	 * @param nItems specify number of file to read
	 * @param maxCount number of all articles
	 * @return Vector of BBSData objects
	 * @see yoonforh.bbs.BBSData
	 */
	private static Vector readNextArticle(String category, int articleNo, int nItems, int maxCount) 
	{
		BBSData data=null;
		Vector vector = new Vector();
		int count=0;
		for (int i=articleNo; (count<nItems && i<=maxCount); i++) {
			try {
				data=readData(category, i);
				if (data != null && data.prevNo<0 /* if thread-beginning article */) {
					vector.insertElementAt(data, 0);
					count++;
				}
			} catch (IOException ie) {
			}
		}

		return vector;
	} // end of readNextArticle()

	static Vector readNextArticle(String category, int articleNo, int nItems) 
			throws IOException 
	{
		return readNextArticle(category, articleNo, nItems, getCount(category));
	}

	/**
	 * printTitle()
	 * this method prints <HEAD> and <TITLE> tags
	 * @param ServletOutputStream output stream to print
	 * @param String title title of this document
	 * @exception java.io.IOException
	 */
	static void printTitle(ServletOutputStream out, String title)
				throws IOException 
	{
		out.println("<HTML><HEAD>");
		out.println("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=EUC-KR\">");
		out.println("<TITLE>"+title+"</TITLE></HEAD>");
		out.println(bodyStatement);
	}

	/**
	 * printArticleHeader()
	 * this method prints article's header
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param int articleNo article number
	 * @exception java.io.IOException
	 */
	static void printArticleHeader(ServletOutputStream out, String category, int articleNo)
				throws IOException 
	{
		BBSData data=readData(category, articleNo);
		/*
		String string="["+articleNo+convertToASCII("¹øÂ° ±Û]")+data.title;
		printTitle(out, string);
		out.println("<CENTER><H1>"+string+"</H1></CENTER>\n");
		*/
		printTitle(out, data.title);
		out.println("<CENTER><H1>"+data.title+"</H1></CENTER>\n");
	}

	/**
	 * getTopArticleNumber()
	 * this method returns thread top article number
	 * @param String category to specify bbs
	 * @param int articleNo article number
	 * @return int thread-top article number
	 */
	static int getTopArticleNumber(String category, int articleNo)
	{
		BBSData data=null;
		int result=articleNo;
		while(true) {
			try {
				data=readData(category, result);
				if (data == null)
					return result;
				if (data.prevNo<0) /* if thread-beginning article */ 
					return result;
				else 
					result=data.prevNo;
			} catch (IOException ie) { break;}
		}
		return result;
	}

	/**
	 * printNextPrevList()
	 * this method prints next and prev article's title
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param int articleNo article number
	 * @exception java.io.IOException
	 */
	static void printNextPrevList(ServletOutputStream out, String category, int articleNo)
				throws IOException 
	{
		Vector vector1=null, vector2=null;
		BBSData data=null;
		articleNo=getTopArticleNumber(category, articleNo);
		vector1=readPrevArticle(category, articleNo, 2); // readPrevArticle includes self
		vector2=readNextArticle(category, articleNo, 2); // readNextArticle includes self
		int size1=vector1.size();
		int size2=vector2.size();
		if (size1<=1 && size2<=1)
			return;
		out.println("<TABLE BORDER=0 WIDTH=610>");
		if (size1>1) {
			try {
				data=(BBSData) vector1.elementAt(1); // 1 is prev

				out.println("<TR><TD ALIGN=LEFT COLSPAN=5><FONT SIZE=2><B>"+convertToASCII("¾Õ ±Û")+"</B></FONT></TD></TR>");
				out.println("<TR>");
				out.println("<TD ALIGN=LEFT BGCOLOR=\"#CCCCCC\"><IMG SRC=\""+docuImageURL+"\" ALT=\"*\"></TD>");
				out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+data.articleNo+"</FONT></TD>");
				if (data.webAddress!=null) {
					out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
					printLink(out, data.name, data.webAddress.toString());
					out.println("</FONT></TD>");
				}
				else if (data.emailAddress!=null) {
					out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
					printLink(out, data.name, data.emailAddress.toString());
					out.println("</FONT></TD>");
				}
				else
					out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+data.name+"</FONT></TD>");
				out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+convertToASCII(formatDateSimple(data.logDate))+"</FONT></TD>");
				out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
				printLink(out, data.title, printURL+"?cate="+category+"&number="+data.articleNo);
				out.println("</FONT></TD>");
				out.println("</TR>");
				// print successive threads
				for (Enumeration enum = data.nextNumbers.elements(); enum.hasMoreElements(); ) {
					int nextNo=((Integer) enum.nextElement()).intValue(); 
					printNextThreadTitle(out, category, nextNo, 0);
				}
			} catch (ArrayIndexOutOfBoundsException e) {
			}
		}
		if (size2>1) {
			try {
				data=(BBSData) vector2.elementAt(0); // 0 is next

				out.println("<TR><TD ALIGN=LEFT COLSPAN=5><FONT SIZE=2><B>"+convertToASCII("´ÙÀ½ ±Û")+"</B></FONT></TD></TR>");
				out.println("<TR>");
				out.println("<TD ALIGN=LEFT BGCOLOR=\"#CCCCCC\"><IMG SRC=\""+docuImageURL+"\" ALT=\"*\"></TD>");
				out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+data.articleNo+"</FONT></TD>");
				if (data.webAddress!=null) {
					out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
					printLink(out, data.name, data.webAddress.toString());
					out.println("</FONT></TD>");
				}
				else if (data.emailAddress!=null) {
					out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
					printLink(out, data.name, data.emailAddress.toString());
					out.println("</FONT></TD>");
				}
				else
					out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+data.name+"</FONT></TD>");
				out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+convertToASCII(formatDateSimple(data.logDate))+"</FONT></TD>");
				out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
				printLink(out, data.title, printURL+"?cate="+category+"&number="+data.articleNo);
				out.println("</FONT></TD>");
				out.println("</TR>");
				// print successive threads
				for (Enumeration enum = data.nextNumbers.elements(); enum.hasMoreElements(); ) {
					int nextNo=((Integer) enum.nextElement()).intValue(); 
					printNextThreadTitle(out, category, nextNo, 0);
				}
			} catch (ArrayIndexOutOfBoundsException e) {
			}
		}
		out.println("</TABLE>");
	}

	/**
	 * printArticleFooter()
	 * this method prints article's footer
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param int articleNo article number
	 * @exception java.io.IOException
	 */
	static void printArticleFooter(ServletOutputStream out, String category, int articleNo)
				throws IOException 
	{
		printNextPrevList(out, category, articleNo);
		out.println("<TABLE BORDER=0 WIDTH=610><TR><TD><HR NOSHADE></TD></TR>");
		out.println("<TR>");
		out.println("<TD ALIGN=LEFT> <FONT SIZE=2 COLOR=\"#743014\">");
		printLink(out, convertToASCII("ÀÌ¾î¼­ ±Û ¾²±â"), postURL+"?cate="+category+"&replyto="+articleNo);
		out.print(" - "); printLink(out, convertToASCII("¸ñ·Ï º¸±â"), listURL+"?cate="+category+"&start="+(articleNo+9)+"&dir=-1");
		out.print(" - "); printLink(out, convertToASCII("»õ±Û ¾²±â"), postURL+"?cate="+category);
		out.print(" - "); printLink(out, convertToASCII("°íÄ¡±â"), editURL+"?cate="+category+"&number="+articleNo);
		out.print(" - "); printLink(out, convertToASCII("Áö¿ì±â"), deleteURL+"?cate="+category+"&number="+articleNo);
		out.println("</FONT>");
		out.println("</TD></TR>");
		out.println("<TR><TD><HR NOSHADE></TD></TR>");
		out.print("<TR><TD>");
		printLink(out, "<IMG SRC=\""+homeImageURL+"\" ALT=\"goto home\">", homeURL+"/"+category);
		out.println("</TD></TR>");
		out.println("</TABLE>");
		out.println("</BODY>\n</HTML>");
	}

	/**
	 * printListHeader()
	 * this method prints article list's header
	 * @param ServletOutputStream output stream to print
	 * @exception java.io.IOException
	 */
	static void printListHeader(ServletOutputStream out)
				throws IOException 
	{
		String string=convertToASCII("±â»ç ¸ñ·Ï º¸±â");
		printTitle(out, string);
		out.println("<CENTER><H1>"+string+"</H1></CENTER>\n");
	}

	/**
	 * printListFooter()
	 * this method prints article list's footer
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param int beginIndex first article number
	 * @param int endIndex last article number
	 * @exception java.io.IOException
	 */
	static void printListFooter(ServletOutputStream out, String category, int beginIndex, int endIndex)
				throws IOException 
	{
		out.println("<TABLE BORDER=0 WIDTH=610><TR><TD><HR NOSHADE></TD></TR>");
		out.println("<TR><TD ALIGN=LEFT> <FONT SIZE=2 COLOR=\"#743014\">");
		printLink(out, convertToASCII("ÀÌÀü ¸ñ·Ï º¸±â"), listURL+"?cate="+category+"&start="+beginIndex+"&dir=-1");
		out.print(" - "); printLink(out, convertToASCII("´ÙÀ½ ¸ñ·Ï º¸±â"), listURL+"?cate="+category+"&start="+endIndex+"&dir=1");
		out.print(" - "); printLink(out, convertToASCII("»õ±Û ¾²±â"), postURL+"?cate="+category);
		out.println("</FONT></TD></TR>");
		out.println("<TR><TD><HR NOSHADE></TD></TR>");
		out.print("<TR><TD>");
		printLink(out, "<IMG SRC=\""+homeImageURL+"\" ALT=\"goto home\">", homeURL+"/"+category);
		out.println("</TD></TR>");
		out.println("<TR><TD><HR NOSHADE></TD></TR>");
		out.println("<TR><TD ALIGN=LEFT> <FONT SIZE=2 COLOR=\"#743014\">");
		printLink(out, convertToASCII("ÀüÃ¼ È¨ÆäÀÌÁö·Î ÀÌµ¿"), homeURL);
		out.println("</TD></TR>");
		out.println("</TABLE>");
		out.println("</BODY>\n</HTML>");
	}

	/**
	 * printListFooterSSI()
	 * this method prints article list's footer
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param int beginIndex first article number
	 * @param int endIndex last article number
	 * @exception java.io.IOException
	 */
	static void printListFooterSSI(ServletOutputStream out, String category, int beginIndex, int endIndex)
				throws IOException 
	{
		out.println("<TABLE BORDER=0 WIDTH=610><TR><TD><HR NOSHADE></TD></TR>");
		out.println("<TR><TD ALIGN=LEFT> <FONT SIZE=2 COLOR=\"#743014\">");
		printLink(out, convertToASCII("ÀÌÀü ¸ñ·Ï º¸±â"), listURL+"?cate="+category+"&start="+beginIndex+"&dir=-1");
		out.print(" - "); printLink(out, convertToASCII("´ÙÀ½ ¸ñ·Ï º¸±â"), listURL+"?cate="+category+"&start="+endIndex+"&dir=1");
		out.print(" - "); printLink(out, convertToASCII("»õ±Û ¾²±â"), postURL+"?cate="+category);
		out.println("</FONT></TD></TR>");
		out.println("</TABLE>");
	}

	/**
	 * printDeleteHeader()
	 * this method prints header when challenged to delete
	 * @param ServletOutputStream output stream to print
	 * @param articleNo article number to delete
	 * @exception java.io.IOException
	 */
	static void printDeleteHeader(ServletOutputStream out, int articleNo)
				throws IOException 
	{
		String string=articleNo+convertToASCII("¹ø ±â»ç »èÁ¦");
		printTitle(out, string);
		out.println("<CENTER><H1>"+string+"</H1></CENTER>");
	}

	/**
	 * printDeleteFooter()
	 * this method prints footer when challenged to delete
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param articleNo article number to delete
	 * @exception java.io.IOException
	 */
	static void printDeleteFooter(ServletOutputStream out, String category, int articleNo)
				throws IOException 
	{
		printNextPrevList(out, category, articleNo);
		out.println("<TABLE BORDER=0 WIDTH=610><TR><TD><HR NOSHADE></TD></TR>");
		out.println("<TR><TD ALIGN=LEFT> <FONT SIZE=2 COLOR=\"#743014\">");
		printLink(out, convertToASCII("¸ñ·Ï º¸±â"), listURL+"?cate="+category+"&start="+(articleNo+9)+"&dir=-1");
		out.print(" - "); printLink(out, convertToASCII("»õ±Û ¾²±â"), postURL+"?cate="+category);
		out.println("</FONT>");
		out.println("</TD></TR>");
		out.println("<TR><TD><HR NOSHADE></TD></TR>");
		out.print("<TR><TD>");
		printLink(out, "<IMG SRC=\""+homeImageURL+"\" ALT=\"goto home\">", homeURL+"/"+category);
		out.println("</TD></TR>");
		out.println("</TABLE>");
		out.println("</BODY>\n</HTML>");
	}

	/**
	 * printEditHeader()
	 * this method prints edit-article header
	 * @param ServletOutputStream output stream to print
	 * @param int articleNo article number
	 * @exception java.io.IOException
	 */
	static void printEditHeader(ServletOutputStream out, int articleNo)
				throws IOException 
	{
		String string=articleNo+convertToASCII("¹øÂ° ±Û ÆíÁý");
		printTitle(out, string);
		out.println("<CENTER><H1>"+string+"</H1></CENTER>\n");
	}

	/**
	 * printEditFooter()
	 * this method prints edit-article footer
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param int articleNo article number
	 * @exception java.io.IOException
	 */
	static void printEditFooter(ServletOutputStream out, String category, int articleNo)
				throws IOException 
	{
		printNextPrevList(out, category, articleNo);
		out.println("<TABLE BORDER=0 WIDTH=610><TR><TD><HR NOSHADE></TD></TR>");
		out.println("<TR><TD ALIGN=LEFT> <FONT SIZE=2 COLOR=\"#743014\">");
		printLink(out, convertToASCII("ÀÌ¾î¼­ ±Û ¾²±â"), postURL+"?cate="+category+"&replyto="+articleNo);
		out.print(" - "); printLink(out, convertToASCII("¸ñ·Ï º¸±â"), listURL+"?cate="+category+"&start="+(articleNo+9)+"&dir=-1");
		out.print(" - "); printLink(out, convertToASCII("»õ±Û ¾²±â"), postURL+"?cate="+category);
		out.print(" - "); printLink(out, convertToASCII("°íÄ¡±â"), editURL+"?cate="+category+"&number="+articleNo);
		out.print(" - "); printLink(out, convertToASCII("Áö¿ì±â"), deleteURL+"?cate="+category+"&number="+articleNo);
		out.println("</FONT>");
		out.println("</TD></TR>");
		out.print("<TR><TD><HR NOSHADE>");
		printLink(out, "<IMG SRC=\""+homeImageURL+"\" ALT=\"goto home\">", homeURL+"/"+category);
		out.println("</TD></TR>");
		out.println("</TABLE>");
		out.println("</BODY>\n</HTML>");
	}

	/**
	 * printAdminEditHeader()
	 * this method prints edit-article header for Administrator
	 * @param ServletOutputStream output stream to print
	 * @param int articleNo article number
	 * @exception java.io.IOException
	 */
	static void printAdminEditHeader(ServletOutputStream out, int articleNo)
				throws IOException 
	{
		String string=articleNo+convertToASCII("¹øÂ° ±Û ÆíÁý(°ü¸®ÀÚ)");
		printTitle(out, string);
		out.println("<CENTER><H1>"+string+"</H1></CENTER>\n");
	}

	/**
	 * printAdminEditFooter()
	 * this method prints edit-article footer for Administrator
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param int articleNo article number
	 * @exception java.io.IOException
	 */
	static void printAdminEditFooter(ServletOutputStream out, String category, int articleNo)
				throws IOException 
	{
		printEditFooter(out, category, articleNo);
	}

	/**
	 * printPostHeader()
	 * this method prints header to post an article
	 * @param ServletOutputStream output stream to print
	 * @exception java.io.IOException
	 */
	static void printPostHeader(ServletOutputStream out)
				throws IOException 
	{
		String string=convertToASCII("°Ô½ÃÆÇ¿¡ ±Û ¾²±â");
		printTitle(out, string);
		out.println("<CENTER><H1>"+string+"</H1></CENTER>\n");
	}

	/**
	 * printPostFooter()
	 * this method prints footer to post an article
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @exception java.io.IOException
	 */
	static void printPostFooter(ServletOutputStream out, String category)
				throws IOException 
	{
		out.println("<TABLE BORDER=0 WIDTH=610>");
		out.println("<TR><TD><HR NOSHADE></TD></TR>");
		out.print("<TR><TD>");
		printLink(out, "<IMG SRC=\""+homeImageURL+"\" ALT=\"goto home\">", homeURL+"/"+category);
		out.println("</TD></TR>");
		out.println("</TABLE>");
		out.println("</BODY>\n</HTML>");
	}

	/**
	 * printEditChallenge()
	 * this method prints article in edit form
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param BBSData data article content to update
	 * @exception java.io.IOException
	 */
	static void printEditChallenge(ServletOutputStream out, String category, BBSData data)
				throws IOException 
	{
		if (data==null) {
			out.println("<H1>Specified article not found!</H1>");
			return;
		}
		out.println("<CENTER><HR NOSHADE WIDTH=60%></CENTER><FORM METHOD=\"POST\" ACTION=\""+editResultURL+"\">");
		out.println("<TABLE><TR><TD ALIGN=RIGHT>");
		out.println(convertToASCII("ÀÌ ¸§"));
		if (data.name!=null)
			out.println("</TD><TD><INPUT NAME=\"NAME_FIELD\" TYPE=\"TEXT\" SIZE=50 VALUE=\""+data.name+"\"></TD></TR>");
		else
			out.println("</TD><TD><INPUT NAME=\"NAME_FIELD\" TYPE=\"TEXT\" SIZE=50></TD></TR>");
		out.println("<TR><TD ALIGN=right>");
		out.println(convertToASCII("¾Ï È£"));
		out.println("</TD><TD><INPUT NAME=\"PASSWORD_FIELD\" TYPE=\"PASSWORD\" SIZE=8></TD></TR>");
		out.println("<TR><TD></TD></TR><TR><TD ALIGN=RIGHT>");
		out.println("E-Mail "+convertToASCII("ÁÖ¼Ò")+"(Optional)</TD>");
		if (data.emailAddress!=null)
			out.println("<TD><INPUT NAME=\"EMAIL_FIELD\" TYPE=\"TEXT\" SIZE=50 VALUE=\""+data.emailAddress.toString()+"\"></TD></TR>");
		else
			out.println("<TD><INPUT NAME=\"EMAIL_FIELD\" TYPE=\"TEXT\" SIZE=50></TD></TR>");
		out.println("<TR><TD ALIGN=RIGHT>"+
			convertToASCII("È¨ÆäÀÌÁö")+" URL(Optional)</TD>");
		if (data.webAddress!=null)
			out.println("<TD><INPUT NAME=\"WEB_FIELD\" TYPE=\"TEXT\" SIZE=50 VALUE=\""+data.webAddress.toString()+"\"></TD></TR>");
		else
			out.println("<TD><INPUT NAME=\"WEB_FIELD\" TYPE=\"TEXT\" SIZE=50></TD></TR>");
		out.println("<TR><TD ALIGN=RIGHT VALIGN=TOP>"+convertToASCII("Á¦ ¸ñ")+"</TD>");
		if (data.title!=null)
			out.println("<TD><INPUT NAME=\"TITLE_FIELD\" SIZE=50 VALUE=\""+data.title+"\"></TD></TR>");
		else
			out.println("<TD><INPUT NAME=\"TITLE_FIELD\" SIZE=50></TD></TR>");
		out.println("<TR><TD ALIGN=RIGHT VALIGN=TOP>"+convertToASCII("²ôÀû²ôÀû")+"</TD>");
		if (data.data!=null)
			out.println("<TD><TEXTAREA NAME=\"COMMENT_FIELD\" ROWS=10 COLS=50>"+data.data+"</TEXTAREA></TD></TR>");
		else
			out.println("<TD><TEXTAREA NAME=\"COMMENT_FIELD\" ROWS=10 COLS=50></TEXTAREA></TD></TR>");
		out.println("<TR><TD></TD><TD><INPUT NAME=\"SUBMIT_BUTTON\" TYPE=\"SUBMIT\" VALUE=");
		out.println(convertToASCII("\"   ±â»ç °íÄ¡±â   \"")+"></TD></TR></TABLE>");
		out.println("<INPUT NAME=\"number\" TYPE=\"HIDDEN\" VALUE="+data.articleNo+">");
		out.println("<INPUT NAME=\"cate\" TYPE=\"HIDDEN\" VALUE="+category+">");
		out.println("<INPUT NAME=\"replyto\" TYPE=\"HIDDEN\" VALUE="+data.prevNo+">");
		out.println("</FORM>");

	}

	/**
	 * printEditResult()
	 * this method prints the result of update an article
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param BBSData newData article content to update
	 * @exception java.io.IOException
	 */
	static void printEditResult(ServletOutputStream out, String category, BBSData newData)
				throws IOException 
	{
		boolean isUpdated=updateDataWithAuth(category, newData);
		if (isUpdated) {
			printArticle(out, category, newData);
		}
		else {
			out.println("<FONT SIZE=2 COLOR=\"#0000FF\">");
			out.print(convertToASCII("ÆÐ½º¿öµå°¡ ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù."));
			out.println("<BR>");
			out.println(convertToASCII("±â»ç °»½Å¿¡ ½ÇÆÐÇÏ¿´½À´Ï´Ù."));
			out.println("</FONT>");
			return;
		}
	}

	/**
	 * printPrevThread()
	 * this method prints prev article in the threads' title
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param int prevNo previous article no. in this thread
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static void printPrevThread(ServletOutputStream out, String category, int prevNo)
				throws IOException 
	{
		try {
			BBSData data=readData(category, prevNo);
			if ( data!=null && data.nextNumbers!=null && !data.nextNumbers.isEmpty() ) {
				out.println("<TR><TD ALIGN=LEFT COLSPAN=5><FONT SIZE=2><B>"+convertToASCII("¾²·¹µå ¾Õ ±Û")+"</B></FONT></TD></TR>");
				// print article header
				/*
				out.println("<TR BGCOLOR=\"FFFFCC\">");
				out.println("<TH BGCOLOR=\"#CCCCCC\"><!--document type(image)//--></TH>");
				out.println("<TH><FONT SIZE=2><B>"+convertToASCII("¹ø È£")+"</B></FONT></TH>");
				out.println("<TH><FONT SIZE=2><B>"+convertToASCII("±Û¾´ÀÌ")+"</B></FONT></TH>");
				out.println("<TH><FONT SIZE=2><B>"+convertToASCII("±Û¾´ ³¯Â¥")+"</B></FONT></TH>");
				out.println("<TH><FONT SIZE=2><B>"+convertToASCII("Á¦ ¸ñ")+"</B></FONT></TH>");
				out.println("</TR>");
				*/
				out.println("<TR>");
				out.println("<TD ALIGN=LEFT BGCOLOR=\"CCCCCC\"><IMG SRC=\""+docuImageURL+"\" ALT=\"*\"></TD>");
				out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+data.articleNo+"</FONT></TD>");
				if (data.webAddress!=null) {
					out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
					printLink(out, data.name, data.webAddress.toString());
					out.println("</FONT></TD>");
				}
				else if (data.emailAddress!=null) {
					out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
					printLink(out, data.name, data.emailAddress.toString());
					out.println("</FONT></TD>");
				}
				else
					out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+data.name+"</FONT></TD>");
				out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+convertToASCII(formatDateSimple(data.logDate))+"</FONT></TD>");
				out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
				printLink(out, data.title, printURL+"?cate="+category+"&number="+data.articleNo);
				out.println("</FONT></TD>");
				out.println("</TR>");
			}
		} catch (IOException ie) {}

	} // end of printPrevThread()

	/**
	 * printNextThreadTitle()
	 * this method prints next article thread's title
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param nextNo number of next article
	 * @param indentBase number of next article
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static void printNextThreadTitle(ServletOutputStream out, String category, int nextNo, int indentBase)
				throws IOException 
	{
		try {
			BBSData data=readData(category, nextNo);
			if (data==null)
				return;

			out.println("<TR>");
			out.print("<TD ALIGN=LEFT BGCOLOR=\"#CCCCCC\">");
			for (int i=0; i<indentBase; i++)
				out.print("&nbsp;&nbsp;"); // per indentBase print two fixed space
			out.println("<IMG SRC=\""+threadImageURL+"\" ALT=\"+--\"><IMG SRC=\""+docuImageURL+"\" ALT=\"*\"></TD>");
			out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+data.articleNo+"</FONT></TD>");
			if (data.webAddress!=null) {
				out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
				printLink(out, data.name, data.webAddress.toString());
				out.println("</FONT></TD>");
			}
			else if (data.emailAddress!=null) {
				out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
				printLink(out, data.name, data.emailAddress.toString());
				out.println("</FONT></TD>");
			}
			else
				out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+data.name+"</FONT></TD>");
			out.println("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>"+convertToASCII(formatDateSimple(data.logDate))+"</FONT></TD>");
			out.print("<TD ALIGN=CENTER BGCOLOR=\"#FFFFCC\"><FONT SIZE=2>");
			printLink(out, data.title, printURL+"?cate="+category+"&number="+data.articleNo);
			out.println("</FONT></TD>");
			out.println("</TR>");

			for (Enumeration enum = data.nextNumbers.elements(); enum.hasMoreElements(); ) {
				int nextNextNo=((Integer) enum.nextElement()).intValue(); 
				printNextThreadTitle(out, category, nextNextNo, indentBase+1); // recursive call
			}
		} catch (IOException ie) {}
	}

	/**
	 * printNextThreads()
	 * this method prints article threads' titles
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param BBSData data file to print
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static void printNextThreads(ServletOutputStream out, String category, BBSData data)
				throws IOException 
	{
		/* if child data exist ... */
		if (data.nextNumbers !=null && !data.nextNumbers.isEmpty()) {
			out.println("<TR><TD ALIGN=LEFT COLSPAN=5><FONT SIZE=2><B>"+convertToASCII("¾²·¹µå ´ÙÀ½ ±Û")+"</B></FONT></TD></TR>");
			/*
			// print article header
			out.println("<TR BGCOLOR=\"FFFFCC\">");
			out.println("<TH BGCOLOR=\"#CCCCCC\"><!--document type(image)//--></TH>");
			out.println("<TH><FONT SIZE=2><B>"+convertToASCII("¹ø È£")+"</B></FONT></TH>");
			out.println("<TH><FONT SIZE=2><B>"+convertToASCII("±Û¾´ÀÌ")+"</B></FONT></TH>");
			out.println("<TH><FONT SIZE=2><B>"+convertToASCII("±Û¾´ ³¯Â¥")+"</B></FONT></TH>");
			out.println("<TH><FONT SIZE=2><B>"+convertToASCII("Á¦ ¸ñ")+"</B></FONT></TH>");
			out.println("</TR>");
			*/
			for (Enumeration enum = data.nextNumbers.elements(); enum.hasMoreElements(); ) {
				int nextNo=((Integer) enum.nextElement()).intValue(); 
				printNextThreadTitle(out, category, nextNo, 0);
			}
		}

	} // end of printNextThreads()

	/**
	 * printArticle()
	 * this method prints formatted article
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param BBSData data file to print
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static void printArticle(ServletOutputStream out, String category, BBSData data)
				throws IOException 
	{
		int count=getCount(category);

		// print table header
		out.println("<FONT SIZE=2 COLOR=\"#0000FF\">");
		out.println("<TABLE BORDER=0 WIDTH=610><TR>");
		out.println("<TD ALIGN=LEFT><FONT SIZE=2><B>"+count+convertToASCII("°³ ±Û Áß ")+data.articleNo+convertToASCII("¹øÂ° ±Û")+"</B></FONT></TD>");
		out.println("</TR></TABLE></FONT>");

		out.println("<TABLE BORDER=1 BGCOLOR=\"#C0FFD5\" WIDTH=610><TR>");
		out.println("<TD ALIGN=CENTER BGCOLOR=\"#C0F180\"><FONT SIZE=2><B>"+convertToASCII("ÀÌ ¸§")+"</B></FONT></TD>");
		if (data.webAddress!=null) {
			out.print("<TD ALIGN=CENTER BGCOLOR=\"#C0FFD5\"><FONT SIZE=2><B>");
			printLink(out, data.name, data.webAddress.toString());
			out.println("</B></FONT></TD>");
		}
		else {
			out.println("<TD ALIGN=CENTER BGCOLOR=\"#C0FFD5\"><FONT SIZE=2><B>"+data.name+"</B></FONT></TD>");
		}
		out.println("</TR>");
		if (data.emailAddress!=null) {
			out.println("<TR>");
			out.println("<TD ALIGN=CENTER BGCOLOR=\"#C0F180\"><FONT SIZE=2><B>E-MAIL</B></FONT></TD>");
			out.print("<TD ALIGN=CENTER BGCOLOR=\"#C0FFD5\"><FONT SIZE=2><B>");
			printLink(out, data.emailAddress.toString(), data.emailAddress.toString());
			out.println("</B></FONT></TD>");
			out.println("</TR>");
		}
		out.println("<TR>");
		out.println("<TD ALIGN=CENTER BGCOLOR=\"#C0F180\"><FONT SIZE=2><B>"
			+convertToASCII("±Û¾´ ³¯Â¥")+"</B></FONT></TD>");
		out.println("<TD ALIGN=CENTER BGCOLOR=\"#C0FFD5\"><FONT SIZE=2><B>"
			+convertToASCII(formatDateInKorean(data.logDate))+"</B></FONT></TD>");
		out.println("</TR>");
		out.println("<TR>");
		out.println("<TD ALIGN=CENTER BGCOLOR=\"#C0F180\"><FONT SIZE=2><B>"
			+convertToASCII("Á¦ ¸ñ")+"</B></FONT></TD>");
		out.println("<TD ALIGN=CENTER BGCOLOR=\"#C0FFD5\"><FONT SIZE=2><B>"
			+data.title+"</B></FONT></TD>");
		out.println("</TR>");
		out.println("</TABLE>");
		out.println("<TABLE BORDER=0 WIDTH=610 BGCOLOR=\"CCCCCC\">");
		out.println("<TR>");
		out.println("<TD><FONT SIZE=2>");
		int beginIndex=0;
		int endIndex=0;
		while ((endIndex=data.data.indexOf('\n', beginIndex))>=0) {
			out.println(data.data.substring(beginIndex, endIndex));
			out.println("<BR>");
			beginIndex=endIndex+1;
		}
		try {
			out.println(data.data.substring(beginIndex));
		} catch (StringIndexOutOfBoundsException e) {
		}
		out.println("</FONT></TD>");
		out.println("</TR>");
		out.println("</TABLE>");

		out.println("<TABLE BORDER=0 WIDTH=610><TR><TD><HR NOSHADE></TD></TR></TABLE>");
		out.println("<TABLE BORDER=0 WIDTH=610>");
		/* if parent data exist ... */
		if (data.prevNo>0)
			printPrevThread(out, category, data.prevNo);

		/* if child data exist ... */
		printNextThreads(out, category, data);
		out.println("</TABLE>");

	} // end of printArticle()

	/**
	 * printArticleList()
	 * this method prints formatted article's header
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param count specify from which file (nth article) to read
	 * @param nItems specify number of file to read
	 * @param isNext specify order
	 * @exception java.io.IOException
	 */
	static int[] printArticleList(ServletOutputStream out, String category, int count, int nItems, boolean isNext)
			throws IOException 
	{
		BBSData data=null;
		Vector vector=null;
		int beginIndex=0, endIndex=0;

		int totalCount=getCount(category);
		if (count<=0) {
			count=totalCount;
			isNext=false;
		}
		if (isNext)
			vector=readNextArticle(category, count, nItems);
		else
			vector=readPrevArticle(category, count, nItems);

		// print html header
		// printListHeader(out);

		// print table header
		out.println("<FONT SIZE=2 COLOR=\"#0000FF\">");
		out.println("<TABLE BORDER=0 WIDTH=610><TR>");
		out.println("<TD ALIGN=LEFT><FONT SIZE=2><B>"+convertToASCII("ÀüÃ¼ ")+totalCount+convertToASCII("°³ ±Û")+"</B></FONT></TD>");
		out.println("</TR></TABLE></FONT>");

		out.println("<TABLE BORDER=0 BGCOLOR=\"#C0FFD5\" WIDTH=610>");
		out.println("<TR BGCOLOR=\"CCCCCC\">");
		out.println("<TH><!--document type(image)//--></TH>");
		out.println("<TH><FONT SIZE=2><B>"+convertToASCII("¹ø È£")+"</B></FONT></TH>");
		out.println("<TH><FONT SIZE=2><B>"+convertToASCII("±Û¾´ÀÌ")+"</B></FONT></TH>");
		out.println("<TH><FONT SIZE=2><B>"+convertToASCII("±Û¾´ ³¯Â¥")+"</B></FONT></TH>");
		out.println("<TH><FONT SIZE=2><B>"+convertToASCII("Á¦ ¸ñ")+"</B></FONT></TH>");
		out.println("</TR>");
		int size=vector.size();
		try {
			for (int i=0; i<size; i++) {
				data=(BBSData) vector.elementAt(i);
				// print article header
				out.println("<TR>");
				out.println("<TD ALIGN=LEFT BGCOLOR=\"#CCCCCC\"><IMG SRC=\""+docuImageURL+"\" ALT=\"*\"></TD>");
				out.println("<TD ALIGN=CENTER><FONT SIZE=2>"+data.articleNo+"</FONT></TD>");
				if (data.webAddress!=null) {
					out.print("<TD ALIGN=CENTER><FONT SIZE=2>");
					printLink(out, data.name, data.webAddress.toString());
					out.println("</FONT></TD>");
				}
				else if (data.emailAddress!=null) {
					out.print("<TD ALIGN=CENTER><FONT SIZE=2>");
					printLink(out, data.name, data.emailAddress.toString());
					out.println("</FONT></TD>");
				}
				else
					out.println("<TD ALIGN=CENTER><FONT SIZE=2>"+data.name+"</FONT></TD>");
				out.println("<TD ALIGN=CENTER><FONT SIZE=2>"+convertToASCII(formatDateSimple(data.logDate))+"</FONT></TD>");
				out.print("<TD ALIGN=CENTER><FONT SIZE=2>");
				printLink(out, data.title, printURL+"?cate="+category+"&number="+data.articleNo);
				out.println("</FONT></TD>");
				out.println("</TR>");
				// print successive threads
				for (Enumeration enum = data.nextNumbers.elements(); enum.hasMoreElements(); ) {
					int nextNo=((Integer) enum.nextElement()).intValue(); 
					printNextThreadTitle(out, category, nextNo, 0);
				}
			}
			beginIndex=((BBSData) vector.elementAt(size-1)).articleNo;
			endIndex=((BBSData) vector.elementAt(0)).articleNo;
		} catch (ArrayIndexOutOfBoundsException e) {
		}
		out.println("</TABLE>");

		// print html footer
		// printListFooter(out, category, beginIndex, endIndex);
		int index[]=new int[2];
		index[0]=beginIndex;
		index[1]=endIndex;
		return index;

	} // end of printArticleList()

	/**
	 * printDeleteChallenge()
	 * this method challenges to delete an article
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param articleNo article number to delete
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static void printDeleteChallenge(ServletOutputStream out, String category, int articleNo)
				throws IOException 
	{
		out.println("<CENTER><HR NOSHADE WIDTH=60%></CENTER>");
		out.println("<FORM METHOD=\"POST\" ACTION=\""+deleteResultURL+"\">");
		out.println("<TABLE><TR>");
		out.println("<TD>");
		out.println(convertToASCII("¾Ï È£"));
		out.println("</TD><TD><INPUT NAME=\"PASSWORD_FIELD\" TYPE=\"PASSWORD\" SIZE=8>");
		out.println("</TD></TR><TR><TD><INPUT NAME=\"SUBMIT_BUTTON\" TYPE=\"SUBMIT\" ");
		out.println("VALUE=\""+convertToASCII("±â»ç »èÁ¦")+"\"></TD>");
		out.println("</TR></TABLE>");
		out.println("<INPUT NAME=\"number\" TYPE=\"HIDDEN\" VALUE="+articleNo+">");
		out.println("<INPUT NAME=\"cate\" TYPE=\"HIDDEN\" VALUE="+category+">");
		out.println("</FORM>");
	} // end of printDeleteChallenge()

	/**
	 * printDeleteResult()
	 * this method prints the result of deleting an article
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param int articleNo article number to delete
	 * @param String password for authentication
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static void printDeleteResult(ServletOutputStream out, String category, int articleNo, String password)
				throws IOException 
	{
		if (articleNo<0) { // delete operation needs to know accurate article number
			out.println("<FONT SIZE=2 COLOR=\"#0000FF\">");
			out.println(convertToASCII("Àß¸øµÈ ±â»ç ¹øÈ£ÀÔ´Ï´Ù."));
			out.println("</FONT>");
			out.println("<TABLE BORDER=0 WIDTH=610><TR><TD><HR NOSHADE></TD></TR>");
			out.println("</TABLE>");
			return;
		}
		BBSData oldData=new BBSData();
		oldData=readData(category, articleNo);
		out.println("<FONT SIZE=2 COLOR=\"#0000FF\">");
		if (password.equals(oldData.passwd) || password.equals(masterKey)) {
			boolean isDeleted=removeData(category, articleNo);

			if (isDeleted) 
				out.println(articleNo+convertToASCII("¹øÂ° ±â»ç°¡ »èÁ¦µÇ¾ú½À´Ï´Ù."));
			else 
				out.println(articleNo+convertToASCII("¹øÂ° ±â»ç »èÁ¦¿¡ ½ÇÆÐÇÏ¿´½À´Ï´Ù."));
		}
		else {
			out.println(convertToASCII("ÆÐ½º¿öµå°¡ ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù."));
//			out.println("DEBUG:old="+oldData.passwd+" new="+password);
		}
		out.println("</FONT>");
		out.println("<TABLE BORDER=0 WIDTH=610><TR><TD><HR NOSHADE></TD></TR>");
		out.println("</TABLE>");
		printArticleList(out, category, articleNo, 10, false);
	} // end of printDeleteResult()

	/**
	 * printPostChallenge()
	 * this method challenges to post an article
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static void printPostChallenge(ServletOutputStream out, String category, int replyTo)
				throws IOException 
	{
		boolean isReply=false;
		BBSData data=null;
		if (replyTo > 0) {
			try {
				data=readData(category, replyTo);
				if (data !=null)
					isReply=true;
			} catch (IOException ie) {
			}
		}
		out.println("<SCRIPT LANGUAGE=\"JavaScript\">");
		out.println("function submit_page(form) {");
		out.println("\tfoundError = false;");
		out.println("\tif(isFieldBlank(form.NAME_FIELD)) {");
		out.print("\t\talert(");
		out.print(convertToASCII("\"ÀÌ¸§ÀÌ ºüÁ³½À´Ï´Ù.\""));
		out.println(");");
		out.println("\t\tfoundError = true;");
		out.println("\t}");
		out.println("\tif(foundError == false && isFieldBlank(form.TITLE_FIELD) == true) {");
		out.print("\t\tform.TITLE_FIELD.value=\"");
		out.print(convertToASCII("Á¦¸ñ¾øÀ½"));
		out.println("\";\n\t}");
		out.println("\tif(foundError == false && isFieldBlank(form.COMMENT_FIELD) == true) {");
		out.print("\t\talert(\"");
		out.print(convertToASCII("³»¿ëÀÌ ¾ø½À´Ï´Ù."));
		out.println("\");");
		out.println("\t\tfoundError = true;\n\t}"); 
		out.println("\tif(foundError == false) {");
		out.println("\t\tdocument.forms[0].submit();\n\t}\n}");
		out.println("function isFieldBlank(theField) {");
		out.println("\tif(theField.value == \"\")");
		out.println("\t\treturn true;\n\telse\n\t\treturn false;\n}");
		out.println("</SCRIPT>");
		out.print("<CENTER><B><FONT SIZE=3>");
		if (isReply)
			out.print(convertToASCII("ÀÌ¾î¼­ ±Û ¾²±â"));
		else
			out.print(convertToASCII("°Ô½ÃÆÇ¿¡ ±Û ¾²±â"));
		out.print("</FONT></B></CENTER>\n<CENTER><FONT COLOR=\"#3333FF\"><FONT SIZE=-1>");
		out.print(convertToASCII("Let's ²ôÀû²ôÀû..."));
		out.print("</FONT></FONT></CENTER>\n<CENTER>\n<HR NOSHADE WIDTH=60%></CENTER>\n");
		out.println("<FORM METHOD=\"POST\" ACTION=\""+postResultURL+"\">");
		out.print("<TABLE>\n<TR>\n<TD ALIGN=RIGHT>");
		out.print(convertToASCII("ÀÌ ¸§"));
		out.println("</TD>");
		out.println("<TD><INPUT NAME=\"NAME_FIELD\" TYPE=\"TEXT\" SIZE=50></TD>");
		out.print("</TR>\n<TR>\n<TD ALIGN=RIGHT>");
		out.print(convertToASCII("¾Ï È£"));
		out.println("</TD>\n<TD><INPUT NAME=\"PASSWORD_FIELD\" TYPE=\"PASSWORD\" SIZE=8></TD>");
		out.print("</TR>\n<TR>\n<TD></TD>\n</TR>\n<TR>\n<TD ALIGN=RIGHT>");
		out.print(convertToASCII("E-Mail ÁÖ¼Ò(Optional)"));
		out.println("</TD>\n<TD><INPUT NAME=\"EMAIL_FIELD\" TYPE=\"TEXT\" SIZE=50></TD>");
		out.print("</TR>\n<TR>\n<TD ALIGN=RIGHT>");
		out.print(convertToASCII("È¨ÆäÀÌÁö URL(Optional)"));
		out.println("</TD>\n<TD><INPUT NAME=\"WEB_FIELD\" TYPE=\"TEXT\" SIZE=50></TD>");
		out.print("</TR>\n<TR>\n<TD ALIGN=RIGHT VALIGN=TOP>");
		out.print(convertToASCII("Á¦ ¸ñ"));
		if (isReply) {
			out.print("</TD>\n<TD><INPUT NAME=\"TITLE_FIELD\" SIZE=50 VALUE=\""+data.title);
			out.print(convertToASCII("ÀÇ ´äº¯"));
			out.println("\"></TD>\n</TR>\n<TR>");
		}
		else
			out.println("</TD>\n<TD><INPUT NAME=\"TITLE_FIELD\" SIZE=50></TD>\n</TR>\n<TR>");
		out.print("<TD ALIGN=RIGHT VALIGN=TOP>");
		out.print(convertToASCII("²ôÀû²ôÀû"));
		out.println("</TD>\n<TD><TEXTAREA NAME=\"COMMENT_FIELD\" ROWS=10 COLS=50></TEXTAREA></TD>");
		out.println("</TR>\n<TR>\n<TD></TD>");
		out.print("<TD><INPUT NAME=\"SUBMIT_BUTTON\" TYPE=\"BUTTON\" VALUE=");
		out.print(convertToASCII("\"   ±Û ¿Ã¸®±â   \""));
		out.println(" OnClick=\"submit_page(this.form)\">");
		out.print("&nbsp;<INPUT NAME=\"RESET_BUTTON\" TYPE=\"RESET\" VALUE=");
		out.print(convertToASCII("\"³»¿ë ´Ù½Ã ¾²±â\""));
		out.println("></TD>\n</TR>\n</TABLE>");
		out.println("<INPUT NAME=\"cate\" TYPE=\"HIDDEN\" VALUE="+category+">");
		if (isReply) // considered when data is null
			out.println("<INPUT NAME=\"replyto\" TYPE=\"HIDDEN\" VALUE="+replyTo+">");
		out.println("</FORM>");
	} // end of printPostChallenge()

	/**
	 * printAdminEditPage()
	 * this method prints a page for admin to edit article manually
	 * @param ServletOutputStream output stream to print
	 * @see yoonforh.bbs.BBSData
	 * @exception java.io.IOException
	 */
	static void printAdminEditPage(ServletOutputStream out)
				throws IOException 
	{
		String title=convertToASCII("±â»ç ÆíÁý(°ü¸®ÀÚ)");
		printTitle(out, title);
		out.println("<CENTER><H1>"+title+"</H1></CENTER>");

		out.println("<CENTER><HR NOSHADE WIDTH=60%></CENTER>");
		out.println("<FORM METHOD=\"POST\" ACTION=\""+adminEditChallengeURL+"\">");
		out.println("<TABLE>");
		out.println("<TR>");
		out.print("<TD>");
		out.print(convertToASCII("¹üÁÖ"));
		out.println("</TD>");
		out.println("<TD><INPUT NAME=\"cate\" SIZE=15></TD>");
		out.println("</TR>");
		out.println("<TR>");
		out.print("<TD>");
		out.print(convertToASCII("±â»ç ¹øÈ£"));
		out.println("</TD>");
		out.println("<TD><INPUT NAME=\"number\" SIZE=15></TD>");
		out.println("</TR>");
		out.println("<TR>");
		out.print("<TD>");
		out.print(convertToASCII("°ü¸®ÀÚ ¾ÏÈ£"));
		out.println("</TD>");
		out.println("<TD><INPUT NAME=\"PASSWORD_FIELD\" TYPE=\"PASSWORD\" SIZE=8></TD>");
		out.println("</TR>");
		out.print("<TR><TD><INPUT NAME=\"SUBMIT_BUTTON\" TYPE=\"SUBMIT\" ");
		out.println("VALUE=\""+convertToASCII("±â»ç ÆíÁý")+"\"></TD>");
		out.println("</TR></TABLE>");
		out.println("</FORM>");

		out.println("<TABLE BORDER=0 WIDTH=610><TR><TD><HR NOSHADE></TD></TR>");
		out.println("<TR><TD ALIGN=LEFT> <FONT SIZE=2 COLOR=\"#743014\">");
		printLink(out, convertToASCII("ÀüÃ¼ È¨ÆäÀÌÁö·Î ÀÌµ¿"), homeURL);
		out.println("</TD></TR>");
		out.println("</TABLE>");
		out.println("</BODY>\n</HTML>");
	} // end of printAdminEditPage()

	/**
	 * printAdminEditChallenge()
	 * this method prints article in edit form for administrator
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param BBSData data article content to update
	 * @exception java.io.IOException
	 */
	static void printAdminEditChallenge(ServletOutputStream out, String category, BBSData data)
				throws IOException 
	{
		if (data==null) {
			out.println("<H1>Specified article not found!</H1>");
			return;
		}
		out.println("<CENTER><HR NOSHADE WIDTH=60%></CENTER><FORM METHOD=\"POST\" ACTION=\""+adminEditResultURL+"\">");
		out.println("<TABLE><TR><TD ALIGN=RIGHT>");
		out.println(convertToASCII("ÀÌ ¸§"));
		if (data.name!=null)
			out.println("</TD><TD><INPUT NAME=\"NAME_FIELD\" TYPE=\"TEXT\" SIZE=50 VALUE=\""+data.name+"\"></TD></TR>");
		else
			out.println("</TD><TD><INPUT NAME=\"NAME_FIELD\" TYPE=\"TEXT\" SIZE=50></TD></TR>");
		out.println("<TR><TD ALIGN=right>");
		out.println(convertToASCII("¾Ï È£"));
		out.println("</TD><TD><INPUT NAME=\"PASSWORD_FIELD\" SIZE=8></TD></TR>"); // show password to adminer
		out.println("<TR><TD></TD></TR><TR><TD ALIGN=RIGHT>");
		out.println("E-Mail "+convertToASCII("ÁÖ¼Ò")+"(Optional)</TD>");
		if (data.emailAddress!=null)
			out.println("<TD><INPUT NAME=\"EMAIL_FIELD\" TYPE=\"TEXT\" SIZE=50 VALUE=\""+data.emailAddress.toString()+"\"></TD></TR>");
		else
			out.println("<TD><INPUT NAME=\"EMAIL_FIELD\" TYPE=\"TEXT\" SIZE=50></TD></TR>");
		out.println("<TR><TD ALIGN=RIGHT>"+
			convertToASCII("È¨ÆäÀÌÁö")+" URL(Optional)</TD>");
		if (data.webAddress!=null)
			out.println("<TD><INPUT NAME=\"WEB_FIELD\" TYPE=\"TEXT\" SIZE=50 VALUE=\""+data.webAddress.toString()+"\"></TD></TR>");
		else
			out.println("<TD><INPUT NAME=\"WEB_FIELD\" TYPE=\"TEXT\" SIZE=50></TD></TR>");
		out.println("<TR><TD ALIGN=RIGHT VALIGN=TOP>"+convertToASCII("Á¦ ¸ñ")+"</TD>");
		if (data.title!=null)
			out.println("<TD><INPUT NAME=\"TITLE_FIELD\" SIZE=50 VALUE=\""+data.title+"\"></TD></TR>");
		else
			out.println("<TD><INPUT NAME=\"TITLE_FIELD\" SIZE=50></TD></TR>");
		out.println("<TR><TD ALIGN=RIGHT VALIGN=TOP>"+convertToASCII("²ôÀû²ôÀû")+"</TD>");
		if (data.data!=null)
			out.println("<TD><TEXTAREA NAME=\"COMMENT_FIELD\" ROWS=10 COLS=50>"+data.data+"</TEXTAREA></TD></TR>");
		else
			out.println("<TD><TEXTAREA NAME=\"COMMENT_FIELD\" ROWS=10 COLS=50></TEXTAREA></TD></TR>");
		out.println("<TR><TD ALIGN=right>");
		out.println(convertToASCII("¾Õ ±Û(thread)"));
		out.println("</TD><TD><INPUT NAME=\"replyto\" SIZE=10 VALUE=\""+data.prevNo+"\"></TD></TR>");
		out.println("<TR><TD ALIGN=right>");
		out.println(convertToASCII("´ÙÀ½ ±Ûµé(thread)"));
		out.print("</TD><TD><INPUT NAME=\"nexts\" SIZE=30 VALUE=\"");
		for (Enumeration enum = data.nextNumbers.elements(); enum.hasMoreElements(); ) {
			out.print( (((Integer) enum.nextElement()).intValue())+"," );
		}
		out.println("\"></TD></TR>");
		out.println("<INPUT NAME=\"cate\" TYPE=\"HIDDEN\" VALUE="+category+">");
		out.println("<INPUT NAME=\"number\" TYPE=\"HIDDEN\" VALUE="+data.articleNo+">");
		out.println("<TR><TD></TD><TD><INPUT NAME=\"SUBMIT_BUTTON\" TYPE=\"SUBMIT\" VALUE=");
		out.println(convertToASCII("\"   ±â»ç °íÄ¡±â   \"")+"></TD></TR></TABLE>");
		out.println("</FORM>");

	}

	/**
	 * printAdminEditResult()
	 * this method prints the result of update an article for administrator
	 * @param ServletOutputStream output stream to print
	 * @param String category to specify bbs
	 * @param BBSData newData article content to update
	 * @exception java.io.IOException
	 */
	static void printAdminEditResult(ServletOutputStream out, String category, BBSData newData)
				throws IOException 
	{
		boolean isUpdated=updateDataWithAuth(category, newData);
		if (isUpdated) {
			printArticle(out, category, newData);
		}
		else {
			out.println("<FONT SIZE=2 COLOR=\"#0000FF\">");
			out.print(convertToASCII("ÆÐ½º¿öµå°¡ ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù."));
			out.println("<BR>");
			out.println(convertToASCII("±â»ç °»½Å¿¡ ½ÇÆÐÇÏ¿´½À´Ï´Ù."));
			out.println("</FONT>");
			return;
		}
	}

	/**
	 * makeLink()
	 * this method add link to specified string
	 * @param string string to make link
	 * @param URLString indicates link
	 * @return String string with URL anchor
	 */
	private static String makeLink(String string, String URLString)
	{
		return ("<A HREF=\""+URLString+"\">"+string+"</A>");
	} // end of makeLink()

	/**
	 * printLink()
	 * this method add link to specified string
	 * @param ServletOutputStream out stream to print
	 * @param string string to make link
	 * @param URLString indicates link
	 * @exception java.io.IOException
	 */
	private static void printLink(ServletOutputStream out, String string, String URLString)
				throws IOException 
	{
		out.print("<A HREF=\"");
		out.print(URLString);
		out.print("\">");
		out.print(string);
		out.print("</A>");
	} // end of printLink()

	/**
	 * formatDateSimple()
	 * this method prints formatted article
	 * @param Date date to format
	 * @return String formatted date
	 */
	private static String formatDateSimple(Date date)
	{
		final int millisPerHour = 60 * 60 * 1000;

		SimpleDateFormat fmt= new SimpleDateFormat();
		fmt=(SimpleDateFormat) DateFormat.getDateTimeInstance(
				DateFormat.SHORT, DateFormat.SHORT, Locale.KOREAN);
		SimpleTimeZone timeZone = new SimpleTimeZone(9*millisPerHour,"KST");
		fmt.setTimeZone(timeZone);

		return fmt.format(date);
	} // end of formatDateSimple()

	/**
	 * formatDateInKorean()
	 * this method prints formatted article
	 * @param Date date to format
	 * @return String formatted date
	 */
	private static String formatDateInKorean(Date date)
	{
		final int millisPerHour = 60 * 60 * 1000;

		SimpleDateFormat fmt= new SimpleDateFormat();
		fmt=(SimpleDateFormat) DateFormat.getDateTimeInstance(
				DateFormat.FULL, DateFormat.FULL, Locale.KOREAN);
		SimpleTimeZone timeZone = new SimpleTimeZone(9*millisPerHour,"KST");
		fmt.setTimeZone(timeZone);

		return fmt.format(date);
	} // end of formatDateInKorean()

	/**
	 * convertToCp949()
	 * this method converts ISO8859_1 encoded character to Cp949 encoded
	 * @param String string to converted
	 * @return String converted string
	 */
	private static String convertToCp949(String str) {
		String result=null;
		try {
			byte[] rawBytes=str.getBytes("8859_1");
			result=new String(rawBytes, "Cp949");
		} catch (java.io.UnsupportedEncodingException e) {
			System.err.println(e.toString());
		}
		return  result;
	}

	/**
	 * convertToASCII()
	 * this method converts Cp949 encoded character to ISO8859_1 encoded
	 * @param String string to converted
	 * @return String converted string
	 */
	private static String convertToASCII(String str) {
		try {
			byte[] kscBytes=str.getBytes("Cp949");
			str=new String(kscBytes, "8859_1");
		} catch (java.io.UnsupportedEncodingException e) {
			System.err.println(e.toString());
		}
		return  str;
	}
} // end of class BBSUtil
