
package jp.riken.brain.ni.samuraigraph.base;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

import javax.print.attribute.standard.MediaSize;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


/**
 * A class with utility methods which are used to transform a text string
 * into a value such as number or color, and vice versa.
 * 
 */
public class SGUtilityText implements SGIConstants, SGIDrawingElementConstants, SGIPropertyFileConstants
{
	
	/**
	 * Array of default available date format.
	 */
	public static final String[] DEFAULT_DATE_FORMAT_ARRAY = {
		"yy/MM/dd",
		"yy.MM.dd",
		"yy-MM-dd",
		"yyyyMMdd",
		"yyMMdd",
		"yy MM dd",
		"yyyy MM dd"
	};


//	public static final DateFormat[] DEFAULT_DATE_FORMAT_ARRAY = {
//		DateFormat.getDateInstance( DateFormat.SHORT ),
//		new SimpleDateFormat("yy.MM.dd"),
//		new SimpleDateFormat("yy-MM-dd"),
//		new SimpleDateFormat("yyyyMMdd"),
//		new SimpleDateFormat("yyMMdd"),
//		new SimpleDateFormat("yy MM dd"),
//		new SimpleDateFormat("yyyy MM dd")
//	};


	// list of date format
	private static final ArrayList DATE_FORMAT_ARRAY_LIST = new ArrayList();



//	/**
//	 * 
//	 * @param args
//	 */
//	public static void main(String[] args) throws Exception
//	{
//System.out.println("<< start >>");
//
//		String str = "1.0 inch";
//		Number num = getLength( str, "pt" );
//		System.out.println(num);
//
///*
//		String[] strArray = {
//			"2005/05/01",
//			"05/05/01",
//			"05/5/1",
//			"2005.05.01",
//			"05.05.01",
//			"05.5.1",
//			"2005-05-01",
//			"2005-5-1",
//			"05-05-01",
//			"05-5-1",
//			"20050501",
//			"050501",
//			"2005 05 01",
//			"05 05 01"
//		};
//
//		for( int ii=0; ii<strArray.length; ii++ )
//		{
//			System.out.println( SGUtilityText.getDate( strArray[ii] ) + "  " + strArray[ii] );
//		}
//*/
//
//
///*
//Locale[] array = {
//	Locale.US, Locale.GERMANY, Locale.FRANCE, Locale.JAPAN, Locale.CHINESE, Locale.ITALIAN };
//for( int ii=0; ii<array.length; ii++ )
//{
//	Locale currentLocale = array[ii];
//	SimpleDateFormat formatter
//		= new SimpleDateFormat( "yyyy MMMM dd EEEE", currentLocale );
//	Date today = new Date();
//	String result = formatter.format( today );
//	System.out.println( "Locale: " + currentLocale.toString() );
//	System.out.println( "Result: " + result );
//	System.out.println();
//}
//*/
//
///*
//		char[] cArray = {
//			'a', 'b', 'c', '\\', '"', '\t', 'd', 'e', 'f', ' ',
//			'"', 'g', 'h', 'i', ' ', 'j', 'k', 'l', 'あ',
//			' ', 'm', 'n', ' ', '"', 'o', 'p', 'q', ' ',
//			'"', 'r', 's', 't', ' ', '"', 'u', '\f', 'v', 'w', '　', 'x'
//		};
//*/
//
///*
//		char[] cArray = {
//			'a', 'b', 'c', '"', 'd', 'e', 'f', ' ', 'g', 'h', 'i', '"'
//		};
//*/
//
//
//
///*
////		String str = "0.0   0.0    -1.0   1.0   \"a\"";
////		String str = "0.0  0.0\r1.0  1.0\n2.0  0.5\r\n3.0  2.0\r\n\r\n4.0  4.0";
////		String str = new String(cArray);
////System.out.println(str);
////System.out.println();
//
//		StringReader sr = new StringReader(str);
//		final BufferedReader br = new BufferedReader(sr);
//System.out.println("*****");
//		String line = null;
//		while( (line=SGUtilityText.readLine(br))!=null )
//		{
//			System.out.println(line);
//		}
//System.out.println("*****");
//System.out.println();
//*/
//
//
///*
//		ArrayList strList = new ArrayList();
//		SGUtilityText.tokenize(str,strList);
//
//System.out.println();
//for( int ii=0; ii<strList.size(); ii++ )
//{
//	String s = (String)strList.get(ii);
////	System.out.println(ii+" *"+s+"*");
//}
//System.out.println(strList);
//*/
//
//
///*
//String str = "G^{(0)}_n(x_i,y_j) = x_i^{n-1}+y_j^n-1";
//
//		ArrayList charList = new ArrayList();
//		ArrayList superList = new ArrayList();
//		ArrayList subList = new ArrayList();
//		SGUtilityText.getSubscriptAndSuperscriptInfo(str,charList,superList,subList);
//
//		if( charList.size() != superList.size() )
//		{
//			throw new Error();
//		}
//		if( superList.size()!=subList.size() )
//		{
//			throw new Error();
//		}
//
//System.out.println();
//System.out.println(str);
//System.out.println();
//for( int ii=0; ii<charList.size(); ii++ )
//{
//	System.out.println(ii+"  "+charList.get(ii)+"  "+superList.get(ii)+"  "+subList.get(ii) );
//}
//System.out.println();
//*/
//
///*
//		String str = "+.40e03";
//		Number num = SGUtilityText.getDouble(str);
//		System.out.println(num);
//*/
//
//
///*
//		String str = "aaa 345 \"\" bbb ccc";
//		ArrayList strList = new ArrayList();
//		SGUtilityText.tokenize(str,strList);
//System.out.println(strList);
//*/
//
///*
//		String str = "1.0 pt";
//		String s = removeUnit( str, str );
//		System.out.println("*"+s+"*");
//*/
//
//
//	}



