view usr/src/cmd/krb5/kadmin/gui/visualrt/sunsoft/jws/visual/rt/type/ListParser.java @ 4:1a15d5aaf794

synchronized with onnv_86 (6202) in onnv-gate
author Koji Uno <koji.uno@sun.com>
date Mon, 31 Aug 2009 14:38:03 +0900
parents c9caec207d52
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * ident	"%Z%%M%	%I%	%E% SMI"
 *
 * Copyright (c) 2000 by Sun Microsystems, Inc.
 * All rights reserved.
 */

/*
 *        Copyright (C) 1996  Active Software, Inc.
 *                  All rights reserved.
 *
 * @(#) ListParser.java 1.16 - last change made 07/25/97
 */

package sunsoft.jws.visual.rt.type;

import sunsoft.jws.visual.rt.base.Global;

import java.util.*;

/**
 * Utility class for parsing lists of things in the style of Tcl.
 *
 * @version 	1.16, 07/25/97
 */
public class ListParser {
    
    // Character constants
    private static final char CHAR_a	= /* NOI18N */ 'a';
    private static final char CHAR_b	= /* NOI18N */ 'b';
    private static final char CHAR_f	= /* NOI18N */ 'f';
    private static final char CHAR_n	= /* NOI18N */ 'n';
    private static final char CHAR_r	= /* NOI18N */ 'r';
    private static final char CHAR_t	= /* NOI18N */ 't';
    private static final char CHAR_x	= /* NOI18N */ 'x';
    private static final char CHAR_A	= /* NOI18N */ 'A';
    private static final char CHAR_F	= /* NOI18N */ 'F';
    private static final char BACKSLASH	= /* NOI18N */ '\\';
    private static final char BACKSPACE	= /* NOI18N */ '\b';
    private static final char DQUOTE	= /* NOI18N */ '"';
    private static final char EQUALS	= /* NOI18N */ '=';
    private static final char FORMFEED	= /* NOI18N */ '\f';
    private static final char LBRACE	= /* NOI18N */ '{';
    private static final char NEWLINE	= /* NOI18N */ '\n';
    private static final char NINE	= /* NOI18N */ '9';
    private static final char NULL	= /* NOI18N */ '\0';
    private static final char RBRACE	= /* NOI18N */ '}';
    private static final char RETURN	= /* NOI18N */ '\r';
    private static final char SPACE	= /* NOI18N */ ' ';
    private static final char TAB		= /* NOI18N */ '\t';
    private static final char ZERO	= /* NOI18N */ '0';
    
    private Vector list;
    
    public ListParser(String str) {
        int begin = 0;
        int end = str.length();
        initList(str, begin, end);
    }
    
    public ListParser(String str, int offset) {
        int begin, end = str.length();
        if (offset >= 0 && offset < end)
            begin = offset;
        else
            begin = end;
        
        initList(str, begin, end);
    }
    
    public ListParser(String str, int begin, int end) {
        int len = str.length();
        if (end < 0 || end > len)
            end = len;
        if (begin < 0)
            begin = 0;
        if (begin > end)
            begin = end;
        
        initList(str, begin, end);
    }
    
    public Enumeration elements() {
        return list.elements();
    }
    
    public int size() {
        return list.size();
    }
    
    private void initList(String str, int begin, int end) {
        list = new Vector();
        
        int len = end-begin;
        char buf[] = new char[len];
        str.getChars(begin, end, buf, 0);
        
        parseList(list, buf);
    }
    
    private void parseList(Vector list, char buf[]) {
        nextIndex = 0;
        
        while (nextIndex < buf.length && buf[nextIndex] != 0) {
            try {
                findElement(buf, nextIndex);
            }
            catch (ParseException ex) {
                list.removeAllElements();
                throw ex;
            }
            
            if (elementSize != 0 ||
		(elementIndex < buf.length && buf[elementIndex] != 0)) {
                if (brace) {
                    list.addElement(new String(buf, elementIndex,
					       elementSize));
                } else {
                    list.addElement(collapse(buf, elementIndex,
					     elementSize));
                }
            }
        }
    }
    
