xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/ServiceLocationAttribute.java (revision 55fea89dcaa64928bed4327112404dcb3e07b79f)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*9a70fc3bSMark J. Nelson  * Common Development and Distribution License (the "License").
6*9a70fc3bSMark J. Nelson  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
227c478bd9Sstevel@tonic-gate  * Copyright 2001,2003 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  *
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
27*9a70fc3bSMark J. Nelson //  ServiceLocationAttribute.java : Class for attributes in SLP.
287c478bd9Sstevel@tonic-gate //  Author:           James Kempf, Erik Guttman
297c478bd9Sstevel@tonic-gate //
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate package com.sun.slp;
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate import java.util.*;
347c478bd9Sstevel@tonic-gate import java.io.*;
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate /**
377c478bd9Sstevel@tonic-gate  * The ServiceLocationAttribute class models SLP attributes.
387c478bd9Sstevel@tonic-gate  *
397c478bd9Sstevel@tonic-gate  * @author James Kempf, Erik Guttman
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate public class ServiceLocationAttribute extends Object
437c478bd9Sstevel@tonic-gate     implements Serializable {
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate     // Characters to escape.
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate     final static String RESERVED = "(),\\!<=>~";
487c478bd9Sstevel@tonic-gate     final static String ESCAPED = RESERVED + "*";
497c478bd9Sstevel@tonic-gate     final static char ESCAPE = '\\';
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate     final static char CTL_LOWER = (char)0x00;
527c478bd9Sstevel@tonic-gate     final static char CTL_UPPER = (char)0x1F;
537c478bd9Sstevel@tonic-gate     final static char DEL = (char)0x7F;
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate     // Whitespace chars.
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate     static final String WHITESPACE = " \n\t\r";
587c478bd9Sstevel@tonic-gate     static final char SPACE = ' ';
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate     // For character escaping.
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate     static final char COMMA = ',';
637c478bd9Sstevel@tonic-gate     static final char PERCENT = '%';
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate     // Bad tag characters.
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate     final private static String BAD_TAG_CHARS = "*\n\t\r";
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate     // For identifying booleans.
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate     final static String TRUE = "true";
727c478bd9Sstevel@tonic-gate     final static String FALSE = "false";
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate     //
757c478bd9Sstevel@tonic-gate     // Package accessable fields.
767c478bd9Sstevel@tonic-gate     //
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate     Vector values = null;
797c478bd9Sstevel@tonic-gate     String id = null;
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate     // For V1 compatibility subclass.
827c478bd9Sstevel@tonic-gate 
ServiceLocationAttribute()837c478bd9Sstevel@tonic-gate     ServiceLocationAttribute() {}
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate     /**
867c478bd9Sstevel@tonic-gate      * Construct a service location attribute.
877c478bd9Sstevel@tonic-gate      *
887c478bd9Sstevel@tonic-gate      * @param id		The attribute name
897c478bd9Sstevel@tonic-gate      * @param values_in	Vector of one or more attribute values. Vector
907c478bd9Sstevel@tonic-gate      *			contents must be uniform in type and one of
917c478bd9Sstevel@tonic-gate      *			Integer, String, Boolean, or byte[]. If the attribute
927c478bd9Sstevel@tonic-gate      *			is a keyword attribute, then values_in should be null.
937c478bd9Sstevel@tonic-gate      * @exception IllegalArgumentException Thrown if the
947c478bd9Sstevel@tonic-gate      *			vector contents is not of the right type or
957c478bd9Sstevel@tonic-gate      *			an argument is null or syntactically incorrect.
967c478bd9Sstevel@tonic-gate      */
977c478bd9Sstevel@tonic-gate 
ServiceLocationAttribute(String id_in, Vector values_in)987c478bd9Sstevel@tonic-gate     public ServiceLocationAttribute(String id_in, Vector values_in)
997c478bd9Sstevel@tonic-gate 	throws IllegalArgumentException {
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	Assert.nonNullParameter(id_in, "id");
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate 	id = id_in;
1047c478bd9Sstevel@tonic-gate 	if (values_in != null &&
1057c478bd9Sstevel@tonic-gate 	    values_in.size() > 0) { // null, empty indicate keyword attribute.
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	    values = (Vector)values_in.clone();
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate 	    verifyValueTypes(values, false);
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 	}
1127c478bd9Sstevel@tonic-gate     }
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate     /**
1157c478bd9Sstevel@tonic-gate      * Construct a service location attribute from a parenthesized expression.
1167c478bd9Sstevel@tonic-gate      * The syntax is:
1177c478bd9Sstevel@tonic-gate      *
1187c478bd9Sstevel@tonic-gate      *	 exp = "(" id "=" value-list ")" | keyword
1197c478bd9Sstevel@tonic-gate      *    value-list = value | value "," value-list
1207c478bd9Sstevel@tonic-gate      *
1217c478bd9Sstevel@tonic-gate      *
1227c478bd9Sstevel@tonic-gate      * @param exp The expression
1237c478bd9Sstevel@tonic-gate      * @param dontTypeCheck True if multivalued booleans and vectors
1247c478bd9Sstevel@tonic-gate      *			   of varying types are allowed.
1257c478bd9Sstevel@tonic-gate      * @exception ServiceLocationException If there are any syntax errors.
1267c478bd9Sstevel@tonic-gate      */
1277c478bd9Sstevel@tonic-gate 
ServiceLocationAttribute(String exp, boolean allowMultiValuedBooleans)1287c478bd9Sstevel@tonic-gate     ServiceLocationAttribute(String exp, boolean allowMultiValuedBooleans)
1297c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	if (exp == null || exp.length() <= 0) {
1327c478bd9Sstevel@tonic-gate 	    new ServiceLocationException(ServiceLocationException.PARSE_ERROR,
1337c478bd9Sstevel@tonic-gate 					 "null_string_parameter",
1347c478bd9Sstevel@tonic-gate 					 new Object[] {exp});
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	}
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 	// If start and end paren, then parse out assignment.
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 	if (exp.startsWith("(") && exp.endsWith(")")) {
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 	    StringTokenizer tk =
1437c478bd9Sstevel@tonic-gate 		new StringTokenizer(exp.substring(1, exp.length() - 1),
1447c478bd9Sstevel@tonic-gate 				    "=",
1457c478bd9Sstevel@tonic-gate 				    true);
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	    try {
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 		// Get the tag.
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 		id =
1527c478bd9Sstevel@tonic-gate 		    unescapeAttributeString(tk.nextToken(), true);
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 		if (id.length() <= 0) {
1557c478bd9Sstevel@tonic-gate 		    throw
1567c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
1577c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1587c478bd9Sstevel@tonic-gate 				"null_id",
1597c478bd9Sstevel@tonic-gate 				new Object[] {exp});
1607c478bd9Sstevel@tonic-gate 		}
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 		tk.nextToken();  // get rid of "="
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 		// Gather the rest.
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 		String rest = tk.nextToken("");
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 		// Parse the comma separated list.
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 		values = SrvLocHeader.parseCommaSeparatedListIn(rest, true);
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 		// Convert to objects.
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 		int i, n = values.size();
1757c478bd9Sstevel@tonic-gate 		Class vecClass = null;
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 		for (i = 0; i < n; i++) {
1787c478bd9Sstevel@tonic-gate 		    String value = (String)values.elementAt(i);
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 		    // Need to determine which type to use.
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 		    Object o = evaluate(value);
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 		    values.setElementAt(o, i);
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 		}
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	    } catch (NoSuchElementException ex) {
1897c478bd9Sstevel@tonic-gate 		throw
1907c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
1917c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1927c478bd9Sstevel@tonic-gate 				"assignment_syntax_err",
1937c478bd9Sstevel@tonic-gate 				new Object[] {exp});
1947c478bd9Sstevel@tonic-gate 	    }
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	    verifyValueTypes(values, allowMultiValuedBooleans);
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	} else {
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 	    // Check to make sure there's no parens.
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	    if (exp.indexOf('(') != -1 || exp.indexOf(')') != -1) {
2037c478bd9Sstevel@tonic-gate 		throw
2047c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
2057c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2067c478bd9Sstevel@tonic-gate 				"assignment_syntax_err",
2077c478bd9Sstevel@tonic-gate 				new Object[] {exp});
2087c478bd9Sstevel@tonic-gate 	    }
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	    // Unescape the keyword.
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 	    id = unescapeAttributeString(exp, true);
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	}
2157c478bd9Sstevel@tonic-gate     }
2167c478bd9Sstevel@tonic-gate 
evaluate(String value)2177c478bd9Sstevel@tonic-gate     static Object evaluate(String value)
2187c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 	Object o = null;
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	// If it can be converted into an integer, then convert it.
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 	try {
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 	    o = Integer.valueOf(value);
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 	} catch (NumberFormatException ex) {
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	    // Wasn't an integer. Try boolean.
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 	    if (value.equalsIgnoreCase(TRUE) ||
2337c478bd9Sstevel@tonic-gate 		value.equalsIgnoreCase(FALSE)) {
2347c478bd9Sstevel@tonic-gate 		o = Boolean.valueOf(value);
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	    } else {
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 		// Process the string to remove escapes.
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 		String val = (String)value;
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 		// If it begins with the opaque prefix, treat it as an
2437c478bd9Sstevel@tonic-gate 		//  opaque.
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 		if (val.startsWith(Opaque.OPAQUE_HEADER)) {
2467c478bd9Sstevel@tonic-gate 		    o = Opaque.unescapeByteArray(val);
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 		} else {
2497c478bd9Sstevel@tonic-gate 		    o = unescapeAttributeString(val, false);
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 		}
2527c478bd9Sstevel@tonic-gate 	    }
2537c478bd9Sstevel@tonic-gate 	}
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 	return o;
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate     }
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate     //
2607c478bd9Sstevel@tonic-gate     // Property accessors.
2617c478bd9Sstevel@tonic-gate     //
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate     /**
2647c478bd9Sstevel@tonic-gate      * @return A vector of attribute values, or null if the attribute is
2657c478bd9Sstevel@tonic-gate      *         a keyword attribute. If the attribute is single-valued, then
2667c478bd9Sstevel@tonic-gate      *         the vector contains only one object.
2677c478bd9Sstevel@tonic-gate      *
2687c478bd9Sstevel@tonic-gate      */
2697c478bd9Sstevel@tonic-gate 
getValues()2707c478bd9Sstevel@tonic-gate     public Vector getValues() {
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	if (values == null) {
2737c478bd9Sstevel@tonic-gate 	    return null;	// keyword case.
2747c478bd9Sstevel@tonic-gate 	}
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	Vector ret = (Vector)values.clone();
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 	// Need to process Opaques.
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 	int i, n = ret.size();
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
2837c478bd9Sstevel@tonic-gate 	    Object o = ret.elementAt(i);
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	    if (o instanceof Opaque) {
2867c478bd9Sstevel@tonic-gate 		o = ((Opaque)o).bytes;
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	    }
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	    ret.setElementAt(o, i);
2917c478bd9Sstevel@tonic-gate 	}
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	return ret;
2947c478bd9Sstevel@tonic-gate     }
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate     /**
2977c478bd9Sstevel@tonic-gate      * @return The attribute name.
2987c478bd9Sstevel@tonic-gate      */
2997c478bd9Sstevel@tonic-gate 
getId()3007c478bd9Sstevel@tonic-gate     public String getId() {
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	return id;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate     }
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate     /**
3077c478bd9Sstevel@tonic-gate      * Return an escaped version of the id parameter , suitable for inclusion
3087c478bd9Sstevel@tonic-gate      * in a query.
3097c478bd9Sstevel@tonic-gate      *
3107c478bd9Sstevel@tonic-gate      * @param str The string to escape as an id.
3117c478bd9Sstevel@tonic-gate      * @return The string with any reserved characters escaped.
3127c478bd9Sstevel@tonic-gate      * @exception IllegalArgumentException Thrown if the
3137c478bd9Sstevel@tonic-gate      *			string contains bad tag characters.
3147c478bd9Sstevel@tonic-gate      */
3157c478bd9Sstevel@tonic-gate 
escapeId(String str)3167c478bd9Sstevel@tonic-gate     static public String escapeId(String str)
3177c478bd9Sstevel@tonic-gate 	throws IllegalArgumentException {
3187c478bd9Sstevel@tonic-gate 	String ret = null;
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	try {
3217c478bd9Sstevel@tonic-gate 	    ret = escapeAttributeString(str, true);
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	} catch (ServiceLocationException ex) {
3247c478bd9Sstevel@tonic-gate 	    throw new IllegalArgumentException(ex.getMessage());
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	}
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	return ret;
3297c478bd9Sstevel@tonic-gate     }
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate     /**
3327c478bd9Sstevel@tonic-gate      * Return an escaped version of the value parameter, suitable for inclusion
3337c478bd9Sstevel@tonic-gate      * in a query. Opaques are stringified.
3347c478bd9Sstevel@tonic-gate      *
3357c478bd9Sstevel@tonic-gate      * @param val The value to escape.
3367c478bd9Sstevel@tonic-gate      * @return The stringified value.
3377c478bd9Sstevel@tonic-gate      * @exception IllegalArgumentException Thrown if the object is not
3387c478bd9Sstevel@tonic-gate      * 	         one of byte[], Integer, Boolean, or String.
3397c478bd9Sstevel@tonic-gate      */
3407c478bd9Sstevel@tonic-gate 
escapeValue(Object val)3417c478bd9Sstevel@tonic-gate     static public String escapeValue(Object val)
3427c478bd9Sstevel@tonic-gate 	throws IllegalArgumentException {
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	// Check type first.
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	typeCheckValue(val);
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate 	// Make Opaque out of byte[].
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 	if (val instanceof byte[]) {
3517c478bd9Sstevel@tonic-gate 	    val = new Opaque((byte[])val);
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 	}
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 	return  escapeValueInternal(val);
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate     }
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate     // Check type to make sure it's OK.
3607c478bd9Sstevel@tonic-gate 
typeCheckValue(Object obj)3617c478bd9Sstevel@tonic-gate     static private void typeCheckValue(Object obj) {
3627c478bd9Sstevel@tonic-gate 	SLPConfig conf = SLPConfig.getSLPConfig();
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	Assert.nonNullParameter(obj, "attribute value vector element");
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	if (obj.equals("")) {
3677c478bd9Sstevel@tonic-gate 	    throw
3687c478bd9Sstevel@tonic-gate 		new IllegalArgumentException(
3697c478bd9Sstevel@tonic-gate 				conf.formatMessage("empty_string_value",
3707c478bd9Sstevel@tonic-gate 						   new Object[0]));
3717c478bd9Sstevel@tonic-gate 	}
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 	if (!(obj instanceof Integer) && !(obj instanceof Boolean) &&
3747c478bd9Sstevel@tonic-gate 	    !(obj instanceof String) && !(obj instanceof byte[])) {
3757c478bd9Sstevel@tonic-gate 	    throw
3767c478bd9Sstevel@tonic-gate 		new IllegalArgumentException(
3777c478bd9Sstevel@tonic-gate 				conf.formatMessage("value_type_error",
3787c478bd9Sstevel@tonic-gate 						   new Object[0]));
3797c478bd9Sstevel@tonic-gate 	}
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate     }
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate     // We know the value's type is OK, so just escape it.
3847c478bd9Sstevel@tonic-gate 
escapeValueInternal(Object val)3857c478bd9Sstevel@tonic-gate     private static String escapeValueInternal(Object val) {
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	String s;
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 	// Escape any characters needing it.
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 	if (val instanceof String) {
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	    try {
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 		s = escapeAttributeString((String)val, false);
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	    } catch (ServiceLocationException ex) {
3987c478bd9Sstevel@tonic-gate 		throw
3997c478bd9Sstevel@tonic-gate 		    new IllegalArgumentException(ex.getMessage());
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 	    }
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	} else {
4047c478bd9Sstevel@tonic-gate 	    s = val.toString();
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	}
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	return s;
4097c478bd9Sstevel@tonic-gate     }
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate     //
4127c478bd9Sstevel@tonic-gate     // Methods for dealing with the type of attribute values.
4137c478bd9Sstevel@tonic-gate     //
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate     // Verify the types of incoming attributes.
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate     protected void
verifyValueTypes(Vector values_in, boolean dontTypeCheck)4187c478bd9Sstevel@tonic-gate 	verifyValueTypes(Vector values_in, boolean dontTypeCheck) {
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 	SLPConfig conf = SLPConfig.getSLPConfig();
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 	// Make sure the types of objects passed in are acceptable
4237c478bd9Sstevel@tonic-gate 	//  and that all objects in the vector have the same type.
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 	int i, n = values_in.size();
4267c478bd9Sstevel@tonic-gate 	Class cls = null;
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
4297c478bd9Sstevel@tonic-gate 	    Object obj = values_in.elementAt(i);
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 	    typeCheckValue(obj);
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	    if (i == 0) {
4347c478bd9Sstevel@tonic-gate 		cls = obj.getClass();
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	    } else if (!cls.equals(obj.getClass()) && !dontTypeCheck) {
4377c478bd9Sstevel@tonic-gate 		throw
4387c478bd9Sstevel@tonic-gate 		    new IllegalArgumentException(
4397c478bd9Sstevel@tonic-gate 				conf.formatMessage("type_mismatch_error",
4407c478bd9Sstevel@tonic-gate 						   new Object[0]));
4417c478bd9Sstevel@tonic-gate 	    }
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 	    // If it's a boolean and there's more than one, signal error
4447c478bd9Sstevel@tonic-gate 	    // unless multivalued booleans are allowed.
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	    if (!dontTypeCheck && i != 0 && obj instanceof Boolean) {
4477c478bd9Sstevel@tonic-gate 		throw
4487c478bd9Sstevel@tonic-gate 		    new IllegalArgumentException(
4497c478bd9Sstevel@tonic-gate 				conf.formatMessage("multivalued_boolean",
4507c478bd9Sstevel@tonic-gate 						   new Object[0]));
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 	    }
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 	    // If it's a byte array, create a Opaque object.
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	    if (obj instanceof byte[]) {
4577c478bd9Sstevel@tonic-gate 		values_in.setElementAt(new Opaque((byte[])obj), i);
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	    } else if (obj instanceof String) {
4607c478bd9Sstevel@tonic-gate 		String val = (String)obj;
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 		// If it's a string and looks like "1" or "true", then
4637c478bd9Sstevel@tonic-gate 		//  append a space onto the end.
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 		try {
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate 		    Object obj2 = evaluate(val);
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 		    if (!(obj2 instanceof String)) {
4707c478bd9Sstevel@tonic-gate 			values_in.setElementAt((String)val + " ", i);
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 		    }
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 		} catch (ServiceLocationException ex) {
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 		    // Ignore for now.
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate 		}
4797c478bd9Sstevel@tonic-gate 	    }
4807c478bd9Sstevel@tonic-gate 	}
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate     }
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate     //
4857c478bd9Sstevel@tonic-gate     // Methods for externalizing attributes.
4867c478bd9Sstevel@tonic-gate     //
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate     /**
4897c478bd9Sstevel@tonic-gate      * Externalize the attribute into a string that can be written
4907c478bd9Sstevel@tonic-gate      * to a byte stream. Includes escaping any characters that
4917c478bd9Sstevel@tonic-gate      * need to be escaped.
4927c478bd9Sstevel@tonic-gate      *
4937c478bd9Sstevel@tonic-gate      * @return String with attribute's external representation.
4947c478bd9Sstevel@tonic-gate      * @exception ServiceLocationException Thrown if the
4957c478bd9Sstevel@tonic-gate      *			string contains unencodable characters.
4967c478bd9Sstevel@tonic-gate      */
4977c478bd9Sstevel@tonic-gate 
externalize()4987c478bd9Sstevel@tonic-gate     String externalize()
4997c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	if (values == null) {	// keyword attribute...
5027c478bd9Sstevel@tonic-gate 	    return escapeAttributeString(id, true);
5037c478bd9Sstevel@tonic-gate 	}
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	Vector v = new Vector();
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	for (Enumeration e = values.elements(); e.hasMoreElements(); ) {
5087c478bd9Sstevel@tonic-gate 	    Object o = e.nextElement();
5097c478bd9Sstevel@tonic-gate 	    String s = null;
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 	    s = escapeValueInternal(o);
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	    v.addElement(s);
5147c478bd9Sstevel@tonic-gate 	}
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	StringBuffer buf =
5177c478bd9Sstevel@tonic-gate 	    new StringBuffer("(" +
5187c478bd9Sstevel@tonic-gate 			     escapeAttributeString(id, true) +
5197c478bd9Sstevel@tonic-gate 			     "=");
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	buf.append(SrvLocHeader.vectorToCommaSeparatedList(v));
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 	buf.append(")");
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 	return buf.toString();
5267c478bd9Sstevel@tonic-gate     }
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate     //
5297c478bd9Sstevel@tonic-gate     // Escaping and unescaping strings.
5307c478bd9Sstevel@tonic-gate     //
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate     /**
5337c478bd9Sstevel@tonic-gate      * Escape any escapable characters to a 2 character escape
5347c478bd9Sstevel@tonic-gate      * in the attribute string.
5357c478bd9Sstevel@tonic-gate      *
5367c478bd9Sstevel@tonic-gate      * @param string The String.
5377c478bd9Sstevel@tonic-gate      * @param badTag Check for bad tag characters if true.
5387c478bd9Sstevel@tonic-gate      * @return The escaped string.
5397c478bd9Sstevel@tonic-gate      * @exception ServiceLocationException Thrown if the string
5407c478bd9Sstevel@tonic-gate      *			contains a character that can't be encoded.
5417c478bd9Sstevel@tonic-gate      */
5427c478bd9Sstevel@tonic-gate 
escapeAttributeString(String string, boolean badTag)5437c478bd9Sstevel@tonic-gate     static String escapeAttributeString(String string,
5447c478bd9Sstevel@tonic-gate 					boolean badTag)
5457c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
5487c478bd9Sstevel@tonic-gate 	int i, n = string.length();
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
5517c478bd9Sstevel@tonic-gate 	    char c = string.charAt(i);
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	    // Check for bad tag characters first.
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	    if (badTag && BAD_TAG_CHARS.indexOf(c) != -1) {
5567c478bd9Sstevel@tonic-gate 		throw
5577c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
5587c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
5597c478bd9Sstevel@tonic-gate 				"bad_id_char",
5607c478bd9Sstevel@tonic-gate 				new Object[] {Integer.toHexString(c)});
5617c478bd9Sstevel@tonic-gate 	    }
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate 	    // Escape if the character is reserved.
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	    if (canEscape(c)) {
5667c478bd9Sstevel@tonic-gate 		buf.append(ESCAPE);
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 		String str = escapeChar(c);
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 		// Pad with zero if less than 2 characters.
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 		if (str.length() <= 1) {
5737c478bd9Sstevel@tonic-gate 		    str = "0" + str;
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 		}
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 		buf.append(str);
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 	    } else {
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 		buf.append(c);
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 	    }
5847c478bd9Sstevel@tonic-gate 	}
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	return buf.toString();
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate     }
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate     /**
5927c478bd9Sstevel@tonic-gate      * Convert any 2 character escapes to the corresponding characters.
5937c478bd9Sstevel@tonic-gate      *
5947c478bd9Sstevel@tonic-gate      * @param string The string to be processed.
5957c478bd9Sstevel@tonic-gate      * @param badTag Check for bad tag characters if true.
5967c478bd9Sstevel@tonic-gate      * @return The processed string.
5977c478bd9Sstevel@tonic-gate      * @exception ServiceLocationException Thrown if an escape
5987c478bd9Sstevel@tonic-gate      *			is improperly formatted.
5997c478bd9Sstevel@tonic-gate      */
6007c478bd9Sstevel@tonic-gate 
unescapeAttributeString(String string, boolean badTag)6017c478bd9Sstevel@tonic-gate     static String unescapeAttributeString(String string,
6027c478bd9Sstevel@tonic-gate 					  boolean badTag)
6037c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	// Process escapes.
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	int i, n = string.length();
6087c478bd9Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer(n);
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
6117c478bd9Sstevel@tonic-gate 	    char c = string.charAt(i);
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 	    // Check for escaped characters.
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	    if (c == ESCAPE) {
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate 		// Get the next two characters.
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 		if (i >= n - 2) {
6207c478bd9Sstevel@tonic-gate 		    throw
6217c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
6227c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6237c478bd9Sstevel@tonic-gate 				"nonterminating_escape",
6247c478bd9Sstevel@tonic-gate 				new Object[] {string});
6257c478bd9Sstevel@tonic-gate 		}
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate 		i++;
6287c478bd9Sstevel@tonic-gate 		c = unescapeChar(string.substring(i, i+2));
6297c478bd9Sstevel@tonic-gate 		i++;
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 		// Check whether it's reserved.
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 		if (!canEscape(c)) {
6347c478bd9Sstevel@tonic-gate 		    throw
6357c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
6367c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6377c478bd9Sstevel@tonic-gate 				"char_not_reserved_attr",
6387c478bd9Sstevel@tonic-gate 				new Object[] {new Character(c), string});
6397c478bd9Sstevel@tonic-gate 		}
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	    } else {
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 		// Check whether the character is reserved.
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 		if (isReserved(c)) {
6467c478bd9Sstevel@tonic-gate 		    throw
6477c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
6487c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6497c478bd9Sstevel@tonic-gate 				"reserved_not_escaped",
6507c478bd9Sstevel@tonic-gate 				new Object[] {new Character(c)});
6517c478bd9Sstevel@tonic-gate 		}
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate 	    }
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	    // If we need to check for a bad tag character, do so now.
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	    if (badTag && BAD_TAG_CHARS.indexOf(c) != -1) {
6587c478bd9Sstevel@tonic-gate 		throw
6597c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
6607c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6617c478bd9Sstevel@tonic-gate 				"bad_id_char",
6627c478bd9Sstevel@tonic-gate 				new Object[] {Integer.toHexString(c)});
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	    }
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate 	    buf.append(c);
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	}
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 	return buf.toString();
6717c478bd9Sstevel@tonic-gate     }
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate     // Return true if the character c can be escaped.
6747c478bd9Sstevel@tonic-gate 
canEscape(char c)6757c478bd9Sstevel@tonic-gate     private static boolean canEscape(char c) {
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 	return ((ESCAPED.indexOf(c) != -1) ||
6787c478bd9Sstevel@tonic-gate 		((c >= CTL_LOWER && c <= CTL_UPPER) || c == DEL));
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate     }
6817c478bd9Sstevel@tonic-gate 
6827c478bd9Sstevel@tonic-gate     // Return true if the character c is reserved.
6837c478bd9Sstevel@tonic-gate 
isReserved(char c)6847c478bd9Sstevel@tonic-gate     private static boolean isReserved(char c) {
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	return ((RESERVED.indexOf(c) != -1) ||
6877c478bd9Sstevel@tonic-gate 		((c >= CTL_LOWER && c <= CTL_UPPER) || c == DEL));
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate     }
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate     /**
6927c478bd9Sstevel@tonic-gate      * Return a string of integers giving the character's encoding in
6937c478bd9Sstevel@tonic-gate      * the character set passed in as encoding.
6947c478bd9Sstevel@tonic-gate      *
6957c478bd9Sstevel@tonic-gate      * @param c The character to escape.
6967c478bd9Sstevel@tonic-gate      * @return The character as a string of integers for the encoding.
6977c478bd9Sstevel@tonic-gate      */
6987c478bd9Sstevel@tonic-gate 
escapeChar(char c)6997c478bd9Sstevel@tonic-gate     static String escapeChar(char c) {
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 	byte[] b = null;
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 	try {
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 	    b = ("" + c).getBytes(Defaults.UTF8);
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	} catch (UnsupportedEncodingException ex) {
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate 	    Assert.slpassert(false, "no_utf8", new Object[0]);
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 	}
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 	int code = 0;
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 	// Assemble the character code.
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 	if (b.length > 3) {
7187c478bd9Sstevel@tonic-gate 	    Assert.slpassert(false,
7197c478bd9Sstevel@tonic-gate 			  "illegal_utf8",
7207c478bd9Sstevel@tonic-gate 			  new Object[] {new Character(c)});
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 	}
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	code = (int)(b[0] & 0xFF);
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 	if (b.length > 1) {
7277c478bd9Sstevel@tonic-gate 	    code = (int)(code | ((b[1] & 0xFF) << 8));
7287c478bd9Sstevel@tonic-gate 	}
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 	if (b.length > 2) {
7317c478bd9Sstevel@tonic-gate 	    code = (int)(code | ((b[2] & 0xFF) << 16));
7327c478bd9Sstevel@tonic-gate 	}
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	String str = Integer.toHexString(code);
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate 	return str;
7377c478bd9Sstevel@tonic-gate     }
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate     /**
7407c478bd9Sstevel@tonic-gate      * Unescape the character encoded as the string.
7417c478bd9Sstevel@tonic-gate      *
7427c478bd9Sstevel@tonic-gate      * @param ch The character as a string of hex digits.
7437c478bd9Sstevel@tonic-gate      * @return The character.
7447c478bd9Sstevel@tonic-gate      * @exception ServiceLocationException If the characters can't be
7457c478bd9Sstevel@tonic-gate      *		 converted into a hex string.
7467c478bd9Sstevel@tonic-gate      */
7477c478bd9Sstevel@tonic-gate 
unescapeChar(String ch)7487c478bd9Sstevel@tonic-gate     static char unescapeChar(String ch)
7497c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate 	int code = 0;
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 	try {
7547c478bd9Sstevel@tonic-gate 	    code = Integer.parseInt(ch, 16);
7557c478bd9Sstevel@tonic-gate 
7567c478bd9Sstevel@tonic-gate 	} catch (NumberFormatException ex) {
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 	    throw
7597c478bd9Sstevel@tonic-gate 		new ServiceLocationException(
7607c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
7617c478bd9Sstevel@tonic-gate 				"not_a_character",
7627c478bd9Sstevel@tonic-gate 				new Object[] {ch});
7637c478bd9Sstevel@tonic-gate 	}
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate 	// Convert to bytes.
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	String str = null;
7687c478bd9Sstevel@tonic-gate 	byte b0 = 0, b1 = 0, b2 = 0, b3 = 0;
7697c478bd9Sstevel@tonic-gate 	byte b[] = null;
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	b0 = (byte) (code & 0xFF);
7727c478bd9Sstevel@tonic-gate 	b1 = (byte) ((code >> 8) & 0xFF);
7737c478bd9Sstevel@tonic-gate 	b2 = (byte) ((code >> 16) & 0xFF);
7747c478bd9Sstevel@tonic-gate 	b3 = (byte) ((code >> 24) & 0xFF);
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	// We allow illegal UTF8 encoding so we can decode byte arrays.
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate 	if (b3 != 0) {
7797c478bd9Sstevel@tonic-gate 	    b = new byte[3];
7807c478bd9Sstevel@tonic-gate 	    b[3] = b3;
7817c478bd9Sstevel@tonic-gate 	    b[2] = b2;
7827c478bd9Sstevel@tonic-gate 	    b[1] = b1;
7837c478bd9Sstevel@tonic-gate 	    b[0] = b0;
7847c478bd9Sstevel@tonic-gate 	} else if (b2 != 0) {
7857c478bd9Sstevel@tonic-gate 	    b = new byte[3];
7867c478bd9Sstevel@tonic-gate 	    b[2] = b2;
7877c478bd9Sstevel@tonic-gate 	    b[1] = b1;
7887c478bd9Sstevel@tonic-gate 	    b[0] = b0;
7897c478bd9Sstevel@tonic-gate 	} else if (b1 != 0) {
7907c478bd9Sstevel@tonic-gate 	    b = new byte[2];
7917c478bd9Sstevel@tonic-gate 	    b[1] = b1;
7927c478bd9Sstevel@tonic-gate 	    b[0] = b0;
7937c478bd9Sstevel@tonic-gate 	} else {
7947c478bd9Sstevel@tonic-gate 	    b = new byte[1];
7957c478bd9Sstevel@tonic-gate 	    b[0] = b0;
7967c478bd9Sstevel@tonic-gate 	}
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 	// Make a string out of it.
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 	try {
8017c478bd9Sstevel@tonic-gate 	    str = new String(b, Defaults.UTF8);
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	} catch (UnsupportedEncodingException ex) {
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate 	    Assert.slpassert(false, "no_utf8", new Object[0]);
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	}
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate 	int len = str.length();
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 	if (str.length() > 1) {
8127c478bd9Sstevel@tonic-gate 	    throw
8137c478bd9Sstevel@tonic-gate 		new ServiceLocationException(
8147c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
8157c478bd9Sstevel@tonic-gate 				"more_than_one",
8167c478bd9Sstevel@tonic-gate 				new Object[] {ch});
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 	}
8197c478bd9Sstevel@tonic-gate 
8207c478bd9Sstevel@tonic-gate 	return (len == 1 ? str.charAt(0):(char)0);
8217c478bd9Sstevel@tonic-gate     }
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate     /**
8247c478bd9Sstevel@tonic-gate      * Merge the values in newAttr into the attribute in the hashtable
8257c478bd9Sstevel@tonic-gate      * if a duplicate attribute, signal error if a type mismatch.
8267c478bd9Sstevel@tonic-gate      * Both the return vector and hashtable are updated, but the
8277c478bd9Sstevel@tonic-gate      * newAttr parameter is left unchanged.
8287c478bd9Sstevel@tonic-gate      *
8297c478bd9Sstevel@tonic-gate      * @param attr The ServiceLocationAttribute to check.
8307c478bd9Sstevel@tonic-gate      * @param attrHash A Hashtable containing the attribute tags as
8317c478bd9Sstevel@tonic-gate      *			keys and the attributes as values.
8327c478bd9Sstevel@tonic-gate      * @param returns A Vector in which to put the attribute when done.
8337c478bd9Sstevel@tonic-gate      * @param dontTypeCheck If this flag is true, the value vector
8347c478bd9Sstevel@tonic-gate      *			   may have two booleans, may
8357c478bd9Sstevel@tonic-gate      *			   contain differently typed objects, or the
8367c478bd9Sstevel@tonic-gate      *			   function may merge a keyword and nonkeyword
8377c478bd9Sstevel@tonic-gate      *			   attribute.
8387c478bd9Sstevel@tonic-gate      * @exception ServiceLocationException Thrown if a type mismatch
8397c478bd9Sstevel@tonic-gate      *			occurs.
8407c478bd9Sstevel@tonic-gate      */
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate     static void
mergeDuplicateAttributes(ServiceLocationAttribute newAttr, Hashtable attrTable, Vector returns, boolean dontTypeCheck)8437c478bd9Sstevel@tonic-gate 	mergeDuplicateAttributes(ServiceLocationAttribute newAttr,
8447c478bd9Sstevel@tonic-gate 				 Hashtable attrTable,
8457c478bd9Sstevel@tonic-gate 				 Vector returns,
8467c478bd9Sstevel@tonic-gate 				 boolean dontTypeCheck)
8477c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 	// Look up the attribute
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	String tag = newAttr.getId().toLowerCase();
8527c478bd9Sstevel@tonic-gate 	ServiceLocationAttribute attr =
8537c478bd9Sstevel@tonic-gate 	    (ServiceLocationAttribute)attrTable.get(tag);
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	// Don't try this trick with ServerAttributes!
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate 	Assert.slpassert((!(attr instanceof ServerAttribute) &&
8587c478bd9Sstevel@tonic-gate 		       !(newAttr instanceof ServerAttribute)),
8597c478bd9Sstevel@tonic-gate 		      "merge_servattr",
8607c478bd9Sstevel@tonic-gate 		      new Object[0]);
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	// If the attribute isn't in the hashtable, then add to
8637c478bd9Sstevel@tonic-gate 	//  vector and hashtable.
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 	if (attr == null) {
8667c478bd9Sstevel@tonic-gate 	    attrTable.put(tag, newAttr);
8677c478bd9Sstevel@tonic-gate 	    returns.addElement(newAttr);
8687c478bd9Sstevel@tonic-gate 	    return;
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 	}
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	Vector attrNewVals = newAttr.values;
8747c478bd9Sstevel@tonic-gate 	Vector attrVals = attr.values;
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 	// If both keywords, nothing further to do.
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 	if (attrVals == null && attrNewVals == null) {
8797c478bd9Sstevel@tonic-gate 	    return;
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate 	}
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate 	// If we are not typechecking and one is keyword while the other
8847c478bd9Sstevel@tonic-gate 	//  is not, then simply merge in the nonkeyword. Otherwise,
8857c478bd9Sstevel@tonic-gate 	//  throw a type check exception.
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate 	if ((attrVals == null && attrNewVals != null) ||
8887c478bd9Sstevel@tonic-gate 	    (attrNewVals == null && attrVals != null)) {
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 	    if (dontTypeCheck) {
8917c478bd9Sstevel@tonic-gate 		Vector vals = (attrNewVals != null ? attrNewVals:attrVals);
8927c478bd9Sstevel@tonic-gate 		attr.values = vals;
8937c478bd9Sstevel@tonic-gate 		newAttr.values = vals;
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 	    } else {
8967c478bd9Sstevel@tonic-gate 		throw
8977c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
8987c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
8997c478bd9Sstevel@tonic-gate 				"attribute_type_mismatch",
9007c478bd9Sstevel@tonic-gate 				new Object[] {newAttr.getId()});
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 	    }
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 	} else {
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 	    // Merge the two vectors. We type check against the attrVals
9077c478bd9Sstevel@tonic-gate 	    //  vector, if we are type checking.
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	    int i, n = attrNewVals.size();
9107c478bd9Sstevel@tonic-gate 	    Object o = attrVals.elementAt(0);
9117c478bd9Sstevel@tonic-gate 	    Class c = o.getClass();
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 	    for (i = 0; i < n; i++) {
9147c478bd9Sstevel@tonic-gate 		Object no = attrNewVals.elementAt(i);
9157c478bd9Sstevel@tonic-gate 
9167c478bd9Sstevel@tonic-gate 		// Check for type mismatch, throw exception if
9177c478bd9Sstevel@tonic-gate 		//  we are type checking.
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 		if ((c != no.getClass()) && !dontTypeCheck) {
9207c478bd9Sstevel@tonic-gate 		    throw
9217c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
9227c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
9237c478bd9Sstevel@tonic-gate 				"attribute_type_mismatch",
9247c478bd9Sstevel@tonic-gate 				new Object[] {newAttr.getId()});
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 		}
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 		// If we are typechecking, and we get two opposite
9297c478bd9Sstevel@tonic-gate 		//  booleans, we need to throw an exception.
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 		if (no instanceof Boolean && !no.equals(o) && !dontTypeCheck) {
9327c478bd9Sstevel@tonic-gate 		    throw
9337c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
9347c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
9357c478bd9Sstevel@tonic-gate 				"boolean_incompat",
9367c478bd9Sstevel@tonic-gate 				new Object[] {newAttr.getId()});
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 		}
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 		// Add the value if it isn't already there.
9417c478bd9Sstevel@tonic-gate 
9427c478bd9Sstevel@tonic-gate 		if (!attrVals.contains(no)) {
9437c478bd9Sstevel@tonic-gate 		    attrVals.addElement(no);
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 		}
9467c478bd9Sstevel@tonic-gate 	    }
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	    // Set the new attribute's values so they are the same as the old.
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 	    newAttr.values = attrVals;
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 	}
9537c478bd9Sstevel@tonic-gate     }
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate     //
9567c478bd9Sstevel@tonic-gate     // Object overrides.
9577c478bd9Sstevel@tonic-gate     //
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate     /**
9607c478bd9Sstevel@tonic-gate      * Return true if the object equals this attribute.
9617c478bd9Sstevel@tonic-gate      */
9627c478bd9Sstevel@tonic-gate 
equals(Object o)9637c478bd9Sstevel@tonic-gate     public boolean equals(Object o) {
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate 	if (!(o instanceof ServiceLocationAttribute)) {
9667c478bd9Sstevel@tonic-gate 	    return false;
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	}
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 	if (o == this) {
9717c478bd9Sstevel@tonic-gate 	    return true;
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 	}
9747c478bd9Sstevel@tonic-gate 
9757c478bd9Sstevel@tonic-gate 	ServiceLocationAttribute sla = (ServiceLocationAttribute)o;
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate 	// check equality of contents, deferring check of all values
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 	Vector vSLA = sla.values;
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate 	if (!sla.getId().equalsIgnoreCase(id)) {
9827c478bd9Sstevel@tonic-gate 	    return false;
9837c478bd9Sstevel@tonic-gate 
9847c478bd9Sstevel@tonic-gate 	}
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 	if (values == null && vSLA == null) {
9877c478bd9Sstevel@tonic-gate 	    return true;
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 	}
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 	if ((values == null && vSLA != null) ||
9927c478bd9Sstevel@tonic-gate 	    (values != null && vSLA == null)) {
9937c478bd9Sstevel@tonic-gate 	    return false;
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	}
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	if (values.size() != vSLA.size()) {
9987c478bd9Sstevel@tonic-gate 	    return false;
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate 	}
10017c478bd9Sstevel@tonic-gate 
10027c478bd9Sstevel@tonic-gate 	// Check contents.
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate 	Object oSLA = vSLA.elementAt(0);
10057c478bd9Sstevel@tonic-gate 	o = values.elementAt(0);
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 	if (o.getClass() != oSLA.getClass()) {
10087c478bd9Sstevel@tonic-gate 	    return false;
10097c478bd9Sstevel@tonic-gate 
10107c478bd9Sstevel@tonic-gate 	}
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 	int i, n = vSLA.size();
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
10157c478bd9Sstevel@tonic-gate 	    oSLA = vSLA.elementAt(i);
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate 	    if (!values.contains(oSLA)) {
10187c478bd9Sstevel@tonic-gate 		return false;
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate 	    }
10217c478bd9Sstevel@tonic-gate 	}
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate 	return true;
10247c478bd9Sstevel@tonic-gate     }
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate     /**
10277c478bd9Sstevel@tonic-gate      * Return a human readable string for the attribute.
10287c478bd9Sstevel@tonic-gate      */
10297c478bd9Sstevel@tonic-gate 
toString()10307c478bd9Sstevel@tonic-gate     public String toString() {
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate 	StringBuffer s = new StringBuffer("(");
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 	s.append(id);
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 	if (values != null) {
10377c478bd9Sstevel@tonic-gate 	    s.append("=");
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate 	    int i, n = values.size();
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 	    for (i = 0; i < n; i++) {
10427c478bd9Sstevel@tonic-gate 		Object o = values.elementAt(i);
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate 		// Identify type.
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 		if (i == 0) {
10477c478bd9Sstevel@tonic-gate 		    s.append(o.getClass().getName());
10487c478bd9Sstevel@tonic-gate 		    s.append(":");
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 		} else {
10517c478bd9Sstevel@tonic-gate 		    s.append(",");
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 		}
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate 		// Stringify object.
10567c478bd9Sstevel@tonic-gate 
10577c478bd9Sstevel@tonic-gate 		s.append(o.toString());
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 	    }
10607c478bd9Sstevel@tonic-gate 	}
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate 	s.append(")");
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 	return s.toString();
10657c478bd9Sstevel@tonic-gate     }
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate     // Overrides Object.hashCode().
10687c478bd9Sstevel@tonic-gate 
hashCode()10697c478bd9Sstevel@tonic-gate     public int hashCode() {
10707c478bd9Sstevel@tonic-gate 	return id.toLowerCase().hashCode();
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate     }
10737c478bd9Sstevel@tonic-gate 
10747c478bd9Sstevel@tonic-gate }
1075