	/**
	 * 
	 */
	public static String getSuperscriptString( final String a, final String b )
	{
		String str = a + "^{" + b + "}";
		return str;
	}



	/**
	 * 
	 */
	public static String getSubscriptString( final String a, final String b )
	{
		String str = a + "_{" + b + "}";
		return str;
	}



	/**
	 * 
	 * @param line			A line.
	 * @param bseList		List of base strings.
	 * @param superList		List of superscript strings.
	 * @param subList		List of subscript strings.
	 * @return
	 */
	public static boolean getSubscriptAndSuperscriptInfo(
		final String line,
		final ArrayList baseList,
		final ArrayList superList,
		final ArrayList subList )
	{

		if( line==null || baseList==null || superList==null || subList==null )
		{
			return false;
		}
		if( line.length() == 0 )
			return false;
		
		// working variables
		char c;
		int len = line.length();
		boolean in_super = false;
		boolean in_sub = false;
		boolean in_brace = false;
		boolean in_escape = false;
		int in_brace_stack = 0;
		String cache_base = "";
		String cache_super = "";
		String cache_sub = "";
		
		for(int i=0; i<len; i++) {
			c = line.charAt(i);
			if( in_super || in_sub ) {
				if( in_brace ) {
					if( in_escape ) {
						if ( in_super ){
							cache_super += '\\';
							cache_super += c;
						} else {
							cache_sub += '\\';
							cache_sub += c;
						}
						in_escape = false;
					} else {
						if( c == '\\' ) {
							in_escape = true;
						} else {
							if( c == '}' && in_brace_stack == 0) {
								in_brace = false;
								in_super = false;
								in_sub = false;
							} else {
								if ( c == '}' )	in_brace_stack--;
								if ( c == '{' )	in_brace_stack++;
								if ( in_super ){
									cache_super += c;
								} else {
									cache_sub += c;
								}
							}
						}
					}
				} else if ( c == '{' ) {
					in_brace = true;
					in_brace_stack = 0;
				} else {
					if ( in_escape == false ) {
						if ( c == '\\' ) {
							in_escape = true;
						} else {
							if ( in_super ){
								cache_super += c; 
								in_super = false;
							} else {
								cache_sub += c;
								in_sub = false;
							}
						}
					} else {
						if ( in_super ){
							cache_super += '\\';
							cache_super += c; 
							in_super = false;
						} else {
							cache_sub += '\\';
							cache_sub += c;
							in_sub = false;
						}
						in_escape = false;
					}
				}
			} else {
				if( c == '^' && in_escape == false ) {
					if( cache_base.length() == 0 || cache_super.length() != 0 )
						return false;
					in_super = true;
				} else 	if( c == '_' && in_escape == false ) {
					if( cache_base.length() == 0 || cache_sub.length() != 0 )
						return false;
					in_sub = true;
				} else {
					if ( c == '\\' && in_escape == false) {
						in_escape = true;
					} else {
						if ( cache_super.length() != 0 || cache_sub.length() != 0 ){
							// store cache string
							baseList.add( cache_base );
							if( cache_super.length() != 0)
								superList.add( cache_super );
							else
								superList.add( null );
							if( cache_sub.length() != 0)
								subList.add( cache_sub );
							else
								subList.add( null );
							// reset string cache
							cache_base = "";
							cache_super = "";
							cache_sub = "";
						}
						cache_base += c;
						in_escape = false;
					}
				}
			}
		}
		if( in_super || in_sub || in_brace || in_escape )
			return false;
	
		// flush cache strings..
		if ( cache_base.length() != 0 ){
			// store cache string
			baseList.add( cache_base );
			if( cache_super.length() != 0)
				superList.add( cache_super );
			else
				superList.add( null );
			if( cache_sub.length() != 0)
				subList.add( cache_sub );
			else
				subList.add( null );
		}
		return true;
	}
	