    /* BEGIN JSTYLED */
    /* 
     *----------------------------------------------------------------------
     *
     * findElement --
     *
     *	Given a character buffer containing a Tcl list, locate the first
     *    (or next) element in the list.
     *
     * Results:
     *    None.
     *
     * Side effects:
     *	If an exception is not thrown, then elementIndex will be set to
     *    the position of the first element of the list, 
     *     and nextIndex will
     *    be set to the position of the character just after 
     *     any white space
     *    following the last character that's part of the element.  If this
     *    is the last argument in the list, then nextIndex will point to the
     *    NULL character at the end of list.  elementSize is set to
     *	the number of characters in the element.  If the element is in
     *	braces, then elementIndex will point to the character after the
     *	opening brace and elementSize will not include either of the braces.
     *	If there isn't an element in the list, elementSize will be zero,
     *	elementIndex will refer to the null character at the end of list,
     *    and brace will be set to true.
     *
     *    Note:  this procedure does NOT collapse backslash sequences.
     *
     *----------------------------------------------------------------------
     */

    /* END JSTYLED */
    // Side effect variables
    private int elementIndex;
    private int nextIndex;
    private int elementSize;
    private boolean brace;
    
    private void findElement(char buf[], int offset) {
        
        int list = offset;
        int p;
        int openBraces = 0;
        boolean inQuotes = false;
        int size = 0;
        char c;
        
        /*
         * Skim off leading white space and check for 
	 * an opening brace or
	 * quote.
	 */
        
        while (list < buf.length && Character.isSpace(buf[list])) {
            list++;
        }
        
        if (list < buf.length && buf[list] == LBRACE) {
            openBraces = 1;
            list++;
        } else if (list < buf.length && buf[list] == DQUOTE) {
            inQuotes = true;
            list++;
        }
        brace = (openBraces == 1);
        p = list;
        
        /*
         * Find the end of the element (either a space or a 
	 * close brace or
	 * the end of the string).
	 */
        
        try {
            while (true) {
                if (p < buf.length)
                    c = buf[p];
                else
                    c = 0;
                switch (c) {
                    
                    /*
                     * Open brace: don't treat specially unless 
		     * the element is
		     * in braces.  In this case, keep a nesting count.
		     */
                    
		case LBRACE:
                    if (openBraces != 0) {
                        openBraces++;
                    }
                    break;
                    
                    /*
                     * Close brace: if element is in braces, 
		     * keep nesting
		     * count and quit when the last close brace
		     * is seen.
                    */
                    
		case RBRACE:
                    if (openBraces == 1) {
                        int p2;
                        
                        size = p - list;
                        p++;
                        if (p >= buf.length || buf[p] == 0 ||
			    Character.isSpace(buf[p])) {
                            throw new DoneException();
                        }
                        for (p2 = p; p2 < buf.length && buf[p2] != 0 &&
				 !Character.isSpace(buf[p2]) && (p2 < p+20);
			     p2++) {
                            /* null body */
                        }
                        
                        String err = new String(buf, p, p2-p);
                        throw new ParseException(
						 /* JSTYLED */
						 Global.fmtMsg("sunsoft.jws.visual.rt.type.ListParser.SpaceExpected", String.valueOf(buf, p, p2-p)));
                        
                    } else if (openBraces != 0) {
                        openBraces--;
                    }
                    break;
                    
                    /*
                     * Backslash:  skip over everything up to 
		     * the end of the
		     * backslash sequence.
		     */
                    
		case BACKSLASH: {
		    IntHolder backslashSize = new IntHolder();
		    backslash(buf, p, backslashSize);
		    p += backslashSize.value - 1;
		    break;
		}
                    
		/*
		 * Space: ignore if element is in braces or 
		 * quotes;  otherwise
		 * terminate element.
		 */
                    
		case SPACE:
		case FORMFEED:
		case NEWLINE:
		case RETURN:
		case TAB:
                    if ((openBraces == 0) && !inQuotes) {
                        size = p - list;
                        throw new DoneException();
                    }
                    break;
                    
                    /*
                     * Double-quote:  if element is in quotes then 
		     * terminate it.
                    */
                    
		case DQUOTE:
                    if (inQuotes) {
                        int p2;
                        
                        size = p-list;
                        p++;
                        if (p >= buf.length || buf[p] == 0 ||
			    Character.isSpace(buf[p])) {
                            throw new DoneException();
                        }
                        for (p2 = p; (p2 < buf.length && buf[p2] != 0)
				 &&
				 (!Character.isSpace(buf[p2])) && (p2 < p+20);
			     p2++) {
                            /* null body */
                        }
                        
                        throw new ParseException(
						 /* JSTYLED */
			 Global.fmtMsg("sunsoft.jws.visual.rt.type.ListParser.SpaceExpected2",
		       String.valueOf(buf, p, p2-p), String.valueOf
				       (buf, p, buf.length-1)));
                        
                    }
                    break;
                    
                    /*
                     * End of list:  terminate element.
                     */
                    
		case 0:
                    if (openBraces != 0) {
			/* BEGIN JSTYLED */
			throw new ParseException(Global.getMsg("sunsoft.jws.visual.rt.type.ListParser.UnmatchedBrace"));
		    } else if (inQuotes) {
			throw new ParseException(Global.getMsg("sunsoft.jws.visual.rt.type.ListParser.UnmatchedQuote"));
		    }
			    
		    size = p - list;
		    throw new DoneException();
		}
		p++;
	    }
	}
	catch (DoneException ex) {
	}
                
	while (p < buf.length && Character.isSpace(buf[p])) {
	    p++;
	}
                
	elementIndex = list;
	nextIndex = p;
	elementSize = size;
    }
            
