10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7298SMark.J.Nelson@Sun.COM  * Common Development and Distribution License (the "License").
6*7298SMark.J.Nelson@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
220Sstevel@tonic-gate  * Copyright 2001,2003 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
27*7298SMark.J.Nelson@Sun.COM //  ServiceLocationAttribute.java : Class for attributes in SLP.
280Sstevel@tonic-gate //  Author:           James Kempf, Erik Guttman
290Sstevel@tonic-gate //
300Sstevel@tonic-gate 
310Sstevel@tonic-gate package com.sun.slp;
320Sstevel@tonic-gate 
330Sstevel@tonic-gate import java.util.*;
340Sstevel@tonic-gate import java.io.*;
350Sstevel@tonic-gate 
360Sstevel@tonic-gate /**
370Sstevel@tonic-gate  * The ServiceLocationAttribute class models SLP attributes.
380Sstevel@tonic-gate  *
390Sstevel@tonic-gate  * @author James Kempf, Erik Guttman
400Sstevel@tonic-gate  */
410Sstevel@tonic-gate 
420Sstevel@tonic-gate public class ServiceLocationAttribute extends Object
430Sstevel@tonic-gate     implements Serializable {
440Sstevel@tonic-gate 
450Sstevel@tonic-gate     // Characters to escape.
460Sstevel@tonic-gate 
470Sstevel@tonic-gate     final static String RESERVED = "(),\\!<=>~";
480Sstevel@tonic-gate     final static String ESCAPED = RESERVED + "*";
490Sstevel@tonic-gate     final static char ESCAPE = '\\';
500Sstevel@tonic-gate 
510Sstevel@tonic-gate     final static char CTL_LOWER = (char)0x00;
520Sstevel@tonic-gate     final static char CTL_UPPER = (char)0x1F;
530Sstevel@tonic-gate     final static char DEL = (char)0x7F;
540Sstevel@tonic-gate 
550Sstevel@tonic-gate     // Whitespace chars.
560Sstevel@tonic-gate 
570Sstevel@tonic-gate     static final String WHITESPACE = " \n\t\r";
580Sstevel@tonic-gate     static final char SPACE = ' ';
590Sstevel@tonic-gate 
600Sstevel@tonic-gate     // For character escaping.
610Sstevel@tonic-gate 
620Sstevel@tonic-gate     static final char COMMA = ',';
630Sstevel@tonic-gate     static final char PERCENT = '%';
640Sstevel@tonic-gate 
650Sstevel@tonic-gate     // Bad tag characters.
660Sstevel@tonic-gate 
670Sstevel@tonic-gate     final private static String BAD_TAG_CHARS = "*\n\t\r";
680Sstevel@tonic-gate 
690Sstevel@tonic-gate     // For identifying booleans.
700Sstevel@tonic-gate 
710Sstevel@tonic-gate     final static String TRUE = "true";
720Sstevel@tonic-gate     final static String FALSE = "false";
730Sstevel@tonic-gate 
740Sstevel@tonic-gate     //
750Sstevel@tonic-gate     // Package accessable fields.
760Sstevel@tonic-gate     //
770Sstevel@tonic-gate 
780Sstevel@tonic-gate     Vector values = null;
790Sstevel@tonic-gate     String id = null;
800Sstevel@tonic-gate 
810Sstevel@tonic-gate     // For V1 compatibility subclass.
820Sstevel@tonic-gate 
ServiceLocationAttribute()830Sstevel@tonic-gate     ServiceLocationAttribute() {}
840Sstevel@tonic-gate 
850Sstevel@tonic-gate     /**
860Sstevel@tonic-gate      * Construct a service location attribute.
870Sstevel@tonic-gate      *
880Sstevel@tonic-gate      * @param id		The attribute name
890Sstevel@tonic-gate      * @param values_in	Vector of one or more attribute values. Vector
900Sstevel@tonic-gate      *			contents must be uniform in type and one of
910Sstevel@tonic-gate      *			Integer, String, Boolean, or byte[]. If the attribute
920Sstevel@tonic-gate      *			is a keyword attribute, then values_in should be null.
930Sstevel@tonic-gate      * @exception IllegalArgumentException Thrown if the
940Sstevel@tonic-gate      *			vector contents is not of the right type or
950Sstevel@tonic-gate      *			an argument is null or syntactically incorrect.
960Sstevel@tonic-gate      */
970Sstevel@tonic-gate 
ServiceLocationAttribute(String id_in, Vector values_in)980Sstevel@tonic-gate     public ServiceLocationAttribute(String id_in, Vector values_in)
990Sstevel@tonic-gate 	throws IllegalArgumentException {
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	Assert.nonNullParameter(id_in, "id");
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 	id = id_in;
1040Sstevel@tonic-gate 	if (values_in != null &&
1050Sstevel@tonic-gate 	    values_in.size() > 0) { // null, empty indicate keyword attribute.
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 	    values = (Vector)values_in.clone();
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	    verifyValueTypes(values, false);
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate 	}
1120Sstevel@tonic-gate     }
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate     /**
1150Sstevel@tonic-gate      * Construct a service location attribute from a parenthesized expression.
1160Sstevel@tonic-gate      * The syntax is:
1170Sstevel@tonic-gate      *
1180Sstevel@tonic-gate      *	 exp = "(" id "=" value-list ")" | keyword
1190Sstevel@tonic-gate      *    value-list = value | value "," value-list
1200Sstevel@tonic-gate      *
1210Sstevel@tonic-gate      *
1220Sstevel@tonic-gate      * @param exp The expression
1230Sstevel@tonic-gate      * @param dontTypeCheck True if multivalued booleans and vectors
1240Sstevel@tonic-gate      *			   of varying types are allowed.
1250Sstevel@tonic-gate      * @exception ServiceLocationException If there are any syntax errors.
1260Sstevel@tonic-gate      */
1270Sstevel@tonic-gate 
ServiceLocationAttribute(String exp, boolean allowMultiValuedBooleans)1280Sstevel@tonic-gate     ServiceLocationAttribute(String exp, boolean allowMultiValuedBooleans)
1290Sstevel@tonic-gate 	throws ServiceLocationException {
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 	if (exp == null || exp.length() <= 0) {
1320Sstevel@tonic-gate 	    new ServiceLocationException(ServiceLocationException.PARSE_ERROR,
1330Sstevel@tonic-gate 					 "null_string_parameter",
1340Sstevel@tonic-gate 					 new Object[] {exp});
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 	}
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 	// If start and end paren, then parse out assignment.
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 	if (exp.startsWith("(") && exp.endsWith(")")) {
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate 	    StringTokenizer tk =
1430Sstevel@tonic-gate 		new StringTokenizer(exp.substring(1, exp.length() - 1),
1440Sstevel@tonic-gate 				    "=",
1450Sstevel@tonic-gate 				    true);
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate 	    try {
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate 		// Get the tag.
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 		id =
1520Sstevel@tonic-gate 		    unescapeAttributeString(tk.nextToken(), true);
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 		if (id.length() <= 0) {
1550Sstevel@tonic-gate 		    throw
1560Sstevel@tonic-gate 			new ServiceLocationException(
1570Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1580Sstevel@tonic-gate 				"null_id",
1590Sstevel@tonic-gate 				new Object[] {exp});
1600Sstevel@tonic-gate 		}
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate 		tk.nextToken();  // get rid of "="
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 		// Gather the rest.
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 		String rest = tk.nextToken("");
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 		// Parse the comma separated list.
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 		values = SrvLocHeader.parseCommaSeparatedListIn(rest, true);
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 		// Convert to objects.
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 		int i, n = values.size();
1750Sstevel@tonic-gate 		Class vecClass = null;
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 		for (i = 0; i < n; i++) {
1780Sstevel@tonic-gate 		    String value = (String)values.elementAt(i);
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 		    // Need to determine which type to use.
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate 		    Object o = evaluate(value);
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 		    values.setElementAt(o, i);
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate 		}
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	    } catch (NoSuchElementException ex) {
1890Sstevel@tonic-gate 		throw
1900Sstevel@tonic-gate 		    new ServiceLocationException(
1910Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1920Sstevel@tonic-gate 				"assignment_syntax_err",
1930Sstevel@tonic-gate 				new Object[] {exp});
1940Sstevel@tonic-gate 	    }
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	    verifyValueTypes(values, allowMultiValuedBooleans);
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	} else {
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	    // Check to make sure there's no parens.
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	    if (exp.indexOf('(') != -1 || exp.indexOf(')') != -1) {
2030Sstevel@tonic-gate 		throw
2040Sstevel@tonic-gate 		    new ServiceLocationException(
2050Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2060Sstevel@tonic-gate 				"assignment_syntax_err",
2070Sstevel@tonic-gate 				new Object[] {exp});
2080Sstevel@tonic-gate 	    }
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	    // Unescape the keyword.
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	    id = unescapeAttributeString(exp, true);
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	}
2150Sstevel@tonic-gate     }
2160Sstevel@tonic-gate 
evaluate(String value)2170Sstevel@tonic-gate     static Object evaluate(String value)
2180Sstevel@tonic-gate 	throws ServiceLocationException {
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	Object o = null;
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	// If it can be converted into an integer, then convert it.
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	try {
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	    o = Integer.valueOf(value);
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	} catch (NumberFormatException ex) {
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 	    // Wasn't an integer. Try boolean.
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 	    if (value.equalsIgnoreCase(TRUE) ||
2330Sstevel@tonic-gate 		value.equalsIgnoreCase(FALSE)) {
2340Sstevel@tonic-gate 		o = Boolean.valueOf(value);
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 	    } else {
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 		// Process the string to remove escapes.
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 		String val = (String)value;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 		// If it begins with the opaque prefix, treat it as an
2430Sstevel@tonic-gate 		//  opaque.
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 		if (val.startsWith(Opaque.OPAQUE_HEADER)) {
2460Sstevel@tonic-gate 		    o = Opaque.unescapeByteArray(val);
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 		} else {
2490Sstevel@tonic-gate 		    o = unescapeAttributeString(val, false);
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 		}
2520Sstevel@tonic-gate 	    }
2530Sstevel@tonic-gate 	}
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	return o;
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate     }
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate     //
2600Sstevel@tonic-gate     // Property accessors.
2610Sstevel@tonic-gate     //
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate     /**
2640Sstevel@tonic-gate      * @return A vector of attribute values, or null if the attribute is
2650Sstevel@tonic-gate      *         a keyword attribute. If the attribute is single-valued, then
2660Sstevel@tonic-gate      *         the vector contains only one object.
2670Sstevel@tonic-gate      *
2680Sstevel@tonic-gate      */
2690Sstevel@tonic-gate 
getValues()2700Sstevel@tonic-gate     public Vector getValues() {
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	if (values == null) {
2730Sstevel@tonic-gate 	    return null;	// keyword case.
2740Sstevel@tonic-gate 	}
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 	Vector ret = (Vector)values.clone();
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 	// Need to process Opaques.
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	int i, n = ret.size();
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
2830Sstevel@tonic-gate 	    Object o = ret.elementAt(i);
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	    if (o instanceof Opaque) {
2860Sstevel@tonic-gate 		o = ((Opaque)o).bytes;
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	    }
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	    ret.setElementAt(o, i);
2910Sstevel@tonic-gate 	}
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	return ret;
2940Sstevel@tonic-gate     }
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate     /**
2970Sstevel@tonic-gate      * @return The attribute name.
2980Sstevel@tonic-gate      */
2990Sstevel@tonic-gate 
getId()3000Sstevel@tonic-gate     public String getId() {
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	return id;
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate     }
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate     /**
3070Sstevel@tonic-gate      * Return an escaped version of the id parameter , suitable for inclusion
3080Sstevel@tonic-gate      * in a query.
3090Sstevel@tonic-gate      *
3100Sstevel@tonic-gate      * @param str The string to escape as an id.
3110Sstevel@tonic-gate      * @return The string with any reserved characters escaped.
3120Sstevel@tonic-gate      * @exception IllegalArgumentException Thrown if the
3130Sstevel@tonic-gate      *			string contains bad tag characters.
3140Sstevel@tonic-gate      */
3150Sstevel@tonic-gate 
escapeId(String str)3160Sstevel@tonic-gate     static public String escapeId(String str)
3170Sstevel@tonic-gate 	throws IllegalArgumentException {
3180Sstevel@tonic-gate 	String ret = null;
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	try {
3210Sstevel@tonic-gate 	    ret = escapeAttributeString(str, true);
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	} catch (ServiceLocationException ex) {
3240Sstevel@tonic-gate 	    throw new IllegalArgumentException(ex.getMessage());
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	}
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	return ret;
3290Sstevel@tonic-gate     }
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate     /**
3320Sstevel@tonic-gate      * Return an escaped version of the value parameter, suitable for inclusion
3330Sstevel@tonic-gate      * in a query. Opaques are stringified.
3340Sstevel@tonic-gate      *
3350Sstevel@tonic-gate      * @param val The value to escape.
3360Sstevel@tonic-gate      * @return The stringified value.
3370Sstevel@tonic-gate      * @exception IllegalArgumentException Thrown if the object is not
3380Sstevel@tonic-gate      * 	         one of byte[], Integer, Boolean, or String.
3390Sstevel@tonic-gate      */
3400Sstevel@tonic-gate 
escapeValue(Object val)3410Sstevel@tonic-gate     static public String escapeValue(Object val)
3420Sstevel@tonic-gate 	throws IllegalArgumentException {
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	// Check type first.
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	typeCheckValue(val);
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	// Make Opaque out of byte[].
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	if (val instanceof byte[]) {
3510Sstevel@tonic-gate 	    val = new Opaque((byte[])val);
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 	}
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 	return  escapeValueInternal(val);
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate     }
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate     // Check type to make sure it's OK.
3600Sstevel@tonic-gate 
typeCheckValue(Object obj)3610Sstevel@tonic-gate     static private void typeCheckValue(Object obj) {
3620Sstevel@tonic-gate 	SLPConfig conf = SLPConfig.getSLPConfig();
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	Assert.nonNullParameter(obj, "attribute value vector element");
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 	if (obj.equals("")) {
3670Sstevel@tonic-gate 	    throw
3680Sstevel@tonic-gate 		new IllegalArgumentException(
3690Sstevel@tonic-gate 				conf.formatMessage("empty_string_value",
3700Sstevel@tonic-gate 						   new Object[0]));
3710Sstevel@tonic-gate 	}
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	if (!(obj instanceof Integer) && !(obj instanceof Boolean) &&
3740Sstevel@tonic-gate 	    !(obj instanceof String) && !(obj instanceof byte[])) {
3750Sstevel@tonic-gate 	    throw
3760Sstevel@tonic-gate 		new IllegalArgumentException(
3770Sstevel@tonic-gate 				conf.formatMessage("value_type_error",
3780Sstevel@tonic-gate 						   new Object[0]));
3790Sstevel@tonic-gate 	}
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate     }
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate     // We know the value's type is OK, so just escape it.
3840Sstevel@tonic-gate 
escapeValueInternal(Object val)3850Sstevel@tonic-gate     private static String escapeValueInternal(Object val) {
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate 	String s;
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	// Escape any characters needing it.
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 	if (val instanceof String) {
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	    try {
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate 		s = escapeAttributeString((String)val, false);
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	    } catch (ServiceLocationException ex) {
3980Sstevel@tonic-gate 		throw
3990Sstevel@tonic-gate 		    new IllegalArgumentException(ex.getMessage());
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 	    }
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 	} else {
4040Sstevel@tonic-gate 	    s = val.toString();
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	}
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	return s;
4090Sstevel@tonic-gate     }
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate     //
4120Sstevel@tonic-gate     // Methods for dealing with the type of attribute values.
4130Sstevel@tonic-gate     //
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate     // Verify the types of incoming attributes.
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate     protected void
verifyValueTypes(Vector values_in, boolean dontTypeCheck)4180Sstevel@tonic-gate 	verifyValueTypes(Vector values_in, boolean dontTypeCheck) {
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 	SLPConfig conf = SLPConfig.getSLPConfig();
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	// Make sure the types of objects passed in are acceptable
4230Sstevel@tonic-gate 	//  and that all objects in the vector have the same type.
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 	int i, n = values_in.size();
4260Sstevel@tonic-gate 	Class cls = null;
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
4290Sstevel@tonic-gate 	    Object obj = values_in.elementAt(i);
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	    typeCheckValue(obj);
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	    if (i == 0) {
4340Sstevel@tonic-gate 		cls = obj.getClass();
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	    } else if (!cls.equals(obj.getClass()) && !dontTypeCheck) {
4370Sstevel@tonic-gate 		throw
4380Sstevel@tonic-gate 		    new IllegalArgumentException(
4390Sstevel@tonic-gate 				conf.formatMessage("type_mismatch_error",
4400Sstevel@tonic-gate 						   new Object[0]));
4410Sstevel@tonic-gate 	    }
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 	    // If it's a boolean and there's more than one, signal error
4440Sstevel@tonic-gate 	    // unless multivalued booleans are allowed.
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 	    if (!dontTypeCheck && i != 0 && obj instanceof Boolean) {
4470Sstevel@tonic-gate 		throw
4480Sstevel@tonic-gate 		    new IllegalArgumentException(
4490Sstevel@tonic-gate 				conf.formatMessage("multivalued_boolean",
4500Sstevel@tonic-gate 						   new Object[0]));
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	    }
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 	    // If it's a byte array, create a Opaque object.
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	    if (obj instanceof byte[]) {
4570Sstevel@tonic-gate 		values_in.setElementAt(new Opaque((byte[])obj), i);
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	    } else if (obj instanceof String) {
4600Sstevel@tonic-gate 		String val = (String)obj;
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 		// If it's a string and looks like "1" or "true", then
4630Sstevel@tonic-gate 		//  append a space onto the end.
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 		try {
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 		    Object obj2 = evaluate(val);
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 		    if (!(obj2 instanceof String)) {
4700Sstevel@tonic-gate 			values_in.setElementAt((String)val + " ", i);
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 		    }
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 		} catch (ServiceLocationException ex) {
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 		    // Ignore for now.
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 		}
4790Sstevel@tonic-gate 	    }
4800Sstevel@tonic-gate 	}
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate     }
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate     //
4850Sstevel@tonic-gate     // Methods for externalizing attributes.
4860Sstevel@tonic-gate     //
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate     /**
4890Sstevel@tonic-gate      * Externalize the attribute into a string that can be written
4900Sstevel@tonic-gate      * to a byte stream. Includes escaping any characters that
4910Sstevel@tonic-gate      * need to be escaped.
4920Sstevel@tonic-gate      *
4930Sstevel@tonic-gate      * @return String with attribute's external representation.
4940Sstevel@tonic-gate      * @exception ServiceLocationException Thrown if the
4950Sstevel@tonic-gate      *			string contains unencodable characters.
4960Sstevel@tonic-gate      */
4970Sstevel@tonic-gate 
externalize()4980Sstevel@tonic-gate     String externalize()
4990Sstevel@tonic-gate 	throws ServiceLocationException {
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 	if (values == null) {	// keyword attribute...
5020Sstevel@tonic-gate 	    return escapeAttributeString(id, true);
5030Sstevel@tonic-gate 	}
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	Vector v = new Vector();
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	for (Enumeration e = values.elements(); e.hasMoreElements(); ) {
5080Sstevel@tonic-gate 	    Object o = e.nextElement();
5090Sstevel@tonic-gate 	    String s = null;
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 	    s = escapeValueInternal(o);
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	    v.addElement(s);
5140Sstevel@tonic-gate 	}
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	StringBuffer buf =
5170Sstevel@tonic-gate 	    new StringBuffer("(" +
5180Sstevel@tonic-gate 			     escapeAttributeString(id, true) +
5190Sstevel@tonic-gate 			     "=");
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 	buf.append(SrvLocHeader.vectorToCommaSeparatedList(v));
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	buf.append(")");
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	return buf.toString();
5260Sstevel@tonic-gate     }
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate     //
5290Sstevel@tonic-gate     // Escaping and unescaping strings.
5300Sstevel@tonic-gate     //
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate     /**
5330Sstevel@tonic-gate      * Escape any escapable characters to a 2 character escape
5340Sstevel@tonic-gate      * in the attribute string.
5350Sstevel@tonic-gate      *
5360Sstevel@tonic-gate      * @param string The String.
5370Sstevel@tonic-gate      * @param badTag Check for bad tag characters if true.
5380Sstevel@tonic-gate      * @return The escaped string.
5390Sstevel@tonic-gate      * @exception ServiceLocationException Thrown if the string
5400Sstevel@tonic-gate      *			contains a character that can't be encoded.
5410Sstevel@tonic-gate      */
5420Sstevel@tonic-gate 
escapeAttributeString(String string, boolean badTag)5430Sstevel@tonic-gate     static String escapeAttributeString(String string,
5440Sstevel@tonic-gate 					boolean badTag)
5450Sstevel@tonic-gate 	throws ServiceLocationException {
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
5480Sstevel@tonic-gate 	int i, n = string.length();
5490Sstevel@tonic-gate 
5500Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
5510Sstevel@tonic-gate 	    char c = string.charAt(i);
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 	    // Check for bad tag characters first.
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	    if (badTag && BAD_TAG_CHARS.indexOf(c) != -1) {
5560Sstevel@tonic-gate 		throw
5570Sstevel@tonic-gate 		    new ServiceLocationException(
5580Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
5590Sstevel@tonic-gate 				"bad_id_char",
5600Sstevel@tonic-gate 				new Object[] {Integer.toHexString(c)});
5610Sstevel@tonic-gate 	    }
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	    // Escape if the character is reserved.
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	    if (canEscape(c)) {
5660Sstevel@tonic-gate 		buf.append(ESCAPE);
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 		String str = escapeChar(c);
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 		// Pad with zero if less than 2 characters.
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 		if (str.length() <= 1) {
5730Sstevel@tonic-gate 		    str = "0" + str;
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 		}
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 		buf.append(str);
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 	    } else {
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 		buf.append(c);
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	    }
5840Sstevel@tonic-gate 	}
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 	return buf.toString();
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate     }
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate     /**
5920Sstevel@tonic-gate      * Convert any 2 character escapes to the corresponding characters.
5930Sstevel@tonic-gate      *
5940Sstevel@tonic-gate      * @param string The string to be processed.
5950Sstevel@tonic-gate      * @param badTag Check for bad tag characters if true.
5960Sstevel@tonic-gate      * @return The processed string.
5970Sstevel@tonic-gate      * @exception ServiceLocationException Thrown if an escape
5980Sstevel@tonic-gate      *			is improperly formatted.
5990Sstevel@tonic-gate      */
6000Sstevel@tonic-gate 
unescapeAttributeString(String string, boolean badTag)6010Sstevel@tonic-gate     static String unescapeAttributeString(String string,
6020Sstevel@tonic-gate 					  boolean badTag)
6030Sstevel@tonic-gate 	throws ServiceLocationException {
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	// Process escapes.
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	int i, n = string.length();
6080Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer(n);
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
6110Sstevel@tonic-gate 	    char c = string.charAt(i);
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 	    // Check for escaped characters.
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	    if (c == ESCAPE) {
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 		// Get the next two characters.
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 		if (i >= n - 2) {
6200Sstevel@tonic-gate 		    throw
6210Sstevel@tonic-gate 			new ServiceLocationException(
6220Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6230Sstevel@tonic-gate 				"nonterminating_escape",
6240Sstevel@tonic-gate 				new Object[] {string});
6250Sstevel@tonic-gate 		}
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate 		i++;
6280Sstevel@tonic-gate 		c = unescapeChar(string.substring(i, i+2));
6290Sstevel@tonic-gate 		i++;
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 		// Check whether it's reserved.
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 		if (!canEscape(c)) {
6340Sstevel@tonic-gate 		    throw
6350Sstevel@tonic-gate 			new ServiceLocationException(
6360Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6370Sstevel@tonic-gate 				"char_not_reserved_attr",
6380Sstevel@tonic-gate 				new Object[] {new Character(c), string});
6390Sstevel@tonic-gate 		}
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	    } else {
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 		// Check whether the character is reserved.
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 		if (isReserved(c)) {
6460Sstevel@tonic-gate 		    throw
6470Sstevel@tonic-gate 			new ServiceLocationException(
6480Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6490Sstevel@tonic-gate 				"reserved_not_escaped",
6500Sstevel@tonic-gate 				new Object[] {new Character(c)});
6510Sstevel@tonic-gate 		}
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	    }
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 	    // If we need to check for a bad tag character, do so now.
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	    if (badTag && BAD_TAG_CHARS.indexOf(c) != -1) {
6580Sstevel@tonic-gate 		throw
6590Sstevel@tonic-gate 		    new ServiceLocationException(
6600Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6610Sstevel@tonic-gate 				"bad_id_char",
6620Sstevel@tonic-gate 				new Object[] {Integer.toHexString(c)});
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 	    }
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 	    buf.append(c);
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	}
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	return buf.toString();
6710Sstevel@tonic-gate     }
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate     // Return true if the character c can be escaped.
6740Sstevel@tonic-gate 
canEscape(char c)6750Sstevel@tonic-gate     private static boolean canEscape(char c) {
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	return ((ESCAPED.indexOf(c) != -1) ||
6780Sstevel@tonic-gate 		((c >= CTL_LOWER && c <= CTL_UPPER) || c == DEL));
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate     }
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate     // Return true if the character c is reserved.
6830Sstevel@tonic-gate 
isReserved(char c)6840Sstevel@tonic-gate     private static boolean isReserved(char c) {
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	return ((RESERVED.indexOf(c) != -1) ||
6870Sstevel@tonic-gate 		((c >= CTL_LOWER && c <= CTL_UPPER) || c == DEL));
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate     }
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate     /**
6920Sstevel@tonic-gate      * Return a string of integers giving the character's encoding in
6930Sstevel@tonic-gate      * the character set passed in as encoding.
6940Sstevel@tonic-gate      *
6950Sstevel@tonic-gate      * @param c The character to escape.
6960Sstevel@tonic-gate      * @return The character as a string of integers for the encoding.
6970Sstevel@tonic-gate      */
6980Sstevel@tonic-gate 
escapeChar(char c)6990Sstevel@tonic-gate     static String escapeChar(char c) {
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 	byte[] b = null;
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 	try {
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 	    b = ("" + c).getBytes(Defaults.UTF8);
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	} catch (UnsupportedEncodingException ex) {
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	    Assert.slpassert(false, "no_utf8", new Object[0]);
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate 	}
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	int code = 0;
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate 	// Assemble the character code.
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	if (b.length > 3) {
7180Sstevel@tonic-gate 	    Assert.slpassert(false,
7190Sstevel@tonic-gate 			  "illegal_utf8",
7200Sstevel@tonic-gate 			  new Object[] {new Character(c)});
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 	}
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate 	code = (int)(b[0] & 0xFF);
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 	if (b.length > 1) {
7270Sstevel@tonic-gate 	    code = (int)(code | ((b[1] & 0xFF) << 8));
7280Sstevel@tonic-gate 	}
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 	if (b.length > 2) {
7310Sstevel@tonic-gate 	    code = (int)(code | ((b[2] & 0xFF) << 16));
7320Sstevel@tonic-gate 	}
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	String str = Integer.toHexString(code);
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate 	return str;
7370Sstevel@tonic-gate     }
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate     /**
7400Sstevel@tonic-gate      * Unescape the character encoded as the string.
7410Sstevel@tonic-gate      *
7420Sstevel@tonic-gate      * @param ch The character as a string of hex digits.
7430Sstevel@tonic-gate      * @return The character.
7440Sstevel@tonic-gate      * @exception ServiceLocationException If the characters can't be
7450Sstevel@tonic-gate      *		 converted into a hex string.
7460Sstevel@tonic-gate      */
7470Sstevel@tonic-gate 
unescapeChar(String ch)7480Sstevel@tonic-gate     static char unescapeChar(String ch)
7490Sstevel@tonic-gate 	throws ServiceLocationException {
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 	int code = 0;
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate 	try {
7540Sstevel@tonic-gate 	    code = Integer.parseInt(ch, 16);
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate 	} catch (NumberFormatException ex) {
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate 	    throw
7590Sstevel@tonic-gate 		new ServiceLocationException(
7600Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
7610Sstevel@tonic-gate 				"not_a_character",
7620Sstevel@tonic-gate 				new Object[] {ch});
7630Sstevel@tonic-gate 	}
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate 	// Convert to bytes.
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 	String str = null;
7680Sstevel@tonic-gate 	byte b0 = 0, b1 = 0, b2 = 0, b3 = 0;
7690Sstevel@tonic-gate 	byte b[] = null;
7700Sstevel@tonic-gate 
7710Sstevel@tonic-gate 	b0 = (byte) (code & 0xFF);
7720Sstevel@tonic-gate 	b1 = (byte) ((code >> 8) & 0xFF);
7730Sstevel@tonic-gate 	b2 = (byte) ((code >> 16) & 0xFF);
7740Sstevel@tonic-gate 	b3 = (byte) ((code >> 24) & 0xFF);
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	// We allow illegal UTF8 encoding so we can decode byte arrays.
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate 	if (b3 != 0) {
7790Sstevel@tonic-gate 	    b = new byte[3];
7800Sstevel@tonic-gate 	    b[3] = b3;
7810Sstevel@tonic-gate 	    b[2] = b2;
7820Sstevel@tonic-gate 	    b[1] = b1;
7830Sstevel@tonic-gate 	    b[0] = b0;
7840Sstevel@tonic-gate 	} else if (b2 != 0) {
7850Sstevel@tonic-gate 	    b = new byte[3];
7860Sstevel@tonic-gate 	    b[2] = b2;
7870Sstevel@tonic-gate 	    b[1] = b1;
7880Sstevel@tonic-gate 	    b[0] = b0;
7890Sstevel@tonic-gate 	} else if (b1 != 0) {
7900Sstevel@tonic-gate 	    b = new byte[2];
7910Sstevel@tonic-gate 	    b[1] = b1;
7920Sstevel@tonic-gate 	    b[0] = b0;
7930Sstevel@tonic-gate 	} else {
7940Sstevel@tonic-gate 	    b = new byte[1];
7950Sstevel@tonic-gate 	    b[0] = b0;
7960Sstevel@tonic-gate 	}
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 	// Make a string out of it.
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	try {
8010Sstevel@tonic-gate 	    str = new String(b, Defaults.UTF8);
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	} catch (UnsupportedEncodingException ex) {
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 	    Assert.slpassert(false, "no_utf8", new Object[0]);
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	}
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 	int len = str.length();
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate 	if (str.length() > 1) {
8120Sstevel@tonic-gate 	    throw
8130Sstevel@tonic-gate 		new ServiceLocationException(
8140Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
8150Sstevel@tonic-gate 				"more_than_one",
8160Sstevel@tonic-gate 				new Object[] {ch});
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 	}
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate 	return (len == 1 ? str.charAt(0):(char)0);
8210Sstevel@tonic-gate     }
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate     /**
8240Sstevel@tonic-gate      * Merge the values in newAttr into the attribute in the hashtable
8250Sstevel@tonic-gate      * if a duplicate attribute, signal error if a type mismatch.
8260Sstevel@tonic-gate      * Both the return vector and hashtable are updated, but the
8270Sstevel@tonic-gate      * newAttr parameter is left unchanged.
8280Sstevel@tonic-gate      *
8290Sstevel@tonic-gate      * @param attr The ServiceLocationAttribute to check.
8300Sstevel@tonic-gate      * @param attrHash A Hashtable containing the attribute tags as
8310Sstevel@tonic-gate      *			keys and the attributes as values.
8320Sstevel@tonic-gate      * @param returns A Vector in which to put the attribute when done.
8330Sstevel@tonic-gate      * @param dontTypeCheck If this flag is true, the value vector
8340Sstevel@tonic-gate      *			   may have two booleans, may
8350Sstevel@tonic-gate      *			   contain differently typed objects, or the
8360Sstevel@tonic-gate      *			   function may merge a keyword and nonkeyword
8370Sstevel@tonic-gate      *			   attribute.
8380Sstevel@tonic-gate      * @exception ServiceLocationException Thrown if a type mismatch
8390Sstevel@tonic-gate      *			occurs.
8400Sstevel@tonic-gate      */
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate     static void
mergeDuplicateAttributes(ServiceLocationAttribute newAttr, Hashtable attrTable, Vector returns, boolean dontTypeCheck)8430Sstevel@tonic-gate 	mergeDuplicateAttributes(ServiceLocationAttribute newAttr,
8440Sstevel@tonic-gate 				 Hashtable attrTable,
8450Sstevel@tonic-gate 				 Vector returns,
8460Sstevel@tonic-gate 				 boolean dontTypeCheck)
8470Sstevel@tonic-gate 	throws ServiceLocationException {
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	// Look up the attribute
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	String tag = newAttr.getId().toLowerCase();
8520Sstevel@tonic-gate 	ServiceLocationAttribute attr =
8530Sstevel@tonic-gate 	    (ServiceLocationAttribute)attrTable.get(tag);
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 	// Don't try this trick with ServerAttributes!
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	Assert.slpassert((!(attr instanceof ServerAttribute) &&
8580Sstevel@tonic-gate 		       !(newAttr instanceof ServerAttribute)),
8590Sstevel@tonic-gate 		      "merge_servattr",
8600Sstevel@tonic-gate 		      new Object[0]);
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 	// If the attribute isn't in the hashtable, then add to
8630Sstevel@tonic-gate 	//  vector and hashtable.
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 	if (attr == null) {
8660Sstevel@tonic-gate 	    attrTable.put(tag, newAttr);
8670Sstevel@tonic-gate 	    returns.addElement(newAttr);
8680Sstevel@tonic-gate 	    return;
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate 	}
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate 	Vector attrNewVals = newAttr.values;
8740Sstevel@tonic-gate 	Vector attrVals = attr.values;
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate 	// If both keywords, nothing further to do.
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	if (attrVals == null && attrNewVals == null) {
8790Sstevel@tonic-gate 	    return;
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate 	}
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	// If we are not typechecking and one is keyword while the other
8840Sstevel@tonic-gate 	//  is not, then simply merge in the nonkeyword. Otherwise,
8850Sstevel@tonic-gate 	//  throw a type check exception.
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate 	if ((attrVals == null && attrNewVals != null) ||
8880Sstevel@tonic-gate 	    (attrNewVals == null && attrVals != null)) {
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate 	    if (dontTypeCheck) {
8910Sstevel@tonic-gate 		Vector vals = (attrNewVals != null ? attrNewVals:attrVals);
8920Sstevel@tonic-gate 		attr.values = vals;
8930Sstevel@tonic-gate 		newAttr.values = vals;
8940Sstevel@tonic-gate 
8950Sstevel@tonic-gate 	    } else {
8960Sstevel@tonic-gate 		throw
8970Sstevel@tonic-gate 		    new ServiceLocationException(
8980Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
8990Sstevel@tonic-gate 				"attribute_type_mismatch",
9000Sstevel@tonic-gate 				new Object[] {newAttr.getId()});
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 	    }
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate 	} else {
9050Sstevel@tonic-gate 
9060Sstevel@tonic-gate 	    // Merge the two vectors. We type check against the attrVals
9070Sstevel@tonic-gate 	    //  vector, if we are type checking.
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	    int i, n = attrNewVals.size();
9100Sstevel@tonic-gate 	    Object o = attrVals.elementAt(0);
9110Sstevel@tonic-gate 	    Class c = o.getClass();
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	    for (i = 0; i < n; i++) {
9140Sstevel@tonic-gate 		Object no = attrNewVals.elementAt(i);
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 		// Check for type mismatch, throw exception if
9170Sstevel@tonic-gate 		//  we are type checking.
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 		if ((c != no.getClass()) && !dontTypeCheck) {
9200Sstevel@tonic-gate 		    throw
9210Sstevel@tonic-gate 			new ServiceLocationException(
9220Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
9230Sstevel@tonic-gate 				"attribute_type_mismatch",
9240Sstevel@tonic-gate 				new Object[] {newAttr.getId()});
9250Sstevel@tonic-gate 
9260Sstevel@tonic-gate 		}
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 		// If we are typechecking, and we get two opposite
9290Sstevel@tonic-gate 		//  booleans, we need to throw an exception.
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate 		if (no instanceof Boolean && !no.equals(o) && !dontTypeCheck) {
9320Sstevel@tonic-gate 		    throw
9330Sstevel@tonic-gate 			new ServiceLocationException(
9340Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
9350Sstevel@tonic-gate 				"boolean_incompat",
9360Sstevel@tonic-gate 				new Object[] {newAttr.getId()});
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate 		}
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 		// Add the value if it isn't already there.
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate 		if (!attrVals.contains(no)) {
9430Sstevel@tonic-gate 		    attrVals.addElement(no);
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 		}
9460Sstevel@tonic-gate 	    }
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	    // Set the new attribute's values so they are the same as the old.
9490Sstevel@tonic-gate 
9500Sstevel@tonic-gate 	    newAttr.values = attrVals;
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate 	}
9530Sstevel@tonic-gate     }
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate     //
9560Sstevel@tonic-gate     // Object overrides.
9570Sstevel@tonic-gate     //
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate     /**
9600Sstevel@tonic-gate      * Return true if the object equals this attribute.
9610Sstevel@tonic-gate      */
9620Sstevel@tonic-gate 
equals(Object o)9630Sstevel@tonic-gate     public boolean equals(Object o) {
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	if (!(o instanceof ServiceLocationAttribute)) {
9660Sstevel@tonic-gate 	    return false;
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 	}
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	if (o == this) {
9710Sstevel@tonic-gate 	    return true;
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate 	}
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 	ServiceLocationAttribute sla = (ServiceLocationAttribute)o;
9760Sstevel@tonic-gate 
9770Sstevel@tonic-gate 	// check equality of contents, deferring check of all values
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate 	Vector vSLA = sla.values;
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate 	if (!sla.getId().equalsIgnoreCase(id)) {
9820Sstevel@tonic-gate 	    return false;
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate 	}
9850Sstevel@tonic-gate 
9860Sstevel@tonic-gate 	if (values == null && vSLA == null) {
9870Sstevel@tonic-gate 	    return true;
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate 	}
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	if ((values == null && vSLA != null) ||
9920Sstevel@tonic-gate 	    (values != null && vSLA == null)) {
9930Sstevel@tonic-gate 	    return false;
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	}
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	if (values.size() != vSLA.size()) {
9980Sstevel@tonic-gate 	    return false;
9990Sstevel@tonic-gate 
10000Sstevel@tonic-gate 	}
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	// Check contents.
10030Sstevel@tonic-gate 
10040Sstevel@tonic-gate 	Object oSLA = vSLA.elementAt(0);
10050Sstevel@tonic-gate 	o = values.elementAt(0);
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate 	if (o.getClass() != oSLA.getClass()) {
10080Sstevel@tonic-gate 	    return false;
10090Sstevel@tonic-gate 
10100Sstevel@tonic-gate 	}
10110Sstevel@tonic-gate 
10120Sstevel@tonic-gate 	int i, n = vSLA.size();
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
10150Sstevel@tonic-gate 	    oSLA = vSLA.elementAt(i);
10160Sstevel@tonic-gate 
10170Sstevel@tonic-gate 	    if (!values.contains(oSLA)) {
10180Sstevel@tonic-gate 		return false;
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate 	    }
10210Sstevel@tonic-gate 	}
10220Sstevel@tonic-gate 
10230Sstevel@tonic-gate 	return true;
10240Sstevel@tonic-gate     }
10250Sstevel@tonic-gate 
10260Sstevel@tonic-gate     /**
10270Sstevel@tonic-gate      * Return a human readable string for the attribute.
10280Sstevel@tonic-gate      */
10290Sstevel@tonic-gate 
toString()10300Sstevel@tonic-gate     public String toString() {
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate 	StringBuffer s = new StringBuffer("(");
10330Sstevel@tonic-gate 
10340Sstevel@tonic-gate 	s.append(id);
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	if (values != null) {
10370Sstevel@tonic-gate 	    s.append("=");
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 	    int i, n = values.size();
10400Sstevel@tonic-gate 
10410Sstevel@tonic-gate 	    for (i = 0; i < n; i++) {
10420Sstevel@tonic-gate 		Object o = values.elementAt(i);
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate 		// Identify type.
10450Sstevel@tonic-gate 
10460Sstevel@tonic-gate 		if (i == 0) {
10470Sstevel@tonic-gate 		    s.append(o.getClass().getName());
10480Sstevel@tonic-gate 		    s.append(":");
10490Sstevel@tonic-gate 
10500Sstevel@tonic-gate 		} else {
10510Sstevel@tonic-gate 		    s.append(",");
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 		}
10540Sstevel@tonic-gate 
10550Sstevel@tonic-gate 		// Stringify object.
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate 		s.append(o.toString());
10580Sstevel@tonic-gate 
10590Sstevel@tonic-gate 	    }
10600Sstevel@tonic-gate 	}
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	s.append(")");
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate 	return s.toString();
10650Sstevel@tonic-gate     }
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate     // Overrides Object.hashCode().
10680Sstevel@tonic-gate 
hashCode()10690Sstevel@tonic-gate     public int hashCode() {
10700Sstevel@tonic-gate 	return id.toLowerCase().hashCode();
10710Sstevel@tonic-gate 
10720Sstevel@tonic-gate     }
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate }
1075