	/**
	 * get unescape string
	 * @return
	 */
	public static final String unescapeString( final String str ) {
		String ret = "";
		boolean in_escape = false;
		for( int ii=0; ii < str.length(); ii++ ) {
			char c = str.charAt(ii);
			if ( in_escape ) {
				ret += c;
				in_escape = false;
			} else {
				if (c == '\\')
					in_escape = true;
				else
					ret += c;
			}
		}
		return ret;
	}
	
	public static boolean tokenize(
			final String str,
			final ArrayList strList,
			final boolean isDataFile )
	{

		if( str==null | strList==null )
		{
			throw new IllegalArgumentException("str==null | strList==null");
		}

		if( str.length()==0 )
		{
			return true;
		}
		SGCSVTokenizer csvt = new SGCSVTokenizer(str, isDataFile);
		while (csvt.hasMoreTokens()) {
			try {
				String result = csvt.nextToken();
				strList.add(result);
			} catch (NoSuchElementException e) {
			}
		}
		return true;
	}


	/**
	 * 
	 * @param charList
	 * @return
	 */
	public static String createString( final ArrayList charList )
	{
		final char[] cArray = new char[charList.size()];
		for( int ii=0; ii<cArray.length; ii++ )
		{
			cArray[ii] = ((Character)charList.get(ii)).charValue();
		}
		final String str = new String(cArray);
		return str;
	}



	/**
	 * 長さが0で無い文字列を取得する
	 */
	public static String readLine( final BufferedReader br )
	{
		String line = null;
		try
		{
			while( true )
			{
				line = br.readLine();
				if( line == null )
				{
					return null;
				}

				if( line.length()!=0 )
				{
					break;
				}
			}
		}
		catch( Exception ex )
		{
			return null;
		}

		return line;
	}



	/**
	 * "{(r1,g1,b1,a1),(r2,g2,b2,a2),...}"の形で色リストの文字列を作成する
	 */
	public static String getColorListString( final List colorList )
	{
		String str = "{";

		for( int ii=0; ii<colorList.size(); ii++ )
		{
			final Color color = (Color)colorList.get(ii);
			final String strColor = SGUtilityText.getColorString(color);
			str += strColor;
			if( ii!=colorList.size()-1 )
			{
				str += ',';
			}
			else
			{
				str += "}";
			}
		}
		
		return str;
	}



	/**
	 * "key={(r1,g1,b1,a1),(r2,g2,b2,a2),...}"の形で色リストの情報を書き出す
	 */
	public static boolean writeColorListPropertyLine(
		final Writer writer, final String key, final List colorList )
		throws IOException
	{
		writer.write( key + "=" );
		String str = getColorListString( colorList );
		writer.write( str + "\n" );
		return true;
	}



	/**
	 * 引数で渡された文字列の最初の方から文字startを、最後の方から文字endを検索し、
	 * 最初に見つかった文字で挟まれた領域の文字列を返す
	 */
	public static String getInnerString( String str, int sChar, int eChar )
	{
		if( str==null )
		{
			return null;
		}

		if( str.length() < 2 )
		{
			return null;
		}

		int start = str.indexOf(sChar) +1;
		int end = str.lastIndexOf(eChar);
		if( end <= start )
		{
			return null;
		}

		String sub = str.substring( start, end );

		return sub;

	}


	//
	private static final int[] getIntegerArray( final String str )
	{
		if( str == null )
		{
			return null;
		}

		String sub = SGUtilityText.getInnerString( str, '(', ')' );

		StringTokenizer st = new StringTokenizer( sub, "," );
		ArrayList strList = new ArrayList();
		while( st.hasMoreTokens() )
		{
			strList.add( st.nextToken() );
		}

		int[] array = new int[strList.size()];
		try
		{
			for( int ii=0; ii<strList.size(); ii++ )
			{
				String sNum = (String)strList.get(ii);
				int num = Integer.valueOf( sNum ).intValue();
				array[ii] = num;
			}
		}
		catch( Exception ex )
		{
			ex.printStackTrace();
		}

		return array;
	}


