
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.DocumentBuilderFactory;

import org.w3c.dom.Document;


/**
 * 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
{
	
	/**
	 * 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;
		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( 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_super ){
						cache_super += c; 
						in_super = false;
					} else {
						cache_sub += c;
						in_sub = false;
					}
				}
			} else {
				if( c == '^') {
					if( cache_base.length() == 0 || cache_super.length() != 0 )
						return false;
					in_super = true;
				} else 	if( c == '_' ) {
					if( cache_base.length() == 0 || cache_sub.length() != 0 )
						return false;
					in_sub = 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;
				}
			}
		}
		if( in_super || in_sub || in_brace )
			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;
	}
	
	public static boolean tokenize(
			final String str,
			final ArrayList strList )
	{

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

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


//	/**
//	 * 区切り文字(半角空白文字' '、タブ文字'\t'、改行文字'\n'、復帰改行文字'\r'、および
//	 * 用紙送り文字'\f')で区切られた文字列を分割する。
//	 * また、二つの連続するダブルクォーテーションマーク('"')で挟まれた領域は一つの文字列と
//	 * みなし、その中に存在する区切り文字は区切り文字とは解釈せずに、文字列の一部とみなす。
//	 * 尚、ダブルクォーテーションマークは区切り文字として解釈される。
//	 * 文字列中にダブルクォーテーションマークを含めたい場合には、直前にバックスラッシュ('\\')を
//	 * 付加して \" とする。
//	 * バックスラッシュが文字列中に単独で存在する場合には無視される。
//	 */
//	public static boolean tokenize2(
//		final String str,
//		final ArrayList strList )
//	{
//
//		if( str==null | strList==null )
//		{
//			throw new IllegalArgumentException("str==null | strList==null");
//		}
//
//		if( str.length()==0 )
//		{
//			return true;
//		}
//
//
//		// get characters array
//		final char[] charArray = str.toCharArray();
//
//
//		// '"'
//		boolean dqModeFlag = false;
//		
//		// '\\'
//		boolean bsModeFlag = false;
//
//
//		//
//		ArrayList delimiterSet = new ArrayList();
//		for( int ii=0; ii<SGIConstants.DELIMITER_SET.length; ii++ )
//		{
//			delimiterSet.add( new Character( SGIConstants.DELIMITER_SET[ii] ) );
//		}
//
//
//		//
//		int cnt = 0;
//		final ArrayList charList = new ArrayList();	// list for temporary use
//		while( true )
//		{
//
//			// get a character that the counter indicates
//			final char c = str.charAt(cnt);
////System.out.print(cnt+"  "+new Character(c)+"  ");
//
//
//			// double quotation mark
//			if( c=='"' )
//			{
////System.out.print("*** dq ***");
////System.out.println("  "+dqModeFlag);
//
//				if( bsModeFlag )
//				{
//					charList.add( new Character(c) );
//					bsModeFlag = false;
//				}
//				else
//				{
//
//					if( charList.size() != 0 )
//					{
//						final String str_ = createString(charList);
//						strList.add(str_);
//						charList.clear();
//					}
//					else
//					{
//						// to accept quoted zero-length string
//						if( dqModeFlag )
//						{
//							strList.add("");
//							charList.clear();
//						}
//					}
//
//					// reverse the flag of double quotation mode
//					dqModeFlag = !dqModeFlag;
//				}
//			
//			}
//			// back slash
//			else if( c=='\\' )
//			{
////System.out.print("*** \\ ***");
////System.out.println("  "+dqModeFlag);
//
//				bsModeFlag = true;
//
//			}
//			// the delimiter set
//			else if( delimiterSet.contains( new Character(c) ) )
//			{
////System.out.print("*** delimiter ***");
////System.out.println("  "+dqModeFlag);
//
//				if( dqModeFlag )
//				{
////System.out.println("aaaaa");
//					charList.add( new Character(c) );
//				}
//				else
//				{
//					if( charList.size() != 0 )
//					{
//						final String str_ = createString(charList);
//						strList.add(str_);
////System.out.println("*"+str_+"*");
//						charList.clear();
//					}
//				}
//
//				bsModeFlag = false;
//
//			}
//			// other characters
//			else
//			{
////System.out.print("*** others ***");
////System.out.println("  "+dqModeFlag);
//
//				// dqMode
//				charList.add( new Character(c) );
//
//				bsModeFlag = false;
//			}
//
//
//			// increment the counter
//			cnt++;
//
//
//			// at the end of the input string
//			if( cnt == str.length() )
//			{
//				if( dqModeFlag )
//				{
////System.out.println("文体が不正です！");
//					return false;
//				}
//
//
//				final String str_ = createString(charList);
//				if( str_==null || str_.length()==0 )
//				{
//					break;
//				}
//
//				strList.add(str_);
//				charList.clear();
//
//				break;
//
//			}
//
//		}
//
//
//
//		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;
	}



	/**
	 * 
	 * @return
	 * @throws Exception
	 */
	public static Document getDocument( URL url )
	{
		Document doc = null;
		InputStream bis = null;
		try
		{
			// get input stream
			bis = new BufferedInputStream( url.openStream() );

			// parse input and create a Document object
			try
			{
				doc = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder().parse( bis, url.toString() );
			}
			catch( Exception ex )
			{
				return null;
			}
		}
		catch( IOException 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;
		}
		else
		{
			u.append( unit );
		}

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

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



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

}