    /*
     *----------------------------------------------------------------------
     *
     * collapse --
     *
     *	Return a new string after eliminating any backslashes that
     *    aren't in braces.
     *
     * Results:
     *	Returns a string that is a substring of buf starting at offset,
     *    and count characters long.  If backslash sequences are found
     *    outside braces, the backslashes are eliminated in the new string.
     *
     * Side effects:
     *	None.
     *
     *----------------------------------------------------------------------
     */
    /* END JSTYLED */
                        
    private String collapse(char buf[], int offset, int count) {
	int p = offset;
	char c;
	IntHolder numRead = new IntHolder();
	char dst[] = new char[buf.length+1];
	int p2 = 0;
                            
	while (count > 0) {
	    if (p < buf.length)
		c = buf[p];
	    else
		c = 0;
                                
	    if (c == BACKSLASH) {
		dst[p2] = backslash(buf, p, numRead);
		p2++;
		p += numRead.value-1;
		count -= numRead.value-1;
	    } else {
		dst[p2] = c;
		p2++;
	    }
	    p++;
	    count--;
	}
	dst[p2] = 0;
                            
	return new String(dst, 0, p2);
    }
                        
	/*
	*------------------------------------------
	*
	* backslash --
	*
	*	Figure out how to handle a backslash sequence.
	*
	* Results:
	*	The return value is the character that should be substituted
	*	in place of the backslash sequence that starts at src.
	*	The "readPtr" variable is set to the number of characters
	*    in the backslash sequence.
	*
	* Side effects:
	*   none
	*
	* Parameters:
	*   char buf[];		Character buffer containing
	* the backslash
	*				sequence.
	*   int offset;		Offset within buf where the backslash
	*				sequence begins.
	*------------------------------------------
	*/
                        
    private static char backslash(char buf[], int offset,
				  IntHolder readPtr) {
                            
	int p = offset+1;
	char result;
	int count;
	char c;
                            
	count = 2;
                            
	if (p < buf.length)
	    c = buf[p];
	else
	    c = 0;
	switch (c) {
	case CHAR_a:
	    result = 0x7;	/* Don't say '\a' here, */
                                /* since some compilers */
	    break;		/* don't support it. */
	case CHAR_b:
	    result = BACKSPACE;
	    break;
	case CHAR_f:
	    result = FORMFEED;
	    break;
	case CHAR_n:
	    result = NEWLINE;
	    break;
	case CHAR_r:
	    result = RETURN;
	    break;
	case CHAR_t:
	    result = TAB;
	    break;
	case CHAR_x:
	    if (isxdigit(buf[p+1])) {
		int p2 = p+1;
		while (isxdigit(buf[p2])) {
		    p2++;
		}
                                    
		result = (char)
		    Integer.parseInt(String.valueOf(buf, p+1, p2), 16);
		count = p2 - offset;
	    } else {
		count = 2;
		result = CHAR_x;
	    }
	    break;
	case NEWLINE:
	    do {
		p++;
	    } while ((buf[p] == SPACE) || (buf[p] == TAB));
	    result = SPACE;
	    count = p - offset;
	    break;
	case 0:
	    result = BACKSLASH;
	    count = 1;
	    break;
	default:
	    if (isdigit(buf[p])) {
		result = (char)(buf[p] - ZERO);
		p++;
		if (!isdigit(buf[p])) {
		    break;
		}
		count = 3;
		result = (char)((result << 3) + (buf[p] - ZERO));
		p++;
		if (!isdigit(buf[p])) {
		    break;
		}
		count = 4;
		result = (char)((result << 3) + (buf[p] - ZERO));
		break;
	    }
	    result = buf[p];
	    count = 2;
	    break;
	}
                            
	if (readPtr != null)
	    readPtr.value = count;
                            
	return result;
    }
    /* BEGIN JSTYLED */
            
