1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * ident	"%Z%%M%	%I%	%E% SMI"
24 *
25 * Copyright (c) 2000 by Sun Microsystems, Inc.
26 * All rights reserved.
27 */
28
29/*
30 *        Copyright (C) 1996  Active Software, Inc.
31 *                  All rights reserved.
32 *
33 * @(#) ListParser.java 1.16 - last change made 07/25/97
34 */
35
36package sunsoft.jws.visual.rt.type;
37
38import sunsoft.jws.visual.rt.base.Global;
39
40import java.util.*;
41
42/**
43 * Utility class for parsing lists of things in the style of Tcl.
44 *
45 * @version 	1.16, 07/25/97
46 */
47public class ListParser {
48
49    // Character constants
50    private static final char CHAR_a	= /* NOI18N */ 'a';
51    private static final char CHAR_b	= /* NOI18N */ 'b';
52    private static final char CHAR_f	= /* NOI18N */ 'f';
53    private static final char CHAR_n	= /* NOI18N */ 'n';
54    private static final char CHAR_r	= /* NOI18N */ 'r';
55    private static final char CHAR_t	= /* NOI18N */ 't';
56    private static final char CHAR_x	= /* NOI18N */ 'x';
57    private static final char CHAR_A	= /* NOI18N */ 'A';
58    private static final char CHAR_F	= /* NOI18N */ 'F';
59    private static final char BACKSLASH	= /* NOI18N */ '\\';
60    private static final char BACKSPACE	= /* NOI18N */ '\b';
61    private static final char DQUOTE	= /* NOI18N */ '"';
62    private static final char EQUALS	= /* NOI18N */ '=';
63    private static final char FORMFEED	= /* NOI18N */ '\f';
64    private static final char LBRACE	= /* NOI18N */ '{';
65    private static final char NEWLINE	= /* NOI18N */ '\n';
66    private static final char NINE	= /* NOI18N */ '9';
67    private static final char NULL	= /* NOI18N */ '\0';
68    private static final char RBRACE	= /* NOI18N */ '}';
69    private static final char RETURN	= /* NOI18N */ '\r';
70    private static final char SPACE	= /* NOI18N */ ' ';
71    private static final char TAB		= /* NOI18N */ '\t';
72    private static final char ZERO	= /* NOI18N */ '0';
73
74    private Vector list;
75
76    public ListParser(String str) {
77        int begin = 0;
78        int end = str.length();
79        initList(str, begin, end);
80    }
81
82    public ListParser(String str, int offset) {
83        int begin, end = str.length();
84        if (offset >= 0 && offset < end)
85            begin = offset;
86        else
87            begin = end;
88
89        initList(str, begin, end);
90    }
91
92    public ListParser(String str, int begin, int end) {
93        int len = str.length();
94        if (end < 0 || end > len)
95            end = len;
96        if (begin < 0)
97            begin = 0;
98        if (begin > end)
99            begin = end;
100
101        initList(str, begin, end);
102    }
103
104    public Enumeration elements() {
105        return list.elements();
106    }
107
108    public int size() {
109        return list.size();
110    }
111
112    private void initList(String str, int begin, int end) {
113        list = new Vector();
114
115        int len = end-begin;
116        char buf[] = new char[len];
117        str.getChars(begin, end, buf, 0);
118
119        parseList(list, buf);
120    }
121
122    private void parseList(Vector list, char buf[]) {
123        nextIndex = 0;
124
125        while (nextIndex < buf.length && buf[nextIndex] != 0) {
126            try {
127                findElement(buf, nextIndex);
128            }
129            catch (ParseException ex) {
130                list.removeAllElements();
131                throw ex;
132            }
133
134            if (elementSize != 0 ||
135		(elementIndex < buf.length && buf[elementIndex] != 0)) {
136                if (brace) {
137                    list.addElement(new String(buf, elementIndex,
138					       elementSize));
139                } else {
140                    list.addElement(collapse(buf, elementIndex,
141					     elementSize));
142                }
143            }
144        }
145    }
146
147    /* BEGIN JSTYLED */
148    /*
149     *----------------------------------------------------------------------
150     *
151     * findElement --
152     *
153     *	Given a character buffer containing a Tcl list, locate the first
154     *    (or next) element in the list.
155     *
156     * Results:
157     *    None.
158     *
159     * Side effects:
160     *	If an exception is not thrown, then elementIndex will be set to
161     *    the position of the first element of the list,
162     *     and nextIndex will
163     *    be set to the position of the character just after
164     *     any white space
165     *    following the last character that's part of the element.  If this
166     *    is the last argument in the list, then nextIndex will point to the
167     *    NULL character at the end of list.  elementSize is set to
168     *	the number of characters in the element.  If the element is in
169     *	braces, then elementIndex will point to the character after the
170     *	opening brace and elementSize will not include either of the braces.
171     *	If there isn't an element in the list, elementSize will be zero,
172     *	elementIndex will refer to the null character at the end of list,
173     *    and brace will be set to true.
174     *
175     *    Note:  this procedure does NOT collapse backslash sequences.
176     *
177     *----------------------------------------------------------------------
178     */
179
180    /* END JSTYLED */
181    // Side effect variables
182    private int elementIndex;
183    private int nextIndex;
184    private int elementSize;
185    private boolean brace;
186
187    private void findElement(char buf[], int offset) {
188
189        int list = offset;
190        int p;
191        int openBraces = 0;
192        boolean inQuotes = false;
193        int size = 0;
194        char c;
195
196        /*
197         * Skim off leading white space and check for
198	 * an opening brace or
199	 * quote.
200	 */
201
202        while (list < buf.length && Character.isSpace(buf[list])) {
203            list++;
204        }
205
206        if (list < buf.length && buf[list] == LBRACE) {
207            openBraces = 1;
208            list++;
209        } else if (list < buf.length && buf[list] == DQUOTE) {
210            inQuotes = true;
211            list++;
212        }
213        brace = (openBraces == 1);
214        p = list;
215
216        /*
217         * Find the end of the element (either a space or a
218	 * close brace or
219	 * the end of the string).
220	 */
221
222        try {
223            while (true) {
224                if (p < buf.length)
225                    c = buf[p];
226                else
227                    c = 0;
228                switch (c) {
229
230                    /*
231                     * Open brace: don't treat specially unless
232		     * the element is
233		     * in braces.  In this case, keep a nesting count.
234		     */
235
236		case LBRACE:
237                    if (openBraces != 0) {
238                        openBraces++;
239                    }
240                    break;
241
242                    /*
243                     * Close brace: if element is in braces,
244		     * keep nesting
245		     * count and quit when the last close brace
246		     * is seen.
247                    */
248
249		case RBRACE:
250                    if (openBraces == 1) {
251                        int p2;
252
253                        size = p - list;
254                        p++;
255                        if (p >= buf.length || buf[p] == 0 ||
256			    Character.isSpace(buf[p])) {
257                            throw new DoneException();
258                        }
259                        for (p2 = p; p2 < buf.length && buf[p2] != 0 &&
260				 !Character.isSpace(buf[p2]) && (p2 < p+20);
261			     p2++) {
262                            /* null body */
263                        }
264
265                        String err = new String(buf, p, p2-p);
266                        throw new ParseException(
267						 /* JSTYLED */
268						 Global.fmtMsg("sunsoft.jws.visual.rt.type.ListParser.SpaceExpected", String.valueOf(buf, p, p2-p)));
269
270                    } else if (openBraces != 0) {
271                        openBraces--;
272                    }
273                    break;
274
275                    /*
276                     * Backslash:  skip over everything up to
277		     * the end of the
278		     * backslash sequence.
279		     */
280
281		case BACKSLASH: {
282		    IntHolder backslashSize = new IntHolder();
283		    backslash(buf, p, backslashSize);
284		    p += backslashSize.value - 1;
285		    break;
286		}
287
288		/*
289		 * Space: ignore if element is in braces or
290		 * quotes;  otherwise
291		 * terminate element.
292		 */
293
294		case SPACE:
295		case FORMFEED:
296		case NEWLINE:
297		case RETURN:
298		case TAB:
299                    if ((openBraces == 0) && !inQuotes) {
300                        size = p - list;
301                        throw new DoneException();
302                    }
303                    break;
304
305                    /*
306                     * Double-quote:  if element is in quotes then
307		     * terminate it.
308                    */
309
310		case DQUOTE:
311                    if (inQuotes) {
312                        int p2;
313
314                        size = p-list;
315                        p++;
316                        if (p >= buf.length || buf[p] == 0 ||
317			    Character.isSpace(buf[p])) {
318                            throw new DoneException();
319                        }
320                        for (p2 = p; (p2 < buf.length && buf[p2] != 0)
321				 &&
322				 (!Character.isSpace(buf[p2])) && (p2 < p+20);
323			     p2++) {
324                            /* null body */
325                        }
326
327                        throw new ParseException(
328						 /* JSTYLED */
329			 Global.fmtMsg("sunsoft.jws.visual.rt.type.ListParser.SpaceExpected2",
330		       String.valueOf(buf, p, p2-p), String.valueOf
331				       (buf, p, buf.length-1)));
332
333                    }
334                    break;
335
336                    /*
337                     * End of list:  terminate element.
338                     */
339
340		case 0:
341                    if (openBraces != 0) {
342			/* BEGIN JSTYLED */
343			throw new ParseException(Global.getMsg("sunsoft.jws.visual.rt.type.ListParser.UnmatchedBrace"));
344		    } else if (inQuotes) {
345			throw new ParseException(Global.getMsg("sunsoft.jws.visual.rt.type.ListParser.UnmatchedQuote"));
346		    }
347
348		    size = p - list;
349		    throw new DoneException();
350		}
351		p++;
352	    }
353	}
354	catch (DoneException ex) {
355	}
356
357	while (p < buf.length && Character.isSpace(buf[p])) {
358	    p++;
359	}
360
361	elementIndex = list;
362	nextIndex = p;
363	elementSize = size;
364    }
365
366    /*
367     *----------------------------------------------------------------------
368     *
369     * collapse --
370     *
371     *	Return a new string after eliminating any backslashes that
372     *    aren't in braces.
373     *
374     * Results:
375     *	Returns a string that is a substring of buf starting at offset,
376     *    and count characters long.  If backslash sequences are found
377     *    outside braces, the backslashes are eliminated in the new string.
378     *
379     * Side effects:
380     *	None.
381     *
382     *----------------------------------------------------------------------
383     */
384    /* END JSTYLED */
385
386    private String collapse(char buf[], int offset, int count) {
387	int p = offset;
388	char c;
389	IntHolder numRead = new IntHolder();
390	char dst[] = new char[buf.length+1];
391	int p2 = 0;
392
393	while (count > 0) {
394	    if (p < buf.length)
395		c = buf[p];
396	    else
397		c = 0;
398
399	    if (c == BACKSLASH) {
400		dst[p2] = backslash(buf, p, numRead);
401		p2++;
402		p += numRead.value-1;
403		count -= numRead.value-1;
404	    } else {
405		dst[p2] = c;
406		p2++;
407	    }
408	    p++;
409	    count--;
410	}
411	dst[p2] = 0;
412
413	return new String(dst, 0, p2);
414    }
415
416	/*
417	*------------------------------------------
418	*
419	* backslash --
420	*
421	*	Figure out how to handle a backslash sequence.
422	*
423	* Results:
424	*	The return value is the character that should be substituted
425	*	in place of the backslash sequence that starts at src.
426	*	The "readPtr" variable is set to the number of characters
427	*    in the backslash sequence.
428	*
429	* Side effects:
430	*   none
431	*
432	* Parameters:
433	*   char buf[];		Character buffer containing
434	* the backslash
435	*				sequence.
436	*   int offset;		Offset within buf where the backslash
437	*				sequence begins.
438	*------------------------------------------
439	*/
440
441    private static char backslash(char buf[], int offset,
442				  IntHolder readPtr) {
443
444	int p = offset+1;
445	char result;
446	int count;
447	char c;
448
449	count = 2;
450
451	if (p < buf.length)
452	    c = buf[p];
453	else
454	    c = 0;
455	switch (c) {
456	case CHAR_a:
457	    result = 0x7;	/* Don't say '\a' here, */
458                                /* since some compilers */
459	    break;		/* don't support it. */
460	case CHAR_b:
461	    result = BACKSPACE;
462	    break;
463	case CHAR_f:
464	    result = FORMFEED;
465	    break;
466	case CHAR_n:
467	    result = NEWLINE;
468	    break;
469	case CHAR_r:
470	    result = RETURN;
471	    break;
472	case CHAR_t:
473	    result = TAB;
474	    break;
475	case CHAR_x:
476	    if (isxdigit(buf[p+1])) {
477		int p2 = p+1;
478		while (isxdigit(buf[p2])) {
479		    p2++;
480		}
481
482		result = (char)
483		    Integer.parseInt(String.valueOf(buf, p+1, p2), 16);
484		count = p2 - offset;
485	    } else {
486		count = 2;
487		result = CHAR_x;
488	    }
489	    break;
490	case NEWLINE:
491	    do {
492		p++;
493	    } while ((buf[p] == SPACE) || (buf[p] == TAB));
494	    result = SPACE;
495	    count = p - offset;
496	    break;
497	case 0:
498	    result = BACKSLASH;
499	    count = 1;
500	    break;
501	default:
502	    if (isdigit(buf[p])) {
503		result = (char)(buf[p] - ZERO);
504		p++;
505		if (!isdigit(buf[p])) {
506		    break;
507		}
508		count = 3;
509		result = (char)((result << 3) + (buf[p] - ZERO));
510		p++;
511		if (!isdigit(buf[p])) {
512		    break;
513		}
514		count = 4;
515		result = (char)((result << 3) + (buf[p] - ZERO));
516		break;
517	    }
518	    result = buf[p];
519	    count = 2;
520	    break;
521	}
522
523	if (readPtr != null)
524	    readPtr.value = count;
525
526	return result;
527    }
528    /* BEGIN JSTYLED */
529
530    /*
531     * The following values are used in the flags
532     * returned by Tcl_ScanElement
533     * and used by Tcl_ConvertElement.  The value
534     * TCL_DONT_USE_BRACES is also
535     * defined in tcl.h;  make sure its value doesn't
536     * overlap with any of the
537     * values below.
538     *
539     * TCL_DONT_USE_BRACES -	1 means the string mustn't
540     * be enclosed in
541     *				braces (e.g. it contains
542     * unmatched braces,
543     *				or ends in a backslash
544     * character, or user
545     *				just doesn't want braces);  handle all
546     *				special characters by adding
547     * backslashes.
548     * USE_BRACES -		1 means the string contains a special
549     *				character that can be handled simply by
550     *				enclosing the entire argument
551     * in braces.
552     * BRACES_UNMATCHED -		1 means that braces
553     * aren't properly matched
554     *				in the argument.
555     */
556
557    private static final int TCL_DONT_USE_BRACES = 1;
558    private static final int USE_BRACES = 2;
559    private static final int BRACES_UNMATCHED = 4;
560
561    /*
562     *-----------------------------------
563     *
564     * scanElement --
565     *
566     *	This procedure is a companion procedure to Tcl_ConvertElement.
567     *	It scans a string to see what needs to be done to it (e.g.
568     *	add backslashes or enclosing braces) to make the string into
569     *	a valid Tcl list element.
570     *
571     * Results:
572     *	The return value is an overestimate of the number of characters
573     *	that will be needed by Tcl_ConvertElement to produce a valid
574     *	list element from string.  The word at *flagPtr is filled in
575     *	with a value needed by Tcl_ConvertElement when doing the actual
576     *	conversion.
577     *
578     * Side effects:
579     *	None.
580     *
581     *---------------------------------------
582    */
583
584    // char *string;	/* String to convert to Tcl list element. */
585    // int *flagPtr;    /* Where to store information to guide */
586    //			     /* Tcl_ConvertElement. */
587
588
589    private static int scanElement(char buf[], IntHolder flagPtr) {
590	int flags, nestingLevel;
591	int p;
592
593	/*
594	 * This procedure and Tcl_ConvertElement together
595	 * do two things:
596	 *
597	 * 1. They produce a proper list, one that will yield back the
598	 * argument strings when evaluated or when disassembled with
599	 * Tcl_SplitList.  This is the most important thing.
600	 *
601	 * 2. They try to produce legible output, which means
602	 *	 minimizing the
603	 * use of backslashes (using braces instead).  However,
604	 *	 there are
605	 * some situations where backslashes must be used
606	 * (e.g. an element
607	 * like "{abc": the leading brace will have to be
608	 *	 backslashed.  For
609	 * each element, one of three things must be done:
610	 *
611	 * (a) Use the element as-is (it doesn't contain
612	 *	 anything special
613	 * characters).  This is the most desirable option.
614	 *
615	 * (b) Enclose the element in braces, but leave the
616	 *	 contents alone.
617	 * This happens if the element contains embedded space,
618	 *	 or if it
619	 * contains characters with special interpretation
620	 * ($, [, ;, or \),
621	 * or if it starts with a brace or double-quote, or
622	 * if there are
623	 * no characters in the element.
624	 *
625	 * (c) Don't enclose the element in braces, but
626	 *	 add backslashes to
627	 * prevent special interpretation of special characters.
628	 *	 This is a
629	 * last resort used when the argument would normally
630	 *	 fall under case
631	 * (b) but contains unmatched braces.  It also occurs
632	 *	 if the last
633	 * character of the argument is a backslash or if the
634	 *	 element contains
635	 * a backslash followed by newline.
636	 *
637	 * The procedure figures out how many bytes will be
638	 *	 needed to store
639	 * the result (actually, it overestimates).  It also
640	 *	 collects information
641	 * about the element in the form of a flags word.
642	 */
643
644	/* END JSTYLED */
645	nestingLevel = 0;
646	flags = 0;
647	if (buf == null) {
648	    buf = new char[0];
649	}
650	p = 0;
651	if ((p >= buf.length) || (buf[p] == LBRACE) ||
652	    (buf[p] == DQUOTE) || (buf[p] == 0)) {
653	    flags |= USE_BRACES;
654	}
655	for (; p < buf.length && buf[p] != 0; p++) {
656	    switch (buf[p]) {
657	    case LBRACE:
658		nestingLevel++;
659		break;
660	    case RBRACE:
661		nestingLevel--;
662		if (nestingLevel < 0) {
663		    flags |= TCL_DONT_USE_BRACES|BRACES_UNMATCHED;
664		}
665		break;
666	    case SPACE:
667	    case FORMFEED:
668	    case NEWLINE:
669	    case RETURN:
670	    case TAB:
671		flags |= USE_BRACES;
672		break;
673	    case BACKSLASH:
674		if ((buf[p+1] == 0) || (buf[p+1] == NEWLINE)) {
675		    flags = TCL_DONT_USE_BRACES;
676		} else {
677		    IntHolder size = new IntHolder();
678
679		    backslash(buf, p, size);
680		    p += size.value-1;
681		    flags |= USE_BRACES;
682		}
683		break;
684	    }
685	}
686	if (nestingLevel != 0) {
687	    flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED;
688	}
689	flagPtr.value = flags;
690
691	/*
692	 * Allow enough space to backslash every character plus leave
693	 * two spaces for braces.
694	 */
695
696	return 2*p + 2;
697    }
698
699    /* BEGIN JSTYLED */
700    /*
701     *------------------------------------------
702     *
703     * convertElement --
704     *
705     *	This is a companion procedure to scanElement.  Given the
706     *	information produced by scanElement, this procedure converts
707     *	a string to a list element equal to that string.
708     *
709     * Results:
710     *	Information is copied to *dst in the form of a list element
711     *	identical to src (i.e. if Tcl_SplitList is applied to dst it
712     *	will produce a string identical to src).  The return value is
713     *	a count of the number of characters copied (not including the
714     *	terminating NULL character).
715     *
716     * Side effects:
717     *	None.
718     *
719     *--------------------------------------
720    */
721    /* END JSTYLED */
722
723    // register char *src;  /* Source information for list element. */
724    // char *dst;	    /* Place to put list-ified element. */
725    // int flags;	    /* Flags produced by Tcl_ScanElement. */
726
727    private static int convertElement(char src[], char dst[],
728				      int flags) {
729
730	int p = 0;
731
732	/*
733	 * See the comment block at the beginning
734	 * of the Tcl_ScanElement
735	 * code for details of how this works.
736	 */
737
738	if ((src == null) || (src.length == 0)) {
739	    dst[p] = LBRACE;
740	    dst[p+1] = RBRACE;
741	    dst[p+2] = 0;
742	    return 2;
743	}
744	if ((flags & USE_BRACES) != 0 &&
745	    (flags & TCL_DONT_USE_BRACES) == 0) {
746	    dst[p] = LBRACE;
747	    p++;
748	    for (int p2 = 0; p2 < src.length && src[p2] != 0;
749		 p++, p2++) {
750		dst[p] = src[p2];
751	    }
752	    dst[p] = RBRACE;
753	    p++;
754	} else {
755	    int p2 = 0;
756	    if (src[p2] == LBRACE) {
757                                /*
758                                 * Can't have a leading brace unless
759				 * the whole element is
760				 * enclosed in braces.  Add a backslash
761				 * before the brace.
762				 * Furthermore, this may destroy the
763				 * balance between open
764				 * and close braces, so set BRACES_UNMATCHED.
765				 */
766
767		dst[p] = BACKSLASH;
768		dst[p+1] = LBRACE;
769		p += 2;
770		p2++;
771		flags |= BRACES_UNMATCHED;
772	    }
773	    for (; p2 < src.length && src[p2] != 0; p2++) {
774		switch (src[p2]) {
775		case SPACE:
776		case BACKSLASH:
777		case DQUOTE:
778		    dst[p] = BACKSLASH;
779		    p++;
780		    break;
781		case LBRACE:
782		case RBRACE:
783		    /* BEGIN JSTYLED */
784		    /*
785		     * It may not seem necessary to backslash
786		     * braces, but
787		     * it is.  The reason for this is that
788		     * the resulting
789		     * list element may actually be an
790		     * element of a sub-list
791		     * enclosed in braces (e.g. if
792		     * Tcl_DStringStartSublist
793		     * has been invoked), so there may be a
794		     * brace mismatch
795		     * if the braces aren't backslashed.
796		     */
797		    /* END JSTYLED */
798
799		    if ((flags & BRACES_UNMATCHED) != 0) {
800			dst[p] = BACKSLASH;
801			p++;
802		    }
803		    break;
804		case FORMFEED:
805		    dst[p] = BACKSLASH;
806		    p++;
807		    dst[p] = CHAR_f;
808		    p++;
809		    continue;
810		case NEWLINE:
811		    dst[p] = BACKSLASH;
812		    p++;
813		    dst[p] = CHAR_n;
814		    p++;
815		    continue;
816		case RETURN:
817		    dst[p] = BACKSLASH;
818		    p++;
819		    dst[p] = CHAR_r;
820		    p++;
821		    continue;
822		case TAB:
823		    dst[p] = BACKSLASH;
824		    p++;
825		    dst[p] = CHAR_t;
826		    p++;
827		    continue;
828		}
829		dst[p] = src[p2];
830		p++;
831	    }
832	}
833	dst[p] = NULL;
834	return p;
835    }
836
837    /*
838     * Returns a new string that is a listified version of the string
839     * argument.  The string will be enclosed with braces if necessary,
840     * and all special characters will be escaped.
841     */
842    public static String list(String string) {
843	char src[] = string.toCharArray();
844
845	IntHolder flagPtr = new IntHolder();
846	int len = scanElement(src, flagPtr);
847	char dst[] = new char[len+1];
848	len = convertElement(src, dst, flagPtr.value);
849
850	return new String(dst, 0, len);
851    }
852
853    /*
854     * Appends a new string to the string buffer argument that is a
855     * listified version of the string argument.  The string will be
856     * enclosed with braces if necessary, and all special characters
857     * will be escaped.
858     */
859    public static void list(String string, StringBuffer buf) {
860	char src[] = string.toCharArray();
861
862	IntHolder flagPtr = new IntHolder();
863	int len = scanElement(src, flagPtr);
864	char dst[] = new char[len+1];
865	len = convertElement(src, dst, flagPtr.value);
866
867	buf.append(dst, 0, len);
868    }
869
870    /*
871     * Returns a new string that is a quoted version of the string
872     * argument.  The string will be enclosed with quotes if necessary,
873     * and all special characters will be escaped.  If the forceQuotes
874     * argument is true, then the string will be enclosed with quotes
875     * even if it is not strictly necessary.  Also, if forceQuotes
876     * is true, then the '\n' character will be replaced with the
877     * string "\n".
878     */
879    public static String quote(String string, boolean forceQuotes) {
880	char src[] = string.toCharArray();
881	char dst[] = quote(src, forceQuotes);
882	return new String(dst);
883    }
884
885    /*
886     * Appends a new string to the string buffer argument that is a
887     * quoted version of the string argument.  The string will be
888     * enclosed with quotes if necessary, and all special characters
889     * will be escaped.  If the forceQuotes argument is true, then the
890     * string will be enclosed with quotes even if it is not strictly
891     * necessary.  Also, if forceQuotes is true, then the '\n'
892     * character
893     * will be replaced with the string "\n".
894     */
895    public static void quote(String string, StringBuffer buf,
896			     boolean forceQuotes) {
897	char src[] = string.toCharArray();
898	char dst[] = quote(src, forceQuotes);
899	buf.append(dst);
900    }
901    /* BEGIN JSTYLED */
902    /**
903     * Puts quotes around the given character array if it
904     *     contains spaces
905     * or double-quotes.  Only part of the string buffer
906     *     is quoted, determined
907     * by the "startIndex" argument.  The substring of the
908     *     buffer starting
909     * at "startIndex" and ending at the end of the buffer is quoted.
910     * This method operates on a string buffer instead of a string for
911     * improved performance.
912     *
913     * The "quote" method also does escaping.  A backslash is placed in
914     * front of any double-quote or backslash in the string
915     *     itself.  Also,
916     * new-line characters are replaced with the
917     *     characters \ and n
918     *
919     * Added argument: forceQuotes.  If this is true, then
920     *     always put quotes
921     * around the text (necessary for code generation).
922     *     Also, replace the
923     * '\n' character with the string "\n".
924     */
925    /* END JSTYLED */
926    public static char[] quote(char src[], boolean forceQuotes) {
927	boolean needQuotes;
928	int backslash = 0;
929
930	if (src.length == 0) {
931	    needQuotes = true;
932	} else {
933	    needQuotes = false;
934	    if (!forceQuotes && src[0] == LBRACE &&
935		src[src.length-1] == RBRACE) {
936		return src;
937	    }
938	}
939
940	for (int i = 0; i < src.length; i++) {
941	    switch (src[i]) {
942	    case LBRACE:
943	    case RBRACE:
944	    case SPACE:
945	    case TAB:
946		needQuotes = true;
947		break;
948
949	    case DQUOTE:
950	    case BACKSLASH:
951		needQuotes = true;
952		backslash++;
953		break;
954
955	    case FORMFEED:
956	    case RETURN:
957	    case NEWLINE:
958		needQuotes = true;
959		if (forceQuotes)
960		    backslash++;
961		break;
962	    }
963	}
964
965	int len = src.length + backslash;
966	if (needQuotes || forceQuotes)
967	    len += 2;
968
969	char dst[] = new char[len];
970	int p = 0;
971
972	if (needQuotes || forceQuotes)
973	    dst[p++] = DQUOTE;
974
975	for (int i = 0; i < src.length; i++) {
976	    switch (src[i]) {
977	    case DQUOTE:
978	    case BACKSLASH:
979		dst[p++] = BACKSLASH;
980		break;
981
982	    case FORMFEED:
983	    case RETURN:
984	    case NEWLINE:
985		if (forceQuotes) {
986		    dst[p++] = BACKSLASH;
987		    switch (src[i]) {
988		    case FORMFEED:
989			dst[p++] = CHAR_f;
990			break;
991		    case RETURN:
992			dst[p++] = CHAR_r;
993			break;
994		    case NEWLINE:
995			dst[p++] = CHAR_n;
996			break;
997		    }
998		    continue;
999		}
1000		break;
1001	    }
1002	    dst[p++] = src[i];
1003	}
1004
1005	if (needQuotes || forceQuotes)
1006	    dst[p++] = DQUOTE;
1007
1008	return dst;
1009    }
1010    /* BEGIN JSTYLED */
1011    /**
1012     * Returns a string that can be used as a newline.
1013     * This string includes
1014     * a carriage return if we are running on Windows.
1015     */
1016    /* END JSTYLED */
1017    public static String newline() {
1018	return (Global.newline());
1019    }
1020
1021    /**
1022     * Appends a newline to buf.  This also appends a carriage return
1023     * if we are running on Windows.
1024     */
1025    public static void newline(StringBuffer buf) {
1026	Global.newline(buf);
1027    }
1028
1029    private static final String indentString = /* NOI18N */"  ";
1030
1031    /**
1032     * Indents "buf" based on the given indent level.
1033     */
1034    public static void indent(StringBuffer buf, int indentLevel) {
1035	for (int i = 0; i < indentLevel; i++)
1036	    buf.append(indentString);
1037    }
1038
1039    public static boolean isdigit(char ch) {
1040	return Character.isDigit(ch);
1041    }
1042
1043    public static boolean isxdigit(char ch) {
1044	return
1045	    ((ch >= ZERO) && (ch <= NINE)) ||
1046	    ((ch >= CHAR_A) && (ch <= CHAR_F)) ||
1047	    ((ch >= CHAR_a) && (ch <= CHAR_f));
1048    }
1049
1050    public static Enumeration getListElements(String s, int mult) {
1051	ListParser parser = new ListParser(s);
1052
1053	// if ((parser.size() % mult) != 0) {
1054	/* JSTYLED */
1055	// System.out.println("ParseWarning: Expecting a multiple of " + mult +
1056	// " list elements, got " + parser.size());
1057	// }
1058
1059	return parser.elements();
1060    }
1061
1062    public static Hashtable makeListTable(String s) {
1063	Enumeration e = getListElements(s, 2);
1064	Hashtable table = new Hashtable();
1065	while (e.hasMoreElements()) {
1066	    try {
1067		table.put((String)e.nextElement(),
1068			  (String)e.nextElement());
1069	    }
1070	    catch (NoSuchElementException ex) {
1071                                /* JSTYLED */
1072		throw new ParseException(Global.fmtMsg("sunsoft.jws.visual.rt.type.ListParser.ExpectingTwoElements", s));
1073	    }
1074	}
1075	return table;
1076    }
1077
1078    public static int parseInt(String s) {
1079	try {
1080	    return Integer.parseInt(s);
1081	}
1082	catch (NumberFormatException ex) {
1083	    throw new ParseException(/* NOI18N */"\n\t" +
1084				     ex.toString());
1085	}
1086    }
1087}
1088
1089
1090/**
1091 * An Exception that can be thrown and caught internally by ListParser.
1092 *
1093 * @see ListParser
1094 * @version 1.16, 07/25/97
1095 */
1096class DoneException extends Exception {
1097    DoneException() {
1098	super();
1099    }
1100
1101    DoneException(String message) {
1102	super(message);
1103    }
1104}
1105