2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1999 by Sun Microsystems, Inc.
2N/A * All rights reserved.
2N/A *
2N/A */
2N/A
2N/A// ServiceLocationAttributeV1.java: SLPv1 character encoding and decoding
2N/A// Author: James Kempf
2N/A// Created On: Fri Oct 9 19:18:17 1998
2N/A// Last Modified By: James Kempf
2N/A// Last Modified On: Sat Oct 24 13:17:58 1998
2N/A// Update Count: 15
2N/A//
2N/A
2N/Apackage com.sun.slp;
2N/A
2N/Aimport java.util.*;
2N/A
2N/A/**
2N/A * Handles attribute string encoding and decoding for SLPv1.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/Aclass ServiceLocationAttributeV1 extends ServiceLocationAttribute {
2N/A
2N/A String charCode = IANACharCode.UTF8; // how to encode the attribute.
2N/A
2N/A // Characters to escape.
2N/A
2N/A final private static String UNESCAPABLE_CHARS = ",=!></*()";
2N/A final private static String ESCAPABLE_CHARS =
2N/A UNESCAPABLE_CHARS + "&#;";
2N/A
2N/A /**
2N/A * Handles radix64 string encoding and decoding for SLPv1.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/A static class Radix64 extends Object {
2N/A
2N/A /**
2N/A * Translates the 6 bit value to the corresponding radix 64
2N/A * representation.
2N/A */
2N/A private static char LUT(char cin) {
2N/A
2N/A int i = (int)(cin & (char)0x00FF);
2N/A char result = ' ';
2N/A
2N/A if (i < 26) {
2N/A result = (char)((char)i + 'A');
2N/A
2N/A } else if (i < 52) {
2N/A result = (char)((char)(i - 26) + 'a');
2N/A
2N/A } else if (i < 62) {
2N/A result = (char)((char)(i - 52) + '0');
2N/A
2N/A } else if (i == 62) {
2N/A result = '+';
2N/A
2N/A } else if (i == 63) {
2N/A result = '/';
2N/A
2N/A }
2N/A
2N/A return result;
2N/A }
2N/A
2N/A /**
2N/A * Translates a radix 64 representation to the 64 bit value which
2N/A * corresponds to it.
2N/A */
2N/A private static char LUT2(char cin, String s)
2N/A throws ServiceLocationException {
2N/A
2N/A int i = (int)(cin & 0x00ff);
2N/A char c = (char) 0xffff;
2N/A
2N/A if (((char)i >= 'A') && ((char)i <= 'Z')) {
2N/A c = (char)((char)i - 'A');
2N/A
2N/A }
2N/A
2N/A if (((char)i >= 'a') && ((char)i <= 'z')) {
2N/A c = (char)((char)i - 'a' +(char) 26);
2N/A
2N/A }
2N/A
2N/A if (((char)i >= '0') && ((char)i <= '9')) {
2N/A c = (char)((char)i - '0' +(char) 52);
2N/A
2N/A }
2N/A
2N/A if ((char)i == '+') {
2N/A c = (char)62;
2N/A
2N/A }
2N/A
2N/A if ((char)i == '/') {
2N/A c = (char)63;
2N/A
2N/A }
2N/A
2N/A if ((char)i == '=') {
2N/A c = (char)0;
2N/A
2N/A }
2N/A
2N/A if (c == 0xffff) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_radix64_error",
2N/A new Object[] {s});
2N/A
2N/A }
2N/A
2N/A return c;
2N/A }
2N/A
2N/A // format of the encoding is "(###:encoding)" where ### is the length
2N/A
2N/A // convert a string in the encoding to the buffer format
2N/A
2N/A static Opaque radix64ToOpaque(String s)
2N/A throws ServiceLocationException {
2N/A
2N/A if (s == null || s.trim().length() == 0) {
2N/A return new Opaque(new byte[0]);
2N/A
2N/A }
2N/A
2N/A int oplen = 0;
2N/A int scan = 0;
2N/A
2N/A while (scan < s.length()) {
2N/A if (s.charAt(scan) == '(') {
2N/A break; // scan till begins
2N/A
2N/A }
2N/A
2N/A scan++;
2N/A }
2N/A
2N/A scan++; // past the '('
2N/A
2N/A while (scan < s.length()) {
2N/A if (Character.isWhitespace(s.charAt(scan)) == false) {
2N/A break;
2N/A
2N/A }
2N/A scan++;
2N/A }
2N/A
2N/A while (scan < s.length()) {
2N/A
2N/A if (Character.isDigit(s.charAt(scan))) {
2N/A oplen *= 10;
2N/A oplen += (s.charAt(scan) - '0');
2N/A scan++;
2N/A
2N/A } else {
2N/A break;
2N/A
2N/A }
2N/A }
2N/A
2N/A if (scan >= s.length()) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_radix64_error",
2N/A new Object[] {s});
2N/A
2N/A }
2N/A
2N/A
2N/A if (s.charAt(scan) != ':') {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_radix64_error",
2N/A new Object[] {s});
2N/A
2N/A }
2N/A
2N/A scan++; // past the ':'
2N/A
2N/A byte b[] = new byte[oplen];
2N/A
2N/A int pos = 0;
2N/A int timesthrough = (oplen/3);
2N/A
2N/A if ((oplen %3) != 0) {
2N/A timesthrough++;
2N/A
2N/A }
2N/A
2N/A for (int i = 0; i < timesthrough; i++) {
2N/A
2N/A // get 4 bytes to make 3 with, skipping blanks
2N/A
2N/A char v[] = new char[4];
2N/A
2N/A for (int x = 0; x < 4; x++) {
2N/A
2N/A while ((scan < s.length()) &&
2N/A Character.isWhitespace(s.charAt(scan))) {
2N/A scan++; // eat white
2N/A
2N/A }
2N/A
2N/A if (scan >= s.length()) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_radix64_error",
2N/A new Object[] {s});
2N/A
2N/A }
2N/A
2N/A v[x] = LUT2(s.charAt(scan), s);
2N/A scan++;
2N/A }
2N/A
2N/A b[pos++] =
2N/A (byte) (((0x3F & v[0]) << 2) + ((0x30 & v[1]) >> 4));
2N/A if (pos >= oplen) break;
2N/A b[pos++] =
2N/A (byte) (((0x0F & v[1]) << 4) + ((0x3C & v[2]) >> 2));
2N/A if (pos >= oplen) break;
2N/A b[pos++] = (byte) (((0x03 & v[2]) << 6) + (0x3F & v[3]));
2N/A
2N/A } // end of conversion loop
2N/A
2N/A if (scan >= s.length()) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_radix64_error",
2N/A new Object[] {s});
2N/A }
2N/A
2N/A if (s.charAt(scan) != ')') {// check for too many chars.
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_radix64_error",
2N/A new Object[] {s});
2N/A
2N/A }
2N/A
2N/A return new Opaque(b);
2N/A }
2N/A
2N/A // convert an Opaque to the encoding
2N/A
2N/A static String opaqueToRadix64(Opaque oq) {
2N/A byte[] b = oq.bytes;
2N/A
2N/A if (b == null) {
2N/A return new String("");
2N/A
2N/A }
2N/A
2N/A StringBuffer sb = new StringBuffer("("+b.length+":");
2N/A
2N/A int datalen;
2N/A int fill = b.length%3;
2N/A
2N/A if (fill == 0) {
2N/A datalen = (b.length / 3) * 4;
2N/A
2N/A } else {
2N/A datalen = ((b.length / 3) + 1) * 4;
2N/A
2N/A }
2N/A
2N/A int dataoffset = 0;
2N/A int more = (b.length%3);
2N/A
2N/A if (more != 0) {
2N/A more = 1;
2N/A
2N/A }
2N/A
2N/A int a[] = new int[4];
2N/A
2N/A for (int i = 0; i < ((b.length/3)+more-1); i++) {
2N/A
2N/A a[0] = (int)(0xFC & (char)b[ dataoffset ]) >> 2;
2N/A a[1] = ((int)(0x03 & (char)b[ dataoffset ]) << 4) +
2N/A ((int)(0xF0 & (char)b[ dataoffset + 1]) >> 4);
2N/A a[2] = ((int)(0x0F & (char)b[ dataoffset + 1]) << 2) +
2N/A ((int)(0xC0 & (char)b[ dataoffset + 2]) >> 6);
2N/A a[3] = (int)(0x3F & (char)b[ dataoffset + 2]);
2N/A
2N/A for (int j = 0; j < 4; j++) {
2N/A sb.append(LUT((char)a[j]));
2N/A
2N/A }
2N/A
2N/A dataoffset += 3;
2N/A }
2N/A
2N/A byte f1 = 0, f2 = 0;
2N/A
2N/A if (fill == 0) {
2N/A f1 = b[ dataoffset + 1 ];
2N/A f2 = b[ dataoffset + 2 ];
2N/A
2N/A } else if (fill == 2) {
2N/A f1 = b[ dataoffset + 1 ];
2N/A
2N/A }
2N/A
2N/A a[0] = (int) (0xFC & (char)b[ dataoffset ]) >> 2;
2N/A a[1] = ((int) (0x03 & (char)b[ dataoffset ]) << 4) +
2N/A ((int) (0xF0 & (char)f1) >> 4);
2N/A a[2] = ((int) (0x0F & (char)f1) << 2) +
2N/A ((int) (0xC0 & (char)f2) >> 6);
2N/A a[3] = (int) (0x3F & (char)f2);
2N/A
2N/A for (int j = 0; j < 4; j++) {
2N/A sb.append(LUT((char) a[j]));
2N/A
2N/A }
2N/A
2N/A sb.append(")");
2N/A
2N/A return sb.toString();
2N/A }
2N/A }
2N/A
2N/A // Create an SLPv1 attribute from a general attribute.
2N/A
2N/A ServiceLocationAttributeV1(ServiceLocationAttribute attr) {
2N/A id = attr.id;
2N/A values = attr.values;
2N/A
2N/A }
2N/A
2N/A // Create an SLPv1 attribute from the parenthesized expression, using
2N/A // charCode to decode any encodings.
2N/A
2N/A ServiceLocationAttributeV1(String exp,
2N/A String charCode,
2N/A boolean allowMultiValuedBooleans)
2N/A throws ServiceLocationException {
2N/A this.charCode = charCode;
2N/A
2N/A // If start and end paren, then parse out assignment.
2N/A
2N/A if (exp.startsWith("(") && exp.endsWith(")")) {
2N/A
2N/A StringTokenizer tk =
2N/A new StringTokenizer(exp.substring(1, exp.length() - 1),
2N/A "=",
2N/A true);
2N/A
2N/A try {
2N/A
2N/A // Get the tag.
2N/A
2N/A id =
2N/A unescapeAttributeString(tk.nextToken(), charCode);
2N/A
2N/A if (id.length() <= 0) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "null_id",
2N/A new Object[] {exp});
2N/A }
2N/A
2N/A tk.nextToken(); // get rid of "="
2N/A
2N/A // Gather the rest.
2N/A
2N/A String rest = tk.nextToken("");
2N/A
2N/A // Parse the comma separated list.
2N/A
2N/A values = SrvLocHeader.parseCommaSeparatedListIn(rest, true);
2N/A
2N/A // Convert to objects.
2N/A
2N/A int i, n = values.size();
2N/A Class vecClass = null;
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A String value = (String)values.elementAt(i);
2N/A
2N/A // Need to determine which type to use.
2N/A
2N/A Object o = evaluate(value, charCode);
2N/A
2N/A // Convert Opaque to byte array.
2N/A
2N/A if (o instanceof Opaque) {
2N/A o = ((Opaque)o).bytes;
2N/A
2N/A }
2N/A
2N/A values.setElementAt(o, i);
2N/A
2N/A }
2N/A
2N/A } catch (NoSuchElementException ex) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "assignment_syntax_err",
2N/A new Object[] {exp});
2N/A }
2N/A
2N/A verifyValueTypes(values, allowMultiValuedBooleans);
2N/A
2N/A } else {
2N/A
2N/A // Check to make sure there's no parens.
2N/A
2N/A if (exp.indexOf('(') != -1 || exp.indexOf(')') != -1) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "assignment_syntax_err",
2N/A new Object[] {exp});
2N/A }
2N/A
2N/A // Unescape the keyword.
2N/A
2N/A id = unescapeAttributeString(exp, charCode);
2N/A
2N/A }
2N/A }
2N/A
2N/A // Duplicate of the one in ServiceLocatioAttribute, except we use our
2N/A // unescapeAttributeString.
2N/A
2N/A static Object evaluate(String value, String charCode)
2N/A throws ServiceLocationException {
2N/A
2N/A Object o = null;
2N/A
2N/A // If it can be converted into an integer, then convert it.
2N/A
2N/A try {
2N/A
2N/A o = Integer.valueOf(value);
2N/A
2N/A } catch (NumberFormatException ex) {
2N/A
2N/A // Wasn't an integer. Try boolean.
2N/A
2N/A if (value.equalsIgnoreCase(TRUE) ||
2N/A value.equalsIgnoreCase(FALSE)) {
2N/A o = Boolean.valueOf(value);
2N/A
2N/A } else {
2N/A
2N/A // Process the string to remove escapes.
2N/A
2N/A String val = (String)value;
2N/A
2N/A // If it begins with the opaque prefix, treat it as an
2N/A // opaque. Use radix64 parser to convert.
2N/A
2N/A if (val.startsWith("(")) {
2N/A o = Radix64.radix64ToOpaque(val);
2N/A
2N/A } else {
2N/A o = unescapeAttributeString(val, charCode);
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A return o;
2N/A
2N/A }
2N/A
2N/A // Externalize the attribute, using its charCode to encode any reserved
2N/A // characters.
2N/A
2N/A String externalize()
2N/A throws ServiceLocationException {
2N/A
2N/A if (values == null) { // keyword attribute...
2N/A return escapeAttributeString(id, charCode);
2N/A }
2N/A
2N/A Vector v = new Vector();
2N/A
2N/A for (Enumeration e = values.elements(); e.hasMoreElements(); ) {
2N/A Object o = e.nextElement();
2N/A String s = null;
2N/A
2N/A s = escapeValueInternal(o, charCode);
2N/A
2N/A v.addElement(s);
2N/A }
2N/A
2N/A StringBuffer buf =
2N/A new StringBuffer("(" +
2N/A escapeAttributeString(id, charCode) +
2N/A "=");
2N/A
2N/A buf.append(SrvLocHeader.vectorToCommaSeparatedList(v));
2N/A
2N/A buf.append(")");
2N/A
2N/A return buf.toString();
2N/A }
2N/A
2N/A // Exactly like the one in ServiceLocationAttribute, but use our
2N/A // escapeAttributeString.
2N/A
2N/A private static String escapeValueInternal(Object val, String charCode) {
2N/A
2N/A String s;
2N/A
2N/A // Escape any characters needing it.
2N/A
2N/A if (val instanceof String) {
2N/A
2N/A try {
2N/A
2N/A s = escapeAttributeString((String)val, charCode);
2N/A
2N/A } catch (ServiceLocationException ex) {
2N/A throw
2N/A new IllegalArgumentException(ex.getMessage());
2N/A
2N/A }
2N/A
2N/A } else if (val instanceof Opaque) {
2N/A
2N/A // Convert to radix 64.
2N/A
2N/A s = Radix64.opaqueToRadix64((Opaque)val);
2N/A
2N/A } else {
2N/A s = val.toString();
2N/A
2N/A }
2N/A
2N/A return s;
2N/A }
2N/A
2N/A // Escape an attribute string with the char code.
2N/A
2N/A static String escapeAttributeString(String string,
2N/A String charCode)
2N/A throws ServiceLocationException {
2N/A
2N/A StringBuffer buf = new StringBuffer();
2N/A int i, n = string.length();
2N/A boolean is8bit =
2N/A (charCode.equals(IANACharCode.ASCII) ||
2N/A charCode.equals(IANACharCode.LATIN1));
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A char c = string.charAt(i);
2N/A
2N/A if (ESCAPABLE_CHARS.indexOf(c) != -1) {
2N/A
2N/A buf.append("&#");
2N/A buf.append(IANACharCode.escapeChar(c, charCode));
2N/A buf.append(";");
2N/A
2N/A } else {
2N/A
2N/A // Need to check ASCII and LATIN1 to make sure that
2N/A // the character is not outside their range of
2N/A // representation.
2N/A
2N/A if (is8bit && (short)c > 255) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_8bit_error",
2N/A new Object[] {new Character(c)});
2N/A }
2N/A
2N/A buf.append(c);
2N/A
2N/A }
2N/A }
2N/A
2N/A return buf.toString();
2N/A }
2N/A
2N/A // Unescape attribute string, using charCode for reserved characters.
2N/A
2N/A static String unescapeAttributeString(String string,
2N/A String charCode)
2N/A throws ServiceLocationException {
2N/A
2N/A // Process escapes.
2N/A
2N/A int i, n = string.length();
2N/A StringBuffer buf = new StringBuffer(n);
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A char c = string.charAt(i);
2N/A
2N/A // Check for invalids.
2N/A
2N/A int idx = -1;
2N/A
2N/A if ((idx = UNESCAPABLE_CHARS.indexOf(c)) != -1) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_escape_error",
2N/A new Object[] {string});
2N/A }
2N/A
2N/A // Check for escapes.
2N/A
2N/A if (c != '&') {
2N/A
2N/A buf.append(c);
2N/A
2N/A } else {
2N/A
2N/A // Check to be sure we've got enough characters left. We need
2N/A // at least 3.
2N/A
2N/A if ((i + 1) >= n) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_escape_error",
2N/A new Object[] {string});
2N/A }
2N/A
2N/A c = string.charAt(++i);
2N/A
2N/A if (c != '#') {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_escape_error",
2N/A new Object[] {string});
2N/A }
2N/A
2N/A // Iterate through numbers, collecting.
2N/A
2N/A StringBuffer num = new StringBuffer(n);
2N/A
2N/A for (i++; i < n; i++) {
2N/A
2N/A c = string.charAt(i);
2N/A
2N/A if (!Character.isDigit(c)) {
2N/A break;
2N/A }
2N/A
2N/A num.append(c);
2N/A }
2N/A
2N/A // If the buffer is empty, then throw exception
2N/A
2N/A if (num.length() <= 0) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_escape_error",
2N/A new Object[] {string});
2N/A }
2N/A
2N/A // If the last one isn't ";", we've got a problem.
2N/A
2N/A if (c != ';') {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_escape_error",
2N/A new Object[] {string});
2N/A }
2N/A
2N/A // OK, now convert to a character and add to buffer.
2N/A
2N/A try {
2N/A buf.append(IANACharCode.unescapeChar(num.toString(),
2N/A charCode));
2N/A
2N/A } catch (NumberFormatException ex) {
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_escape_error",
2N/A new Object[] {string});
2N/A }
2N/A }
2N/A }
2N/A
2N/A return buf.toString();
2N/A }
2N/A}