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 2001,2003 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A *
2N/A */
2N/A
2N/A// ServiceLocationAttribute.java : Class for attributes in SLP.
2N/A// Author: James Kempf, Erik Guttman
2N/A//
2N/A
2N/Apackage com.sun.slp;
2N/A
2N/Aimport java.util.*;
2N/Aimport java.io.*;
2N/A
2N/A/**
2N/A * The ServiceLocationAttribute class models SLP attributes.
2N/A *
2N/A * @author James Kempf, Erik Guttman
2N/A */
2N/A
2N/Apublic class ServiceLocationAttribute extends Object
2N/A implements Serializable {
2N/A
2N/A // Characters to escape.
2N/A
2N/A final static String RESERVED = "(),\\!<=>~";
2N/A final static String ESCAPED = RESERVED + "*";
2N/A final static char ESCAPE = '\\';
2N/A
2N/A final static char CTL_LOWER = (char)0x00;
2N/A final static char CTL_UPPER = (char)0x1F;
2N/A final static char DEL = (char)0x7F;
2N/A
2N/A // Whitespace chars.
2N/A
2N/A static final String WHITESPACE = " \n\t\r";
2N/A static final char SPACE = ' ';
2N/A
2N/A // For character escaping.
2N/A
2N/A static final char COMMA = ',';
2N/A static final char PERCENT = '%';
2N/A
2N/A // Bad tag characters.
2N/A
2N/A final private static String BAD_TAG_CHARS = "*\n\t\r";
2N/A
2N/A // For identifying booleans.
2N/A
2N/A final static String TRUE = "true";
2N/A final static String FALSE = "false";
2N/A
2N/A //
2N/A // Package accessable fields.
2N/A //
2N/A
2N/A Vector values = null;
2N/A String id = null;
2N/A
2N/A // For V1 compatibility subclass.
2N/A
2N/A ServiceLocationAttribute() {}
2N/A
2N/A /**
2N/A * Construct a service location attribute.
2N/A *
2N/A * @param id The attribute name
2N/A * @param values_in Vector of one or more attribute values. Vector
2N/A * contents must be uniform in type and one of
2N/A * Integer, String, Boolean, or byte[]. If the attribute
2N/A * is a keyword attribute, then values_in should be null.
2N/A * @exception IllegalArgumentException Thrown if the
2N/A * vector contents is not of the right type or
2N/A * an argument is null or syntactically incorrect.
2N/A */
2N/A
2N/A public ServiceLocationAttribute(String id_in, Vector values_in)
2N/A throws IllegalArgumentException {
2N/A
2N/A Assert.nonNullParameter(id_in, "id");
2N/A
2N/A id = id_in;
2N/A if (values_in != null &&
2N/A values_in.size() > 0) { // null, empty indicate keyword attribute.
2N/A
2N/A values = (Vector)values_in.clone();
2N/A
2N/A verifyValueTypes(values, false);
2N/A
2N/A }
2N/A }
2N/A
2N/A /**
2N/A * Construct a service location attribute from a parenthesized expression.
2N/A * The syntax is:
2N/A *
2N/A * exp = "(" id "=" value-list ")" | keyword
2N/A * value-list = value | value "," value-list
2N/A *
2N/A *
2N/A * @param exp The expression
2N/A * @param dontTypeCheck True if multivalued booleans and vectors
2N/A * of varying types are allowed.
2N/A * @exception ServiceLocationException If there are any syntax errors.
2N/A */
2N/A
2N/A ServiceLocationAttribute(String exp, boolean allowMultiValuedBooleans)
2N/A throws ServiceLocationException {
2N/A
2N/A if (exp == null || exp.length() <= 0) {
2N/A new ServiceLocationException(ServiceLocationException.PARSE_ERROR,
2N/A "null_string_parameter",
2N/A new Object[] {exp});
2N/A
2N/A }
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(), true);
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);
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, true);
2N/A
2N/A }
2N/A }
2N/A
2N/A static Object evaluate(String value)
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.
2N/A
2N/A if (val.startsWith(Opaque.OPAQUE_HEADER)) {
2N/A o = Opaque.unescapeByteArray(val);
2N/A
2N/A } else {
2N/A o = unescapeAttributeString(val, false);
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A return o;
2N/A
2N/A }
2N/A
2N/A //
2N/A // Property accessors.
2N/A //
2N/A
2N/A /**
2N/A * @return A vector of attribute values, or null if the attribute is
2N/A * a keyword attribute. If the attribute is single-valued, then
2N/A * the vector contains only one object.
2N/A *
2N/A */
2N/A
2N/A public Vector getValues() {
2N/A
2N/A if (values == null) {
2N/A return null; // keyword case.
2N/A }
2N/A
2N/A Vector ret = (Vector)values.clone();
2N/A
2N/A // Need to process Opaques.
2N/A
2N/A int i, n = ret.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A Object o = ret.elementAt(i);
2N/A
2N/A if (o instanceof Opaque) {
2N/A o = ((Opaque)o).bytes;
2N/A
2N/A }
2N/A
2N/A ret.setElementAt(o, i);
2N/A }
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A /**
2N/A * @return The attribute name.
2N/A */
2N/A
2N/A public String getId() {
2N/A
2N/A return id;
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Return an escaped version of the id parameter , suitable for inclusion
2N/A * in a query.
2N/A *
2N/A * @param str The string to escape as an id.
2N/A * @return The string with any reserved characters escaped.
2N/A * @exception IllegalArgumentException Thrown if the
2N/A * string contains bad tag characters.
2N/A */
2N/A
2N/A static public String escapeId(String str)
2N/A throws IllegalArgumentException {
2N/A String ret = null;
2N/A
2N/A try {
2N/A ret = escapeAttributeString(str, true);
2N/A
2N/A } catch (ServiceLocationException ex) {
2N/A throw new IllegalArgumentException(ex.getMessage());
2N/A
2N/A }
2N/A
2N/A return ret;
2N/A }
2N/A
2N/A /**
2N/A * Return an escaped version of the value parameter, suitable for inclusion
2N/A * in a query. Opaques are stringified.
2N/A *
2N/A * @param val The value to escape.
2N/A * @return The stringified value.
2N/A * @exception IllegalArgumentException Thrown if the object is not
2N/A * one of byte[], Integer, Boolean, or String.
2N/A */
2N/A
2N/A static public String escapeValue(Object val)
2N/A throws IllegalArgumentException {
2N/A
2N/A // Check type first.
2N/A
2N/A typeCheckValue(val);
2N/A
2N/A // Make Opaque out of byte[].
2N/A
2N/A if (val instanceof byte[]) {
2N/A val = new Opaque((byte[])val);
2N/A
2N/A }
2N/A
2N/A return escapeValueInternal(val);
2N/A
2N/A }
2N/A
2N/A // Check type to make sure it's OK.
2N/A
2N/A static private void typeCheckValue(Object obj) {
2N/A SLPConfig conf = SLPConfig.getSLPConfig();
2N/A
2N/A Assert.nonNullParameter(obj, "attribute value vector element");
2N/A
2N/A if (obj.equals("")) {
2N/A throw
2N/A new IllegalArgumentException(
2N/A conf.formatMessage("empty_string_value",
2N/A new Object[0]));
2N/A }
2N/A
2N/A if (!(obj instanceof Integer) && !(obj instanceof Boolean) &&
2N/A !(obj instanceof String) && !(obj instanceof byte[])) {
2N/A throw
2N/A new IllegalArgumentException(
2N/A conf.formatMessage("value_type_error",
2N/A new Object[0]));
2N/A }
2N/A
2N/A }
2N/A
2N/A // We know the value's type is OK, so just escape it.
2N/A
2N/A private static String escapeValueInternal(Object val) {
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, false);
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 {
2N/A s = val.toString();
2N/A
2N/A }
2N/A
2N/A return s;
2N/A }
2N/A
2N/A //
2N/A // Methods for dealing with the type of attribute values.
2N/A //
2N/A
2N/A // Verify the types of incoming attributes.
2N/A
2N/A protected void
2N/A verifyValueTypes(Vector values_in, boolean dontTypeCheck) {
2N/A
2N/A SLPConfig conf = SLPConfig.getSLPConfig();
2N/A
2N/A // Make sure the types of objects passed in are acceptable
2N/A // and that all objects in the vector have the same type.
2N/A
2N/A int i, n = values_in.size();
2N/A Class cls = null;
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A Object obj = values_in.elementAt(i);
2N/A
2N/A typeCheckValue(obj);
2N/A
2N/A if (i == 0) {
2N/A cls = obj.getClass();
2N/A
2N/A } else if (!cls.equals(obj.getClass()) && !dontTypeCheck) {
2N/A throw
2N/A new IllegalArgumentException(
2N/A conf.formatMessage("type_mismatch_error",
2N/A new Object[0]));
2N/A }
2N/A
2N/A // If it's a boolean and there's more than one, signal error
2N/A // unless multivalued booleans are allowed.
2N/A
2N/A if (!dontTypeCheck && i != 0 && obj instanceof Boolean) {
2N/A throw
2N/A new IllegalArgumentException(
2N/A conf.formatMessage("multivalued_boolean",
2N/A new Object[0]));
2N/A
2N/A }
2N/A
2N/A // If it's a byte array, create a Opaque object.
2N/A
2N/A if (obj instanceof byte[]) {
2N/A values_in.setElementAt(new Opaque((byte[])obj), i);
2N/A
2N/A } else if (obj instanceof String) {
2N/A String val = (String)obj;
2N/A
2N/A // If it's a string and looks like "1" or "true", then
2N/A // append a space onto the end.
2N/A
2N/A try {
2N/A
2N/A Object obj2 = evaluate(val);
2N/A
2N/A if (!(obj2 instanceof String)) {
2N/A values_in.setElementAt((String)val + " ", i);
2N/A
2N/A }
2N/A
2N/A } catch (ServiceLocationException ex) {
2N/A
2N/A // Ignore for now.
2N/A
2N/A }
2N/A }
2N/A }
2N/A
2N/A }
2N/A
2N/A //
2N/A // Methods for externalizing attributes.
2N/A //
2N/A
2N/A /**
2N/A * Externalize the attribute into a string that can be written
2N/A * to a byte stream. Includes escaping any characters that
2N/A * need to be escaped.
2N/A *
2N/A * @return String with attribute's external representation.
2N/A * @exception ServiceLocationException Thrown if the
2N/A * string contains unencodable characters.
2N/A */
2N/A
2N/A String externalize()
2N/A throws ServiceLocationException {
2N/A
2N/A if (values == null) { // keyword attribute...
2N/A return escapeAttributeString(id, true);
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);
2N/A
2N/A v.addElement(s);
2N/A }
2N/A
2N/A StringBuffer buf =
2N/A new StringBuffer("(" +
2N/A escapeAttributeString(id, true) +
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 //
2N/A // Escaping and unescaping strings.
2N/A //
2N/A
2N/A /**
2N/A * Escape any escapable characters to a 2 character escape
2N/A * in the attribute string.
2N/A *
2N/A * @param string The String.
2N/A * @param badTag Check for bad tag characters if true.
2N/A * @return The escaped string.
2N/A * @exception ServiceLocationException Thrown if the string
2N/A * contains a character that can't be encoded.
2N/A */
2N/A
2N/A static String escapeAttributeString(String string,
2N/A boolean badTag)
2N/A throws ServiceLocationException {
2N/A
2N/A StringBuffer buf = new StringBuffer();
2N/A int i, n = string.length();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A char c = string.charAt(i);
2N/A
2N/A // Check for bad tag characters first.
2N/A
2N/A if (badTag && BAD_TAG_CHARS.indexOf(c) != -1) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "bad_id_char",
2N/A new Object[] {Integer.toHexString(c)});
2N/A }
2N/A
2N/A // Escape if the character is reserved.
2N/A
2N/A if (canEscape(c)) {
2N/A buf.append(ESCAPE);
2N/A
2N/A String str = escapeChar(c);
2N/A
2N/A // Pad with zero if less than 2 characters.
2N/A
2N/A if (str.length() <= 1) {
2N/A str = "0" + str;
2N/A
2N/A }
2N/A
2N/A buf.append(str);
2N/A
2N/A } else {
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
2N/A
2N/A /**
2N/A * Convert any 2 character escapes to the corresponding characters.
2N/A *
2N/A * @param string The string to be processed.
2N/A * @param badTag Check for bad tag characters if true.
2N/A * @return The processed string.
2N/A * @exception ServiceLocationException Thrown if an escape
2N/A * is improperly formatted.
2N/A */
2N/A
2N/A static String unescapeAttributeString(String string,
2N/A boolean badTag)
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 escaped characters.
2N/A
2N/A if (c == ESCAPE) {
2N/A
2N/A // Get the next two characters.
2N/A
2N/A if (i >= n - 2) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "nonterminating_escape",
2N/A new Object[] {string});
2N/A }
2N/A
2N/A i++;
2N/A c = unescapeChar(string.substring(i, i+2));
2N/A i++;
2N/A
2N/A // Check whether it's reserved.
2N/A
2N/A if (!canEscape(c)) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "char_not_reserved_attr",
2N/A new Object[] {new Character(c), string});
2N/A }
2N/A
2N/A } else {
2N/A
2N/A // Check whether the character is reserved.
2N/A
2N/A if (isReserved(c)) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "reserved_not_escaped",
2N/A new Object[] {new Character(c)});
2N/A }
2N/A
2N/A }
2N/A
2N/A // If we need to check for a bad tag character, do so now.
2N/A
2N/A if (badTag && BAD_TAG_CHARS.indexOf(c) != -1) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "bad_id_char",
2N/A new Object[] {Integer.toHexString(c)});
2N/A
2N/A }
2N/A
2N/A buf.append(c);
2N/A
2N/A }
2N/A
2N/A return buf.toString();
2N/A }
2N/A
2N/A // Return true if the character c can be escaped.
2N/A
2N/A private static boolean canEscape(char c) {
2N/A
2N/A return ((ESCAPED.indexOf(c) != -1) ||
2N/A ((c >= CTL_LOWER && c <= CTL_UPPER) || c == DEL));
2N/A
2N/A }
2N/A
2N/A // Return true if the character c is reserved.
2N/A
2N/A private static boolean isReserved(char c) {
2N/A
2N/A return ((RESERVED.indexOf(c) != -1) ||
2N/A ((c >= CTL_LOWER && c <= CTL_UPPER) || c == DEL));
2N/A
2N/A }
2N/A
2N/A /**
2N/A * Return a string of integers giving the character's encoding in
2N/A * the character set passed in as encoding.
2N/A *
2N/A * @param c The character to escape.
2N/A * @return The character as a string of integers for the encoding.
2N/A */
2N/A
2N/A static String escapeChar(char c) {
2N/A
2N/A byte[] b = null;
2N/A
2N/A try {
2N/A
2N/A b = ("" + c).getBytes(Defaults.UTF8);
2N/A
2N/A } catch (UnsupportedEncodingException ex) {
2N/A
2N/A Assert.slpassert(false, "no_utf8", new Object[0]);
2N/A
2N/A }
2N/A
2N/A int code = 0;
2N/A
2N/A // Assemble the character code.
2N/A
2N/A if (b.length > 3) {
2N/A Assert.slpassert(false,
2N/A "illegal_utf8",
2N/A new Object[] {new Character(c)});
2N/A
2N/A }
2N/A
2N/A code = (int)(b[0] & 0xFF);
2N/A
2N/A if (b.length > 1) {
2N/A code = (int)(code | ((b[1] & 0xFF) << 8));
2N/A }
2N/A
2N/A if (b.length > 2) {
2N/A code = (int)(code | ((b[2] & 0xFF) << 16));
2N/A }
2N/A
2N/A String str = Integer.toHexString(code);
2N/A
2N/A return str;
2N/A }
2N/A
2N/A /**
2N/A * Unescape the character encoded as the string.
2N/A *
2N/A * @param ch The character as a string of hex digits.
2N/A * @return The character.
2N/A * @exception ServiceLocationException If the characters can't be
2N/A * converted into a hex string.
2N/A */
2N/A
2N/A static char unescapeChar(String ch)
2N/A throws ServiceLocationException {
2N/A
2N/A int code = 0;
2N/A
2N/A try {
2N/A code = Integer.parseInt(ch, 16);
2N/A
2N/A } catch (NumberFormatException ex) {
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "not_a_character",
2N/A new Object[] {ch});
2N/A }
2N/A
2N/A // Convert to bytes.
2N/A
2N/A String str = null;
2N/A byte b0 = 0, b1 = 0, b2 = 0, b3 = 0;
2N/A byte b[] = null;
2N/A
2N/A b0 = (byte) (code & 0xFF);
2N/A b1 = (byte) ((code >> 8) & 0xFF);
2N/A b2 = (byte) ((code >> 16) & 0xFF);
2N/A b3 = (byte) ((code >> 24) & 0xFF);
2N/A
2N/A // We allow illegal UTF8 encoding so we can decode byte arrays.
2N/A
2N/A if (b3 != 0) {
2N/A b = new byte[3];
2N/A b[3] = b3;
2N/A b[2] = b2;
2N/A b[1] = b1;
2N/A b[0] = b0;
2N/A } else if (b2 != 0) {
2N/A b = new byte[3];
2N/A b[2] = b2;
2N/A b[1] = b1;
2N/A b[0] = b0;
2N/A } else if (b1 != 0) {
2N/A b = new byte[2];
2N/A b[1] = b1;
2N/A b[0] = b0;
2N/A } else {
2N/A b = new byte[1];
2N/A b[0] = b0;
2N/A }
2N/A
2N/A // Make a string out of it.
2N/A
2N/A try {
2N/A str = new String(b, Defaults.UTF8);
2N/A
2N/A } catch (UnsupportedEncodingException ex) {
2N/A
2N/A Assert.slpassert(false, "no_utf8", new Object[0]);
2N/A
2N/A }
2N/A
2N/A int len = str.length();
2N/A
2N/A if (str.length() > 1) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "more_than_one",
2N/A new Object[] {ch});
2N/A
2N/A }
2N/A
2N/A return (len == 1 ? str.charAt(0):(char)0);
2N/A }
2N/A
2N/A /**
2N/A * Merge the values in newAttr into the attribute in the hashtable
2N/A * if a duplicate attribute, signal error if a type mismatch.
2N/A * Both the return vector and hashtable are updated, but the
2N/A * newAttr parameter is left unchanged.
2N/A *
2N/A * @param attr The ServiceLocationAttribute to check.
2N/A * @param attrHash A Hashtable containing the attribute tags as
2N/A * keys and the attributes as values.
2N/A * @param returns A Vector in which to put the attribute when done.
2N/A * @param dontTypeCheck If this flag is true, the value vector
2N/A * may have two booleans, may
2N/A * contain differently typed objects, or the
2N/A * function may merge a keyword and nonkeyword
2N/A * attribute.
2N/A * @exception ServiceLocationException Thrown if a type mismatch
2N/A * occurs.
2N/A */
2N/A
2N/A static void
2N/A mergeDuplicateAttributes(ServiceLocationAttribute newAttr,
2N/A Hashtable attrTable,
2N/A Vector returns,
2N/A boolean dontTypeCheck)
2N/A throws ServiceLocationException {
2N/A
2N/A // Look up the attribute
2N/A
2N/A String tag = newAttr.getId().toLowerCase();
2N/A ServiceLocationAttribute attr =
2N/A (ServiceLocationAttribute)attrTable.get(tag);
2N/A
2N/A // Don't try this trick with ServerAttributes!
2N/A
2N/A Assert.slpassert((!(attr instanceof ServerAttribute) &&
2N/A !(newAttr instanceof ServerAttribute)),
2N/A "merge_servattr",
2N/A new Object[0]);
2N/A
2N/A // If the attribute isn't in the hashtable, then add to
2N/A // vector and hashtable.
2N/A
2N/A if (attr == null) {
2N/A attrTable.put(tag, newAttr);
2N/A returns.addElement(newAttr);
2N/A return;
2N/A
2N/A }
2N/A
2N/A
2N/A Vector attrNewVals = newAttr.values;
2N/A Vector attrVals = attr.values;
2N/A
2N/A // If both keywords, nothing further to do.
2N/A
2N/A if (attrVals == null && attrNewVals == null) {
2N/A return;
2N/A
2N/A }
2N/A
2N/A // If we are not typechecking and one is keyword while the other
2N/A // is not, then simply merge in the nonkeyword. Otherwise,
2N/A // throw a type check exception.
2N/A
2N/A if ((attrVals == null && attrNewVals != null) ||
2N/A (attrNewVals == null && attrVals != null)) {
2N/A
2N/A if (dontTypeCheck) {
2N/A Vector vals = (attrNewVals != null ? attrNewVals:attrVals);
2N/A attr.values = vals;
2N/A newAttr.values = vals;
2N/A
2N/A } else {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "attribute_type_mismatch",
2N/A new Object[] {newAttr.getId()});
2N/A
2N/A }
2N/A
2N/A } else {
2N/A
2N/A // Merge the two vectors. We type check against the attrVals
2N/A // vector, if we are type checking.
2N/A
2N/A int i, n = attrNewVals.size();
2N/A Object o = attrVals.elementAt(0);
2N/A Class c = o.getClass();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A Object no = attrNewVals.elementAt(i);
2N/A
2N/A // Check for type mismatch, throw exception if
2N/A // we are type checking.
2N/A
2N/A if ((c != no.getClass()) && !dontTypeCheck) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "attribute_type_mismatch",
2N/A new Object[] {newAttr.getId()});
2N/A
2N/A }
2N/A
2N/A // If we are typechecking, and we get two opposite
2N/A // booleans, we need to throw an exception.
2N/A
2N/A if (no instanceof Boolean && !no.equals(o) && !dontTypeCheck) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "boolean_incompat",
2N/A new Object[] {newAttr.getId()});
2N/A
2N/A }
2N/A
2N/A // Add the value if it isn't already there.
2N/A
2N/A if (!attrVals.contains(no)) {
2N/A attrVals.addElement(no);
2N/A
2N/A }
2N/A }
2N/A
2N/A // Set the new attribute's values so they are the same as the old.
2N/A
2N/A newAttr.values = attrVals;
2N/A
2N/A }
2N/A }
2N/A
2N/A //
2N/A // Object overrides.
2N/A //
2N/A
2N/A /**
2N/A * Return true if the object equals this attribute.
2N/A */
2N/A
2N/A public boolean equals(Object o) {
2N/A
2N/A if (!(o instanceof ServiceLocationAttribute)) {
2N/A return false;
2N/A
2N/A }
2N/A
2N/A if (o == this) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A ServiceLocationAttribute sla = (ServiceLocationAttribute)o;
2N/A
2N/A // check equality of contents, deferring check of all values
2N/A
2N/A Vector vSLA = sla.values;
2N/A
2N/A if (!sla.getId().equalsIgnoreCase(id)) {
2N/A return false;
2N/A
2N/A }
2N/A
2N/A if (values == null && vSLA == null) {
2N/A return true;
2N/A
2N/A }
2N/A
2N/A if ((values == null && vSLA != null) ||
2N/A (values != null && vSLA == null)) {
2N/A return false;
2N/A
2N/A }
2N/A
2N/A if (values.size() != vSLA.size()) {
2N/A return false;
2N/A
2N/A }
2N/A
2N/A // Check contents.
2N/A
2N/A Object oSLA = vSLA.elementAt(0);
2N/A o = values.elementAt(0);
2N/A
2N/A if (o.getClass() != oSLA.getClass()) {
2N/A return false;
2N/A
2N/A }
2N/A
2N/A int i, n = vSLA.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A oSLA = vSLA.elementAt(i);
2N/A
2N/A if (!values.contains(oSLA)) {
2N/A return false;
2N/A
2N/A }
2N/A }
2N/A
2N/A return true;
2N/A }
2N/A
2N/A /**
2N/A * Return a human readable string for the attribute.
2N/A */
2N/A
2N/A public String toString() {
2N/A
2N/A StringBuffer s = new StringBuffer("(");
2N/A
2N/A s.append(id);
2N/A
2N/A if (values != null) {
2N/A s.append("=");
2N/A
2N/A int i, n = values.size();
2N/A
2N/A for (i = 0; i < n; i++) {
2N/A Object o = values.elementAt(i);
2N/A
2N/A // Identify type.
2N/A
2N/A if (i == 0) {
2N/A s.append(o.getClass().getName());
2N/A s.append(":");
2N/A
2N/A } else {
2N/A s.append(",");
2N/A
2N/A }
2N/A
2N/A // Stringify object.
2N/A
2N/A s.append(o.toString());
2N/A
2N/A }
2N/A }
2N/A
2N/A s.append(")");
2N/A
2N/A return s.toString();
2N/A }
2N/A
2N/A // Overrides Object.hashCode().
2N/A
2N/A public int hashCode() {
2N/A return id.toLowerCase().hashCode();
2N/A
2N/A }
2N/A
2N/A}