	/**
	 * 引数で渡された"(r,g,b)" or "(r,g,b,a)"のフォーマットの色情報の文字列から色を作成して返す
	 */
	public static Color getColorFromString( final String str )
	{
		final int[] array = getIntegerArray( str );
		final int len = array.length;
		if( len!=4 & len!=3 )
		{
			return null;
		}
		for( int ii=0; ii<len; ii++ )
		{
			if( array[ii]<0 | array[ii]>255 )
			{
				return null;
			}
		}

		Color color = null;
		if( len==3 )
		{
			color = new Color( array[0], array[1], array[2] );
		}
		else if( len==4 )
		{
			color = new Color( array[0], array[1], array[2], array[3] );
		}

		return color;
	}


	/**
	 * 引数で渡された色から"(r,g,b,a)"のフォーマットで色情報の文字列を作成して返す
	 */
	public static String getColorString( final Color color )
	{
		final String strColor = new String( "("
			+ color.getRed() +","
			+ color.getGreen() +","
			+ color.getBlue() +","
			+ color.getAlpha() +")"
		);
		
		return strColor;
	}



	/**
	 * 
	 * @param value
	 * @return
	 */
	public static Boolean getBoolean( final String value )
	{
		if( value==null )
		{
			return null;
		}

		final String v = value.toLowerCase();
		Boolean b = null;
		if( Boolean.TRUE.toString().equals(v) )
		{
			b = Boolean.TRUE;
		}
		else if( Boolean.FALSE.toString().equals(v) )
		{
			b = Boolean.FALSE;
		}
		else
		{
			return null;
		}

		return b;
	}


	/**
	 * 
	 */
	public static Integer getInteger( final String value )
	{
		Number num = parse(value);
		if( num==null )
		{
			return null;
		}
		return new Integer( num.intValue() );
	}


	/**
	 * 
	 */
	public static Float getFloat( final String value )
	{
		Number num = parse(value);
		if( num==null )
		{
			return null;
		}
		return new Float( num.floatValue() );
	}


	/**
	 * 
	 */
	public static Double getDouble( final String value )
	{
		Number num = parse(value);
		if( num==null )
		{
			return null;
		}
		return new Double( num.doubleValue() );
	}

	/**
	 * 
	 * @param line
	 * @return
	 */
	public static String getCSVString( final String value )
	{
		final String SPACE = " ";
		final String TAB = "\t";
		boolean use_quote = false;
		String str = "";
		for(int ii=0; ii<value.length(); ii++){
			char c = value.charAt(ii);
			if( c== ',' ) {
				use_quote = true;
			} else if (c == '"'){
				use_quote = true;
				str += '"';
			}
			str += c;
		}
		if( value.startsWith(SPACE) || value.startsWith(TAB) || 
				value.endsWith(SPACE) || value.endsWith(TAB) )
			use_quote = true;
		if(use_quote)
			str = "\"" + str + "\"";
		return str;
	}

	/**
	 * 
	 * @param line
	 * @return
	 */
	public static List getColorList( final String line )
	{
		String sub = SGUtilityText.getInnerString( line, '{', '}' );
		if( sub==null )
		{
			return null;
		}

		ArrayList strColorList = SGUtilityText.getStringListFromCommaSeparatedLine( sub );
		ArrayList colorList = new ArrayList();
		for( int ii=0; ii<strColorList.size(); ii++ )
		{
			String strColor = (String)strColorList.get(ii);
			Color color = SGUtilityText.getColorFromString(strColor);
			colorList.add(color);
		}

		return colorList;
	}



	/**
	 * (str1),(str2),...,(strN)　→　文字列リスト
	 */
	public static ArrayList getStringListFromCommaSeparatedLine( String line )
	{
//System.out.println("*** getStringListFromCommaSeparatedLine ***");

		int fromIndex = 0;
		int begin;
		int end;

		ArrayList strList = new ArrayList();
		while( true )
		{
			begin = line.indexOf( "(", fromIndex );
			end = line.indexOf( ")", fromIndex );

//System.out.println("begin="+begin+"  "+"end="+end);

			if( begin >= end )
			{
				break;
			}

			String sub = line.substring( begin, end+1 );
			if( sub==null )
			{
				break;
			}

//System.out.println(sub);

			strList.add(sub);
			fromIndex = end+1;
		}

		return strList;
	}




	/**
	 * 
	 */
	public static String getFontStyleName( final int style )
	{

		String name = null;
		switch( style )
		{

			case Font.PLAIN :
			{
				name = FONT_PLAIN;
				break;
			}

			case Font.BOLD :
			{
				name = FONT_BOLD;
				break;
				
			}

			case Font.ITALIC :
			{
				name = FONT_ITALIC;
				break;
				
			}

			case ( Font.BOLD | Font.ITALIC ) :
			{
				name = FONT_BOLD_ITALIC;
				break;
				
			}

			default :
			{
				
			}

		}

		return name;
	}