    /*
     * The following values are used in the flags 
     * returned by Tcl_ScanElement
     * and used by Tcl_ConvertElement.  The value 
     * TCL_DONT_USE_BRACES is also
     * defined in tcl.h;  make sure its value doesn't 
     * overlap with any of the
     * values below.
     *
     * TCL_DONT_USE_BRACES -	1 means the string mustn't 
     * be enclosed in
     *				braces (e.g. it contains 
     * unmatched braces,
     *				or ends in a backslash 
     * character, or user
     *				just doesn't want braces);  handle all
     *				special characters by adding 
     * backslashes.
     * USE_BRACES -		1 means the string contains a special
     *				character that can be handled simply by
     *				enclosing the entire argument 
     * in braces.
     * BRACES_UNMATCHED -		1 means that braces 
     * aren't properly matched
     *				in the argument.
     */
            
    private static final int TCL_DONT_USE_BRACES = 1;
    private static final int USE_BRACES = 2;
    private static final int BRACES_UNMATCHED = 4;
            
    /*
     *-----------------------------------
     *
     * scanElement --
     *
     *	This procedure is a companion procedure to Tcl_ConvertElement.
     *	It scans a string to see what needs to be done to it (e.g.
     *	add backslashes or enclosing braces) to make the string into
     *	a valid Tcl list element.
     *
     * Results:
     *	The return value is an overestimate of the number of characters
     *	that will be needed by Tcl_ConvertElement to produce a valid
     *	list element from string.  The word at *flagPtr is filled in
     *	with a value needed by Tcl_ConvertElement when doing the actual
     *	conversion.
     *
     * Side effects:
     *	None.
     *
     *---------------------------------------
    */
            
    // char *string;	/* String to convert to Tcl list element. */
    // int *flagPtr;    /* Where to store information to guide */
    //			     /* Tcl_ConvertElement. */

	
    private static int scanElement(char buf[], IntHolder flagPtr) {
	int flags, nestingLevel;
	int p;
                
	/*
	 * This procedure and Tcl_ConvertElement together 
	 * do two things:
	 *
	 * 1. They produce a proper list, one that will yield back the
	 * argument strings when evaluated or when disassembled with
	 * Tcl_SplitList.  This is the most important thing.
	 * 
	 * 2. They try to produce legible output, which means 
	 *	 minimizing the
	 * use of backslashes (using braces instead).  However, 
	 *	 there are
	 * some situations where backslashes must be used 
	 * (e.g. an element
	 * like "{abc": the leading brace will have to be 
	 *	 backslashed.  For
	 * each element, one of three things must be done:
	 *
	 * (a) Use the element as-is (it doesn't contain 
	 *	 anything special
	 * characters).  This is the most desirable option.
	 *
	 * (b) Enclose the element in braces, but leave the 
	 *	 contents alone.
	 * This happens if the element contains embedded space, 
	 *	 or if it
	 * contains characters with special interpretation 
	 * ($, [, ;, or \),
	 * or if it starts with a brace or double-quote, or 
	 * if there are
	 * no characters in the element.
	 *
	 * (c) Don't enclose the element in braces, but 
	 *	 add backslashes to
	 * prevent special interpretation of special characters.  
	 *	 This is a
	 * last resort used when the argument would normally 
	 *	 fall under case
	 * (b) but contains unmatched braces.  It also occurs 
	 *	 if the last
	 * character of the argument is a backslash or if the 
	 *	 element contains
	 * a backslash followed by newline.
	 *
	 * The procedure figures out how many bytes will be 
	 *	 needed to store
	 * the result (actually, it overestimates).  It also 
	 *	 collects information
	 * about the element in the form of a flags word.
	 */
                
	/* END JSTYLED */
	nestingLevel = 0;
	flags = 0;
	if (buf == null) {
	    buf = new char[0];
	}
	p = 0;
	if ((p >= buf.length) || (buf[p] == LBRACE) ||
	    (buf[p] == DQUOTE) || (buf[p] == 0)) {
	    flags |= USE_BRACES;
	}
	for (; p < buf.length && buf[p] != 0; p++) {
	    switch (buf[p]) {
	    case LBRACE:
		nestingLevel++;
		break;
	    case RBRACE:
		nestingLevel--;
		if (nestingLevel < 0) {
		    flags |= TCL_DONT_USE_BRACES|BRACES_UNMATCHED;
		}
		break;
	    case SPACE:
	    case FORMFEED:
	    case NEWLINE:
	    case RETURN:
	    case TAB:
		flags |= USE_BRACES;
		break;
	    case BACKSLASH:
		if ((buf[p+1] == 0) || (buf[p+1] == NEWLINE)) {
		    flags = TCL_DONT_USE_BRACES;
		} else {
		    IntHolder size = new IntHolder();
                                    
		    backslash(buf, p, size);
		    p += size.value-1;
		    flags |= USE_BRACES;
		}
		break;
	    }
	}
	if (nestingLevel != 0) {
	    flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED;
	}
	flagPtr.value = flags;
                        
	/*
	 * Allow enough space to backslash every character plus leave
	 * two spaces for braces.
	 */
                        
	return 2*p + 2;
    }
                    
