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
59a70fc3bSMark J. Nelson  * Common Development and Distribution License (the "License").
69a70fc3bSMark 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 (c) 1999 by Sun Microsystems, Inc.
237c478bd9Sstevel@tonic-gate  * All rights reserved.
247c478bd9Sstevel@tonic-gate  *
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate //  ServiceLocationAttributeV1.java: SLPv1 character encoding and decoding
287c478bd9Sstevel@tonic-gate //  Author:           James Kempf
297c478bd9Sstevel@tonic-gate //  Created On:       Fri Oct  9 19:18:17 1998
307c478bd9Sstevel@tonic-gate //  Last Modified By: James Kempf
317c478bd9Sstevel@tonic-gate //  Last Modified On: Sat Oct 24 13:17:58 1998
327c478bd9Sstevel@tonic-gate //  Update Count:     15
337c478bd9Sstevel@tonic-gate //
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate package com.sun.slp;
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate import java.util.*;
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate /**
407c478bd9Sstevel@tonic-gate  * Handles attribute string encoding and decoding for SLPv1.
417c478bd9Sstevel@tonic-gate  *
427c478bd9Sstevel@tonic-gate  * @author James Kempf
437c478bd9Sstevel@tonic-gate  */
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate class ServiceLocationAttributeV1 extends ServiceLocationAttribute {
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate     String charCode = IANACharCode.UTF8;  // how to encode the attribute.
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate     // Characters to escape.
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate     final private static String UNESCAPABLE_CHARS = ",=!></*()";
527c478bd9Sstevel@tonic-gate     final private static String ESCAPABLE_CHARS =
537c478bd9Sstevel@tonic-gate 	UNESCAPABLE_CHARS + "&#;";
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate     /**
567c478bd9Sstevel@tonic-gate      * Handles radix64 string encoding and decoding for SLPv1.
577c478bd9Sstevel@tonic-gate      *
587c478bd9Sstevel@tonic-gate      * @author James Kempf
597c478bd9Sstevel@tonic-gate      */
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate     static class Radix64 extends Object {
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate 	/**
647c478bd9Sstevel@tonic-gate 	 * Translates the 6 bit value to the corresponding radix 64
657c478bd9Sstevel@tonic-gate 	 * representation.
667c478bd9Sstevel@tonic-gate 	 */
LUT(char cin)677c478bd9Sstevel@tonic-gate 	private static char LUT(char cin) {
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate 	    int i = (int)(cin & (char)0x00FF);
707c478bd9Sstevel@tonic-gate 	    char result = ' ';
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 	    if (i < 26) {
737c478bd9Sstevel@tonic-gate 		result = (char)((char)i + 'A');
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate 	    } else if (i < 52) {
767c478bd9Sstevel@tonic-gate 		result = (char)((char)(i - 26) + 'a');
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate 	    } else if (i < 62) {
797c478bd9Sstevel@tonic-gate 		result = (char)((char)(i - 52) + '0');
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 	    } else if (i == 62) {
827c478bd9Sstevel@tonic-gate 		result = '+';
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate 	    } else if (i == 63) {
857c478bd9Sstevel@tonic-gate 		result = '/';
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate 	    }
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 	    return result;
907c478bd9Sstevel@tonic-gate 	}
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	/**
937c478bd9Sstevel@tonic-gate 	 * Translates a radix 64 representation to the 64 bit value which
947c478bd9Sstevel@tonic-gate 	 * corresponds to it.
957c478bd9Sstevel@tonic-gate 	 */
LUT2(char cin, String s)967c478bd9Sstevel@tonic-gate 	private static char LUT2(char cin, String s)
977c478bd9Sstevel@tonic-gate 	    throws ServiceLocationException {
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate 	    int i = (int)(cin & 0x00ff);
1007c478bd9Sstevel@tonic-gate 	    char c = (char) 0xffff;
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate 	    if (((char)i >= 'A') && ((char)i <= 'Z')) {
1037c478bd9Sstevel@tonic-gate 		c = (char)((char)i - 'A');
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate 	    }
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	    if (((char)i >= 'a') && ((char)i <= 'z')) {
1087c478bd9Sstevel@tonic-gate 		c = (char)((char)i - 'a' +(char) 26);
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	    }
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	    if (((char)i >= '0') && ((char)i <= '9')) {
1137c478bd9Sstevel@tonic-gate 		c = (char)((char)i - '0' +(char) 52);
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 	    }
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate 	    if ((char)i == '+') {
1187c478bd9Sstevel@tonic-gate 		c = (char)62;
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 	    }
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate 	    if ((char)i == '/') {
1237c478bd9Sstevel@tonic-gate 		c = (char)63;
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 	    }
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate 	    if ((char)i == '=') {
1287c478bd9Sstevel@tonic-gate 		c = (char)0;
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	    }
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	    if (c == 0xffff) {
1337c478bd9Sstevel@tonic-gate 		throw
1347c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
1357c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1367c478bd9Sstevel@tonic-gate 				"v1_radix64_error",
1377c478bd9Sstevel@tonic-gate 				new Object[] {s});
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate 	    }
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 	    return c;
1427c478bd9Sstevel@tonic-gate 	}
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 	// format of the encoding is "(###:encoding)" where ### is the length
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	// convert a string in the encoding to the buffer format
1477c478bd9Sstevel@tonic-gate 
radix64ToOpaque(String s)1487c478bd9Sstevel@tonic-gate 	static Opaque radix64ToOpaque(String s)
1497c478bd9Sstevel@tonic-gate 	    throws ServiceLocationException {
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 	    if (s == null || s.trim().length() == 0) {
1527c478bd9Sstevel@tonic-gate 		return new Opaque(new byte[0]);
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 	    }
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	    int oplen = 0;
1577c478bd9Sstevel@tonic-gate 	    int scan = 0;
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	    while (scan < s.length()) {
1607c478bd9Sstevel@tonic-gate 		if (s.charAt(scan) == '(') {
1617c478bd9Sstevel@tonic-gate 		    break;  // scan till begins
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 		}
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 		scan++;
1667c478bd9Sstevel@tonic-gate 	    }
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	    scan++; // past the '('
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	    while (scan < s.length()) {
1717c478bd9Sstevel@tonic-gate 		if (Character.isWhitespace(s.charAt(scan)) == false) {
1727c478bd9Sstevel@tonic-gate 		    break;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 		}
1757c478bd9Sstevel@tonic-gate 		scan++;
1767c478bd9Sstevel@tonic-gate 	    }
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 	    while (scan < s.length()) {
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 		if (Character.isDigit(s.charAt(scan))) {
1817c478bd9Sstevel@tonic-gate 		    oplen *= 10;
1827c478bd9Sstevel@tonic-gate 		    oplen += (s.charAt(scan) - '0');
1837c478bd9Sstevel@tonic-gate 		    scan++;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 		} else {
1867c478bd9Sstevel@tonic-gate 		    break;
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 		}
1897c478bd9Sstevel@tonic-gate 	    }
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	    if (scan >= s.length()) {
1927c478bd9Sstevel@tonic-gate 		throw
1937c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
1947c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1957c478bd9Sstevel@tonic-gate 				"v1_radix64_error",
1967c478bd9Sstevel@tonic-gate 				new Object[] {s});
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	    }
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	    if (s.charAt(scan) != ':') {
2027c478bd9Sstevel@tonic-gate 		throw
2037c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
2047c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2057c478bd9Sstevel@tonic-gate 				"v1_radix64_error",
2067c478bd9Sstevel@tonic-gate 				new Object[] {s});
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	    }
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	    scan++; // past the ':'
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 	    byte b[] = new byte[oplen];
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	    int pos = 0;
2157c478bd9Sstevel@tonic-gate 	    int timesthrough = (oplen/3);
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	    if ((oplen %3) != 0) {
2187c478bd9Sstevel@tonic-gate 		timesthrough++;
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 	    }
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	    for (int i = 0; i < timesthrough; i++) {
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 		// get 4 bytes to make 3 with, skipping blanks
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 		char v[] = new char[4];
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 		for (int x = 0; x < 4; x++) {
229*55fea89dSDan Cross 
2307c478bd9Sstevel@tonic-gate 		    while ((scan < s.length()) &&
2317c478bd9Sstevel@tonic-gate 			   Character.isWhitespace(s.charAt(scan))) {
2327c478bd9Sstevel@tonic-gate 			scan++; // eat white
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 		    }
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 		    if (scan >= s.length()) {
2377c478bd9Sstevel@tonic-gate 			throw
2387c478bd9Sstevel@tonic-gate 			    new ServiceLocationException(
2397c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2407c478bd9Sstevel@tonic-gate 				"v1_radix64_error",
2417c478bd9Sstevel@tonic-gate 				new Object[] {s});
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 		    }
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 		    v[x] = LUT2(s.charAt(scan), s);
2467c478bd9Sstevel@tonic-gate 		    scan++;
2477c478bd9Sstevel@tonic-gate 		}
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 		b[pos++] =
2507c478bd9Sstevel@tonic-gate 		    (byte) (((0x3F & v[0]) << 2) + ((0x30 & v[1]) >> 4));
2517c478bd9Sstevel@tonic-gate 		if (pos >= oplen) break;
2527c478bd9Sstevel@tonic-gate 		b[pos++] =
2537c478bd9Sstevel@tonic-gate 		    (byte) (((0x0F & v[1]) << 4) + ((0x3C & v[2]) >> 2));
2547c478bd9Sstevel@tonic-gate 		if (pos >= oplen) break;
2557c478bd9Sstevel@tonic-gate 		b[pos++] = (byte) (((0x03 & v[2]) << 6) + (0x3F & v[3]));
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	    } // end of conversion loop
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	    if (scan >= s.length()) {
2607c478bd9Sstevel@tonic-gate 		throw
2617c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
2627c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2637c478bd9Sstevel@tonic-gate 				"v1_radix64_error",
2647c478bd9Sstevel@tonic-gate 				new Object[] {s});
2657c478bd9Sstevel@tonic-gate 	    }
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	    if (s.charAt(scan) != ')') {// check for too many chars.
2687c478bd9Sstevel@tonic-gate 		throw
2697c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
2707c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2717c478bd9Sstevel@tonic-gate 				"v1_radix64_error",
2727c478bd9Sstevel@tonic-gate 				new Object[] {s});
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	    }
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	    return new Opaque(b);
2777c478bd9Sstevel@tonic-gate 	}
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 	// convert an Opaque to the encoding
2807c478bd9Sstevel@tonic-gate 
opaqueToRadix64(Opaque oq)2817c478bd9Sstevel@tonic-gate 	static String opaqueToRadix64(Opaque oq) {
2827c478bd9Sstevel@tonic-gate 	    byte[] b = oq.bytes;
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	    if (b == null) {
2857c478bd9Sstevel@tonic-gate 		return new String("");
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	    }
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	    StringBuffer sb = new StringBuffer("("+b.length+":");
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 	    int datalen;
2927c478bd9Sstevel@tonic-gate 	    int fill = b.length%3;
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	    if (fill == 0) {
2957c478bd9Sstevel@tonic-gate 		datalen = (b.length / 3) * 4;
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	    } else {
2987c478bd9Sstevel@tonic-gate 		datalen = ((b.length / 3) + 1) * 4;
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	    }
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	    int dataoffset = 0;
3037c478bd9Sstevel@tonic-gate 	    int more = (b.length%3);
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	    if (more != 0) {
3067c478bd9Sstevel@tonic-gate 		more = 1;
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 	    }
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 	    int a[] = new int[4];
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	    for (int i = 0; i < ((b.length/3)+more-1); i++) {
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 		a[0] =   (int)(0xFC & (char)b[ dataoffset    ]) >> 2;
3157c478bd9Sstevel@tonic-gate 		a[1] =  ((int)(0x03 & (char)b[ dataoffset    ]) << 4) +
3167c478bd9Sstevel@tonic-gate 		    ((int)(0xF0 & (char)b[ dataoffset + 1]) >> 4);
3177c478bd9Sstevel@tonic-gate 		a[2] =  ((int)(0x0F & (char)b[ dataoffset + 1]) << 2) +
3187c478bd9Sstevel@tonic-gate 		    ((int)(0xC0 & (char)b[ dataoffset + 2]) >> 6);
3197c478bd9Sstevel@tonic-gate 		a[3] =   (int)(0x3F & (char)b[ dataoffset + 2]);
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 		for (int j = 0; j < 4; j++) {
3227c478bd9Sstevel@tonic-gate 		    sb.append(LUT((char)a[j]));
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 		}
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 		dataoffset += 3;
3277c478bd9Sstevel@tonic-gate 	    }
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 	    byte f1 = 0, f2 = 0;
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	    if (fill == 0) {
3327c478bd9Sstevel@tonic-gate 		f1 = b[ dataoffset + 1 ];
3337c478bd9Sstevel@tonic-gate 		f2 = b[ dataoffset + 2 ];
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	    } else if (fill == 2) {
3367c478bd9Sstevel@tonic-gate 		f1 = b[ dataoffset + 1 ];
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	    }
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 	    a[0] = (int) (0xFC & (char)b[ dataoffset ]) >> 2;
3417c478bd9Sstevel@tonic-gate 	    a[1] = ((int) (0x03 & (char)b[ dataoffset ]) << 4) +
3427c478bd9Sstevel@tonic-gate 		((int) (0xF0 & (char)f1) >> 4);
3437c478bd9Sstevel@tonic-gate 	    a[2] = ((int) (0x0F & (char)f1) << 2) +
3447c478bd9Sstevel@tonic-gate 		((int) (0xC0 & (char)f2) >> 6);
3457c478bd9Sstevel@tonic-gate 	    a[3] = (int) (0x3F & (char)f2);
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	    for (int j = 0; j < 4; j++) {
3487c478bd9Sstevel@tonic-gate 		sb.append(LUT((char) a[j]));
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 	    }
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 	    sb.append(")");
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 	    return sb.toString();
3557c478bd9Sstevel@tonic-gate 	}
3567c478bd9Sstevel@tonic-gate     }
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate     // Create an SLPv1 attribute from a general attribute.
3597c478bd9Sstevel@tonic-gate 
ServiceLocationAttributeV1(ServiceLocationAttribute attr)3607c478bd9Sstevel@tonic-gate     ServiceLocationAttributeV1(ServiceLocationAttribute attr) {
3617c478bd9Sstevel@tonic-gate 	id = attr.id;
3627c478bd9Sstevel@tonic-gate 	values = attr.values;
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate     }
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate     // Create an SLPv1 attribute from the parenthesized expression, using
3677c478bd9Sstevel@tonic-gate     //  charCode to decode any encodings.
3687c478bd9Sstevel@tonic-gate 
ServiceLocationAttributeV1(String exp, String charCode, boolean allowMultiValuedBooleans)3697c478bd9Sstevel@tonic-gate     ServiceLocationAttributeV1(String exp,
3707c478bd9Sstevel@tonic-gate 			       String charCode,
3717c478bd9Sstevel@tonic-gate 			       boolean allowMultiValuedBooleans)
3727c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
3737c478bd9Sstevel@tonic-gate 	this.charCode = charCode;
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	// If start and end paren, then parse out assignment.
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	if (exp.startsWith("(") && exp.endsWith(")")) {
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	    StringTokenizer tk =
3807c478bd9Sstevel@tonic-gate 		new StringTokenizer(exp.substring(1, exp.length() - 1),
3817c478bd9Sstevel@tonic-gate 				    "=",
3827c478bd9Sstevel@tonic-gate 				    true);
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	    try {
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 		// Get the tag.
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 		id =
3897c478bd9Sstevel@tonic-gate 		    unescapeAttributeString(tk.nextToken(), charCode);
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 		if (id.length() <= 0) {
3927c478bd9Sstevel@tonic-gate 		    throw
3937c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
3947c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
3957c478bd9Sstevel@tonic-gate 				"null_id",
3967c478bd9Sstevel@tonic-gate 				new Object[] {exp});
3977c478bd9Sstevel@tonic-gate 		}
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 		tk.nextToken();  // get rid of "="
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 		// Gather the rest.
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 		String rest = tk.nextToken("");
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 		// Parse the comma separated list.
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 		values = SrvLocHeader.parseCommaSeparatedListIn(rest, true);
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 		// Convert to objects.
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 		int i, n = values.size();
4127c478bd9Sstevel@tonic-gate 		Class vecClass = null;
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 		for (i = 0; i < n; i++) {
4157c478bd9Sstevel@tonic-gate 		    String value = (String)values.elementAt(i);
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 		    // Need to determine which type to use.
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 		    Object o = evaluate(value, charCode);
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 		    // Convert Opaque to byte array.
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 		    if (o instanceof Opaque) {
4247c478bd9Sstevel@tonic-gate 			o = ((Opaque)o).bytes;
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 		    }
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 		    values.setElementAt(o, i);
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 		}
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	    } catch (NoSuchElementException ex) {
4337c478bd9Sstevel@tonic-gate 		throw
4347c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
4357c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
4367c478bd9Sstevel@tonic-gate 				"assignment_syntax_err",
4377c478bd9Sstevel@tonic-gate 				new Object[] {exp});
4387c478bd9Sstevel@tonic-gate 	    }
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 	    verifyValueTypes(values, allowMultiValuedBooleans);
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 	} else {
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	    // Check to make sure there's no parens.
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	    if (exp.indexOf('(') != -1 || exp.indexOf(')') != -1) {
4477c478bd9Sstevel@tonic-gate 		throw
4487c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
4497c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
4507c478bd9Sstevel@tonic-gate 				"assignment_syntax_err",
4517c478bd9Sstevel@tonic-gate 				new Object[] {exp});
4527c478bd9Sstevel@tonic-gate 	    }
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 	    // Unescape the keyword.
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	    id = unescapeAttributeString(exp, charCode);
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	}
4597c478bd9Sstevel@tonic-gate     }
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate     // Duplicate of the one in ServiceLocatioAttribute, except we use our
4627c478bd9Sstevel@tonic-gate     //  unescapeAttributeString.
4637c478bd9Sstevel@tonic-gate 
evaluate(String value, String charCode)4647c478bd9Sstevel@tonic-gate     static Object evaluate(String value, String charCode)
4657c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate 	Object o = null;
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	// If it can be converted into an integer, then convert it.
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 	try {
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	    o = Integer.valueOf(value);
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 	} catch (NumberFormatException ex) {
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 	    // Wasn't an integer. Try boolean.
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	    if (value.equalsIgnoreCase(TRUE) ||
4807c478bd9Sstevel@tonic-gate 		value.equalsIgnoreCase(FALSE)) {
4817c478bd9Sstevel@tonic-gate 		o = Boolean.valueOf(value);
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 	    } else {
484*55fea89dSDan Cross 
4857c478bd9Sstevel@tonic-gate 		// Process the string to remove escapes.
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 		String val = (String)value;
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 		// If it begins with the opaque prefix, treat it as an
4907c478bd9Sstevel@tonic-gate 		//  opaque. Use radix64 parser to convert.
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 		if (val.startsWith("(")) {
4937c478bd9Sstevel@tonic-gate 		    o = Radix64.radix64ToOpaque(val);
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 		} else {
4967c478bd9Sstevel@tonic-gate 		    o = unescapeAttributeString(val, charCode);
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 		}
4997c478bd9Sstevel@tonic-gate 	    }
5007c478bd9Sstevel@tonic-gate 	}
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 	return o;
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate     }
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate     // Externalize the attribute, using its charCode to encode any reserved
5077c478bd9Sstevel@tonic-gate     //  characters.
5087c478bd9Sstevel@tonic-gate 
externalize()5097c478bd9Sstevel@tonic-gate     String externalize()
5107c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	if (values == null) {	// keyword attribute...
5137c478bd9Sstevel@tonic-gate 	    return escapeAttributeString(id, charCode);
5147c478bd9Sstevel@tonic-gate 	}
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	Vector v = new Vector();
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 	for (Enumeration e = values.elements(); e.hasMoreElements(); ) {
5197c478bd9Sstevel@tonic-gate 	    Object o = e.nextElement();
5207c478bd9Sstevel@tonic-gate 	    String s = null;
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 	    s = escapeValueInternal(o, charCode);
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 	    v.addElement(s);
5257c478bd9Sstevel@tonic-gate 	}
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	StringBuffer buf =
5287c478bd9Sstevel@tonic-gate 	    new StringBuffer("(" +
5297c478bd9Sstevel@tonic-gate 			     escapeAttributeString(id, charCode) +
5307c478bd9Sstevel@tonic-gate 			     "=");
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	buf.append(SrvLocHeader.vectorToCommaSeparatedList(v));
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 	buf.append(")");
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	return buf.toString();
5377c478bd9Sstevel@tonic-gate     }
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate     // Exactly like the one in ServiceLocationAttribute, but use our
5407c478bd9Sstevel@tonic-gate     //  escapeAttributeString.
5417c478bd9Sstevel@tonic-gate 
escapeValueInternal(Object val, String charCode)5427c478bd9Sstevel@tonic-gate     private static String escapeValueInternal(Object val, String charCode) {
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	String s;
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	// Escape any characters needing it.
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 	if (val instanceof String) {
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	    try {
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 		s = escapeAttributeString((String)val, charCode);
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 	    } catch (ServiceLocationException ex) {
5557c478bd9Sstevel@tonic-gate 		throw
5567c478bd9Sstevel@tonic-gate 		    new IllegalArgumentException(ex.getMessage());
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 	    }
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	} else if (val instanceof Opaque) {
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 	    // Convert to radix 64.
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 	    s = Radix64.opaqueToRadix64((Opaque)val);
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 	} else {
5677c478bd9Sstevel@tonic-gate 	    s = val.toString();
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	}
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 	return s;
5727c478bd9Sstevel@tonic-gate     }
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate     // Escape an attribute string with the char code.
5757c478bd9Sstevel@tonic-gate 
escapeAttributeString(String string, String charCode)5767c478bd9Sstevel@tonic-gate     static String escapeAttributeString(String string,
5777c478bd9Sstevel@tonic-gate 					String charCode)
5787c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
5817c478bd9Sstevel@tonic-gate 	int i, n = string.length();
5827c478bd9Sstevel@tonic-gate 	boolean is8bit =
5837c478bd9Sstevel@tonic-gate 	    (charCode.equals(IANACharCode.ASCII) ||
5847c478bd9Sstevel@tonic-gate 	    charCode.equals(IANACharCode.LATIN1));
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
5877c478bd9Sstevel@tonic-gate 	    char c = string.charAt(i);
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	    if (ESCAPABLE_CHARS.indexOf(c) != -1) {
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 		buf.append("&#");
5927c478bd9Sstevel@tonic-gate 		buf.append(IANACharCode.escapeChar(c, charCode));
5937c478bd9Sstevel@tonic-gate 		buf.append(";");
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	    } else {
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 		// Need to check ASCII and LATIN1 to make sure that
5987c478bd9Sstevel@tonic-gate 		//  the character is not outside their range of
5997c478bd9Sstevel@tonic-gate 		//  representation.
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 		if (is8bit && (short)c > 255) {
6027c478bd9Sstevel@tonic-gate 		    throw
6037c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
6047c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6057c478bd9Sstevel@tonic-gate 				"v1_8bit_error",
6067c478bd9Sstevel@tonic-gate 				new Object[] {new Character(c)});
6077c478bd9Sstevel@tonic-gate 		}
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 		buf.append(c);
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 	    }
6127c478bd9Sstevel@tonic-gate 	}
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	return buf.toString();
6157c478bd9Sstevel@tonic-gate     }
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate     // Unescape attribute string, using charCode for reserved characters.
6187c478bd9Sstevel@tonic-gate 
unescapeAttributeString(String string, String charCode)6197c478bd9Sstevel@tonic-gate     static String unescapeAttributeString(String string,
6207c478bd9Sstevel@tonic-gate 					  String charCode)
6217c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	// Process escapes.
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 	int i, n = string.length();
6267c478bd9Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer(n);
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
6297c478bd9Sstevel@tonic-gate 	    char c = string.charAt(i);
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	    // Check for invalids.
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	    int idx = -1;
634*55fea89dSDan Cross 
6357c478bd9Sstevel@tonic-gate 	    if ((idx = UNESCAPABLE_CHARS.indexOf(c)) != -1) {
6367c478bd9Sstevel@tonic-gate 		throw
6377c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
6387c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6397c478bd9Sstevel@tonic-gate 				"v1_escape_error",
6407c478bd9Sstevel@tonic-gate 				new Object[] {string});
6417c478bd9Sstevel@tonic-gate 	    }
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 	    // Check for escapes.
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	    if (c != '&') {
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 		buf.append(c);
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	    } else {
650*55fea89dSDan Cross 
6517c478bd9Sstevel@tonic-gate 		// Check to be sure we've got enough characters left. We need
6527c478bd9Sstevel@tonic-gate 		// at least 3.
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 		if ((i + 1) >= n) {
6557c478bd9Sstevel@tonic-gate 		    throw
6567c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
6577c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6587c478bd9Sstevel@tonic-gate 				"v1_escape_error",
6597c478bd9Sstevel@tonic-gate 				new Object[] {string});
6607c478bd9Sstevel@tonic-gate 		}
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 		c = string.charAt(++i);
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 		if (c != '#') {
6657c478bd9Sstevel@tonic-gate 		    throw
6667c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
6677c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6687c478bd9Sstevel@tonic-gate 				"v1_escape_error",
6697c478bd9Sstevel@tonic-gate 				new Object[] {string});
6707c478bd9Sstevel@tonic-gate 		}
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 		// Iterate through numbers, collecting.
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 		StringBuffer num = new StringBuffer(n);
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 		for (i++; i < n; i++) {
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 		    c = string.charAt(i);
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 		    if (!Character.isDigit(c)) {
6817c478bd9Sstevel@tonic-gate 			break;
6827c478bd9Sstevel@tonic-gate 		    }
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 		    num.append(c);
6857c478bd9Sstevel@tonic-gate 		}
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 		// If the buffer is empty, then throw exception
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 		if (num.length() <= 0) {
6907c478bd9Sstevel@tonic-gate 		    throw
6917c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
6927c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6937c478bd9Sstevel@tonic-gate 				"v1_escape_error",
6947c478bd9Sstevel@tonic-gate 				new Object[] {string});
6957c478bd9Sstevel@tonic-gate 		}
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 		// If the last one isn't ";", we've got a problem.
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 		if (c != ';') {
7007c478bd9Sstevel@tonic-gate 		    throw
7017c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
7027c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
7037c478bd9Sstevel@tonic-gate 				"v1_escape_error",
7047c478bd9Sstevel@tonic-gate 				new Object[] {string});
7057c478bd9Sstevel@tonic-gate 		}
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 		// OK, now convert to a character and add to buffer.
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate 		try {
7107c478bd9Sstevel@tonic-gate 		    buf.append(IANACharCode.unescapeChar(num.toString(),
7117c478bd9Sstevel@tonic-gate 							 charCode));
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 		} catch (NumberFormatException ex) {
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 		    throw
7167c478bd9Sstevel@tonic-gate 			new ServiceLocationException(
7177c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
7187c478bd9Sstevel@tonic-gate 				"v1_escape_error",
7197c478bd9Sstevel@tonic-gate 				new Object[] {string});
7207c478bd9Sstevel@tonic-gate 		}
7217c478bd9Sstevel@tonic-gate 	    }
7227c478bd9Sstevel@tonic-gate 	}
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	return buf.toString();
7257c478bd9Sstevel@tonic-gate     }
7267c478bd9Sstevel@tonic-gate }
727