	/**
	 * 
	 */
	public static int getFontStyle( final String name )
	{

		if( name==null )
		{
			return -1;
		}

		final String str = name.toLowerCase();

		int style;
		if( str.equals( FONT_PLAIN.toLowerCase() ) )
		{
			style = Font.PLAIN;
		}
		else if( str.equals( FONT_BOLD.toLowerCase() ) )
		{
			style = Font.BOLD;
		}
		else if( str.equals( FONT_ITALIC.toLowerCase() ) )
		{
			style = Font.ITALIC;
		}
		else if( str.equals( FONT_BOLD_ITALIC.toLowerCase() ) )
		{
			style = Font.BOLD | Font.ITALIC;
		}
		else
		{
			style = -1;
		}

		return style;
	}



	/**
	 * 
	 */
	public static String getScaleTypeName( final int type )
	{

		String name = null;
		switch( type )
		{
			case SGAxis.LINEAR_SCALE :
			{
				name = SGIConstants.SCALE_TYPE_LINEAR;
				break;
			}
			
			case SGAxis.LOG_SCALE :
			{
				name = SGIConstants.SCALE_TYPE_LOG;
				break;
			}

			default :
			{
				
			}
		}

		return name;
	}



	/**
	 * 
	 */
	public static int getScaleType( final String name )
	{
		if( name==null )
		{
			return -1;
		}

		String str = name.toLowerCase();

		int type = -1;
		if( str.equals( SCALE_TYPE_LINEAR.toLowerCase() ) )
		{
			type = SGAxis.LINEAR_SCALE;
		}
		else if( str.equals( SCALE_TYPE_LOG.toLowerCase() ) )
		{
			type = SGAxis.LOG_SCALE;
		}

		return type;
	}



	/**
	 * 
	 * @param spec
	 * @return
	 */
	public static Document getDocument( String spec )
	{
		Document doc = null;

		// create an URL instance
		try
		{
			URL url = new URL(spec);
			doc = getDocument(url);
		}
		catch( MalformedURLException ex )
		{
			return null;
		}

		return doc;
	}


	/**
	 * property entity resolver
	 * @author okumura
	 *
	 */
	private static class PropertyEntityResolver implements EntityResolver
	{
		public InputSource resolveEntity( final String publicId, final String systemId )
		throws SAXException, IOException
		{
			if( publicId.startsWith( PROPERTY_FILE_PUBLIC_ID ) &&
			    systemId.startsWith( PROPERTY_FILE_SYSTEM_ID ) )
			{
				String name = SGIConstants.RESOURCES_DIRNAME + PROPERTY_DTD_FILE_NAME;
				InputStream in = this.getClass().getResourceAsStream( name );
				return new InputSource( in );
			}
			return null;
		}
	}

	private static EntityResolver mPropertyEntityResolver = new PropertyEntityResolver();
	
	/**
	 * 
	 * @return
	 * @throws Exception
	 */
	public static Document getDocument( URL url )
	{
		Document doc = null;
		InputStream bis = null;
		// get input stream
		try
		{
			bis =  new BufferedInputStream( url.openStream() );
		}
		catch( IOException ex )
		{
			ex.printStackTrace();
			return null;
		}
		// parse input and create a Document object
		try
		{
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			builder.setEntityResolver( SGUtilityText.mPropertyEntityResolver );
			doc = builder.parse( bis, url.toString() );
		}
		catch( Exception ex )
		{
			return null;
		}
		finally
		{
			// close the input stream
			try
			{
				if( bis != null )
					bis.close();
			}
			catch( IOException ex )
			{
			}
		}

		return doc;
	}

	
	
	/**
	 * 
	 * @param str
	 * @return
	 */
	private static Number parse( final String str )
	{
		if( str==null )
		{
			return null;
		}

		String strNew = str;

		// remove the first character of '+'
		if( str.startsWith("+") )
		{
			if( str.length() > 1 )
			{
				strNew = strNew.substring(1);
			}
		}

		// from e to E
		strNew = strNew.toUpperCase();
		
		// from E+ to E
		strNew = strNew.replaceAll( "E\\+", "E" );

		// parse the string
		ParsePosition pos = new ParsePosition(0);
		Number num = NumberFormat.getInstance().parse( strNew, pos );
		if( pos.getIndex()!=strNew.length() )
		{
			return null;
		}

		return num;
	}
	
	

	/**
	 * 
	 * @param str
	 * @return
	 */
	public static Number getLengthInPoint( final String str )
	{
		return getLength( str, pt );
	}