    /* BEGIN JSTYLED */
    /*
     *------------------------------------------
     *
     * convertElement --
     *
     *	This is a companion procedure to scanElement.  Given the
     *	information produced by scanElement, this procedure converts
     *	a string to a list element equal to that string.
     *
     * Results:
     *	Information is copied to *dst in the form of a list element
     *	identical to src (i.e. if Tcl_SplitList is applied to dst it
     *	will produce a string identical to src).  The return value is
     *	a count of the number of characters copied (not including the
     *	terminating NULL character).
     *
     * Side effects:
     *	None.
     *
     *--------------------------------------
    */
    /* END JSTYLED */
                    
    // register char *src;  /* Source information for list element. */
    // char *dst;	    /* Place to put list-ified element. */
    // int flags;	    /* Flags produced by Tcl_ScanElement. */
                    
    private static int convertElement(char src[], char dst[],
				      int flags) {
                        
	int p = 0;
                        
	/*
	 * See the comment block at the beginning 
	 * of the Tcl_ScanElement
	 * code for details of how this works.
	 */
                        
	if ((src == null) || (src.length == 0)) {
	    dst[p] = LBRACE;
	    dst[p+1] = RBRACE;
	    dst[p+2] = 0;
	    return 2;
	}
	if ((flags & USE_BRACES) != 0 &&
	    (flags & TCL_DONT_USE_BRACES) == 0) {
	    dst[p] = LBRACE;
	    p++;
	    for (int p2 = 0; p2 < src.length && src[p2] != 0;
		 p++, p2++) {
		dst[p] = src[p2];
	    }
	    dst[p] = RBRACE;
	    p++;
	} else {
	    int p2 = 0;
	    if (src[p2] == LBRACE) {
                                /*
                                 * Can't have a leading brace unless 
				 * the whole element is
				 * enclosed in braces.  Add a backslash
				 * before the brace.
				 * Furthermore, this may destroy the
				 * balance between open
				 * and close braces, so set BRACES_UNMATCHED.
				 */
                                
		dst[p] = BACKSLASH;
		dst[p+1] = LBRACE;
		p += 2;
		p2++;
		flags |= BRACES_UNMATCHED;
	    }
	    for (; p2 < src.length && src[p2] != 0; p2++) {
		switch (src[p2]) {
		case SPACE:
		case BACKSLASH:
		case DQUOTE:
		    dst[p] = BACKSLASH;
		    p++;
		    break;
		case LBRACE:
		case RBRACE:
		    /* BEGIN JSTYLED */
		    /*
		     * It may not seem necessary to backslash 
		     * braces, but
		     * it is.  The reason for this is that 
		     * the resulting
		     * list element may actually be an 
		     * element of a sub-list
		     * enclosed in braces (e.g. if 
		     * Tcl_DStringStartSublist
		     * has been invoked), so there may be a 
		     * brace mismatch
		     * if the braces aren't backslashed.
		     */
		    /* END JSTYLED */
                                    
		    if ((flags & BRACES_UNMATCHED) != 0) {
			dst[p] = BACKSLASH;
			p++;
		    }
		    break;
		case FORMFEED:
		    dst[p] = BACKSLASH;
		    p++;
		    dst[p] = CHAR_f;
		    p++;
		    continue;
		case NEWLINE:
		    dst[p] = BACKSLASH;
		    p++;
		    dst[p] = CHAR_n;
		    p++;
		    continue;
		case RETURN:
		    dst[p] = BACKSLASH;
		    p++;
		    dst[p] = CHAR_r;
		    p++;
		    continue;
		case TAB:
		    dst[p] = BACKSLASH;
		    p++;
		    dst[p] = CHAR_t;
		    p++;
		    continue;
		}
		dst[p] = src[p2];
		p++;
	    }
	}
	dst[p] = NULL;
	return p;
    }
                    
