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 (c) 1999 by Sun Microsystems, Inc.
230Sstevel@tonic-gate  * All rights reserved.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate //  ServiceLocationAttributeV1.java: SLPv1 character encoding and decoding
280Sstevel@tonic-gate //  Author:           James Kempf
290Sstevel@tonic-gate //  Created On:       Fri Oct  9 19:18:17 1998
300Sstevel@tonic-gate //  Last Modified By: James Kempf
310Sstevel@tonic-gate //  Last Modified On: Sat Oct 24 13:17:58 1998
320Sstevel@tonic-gate //  Update Count:     15
330Sstevel@tonic-gate //
340Sstevel@tonic-gate 
350Sstevel@tonic-gate package com.sun.slp;
360Sstevel@tonic-gate 
370Sstevel@tonic-gate import java.util.*;
380Sstevel@tonic-gate 
390Sstevel@tonic-gate /**
400Sstevel@tonic-gate  * Handles attribute string encoding and decoding for SLPv1.
410Sstevel@tonic-gate  *
420Sstevel@tonic-gate  * @author James Kempf
430Sstevel@tonic-gate  */
440Sstevel@tonic-gate 
450Sstevel@tonic-gate class ServiceLocationAttributeV1 extends ServiceLocationAttribute {
460Sstevel@tonic-gate 
470Sstevel@tonic-gate     String charCode = IANACharCode.UTF8;  // how to encode the attribute.
480Sstevel@tonic-gate 
490Sstevel@tonic-gate     // Characters to escape.
500Sstevel@tonic-gate 
510Sstevel@tonic-gate     final private static String UNESCAPABLE_CHARS = ",=!></*()";
520Sstevel@tonic-gate     final private static String ESCAPABLE_CHARS =
530Sstevel@tonic-gate 	UNESCAPABLE_CHARS + "&#;";
540Sstevel@tonic-gate 
550Sstevel@tonic-gate     /**
560Sstevel@tonic-gate      * Handles radix64 string encoding and decoding for SLPv1.
570Sstevel@tonic-gate      *
580Sstevel@tonic-gate      * @author James Kempf
590Sstevel@tonic-gate      */
600Sstevel@tonic-gate 
610Sstevel@tonic-gate     static class Radix64 extends Object {
620Sstevel@tonic-gate 
630Sstevel@tonic-gate 	/**
640Sstevel@tonic-gate 	 * Translates the 6 bit value to the corresponding radix 64
650Sstevel@tonic-gate 	 * representation.
660Sstevel@tonic-gate 	 */
LUT(char cin)670Sstevel@tonic-gate 	private static char LUT(char cin) {
680Sstevel@tonic-gate 
690Sstevel@tonic-gate 	    int i = (int)(cin & (char)0x00FF);
700Sstevel@tonic-gate 	    char result = ' ';
710Sstevel@tonic-gate 
720Sstevel@tonic-gate 	    if (i < 26) {
730Sstevel@tonic-gate 		result = (char)((char)i + 'A');
740Sstevel@tonic-gate 
750Sstevel@tonic-gate 	    } else if (i < 52) {
760Sstevel@tonic-gate 		result = (char)((char)(i - 26) + 'a');
770Sstevel@tonic-gate 
780Sstevel@tonic-gate 	    } else if (i < 62) {
790Sstevel@tonic-gate 		result = (char)((char)(i - 52) + '0');
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	    } else if (i == 62) {
820Sstevel@tonic-gate 		result = '+';
830Sstevel@tonic-gate 
840Sstevel@tonic-gate 	    } else if (i == 63) {
850Sstevel@tonic-gate 		result = '/';
860Sstevel@tonic-gate 
870Sstevel@tonic-gate 	    }
880Sstevel@tonic-gate 
890Sstevel@tonic-gate 	    return result;
900Sstevel@tonic-gate 	}
910Sstevel@tonic-gate 
920Sstevel@tonic-gate 	/**
930Sstevel@tonic-gate 	 * Translates a radix 64 representation to the 64 bit value which
940Sstevel@tonic-gate 	 * corresponds to it.
950Sstevel@tonic-gate 	 */
LUT2(char cin, String s)960Sstevel@tonic-gate 	private static char LUT2(char cin, String s)
970Sstevel@tonic-gate 	    throws ServiceLocationException {
980Sstevel@tonic-gate 
990Sstevel@tonic-gate 	    int i = (int)(cin & 0x00ff);
1000Sstevel@tonic-gate 	    char c = (char) 0xffff;
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate 	    if (((char)i >= 'A') && ((char)i <= 'Z')) {
1030Sstevel@tonic-gate 		c = (char)((char)i - 'A');
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	    }
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 	    if (((char)i >= 'a') && ((char)i <= 'z')) {
1080Sstevel@tonic-gate 		c = (char)((char)i - 'a' +(char) 26);
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	    }
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	    if (((char)i >= '0') && ((char)i <= '9')) {
1130Sstevel@tonic-gate 		c = (char)((char)i - '0' +(char) 52);
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	    }
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	    if ((char)i == '+') {
1180Sstevel@tonic-gate 		c = (char)62;
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 	    }
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 	    if ((char)i == '/') {
1230Sstevel@tonic-gate 		c = (char)63;
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate 	    }
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	    if ((char)i == '=') {
1280Sstevel@tonic-gate 		c = (char)0;
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate 	    }
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate 	    if (c == 0xffff) {
1330Sstevel@tonic-gate 		throw
1340Sstevel@tonic-gate 		    new ServiceLocationException(
1350Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1360Sstevel@tonic-gate 				"v1_radix64_error",
1370Sstevel@tonic-gate 				new Object[] {s});
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 	    }
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate 	    return c;
1420Sstevel@tonic-gate 	}
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 	// format of the encoding is "(###:encoding)" where ### is the length
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	// convert a string in the encoding to the buffer format
1470Sstevel@tonic-gate 
radix64ToOpaque(String s)1480Sstevel@tonic-gate 	static Opaque radix64ToOpaque(String s)
1490Sstevel@tonic-gate 	    throws ServiceLocationException {
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	    if (s == null || s.trim().length() == 0) {
1520Sstevel@tonic-gate 		return new Opaque(new byte[0]);
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 	    }
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	    int oplen = 0;
1570Sstevel@tonic-gate 	    int scan = 0;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	    while (scan < s.length()) {
1600Sstevel@tonic-gate 		if (s.charAt(scan) == '(') {
1610Sstevel@tonic-gate 		    break;  // scan till begins
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 		}
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 		scan++;
1660Sstevel@tonic-gate 	    }
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	    scan++; // past the '('
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	    while (scan < s.length()) {
1710Sstevel@tonic-gate 		if (Character.isWhitespace(s.charAt(scan)) == false) {
1720Sstevel@tonic-gate 		    break;
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 		}
1750Sstevel@tonic-gate 		scan++;
1760Sstevel@tonic-gate 	    }
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 	    while (scan < s.length()) {
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 		if (Character.isDigit(s.charAt(scan))) {
1810Sstevel@tonic-gate 		    oplen *= 10;
1820Sstevel@tonic-gate 		    oplen += (s.charAt(scan) - '0');
1830Sstevel@tonic-gate 		    scan++;
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 		} else {
1860Sstevel@tonic-gate 		    break;
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 		}
1890Sstevel@tonic-gate 	    }
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	    if (scan >= s.length()) {
1920Sstevel@tonic-gate 		throw
1930Sstevel@tonic-gate 		    new ServiceLocationException(
1940Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1950Sstevel@tonic-gate 				"v1_radix64_error",
1960Sstevel@tonic-gate 				new Object[] {s});
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	    }
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	    if (s.charAt(scan) != ':') {
2020Sstevel@tonic-gate 		throw
2030Sstevel@tonic-gate 		    new ServiceLocationException(
2040Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2050Sstevel@tonic-gate 				"v1_radix64_error",
2060Sstevel@tonic-gate 				new Object[] {s});
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	    }
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	    scan++; // past the ':'
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	    byte b[] = new byte[oplen];
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	    int pos = 0;
2150Sstevel@tonic-gate 	    int timesthrough = (oplen/3);
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	    if ((oplen %3) != 0) {
2180Sstevel@tonic-gate 		timesthrough++;
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	    }
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	    for (int i = 0; i < timesthrough; i++) {
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 		// get 4 bytes to make 3 with, skipping blanks
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 		char v[] = new char[4];
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 		for (int x = 0; x < 4; x++) {
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 		    while ((scan < s.length()) &&
2310Sstevel@tonic-gate 			   Character.isWhitespace(s.charAt(scan))) {
2320Sstevel@tonic-gate 			scan++; // eat white
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 		    }
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 		    if (scan >= s.length()) {
2370Sstevel@tonic-gate 			throw
2380Sstevel@tonic-gate 			    new ServiceLocationException(
2390Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2400Sstevel@tonic-gate 				"v1_radix64_error",
2410Sstevel@tonic-gate 				new Object[] {s});
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 		    }
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 		    v[x] = LUT2(s.charAt(scan), s);
2460Sstevel@tonic-gate 		    scan++;
2470Sstevel@tonic-gate 		}
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 		b[pos++] =
2500Sstevel@tonic-gate 		    (byte) (((0x3F & v[0]) << 2) + ((0x30 & v[1]) >> 4));
2510Sstevel@tonic-gate 		if (pos >= oplen) break;
2520Sstevel@tonic-gate 		b[pos++] =
2530Sstevel@tonic-gate 		    (byte) (((0x0F & v[1]) << 4) + ((0x3C & v[2]) >> 2));
2540Sstevel@tonic-gate 		if (pos >= oplen) break;
2550Sstevel@tonic-gate 		b[pos++] = (byte) (((0x03 & v[2]) << 6) + (0x3F & v[3]));
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	    } // end of conversion loop
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	    if (scan >= s.length()) {
2600Sstevel@tonic-gate 		throw
2610Sstevel@tonic-gate 		    new ServiceLocationException(
2620Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2630Sstevel@tonic-gate 				"v1_radix64_error",
2640Sstevel@tonic-gate 				new Object[] {s});
2650Sstevel@tonic-gate 	    }
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	    if (s.charAt(scan) != ')') {// check for too many chars.
2680Sstevel@tonic-gate 		throw
2690Sstevel@tonic-gate 		    new ServiceLocationException(
2700Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2710Sstevel@tonic-gate 				"v1_radix64_error",
2720Sstevel@tonic-gate 				new Object[] {s});
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	    }
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 	    return new Opaque(b);
2770Sstevel@tonic-gate 	}
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	// convert an Opaque to the encoding
2800Sstevel@tonic-gate 
opaqueToRadix64(Opaque oq)2810Sstevel@tonic-gate 	static String opaqueToRadix64(Opaque oq) {
2820Sstevel@tonic-gate 	    byte[] b = oq.bytes;
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 	    if (b == null) {
2850Sstevel@tonic-gate 		return new String("");
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 	    }
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	    StringBuffer sb = new StringBuffer("("+b.length+":");
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	    int datalen;
2920Sstevel@tonic-gate 	    int fill = b.length%3;
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	    if (fill == 0) {
2950Sstevel@tonic-gate 		datalen = (b.length / 3) * 4;
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	    } else {
2980Sstevel@tonic-gate 		datalen = ((b.length / 3) + 1) * 4;
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	    }
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	    int dataoffset = 0;
3030Sstevel@tonic-gate 	    int more = (b.length%3);
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 	    if (more != 0) {
3060Sstevel@tonic-gate 		more = 1;
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 	    }
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	    int a[] = new int[4];
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	    for (int i = 0; i < ((b.length/3)+more-1); i++) {
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 		a[0] =   (int)(0xFC & (char)b[ dataoffset    ]) >> 2;
3150Sstevel@tonic-gate 		a[1] =  ((int)(0x03 & (char)b[ dataoffset    ]) << 4) +
3160Sstevel@tonic-gate 		    ((int)(0xF0 & (char)b[ dataoffset + 1]) >> 4);
3170Sstevel@tonic-gate 		a[2] =  ((int)(0x0F & (char)b[ dataoffset + 1]) << 2) +
3180Sstevel@tonic-gate 		    ((int)(0xC0 & (char)b[ dataoffset + 2]) >> 6);
3190Sstevel@tonic-gate 		a[3] =   (int)(0x3F & (char)b[ dataoffset + 2]);
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 		for (int j = 0; j < 4; j++) {
3220Sstevel@tonic-gate 		    sb.append(LUT((char)a[j]));
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 		}
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 		dataoffset += 3;
3270Sstevel@tonic-gate 	    }
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	    byte f1 = 0, f2 = 0;
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	    if (fill == 0) {
3320Sstevel@tonic-gate 		f1 = b[ dataoffset + 1 ];
3330Sstevel@tonic-gate 		f2 = b[ dataoffset + 2 ];
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate 	    } else if (fill == 2) {
3360Sstevel@tonic-gate 		f1 = b[ dataoffset + 1 ];
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	    }
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	    a[0] = (int) (0xFC & (char)b[ dataoffset ]) >> 2;
3410Sstevel@tonic-gate 	    a[1] = ((int) (0x03 & (char)b[ dataoffset ]) << 4) +
3420Sstevel@tonic-gate 		((int) (0xF0 & (char)f1) >> 4);
3430Sstevel@tonic-gate 	    a[2] = ((int) (0x0F & (char)f1) << 2) +
3440Sstevel@tonic-gate 		((int) (0xC0 & (char)f2) >> 6);
3450Sstevel@tonic-gate 	    a[3] = (int) (0x3F & (char)f2);
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	    for (int j = 0; j < 4; j++) {
3480Sstevel@tonic-gate 		sb.append(LUT((char) a[j]));
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	    }
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	    sb.append(")");
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 	    return sb.toString();
3550Sstevel@tonic-gate 	}
3560Sstevel@tonic-gate     }
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate     // Create an SLPv1 attribute from a general attribute.
3590Sstevel@tonic-gate 
ServiceLocationAttributeV1(ServiceLocationAttribute attr)3600Sstevel@tonic-gate     ServiceLocationAttributeV1(ServiceLocationAttribute attr) {
3610Sstevel@tonic-gate 	id = attr.id;
3620Sstevel@tonic-gate 	values = attr.values;
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate     }
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate     // Create an SLPv1 attribute from the parenthesized expression, using
3670Sstevel@tonic-gate     //  charCode to decode any encodings.
3680Sstevel@tonic-gate 
ServiceLocationAttributeV1(String exp, String charCode, boolean allowMultiValuedBooleans)3690Sstevel@tonic-gate     ServiceLocationAttributeV1(String exp,
3700Sstevel@tonic-gate 			       String charCode,
3710Sstevel@tonic-gate 			       boolean allowMultiValuedBooleans)
3720Sstevel@tonic-gate 	throws ServiceLocationException {
3730Sstevel@tonic-gate 	this.charCode = charCode;
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate 	// If start and end paren, then parse out assignment.
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 	if (exp.startsWith("(") && exp.endsWith(")")) {
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	    StringTokenizer tk =
3800Sstevel@tonic-gate 		new StringTokenizer(exp.substring(1, exp.length() - 1),
3810Sstevel@tonic-gate 				    "=",
3820Sstevel@tonic-gate 				    true);
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	    try {
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 		// Get the tag.
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 		id =
3890Sstevel@tonic-gate 		    unescapeAttributeString(tk.nextToken(), charCode);
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 		if (id.length() <= 0) {
3920Sstevel@tonic-gate 		    throw
3930Sstevel@tonic-gate 			new ServiceLocationException(
3940Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
3950Sstevel@tonic-gate 				"null_id",
3960Sstevel@tonic-gate 				new Object[] {exp});
3970Sstevel@tonic-gate 		}
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 		tk.nextToken();  // get rid of "="
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 		// Gather the rest.
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 		String rest = tk.nextToken("");
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 		// Parse the comma separated list.
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 		values = SrvLocHeader.parseCommaSeparatedListIn(rest, true);
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 		// Convert to objects.
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 		int i, n = values.size();
4120Sstevel@tonic-gate 		Class vecClass = null;
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 		for (i = 0; i < n; i++) {
4150Sstevel@tonic-gate 		    String value = (String)values.elementAt(i);
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 		    // Need to determine which type to use.
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 		    Object o = evaluate(value, charCode);
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 		    // Convert Opaque to byte array.
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 		    if (o instanceof Opaque) {
4240Sstevel@tonic-gate 			o = ((Opaque)o).bytes;
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 		    }
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 		    values.setElementAt(o, i);
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 		}
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 	    } catch (NoSuchElementException ex) {
4330Sstevel@tonic-gate 		throw
4340Sstevel@tonic-gate 		    new ServiceLocationException(
4350Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
4360Sstevel@tonic-gate 				"assignment_syntax_err",
4370Sstevel@tonic-gate 				new Object[] {exp});
4380Sstevel@tonic-gate 	    }
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate 	    verifyValueTypes(values, allowMultiValuedBooleans);
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	} else {
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 	    // Check to make sure there's no parens.
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 	    if (exp.indexOf('(') != -1 || exp.indexOf(')') != -1) {
4470Sstevel@tonic-gate 		throw
4480Sstevel@tonic-gate 		    new ServiceLocationException(
4490Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
4500Sstevel@tonic-gate 				"assignment_syntax_err",
4510Sstevel@tonic-gate 				new Object[] {exp});
4520Sstevel@tonic-gate 	    }
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 	    // Unescape the keyword.
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	    id = unescapeAttributeString(exp, charCode);
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 	}
4590Sstevel@tonic-gate     }
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate     // Duplicate of the one in ServiceLocatioAttribute, except we use our
4620Sstevel@tonic-gate     //  unescapeAttributeString.
4630Sstevel@tonic-gate 
evaluate(String value, String charCode)4640Sstevel@tonic-gate     static Object evaluate(String value, String charCode)
4650Sstevel@tonic-gate 	throws ServiceLocationException {
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 	Object o = null;
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 	// If it can be converted into an integer, then convert it.
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 	try {
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	    o = Integer.valueOf(value);
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 	} catch (NumberFormatException ex) {
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 	    // Wasn't an integer. Try boolean.
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	    if (value.equalsIgnoreCase(TRUE) ||
4800Sstevel@tonic-gate 		value.equalsIgnoreCase(FALSE)) {
4810Sstevel@tonic-gate 		o = Boolean.valueOf(value);
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	    } else {
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 		// Process the string to remove escapes.
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 		String val = (String)value;
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 		// If it begins with the opaque prefix, treat it as an
4900Sstevel@tonic-gate 		//  opaque. Use radix64 parser to convert.
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 		if (val.startsWith("(")) {
4930Sstevel@tonic-gate 		    o = Radix64.radix64ToOpaque(val);
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 		} else {
4960Sstevel@tonic-gate 		    o = unescapeAttributeString(val, charCode);
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 		}
4990Sstevel@tonic-gate 	    }
5000Sstevel@tonic-gate 	}
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	return o;
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate     }
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate     // Externalize the attribute, using its charCode to encode any reserved
5070Sstevel@tonic-gate     //  characters.
5080Sstevel@tonic-gate 
externalize()5090Sstevel@tonic-gate     String externalize()
5100Sstevel@tonic-gate 	throws ServiceLocationException {
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	if (values == null) {	// keyword attribute...
5130Sstevel@tonic-gate 	    return escapeAttributeString(id, charCode);
5140Sstevel@tonic-gate 	}
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	Vector v = new Vector();
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	for (Enumeration e = values.elements(); e.hasMoreElements(); ) {
5190Sstevel@tonic-gate 	    Object o = e.nextElement();
5200Sstevel@tonic-gate 	    String s = null;
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	    s = escapeValueInternal(o, charCode);
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 	    v.addElement(s);
5250Sstevel@tonic-gate 	}
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	StringBuffer buf =
5280Sstevel@tonic-gate 	    new StringBuffer("(" +
5290Sstevel@tonic-gate 			     escapeAttributeString(id, charCode) +
5300Sstevel@tonic-gate 			     "=");
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate 	buf.append(SrvLocHeader.vectorToCommaSeparatedList(v));
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	buf.append(")");
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 	return buf.toString();
5370Sstevel@tonic-gate     }
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate     // Exactly like the one in ServiceLocationAttribute, but use our
5400Sstevel@tonic-gate     //  escapeAttributeString.
5410Sstevel@tonic-gate 
escapeValueInternal(Object val, String charCode)5420Sstevel@tonic-gate     private static String escapeValueInternal(Object val, String charCode) {
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 	String s;
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 	// Escape any characters needing it.
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 	if (val instanceof String) {
5490Sstevel@tonic-gate 
5500Sstevel@tonic-gate 	    try {
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 		s = escapeAttributeString((String)val, charCode);
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	    } catch (ServiceLocationException ex) {
5550Sstevel@tonic-gate 		throw
5560Sstevel@tonic-gate 		    new IllegalArgumentException(ex.getMessage());
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 	    }
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	} else if (val instanceof Opaque) {
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	    // Convert to radix 64.
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 	    s = Radix64.opaqueToRadix64((Opaque)val);
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	} else {
5670Sstevel@tonic-gate 	    s = val.toString();
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	}
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	return s;
5720Sstevel@tonic-gate     }
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate     // Escape an attribute string with the char code.
5750Sstevel@tonic-gate 
escapeAttributeString(String string, String charCode)5760Sstevel@tonic-gate     static String escapeAttributeString(String string,
5770Sstevel@tonic-gate 					String charCode)
5780Sstevel@tonic-gate 	throws ServiceLocationException {
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
5810Sstevel@tonic-gate 	int i, n = string.length();
5820Sstevel@tonic-gate 	boolean is8bit =
5830Sstevel@tonic-gate 	    (charCode.equals(IANACharCode.ASCII) ||
5840Sstevel@tonic-gate 	    charCode.equals(IANACharCode.LATIN1));
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
5870Sstevel@tonic-gate 	    char c = string.charAt(i);
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	    if (ESCAPABLE_CHARS.indexOf(c) != -1) {
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 		buf.append("&#");
5920Sstevel@tonic-gate 		buf.append(IANACharCode.escapeChar(c, charCode));
5930Sstevel@tonic-gate 		buf.append(";");
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	    } else {
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 		// Need to check ASCII and LATIN1 to make sure that
5980Sstevel@tonic-gate 		//  the character is not outside their range of
5990Sstevel@tonic-gate 		//  representation.
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 		if (is8bit && (short)c > 255) {
6020Sstevel@tonic-gate 		    throw
6030Sstevel@tonic-gate 			new ServiceLocationException(
6040Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6050Sstevel@tonic-gate 				"v1_8bit_error",
6060Sstevel@tonic-gate 				new Object[] {new Character(c)});
6070Sstevel@tonic-gate 		}
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 		buf.append(c);
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 	    }
6120Sstevel@tonic-gate 	}
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	return buf.toString();
6150Sstevel@tonic-gate     }
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate     // Unescape attribute string, using charCode for reserved characters.
6180Sstevel@tonic-gate 
unescapeAttributeString(String string, String charCode)6190Sstevel@tonic-gate     static String unescapeAttributeString(String string,
6200Sstevel@tonic-gate 					  String charCode)
6210Sstevel@tonic-gate 	throws ServiceLocationException {
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 	// Process escapes.
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 	int i, n = string.length();
6260Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer(n);
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
6290Sstevel@tonic-gate 	    char c = string.charAt(i);
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 	    // Check for invalids.
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 	    int idx = -1;
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	    if ((idx = UNESCAPABLE_CHARS.indexOf(c)) != -1) {
6360Sstevel@tonic-gate 		throw
6370Sstevel@tonic-gate 		    new ServiceLocationException(
6380Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6390Sstevel@tonic-gate 				"v1_escape_error",
6400Sstevel@tonic-gate 				new Object[] {string});
6410Sstevel@tonic-gate 	    }
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	    // Check for escapes.
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 	    if (c != '&') {
6460Sstevel@tonic-gate 
6470Sstevel@tonic-gate 		buf.append(c);
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	    } else {
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 		// Check to be sure we've got enough characters left. We need
6520Sstevel@tonic-gate 		// at least 3.
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 		if ((i + 1) >= n) {
6550Sstevel@tonic-gate 		    throw
6560Sstevel@tonic-gate 			new ServiceLocationException(
6570Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6580Sstevel@tonic-gate 				"v1_escape_error",
6590Sstevel@tonic-gate 				new Object[] {string});
6600Sstevel@tonic-gate 		}
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 		c = string.charAt(++i);
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 		if (c != '#') {
6650Sstevel@tonic-gate 		    throw
6660Sstevel@tonic-gate 			new ServiceLocationException(
6670Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6680Sstevel@tonic-gate 				"v1_escape_error",
6690Sstevel@tonic-gate 				new Object[] {string});
6700Sstevel@tonic-gate 		}
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate 		// Iterate through numbers, collecting.
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 		StringBuffer num = new StringBuffer(n);
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 		for (i++; i < n; i++) {
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 		    c = string.charAt(i);
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 		    if (!Character.isDigit(c)) {
6810Sstevel@tonic-gate 			break;
6820Sstevel@tonic-gate 		    }
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate 		    num.append(c);
6850Sstevel@tonic-gate 		}
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 		// If the buffer is empty, then throw exception
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate 		if (num.length() <= 0) {
6900Sstevel@tonic-gate 		    throw
6910Sstevel@tonic-gate 			new ServiceLocationException(
6920Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6930Sstevel@tonic-gate 				"v1_escape_error",
6940Sstevel@tonic-gate 				new Object[] {string});
6950Sstevel@tonic-gate 		}
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 		// If the last one isn't ";", we've got a problem.
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 		if (c != ';') {
7000Sstevel@tonic-gate 		    throw
7010Sstevel@tonic-gate 			new ServiceLocationException(
7020Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
7030Sstevel@tonic-gate 				"v1_escape_error",
7040Sstevel@tonic-gate 				new Object[] {string});
7050Sstevel@tonic-gate 		}
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 		// OK, now convert to a character and add to buffer.
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 		try {
7100Sstevel@tonic-gate 		    buf.append(IANACharCode.unescapeChar(num.toString(),
7110Sstevel@tonic-gate 							 charCode));
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 		} catch (NumberFormatException ex) {
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate 		    throw
7160Sstevel@tonic-gate 			new ServiceLocationException(
7170Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
7180Sstevel@tonic-gate 				"v1_escape_error",
7190Sstevel@tonic-gate 				new Object[] {string});
7200Sstevel@tonic-gate 		}
7210Sstevel@tonic-gate 	    }
7220Sstevel@tonic-gate 	}
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate 	return buf.toString();
7250Sstevel@tonic-gate     }
7260Sstevel@tonic-gate }
727