	/**
	 * 
	 * @param str
	 * @return
	 */
	public static Number getLength( final String str, final String unit )
	{
		if( str==null )
		{
			return null;
		}
		
		if( str.length()==0 )
		{
			return null;
		}

		final String ls = str.toLowerCase();
		final String[] units = getUnitsArrayOfLength();
		for( int ii=0; ii<units.length; ii++ )
		{
			if( ls.endsWith( units[ii] ) )
			{
				Number num = removeUnit( ls, units[ii] );
				if( num!=null )
				{
					return new Double( convert( num.doubleValue(), units[ii], unit ) );
				}
			}
		}

		return null;
	}

	
	
	/**
	 * 
	 * @param str
	 * @param unit
	 * @return
	 */
	public static Float getFloat( final String str, final String unit )
	{
		Number num = SGUtilityText.removeUnit(str,unit);
		if( num==null )
		{
			return null;
		}
		return new Float( num.floatValue() );
	}

	
	/**
	 * 
	 * @param str
	 * @param unit
	 * @return
	 */
	public static Double getDouble( final String str, final String unit )
	{
		Number num = SGUtilityText.removeUnit(str,unit);
		if( num==null )
		{
			return null;
		}
		return new Double( num.doubleValue() );
	}

	
	
	/**
	 * Check whether the string is valid.
	 * @param str	a string
	 * @return		whether the string is valid
	 */
	public static boolean isValidString( final String str )
	{
		if( str==null )
		{
			return false;
		}

		final int len = str.length();
		
		// if the length of the string equals to zero
		if( len==0 )
		{
			return false;
		}

		// if the string consists of spaces
		boolean flag = false;
		for( int ii=0; ii<len; ii++ )
		{
			final char c = str.charAt(ii);
			if( c!=' ' && c!=twoByteSpaceChar && c!='\t' && c!='\n' && c!='\r' && c!='\f' )
			{
				flag = true;
				break;
			}
		}
		if( !flag )
		{
			return false;
		}

		return true;
	}



	/**
	 * 
	 * @param list
	 * @param name
	 * @return
	 */
	public static String getSerialName(
		List list, final String name )
	{
		String nameNew = name;

		int max = -1;
		for( int ii=0; ii<list.size(); ii++ )
		{
			String str = (String)list.get(ii);
			if( str.startsWith(name) )
			{
				String sub = str.substring( name.length() );
				if( sub.startsWith("(") & sub.endsWith(")") )
				{
					String sub2 = sub.substring(1,sub.length()-1);
					Integer index = SGUtilityText.getInteger(sub2);
					if( index!=null )
					{
						int n = index.intValue();
						if( n > max )
						{
							max = n;
						}
					}
				}
				else if( sub.equals("") )
				{
					max = 0;
				}
			}
		}

		if( max!=-1 )
		{
			max++;
			nameNew += "(" + max + ")";
		}

		return nameNew;
	}



	/**
	 * Add an available date format
	 * @param format
	 */
	public static void addAvailableDateFormat( final String format )
	{
		DATE_FORMAT_ARRAY_LIST.add( new SimpleDateFormat( format ) );
	}


	
	/**
	 * Parse the string and returns a Date object.
	 * @param str - a text string to be parsed
	 * @return a Date object
	 */
	public static Date getDate( final String str )
	{
		Date date = null;

		ArrayList list = DATE_FORMAT_ARRAY_LIST;
		for( int ii=0; ii<list.size(); ii++ )
		{
			DateFormat df = (DateFormat)list.get(ii);

			// parse the string
			ParsePosition pos = new ParsePosition(0);
			Date d = df.parse( str, pos );
			if( pos.getIndex()==str.length() )
			{
				if( d!=null )
				{
					date = d;
					break;
				}
			}
		}

		return date;
	}



	/**
	 * 
	 * @return
	 */
	public static String[] getUnitsArrayOfLength()
	{
		return new String[]{ SGIConstants.cm, SGIConstants.mm, SGIConstants.pt, SGIConstants.inch };
	}