    /*
     * Returns a new string that is a listified version of the string
     * argument.  The string will be enclosed with braces if necessary,
     * and all special characters will be escaped.
     */
    public static String list(String string) {
	char src[] = string.toCharArray();
                        
	IntHolder flagPtr = new IntHolder();
	int len = scanElement(src, flagPtr);
	char dst[] = new char[len+1];
	len = convertElement(src, dst, flagPtr.value);
                        
	return new String(dst, 0, len);
    }
                    
    /*
     * Appends a new string to the string buffer argument that is a
     * listified version of the string argument.  The string will be
     * enclosed with braces if necessary, and all special characters
     * will be escaped.
     */
    public static void list(String string, StringBuffer buf) {
	char src[] = string.toCharArray();
                        
	IntHolder flagPtr = new IntHolder();
	int len = scanElement(src, flagPtr);
	char dst[] = new char[len+1];
	len = convertElement(src, dst, flagPtr.value);
                        
	buf.append(dst, 0, len);
    }
                    
    /*
     * Returns a new string that is a quoted version of the string
     * argument.  The string will be enclosed with quotes if necessary,
     * and all special characters will be escaped.  If the forceQuotes
     * argument is true, then the string will be enclosed with quotes
     * even if it is not strictly necessary.  Also, if forceQuotes
     * is true, then the '\n' character will be replaced with the
     * string "\n".
     */
    public static String quote(String string, boolean forceQuotes) {
	char src[] = string.toCharArray();
	char dst[] = quote(src, forceQuotes);
	return new String(dst);
    }
                    