	/**
	 * 
	 * @param unit
	 * @return
	 */
	public static boolean isLengthUnit( final String unit )
	{
		if( unit==null )
		{
			throw new IllegalArgumentException();
		}
		
		final String u = unit.toLowerCase();
		final String[] array = SGUtilityText.getUnitsArrayOfLength();
		boolean flag = false;
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii].equals(u) )
			{
				flag = true;
				break;
			}
		}
		return flag;
	}



	/**
	 * 
	 * @param value
	 * @param unitOld
	 * @param unitNew
	 * @return
	 */
	public static double convert(
		final double value, final String unitOld, final String unitNew )
	{
		// check
		if( unitOld==null | unitNew==null )
		{
			throw new IllegalArgumentException();
		}

		if( !isLengthUnit( unitOld ) | !isLengthUnit( unitNew ) )
		{
			throw new IllegalArgumentException();
		}

		final String uOld = unitOld.toLowerCase();
		final String uNew = unitNew.toLowerCase();

		// no needs of conversion
		if( uOld.equals(uNew) )
		{
			return value;
		}

		final String[] units = SGUtilityText.getUnitsArrayOfLength();
		final float[] ratioArray = {
			1.0f, 0.10f, CM_POINT_RATIO, CM_INCH_RATIO
		};
		int indexOld = -1;
		for( int ii=0; ii<units.length; ii++ )
		{
			if( units[ii].equals(uOld) )
			{
				indexOld = ii;
				break;
			}
		}
		if( indexOld==-1 )
		{
			throw new IllegalArgumentException();
		}
		int indexNew = -1;
		for( int ii=0; ii<units.length; ii++ )
		{
			if( units[ii].equals(uNew) )
			{
				indexNew = ii;
				break;
			}
		}
		if( indexNew==-1 )
		{
			throw new IllegalArgumentException();
		}
	
		final float ratio = ratioArray[indexOld]/ratioArray[indexNew];
	
		return value*ratio;
	}



	/**
	 * 
	 * @param value
	 * @param unit
	 * @return
	 */
	public static double convertToPoint( final double value, final String unit )
	{
		return convert( value, unit, pt );
	}


	/**
	 * 
	 * @param value
	 * @param unit
	 * @return
	 */
	public static double convertFromPoint( final double value, final String unit )
	{
		return convert( value, pt, unit );
	}


	/**
	 * Parse and convert to the given unit.
	 * ex. str - 1 inch , unit - cm is converted to "2.54"
	 * @param str - a string to be parsed
	 * @return parsed string
	 */
	public static String convertString( final String str, final String unit )
	{
		if( str==null | unit==null )
		{
			return null;
		}
		if( str.length()==0 | unit.length()==0 )
		{
			return null;
		}

		final String s = str.toLowerCase();
		final String u = unit.toLowerCase();
	
		// check whether the string ends with the suffix of unit
		String[] unitsArray = SGUtilityText.getUnitsArrayOfLength();
		String suffix = null;
		for( int ii=0; ii<unitsArray.length; ii++ )
		{
			if( str.endsWith( unitsArray[ii] ) )
			{
				suffix = unitsArray[ii];
				break;
			}
		}
		if( suffix==null )
		{
			return null;
		}
	
		Number value = SGUtilityText.removeUnit( s, suffix );
		if( value==null )
		{
			return null;
		}
		final double num = value.doubleValue();

		//
		final double numNew = SGUtilityText.convert( num, suffix, u );
		String valueNew = Double.toString( numNew );
	
		return valueNew;
	}



	/**
	 * Remove the unit from a text string, parse and create a Number object.
	 * @param str - a text string to be parsed
	 * @param unit - a text string of unit to be removed
	 * @return parsed result
	 */
	public static Number removeUnit( final String str, final String unit )
	{
		final String sub = removeSuffix( str, unit );
		if( sub==null )
		{
			return null;
		}

		//
		final StringTokenizer st = new StringTokenizer( sub );
		final ArrayList tokenList = new ArrayList();
		while( st.hasMoreTokens() )
		{
			tokenList.add( st.nextToken() );
		}
		if( tokenList.size()!=1 )
		{
			return null;
		}
		final String value = (String)tokenList.get(0);

		return SGUtilityText.getDouble( value );
	}



	/**
	 * Remove the suffix from a text string.
	 * @param str - a text string to be parsed
	 * @param suffix - a text string to be removed
	 * @return parsed result
	 */
	public static String removeSuffix( final String str, final String suffix )
	{
		if( str==null )
		{
			return null;
		}
		if( str.length()==0 )
		{
			return null;
		}
		if( suffix==null )
		{
			return str;
		}
	
		if( str.endsWith( suffix ) == false )
		{
			return str;
		}
	
		// create a new string without suffix
		int index = -1;
		while( true )
		{
			int num = str.indexOf(suffix,index+1);
			if( num==-1 )
			{
				break;
			}
			index = num;
		}
		String sub = str.substring(0,index);
		if( sub.length()==0 )
		{
			return null;
		}

		return sub;
	}


	public static final Color[] defaultColors = {
		Color.RED,
		Color.GREEN,
		Color.BLUE,
		Color.CYAN,
		Color.MAGENTA,
		Color.YELLOW,
		Color.ORANGE,
		Color.PINK,
		Color.WHITE,
		Color.LIGHT_GRAY,
		Color.GRAY,
		Color.DARK_GRAY,
		Color.BLACK
	};


	public static final String[] names = {
		"RED",
		"GREEN",
		"BLUE",
		"CYAN",
		"MAGENTA",
		"YELLOW",
		"ORANGE",
		"PINK",
		"WHITE",
		"LIGHT_GRAY",
		"GRAY",
		"DARK_GRAY",
		"BLACK"
	};


	/**
	 * 
	 * @param name
	 * @return
	 */
	public static Color getColor( final String name )
	{
		if( name==null )
		{
			throw new IllegalArgumentException("name==null");
		}
		String n = name.toUpperCase();

		Color cl = null;

		for( int ii=0; ii<names.length; ii++ )
		{
			if( names[ii].equals(n) )
			{
				cl = defaultColors[ii];
				break;
			}
		}

		return cl;
	}


	/**
	 * 
	 * @param r
	 * @param g
	 * @param b
	 * @return
	 */
	public static Color getColor( final String r, final String g, final String b )
	{
		Integer nr = getInteger(r);
		if( nr==null ) return null;

		Integer ng = getInteger(g);
		if( ng==null ) return null;

		Integer nb = getInteger(b);
		if( nb==null ) return null;

		if( isValidColor( nr.intValue(), ng.intValue(), nb.intValue() ) == false )
		{
			return null;
		}

		return new Color( nr.intValue(), ng.intValue(), nb.intValue() );
	}


	private static boolean isValidColor( final int r, final int g, final int b )
	{
		return isValidColor(r) & isValidColor(g) & isValidColor(b);
	}


	private static boolean isValidColor( final int c )
	{
		return ( c>=0 & c<256 );
	}


	/**
	 * 
	 * @param str
	 * @return
	 */
	public static final MediaSize getMediaSize( final String str )
	{
		MediaSize size = null;
		if( PAPER_SIZE_A4.equals( str ) )
		{
			size = MediaSize.ISO.A4;
		}
		else if( PAPER_SIZE_A3.equals( str ) )
		{
			size = MediaSize.ISO.A3;
		}
		else if( PAPER_SIZE_B5.equals( str ) )
		{
			size = MediaSize.ISO.B5;
		}
		else if( PAPER_SIZE_B4.equals( str ) )
		{
			size = MediaSize.ISO.B4;
		}
		else if( PAPER_SIZE_US_LETTER.equals( str ) )
		{
			size = MediaSize.NA.LETTER;
		}

		return size;
	}


	/**
	 * 
	 * @param str
	 * @return
	 */
	public static Dimension getDimension( final String str )
	{
		final int[] array = getIntegerArray( str );
		if( array==null )
		{
			return null;
		}

		final int len = array.length;
		if( len!=2 )
		{
			return null;
		}
		if( array[0]<0 | array[1]<0 )
		{
			return null;
		}

		final Dimension dim = new Dimension( array[0], array[1] );

		return dim;
	}



	/**
	 * 
	 * @param value
	 * @return
	 */
	public static Number getNumber(
		final String value, final StringBuffer u )
	{
		if( value==null )
		{
			return null;
		}
		String str = value.toLowerCase();

		String unit = null;
		String[] units = SGUtilityText.getUnitsArrayOfLength();
		for( int ii=0; ii<units.length; ii++ )
		{
			if( str.endsWith( units[ii] ) )
			{
				unit = units[ii];
				break;
			}
		}
		
		if( unit==null )
		{
			return null;
		}
		u.append( unit );

		Number num = SGUtilityText.removeUnit( str, unit );
		if( num==null )
		{
			return null;
		}

		return new Double( num.doubleValue() );
	}

	
	// returns a string of "true" or "false"
	public static String getBooleanString( final String value )
	{
		final String sTrue = Boolean.TRUE.toString();
		final String sFalse = Boolean.FALSE.toString();
		Number num = SGUtilityText.getInteger(value);

		String ret = null;
		if( num!=null )
		{
			ret = ( value.equals("0") ? sTrue : sFalse );
		}
		else
		{
			final String str = value.toLowerCase();
			if( str.equals( sTrue ) )
			{
			   ret = sTrue;
			}
			else if( str.equals( sFalse ) )
			{
			   ret = sFalse;
			}
		}

		return ret;
	}

	// static initializer
	static
	{
		String[] array = DEFAULT_DATE_FORMAT_ARRAY;
		for( int ii=0; ii<array.length; ii++ )
		{
			addAvailableDateFormat( array[ii] );
		}
	};

}