    /*
     * Appends a new string to the string buffer argument that is a
     * quoted version of the string argument.  The string will be
     * enclosed with quotes if necessary, and all special characters
     * will be escaped.  If the forceQuotes argument is true, then the
     * string will be enclosed with quotes even if it is not strictly
     * necessary.  Also, if forceQuotes is true, then the '\n' 
     * character
     * will be replaced with the string "\n".
     */
    public static void quote(String string, StringBuffer buf,
			     boolean forceQuotes) {
	char src[] = string.toCharArray();
	char dst[] = quote(src, forceQuotes);
	buf.append(dst);
    }
    /* BEGIN JSTYLED */
    /**
     * Puts quotes around the given character array if it 
     *     contains spaces
     * or double-quotes.  Only part of the string buffer 
     *     is quoted, determined
     * by the "startIndex" argument.  The substring of the 
     *     buffer starting
     * at "startIndex" and ending at the end of the buffer is quoted.
     * This method operates on a string buffer instead of a string for
     * improved performance.
     *
     * The "quote" method also does escaping.  A backslash is placed in
     * front of any double-quote or backslash in the string 
     *     itself.  Also,
     * new-line characters are replaced with the 
     *     characters \ and n
     *
     * Added argument: forceQuotes.  If this is true, then 
     *     always put quotes
     * around the text (necessary for code generation).  
     *     Also, replace the
     * '\n' character with the string "\n".
     */
    /* END JSTYLED */
    public static char[] quote(char src[], boolean forceQuotes) {
	boolean needQuotes;
	int backslash = 0;
                        
	if (src.length == 0) {
	    needQuotes = true;
	} else {
	    needQuotes = false;
	    if (!forceQuotes && src[0] == LBRACE &&
		src[src.length-1] == RBRACE) {
		return src;
	    }
	}
                        
	for (int i = 0; i < src.length; i++) {
	    switch (src[i]) {
	    case LBRACE:
	    case RBRACE:
	    case SPACE:
	    case TAB:
		needQuotes = true;
		break;
                                
	    case DQUOTE:
	    case BACKSLASH:
		needQuotes = true;
		backslash++;
		break;
                                
	    case FORMFEED:
	    case RETURN:
	    case NEWLINE:
		needQuotes = true;
		if (forceQuotes)
		    backslash++;
		break;
	    }
	}
                        
	int len = src.length + backslash;
	if (needQuotes || forceQuotes)
	    len += 2;
                        
	char dst[] = new char[len];
	int p = 0;
                        
	if (needQuotes || forceQuotes)
	    dst[p++] = DQUOTE;
                        
	for (int i = 0; i < src.length; i++) {
	    switch (src[i]) {
	    case DQUOTE:
	    case BACKSLASH:
		dst[p++] = BACKSLASH;
		break;
                                
	    case FORMFEED:
	    case RETURN:
	    case NEWLINE:
		if (forceQuotes) {
		    dst[p++] = BACKSLASH;
		    switch (src[i]) {
		    case FORMFEED:
			dst[p++] = CHAR_f;
			break;
		    case RETURN:
			dst[p++] = CHAR_r;
			break;
		    case NEWLINE:
			dst[p++] = CHAR_n;
			break;
		    }
		    continue;
		}
		break;
	    }
	    dst[p++] = src[i];
	}
                        
	if (needQuotes || forceQuotes)
	    dst[p++] = DQUOTE;
                        
	return dst;
    }
    /* BEGIN JSTYLED */
    /**
     * Returns a string that can be used as a newline.  
     * This string includes
     * a carriage return if we are running on Windows.
     */
    /* END JSTYLED */
    public static String newline() {
	return (Global.newline());
    }
                    
    /**
     * Appends a newline to buf.  This also appends a carriage return
     * if we are running on Windows.
     */
    public static void newline(StringBuffer buf) {
	Global.newline(buf);
    }
                    
    private static final String indentString = /* NOI18N */"  ";
                    
    /**
     * Indents "buf" based on the given indent level.
     */
    public static void indent(StringBuffer buf, int indentLevel) {
	for (int i = 0; i < indentLevel; i++)
	    buf.append(indentString);
    }
                    
    public static boolean isdigit(char ch) {
	return Character.isDigit(ch);
    }
                    
    public static boolean isxdigit(char ch) {
	return
	    ((ch >= ZERO) && (ch <= NINE)) ||
	    ((ch >= CHAR_A) && (ch <= CHAR_F)) ||
	    ((ch >= CHAR_a) && (ch <= CHAR_f));
    }
                    
    public static Enumeration getListElements(String s, int mult) {
	ListParser parser = new ListParser(s);
                        
	// if ((parser.size() % mult) != 0) {
	/* JSTYLED */
	// System.out.println("ParseWarning: Expecting a multiple of " + mult +
	// " list elements, got " + parser.size());
	// }
                        
	return parser.elements();
    }
                    
    public static Hashtable makeListTable(String s) {
	Enumeration e = getListElements(s, 2);
	Hashtable table = new Hashtable();
	while (e.hasMoreElements()) {
	    try {
		table.put((String)e.nextElement(),
			  (String)e.nextElement());
	    }
	    catch (NoSuchElementException ex) {
                                /* JSTYLED */
		throw new ParseException(Global.fmtMsg("sunsoft.jws.visual.rt.type.ListParser.ExpectingTwoElements", s));
	    }
	}
	return table;
    }
                    
    public static int parseInt(String s) {
	try {
	    return Integer.parseInt(s);
	}
	catch (NumberFormatException ex) {
	    throw new ParseException(/* NOI18N */"\n\t" +
				     ex.toString());
	}
    }
}
                
                
/**
 * An Exception that can be thrown and caught internally by ListParser.
 *
 * @see ListParser
 * @version 1.16, 07/25/97
 */
class DoneException extends Exception {
    DoneException() {
	super();
    }
                    
    DoneException(String message) {
	super(message);
    }
}