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// SLPV1SSrvMsg.java: SLPv1 server side service rqst/reply.
2N/A// Author: James Kempf
2N/A// Created On: Thu Sep 10 15:33:58 1998
2N/A// Last Modified By: James Kempf
2N/A// Last Modified On: Fri Nov 6 14:03:00 1998
2N/A// Update Count: 41
2N/A//
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/**
2N/A * The SLPV1SSrvMsg class models the SLP server side service request message.
2N/A *
2N/A * @author James Kempf
2N/A */
2N/A
2N/Aclass SLPV1SSrvMsg extends SSrvMsg {
2N/A
2N/A // For eating whitespace.
2N/A
2N/A final static char SPACE = ' ';
2N/A
2N/A // Comma for list parsing.
2N/A
2N/A final static char COMMA = ',';
2N/A
2N/A // Logical operators.
2N/A
2N/A final static char OR_OP = '|';
2N/A final static char AND_OP = '&';
2N/A
2N/A // Logical operator corner case needs this.
2N/A
2N/A final static char HASH = '#';
2N/A
2N/A // Comparison/Assignment operators.
2N/A
2N/A final static char EQUAL_OP = '=';
2N/A final static char NOT_OP = '!';
2N/A final static char LESS_OP = '<';
2N/A final static char GREATER_OP = '>';
2N/A final static char GEQUAL_OP = 'g';
2N/A final static char LEQUAL_OP = 'l';
2N/A
2N/A // Parens.
2N/A
2N/A final static char OPEN_PAREN = '(';
2N/A final static char CLOSE_PAREN = ')';
2N/A
2N/A // LDAP present operator
2N/A
2N/A final static char PRESENT = '*';
2N/A
2N/A // Wildcard operator.
2N/A
2N/A final static String WILDCARD = "*";
2N/A
2N/A // Character code for parsing.
2N/A
2N/A String charCode = IANACharCode.UTF8;
2N/A
2N/A // For creating a null reply.
2N/A
2N/A protected SLPV1SSrvMsg() {}
2N/A
2N/A // Construct a SLPV1SSrvMsg from the input stream.
2N/A
2N/A SLPV1SSrvMsg(SrvLocHeader hdr, DataInputStream dis)
2N/A throws ServiceLocationException, IOException {
2N/A super(hdr, dis);
2N/A
2N/A }
2N/A
2N/A // Construct an empty SLPV1SSrvMsg, for monolingual off.
2N/A
2N/A static SrvLocMsg makeEmptyReply(SLPHeaderV1 hdr)
2N/A throws ServiceLocationException {
2N/A
2N/A SLPV1SSrvMsg msg = new SLPV1SSrvMsg();
2N/A msg.hdr = hdr;
2N/A
2N/A msg.makeReply(new Hashtable(), null);
2N/A
2N/A return msg;
2N/A
2N/A }
2N/A
2N/A // Initialize the message from the input stream.
2N/A
2N/A void initialize(DataInputStream dis)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A SLPHeaderV1 hdr = (SLPHeaderV1)getHeader();
2N/A StringBuffer buf = new StringBuffer();
2N/A
2N/A // First get the previous responder.
2N/A
2N/A hdr.parsePreviousRespondersIn(dis);
2N/A
2N/A // Now get the raw query.
2N/A
2N/A hdr.getString(buf, dis);
2N/A
2N/A String rq = buf.toString();
2N/A
2N/A // Parse the raw query to pull out the service type, scope,
2N/A // and query.
2N/A
2N/A StringTokenizer st = new StringTokenizer(rq, "/", true);
2N/A
2N/A try {
2N/A
2N/A String type =
2N/A Defaults.SERVICE_PREFIX + ":" +
2N/A st.nextToken().trim().toLowerCase() + ":";
2N/A
2N/A serviceType =
2N/A hdr.checkServiceType(type);
2N/A
2N/A st.nextToken(); // get rid of slash.
2N/A
2N/A // Get the scope.
2N/A
2N/A String scope = st.nextToken().trim().toLowerCase();
2N/A
2N/A // Special case if scope is empty (meaning the next
2N/A // token will be a slash).
2N/A
2N/A if (scope.equals("/")) {
2N/A scope = "";
2N/A
2N/A } else {
2N/A
2N/A st.nextToken(); // get rid of slash.
2N/A
2N/A if (scope.length() > 0) {
2N/A
2N/A // Validate the scope name.
2N/A
2N/A hdr.validateScope(scope);
2N/A }
2N/A }
2N/A
2N/A // Set up scopes vector.
2N/A
2N/A hdr.scopes = new Vector();
2N/A
2N/A // Substitute default scope here.
2N/A
2N/A if (scope.length() <= 0) {
2N/A scope = Defaults.DEFAULT_SCOPE;
2N/A
2N/A }
2N/A
2N/A hdr.scopes.addElement(scope.toLowerCase().trim());
2N/A
2N/A // Parsing the query is complicated by opaques having slashes.
2N/A
2N/A String q = "";
2N/A
2N/A while (st.hasMoreTokens()) {
2N/A q = q + st.nextToken();
2N/A
2N/A }
2N/A
2N/A // Drop off the final backslash, error if none.
2N/A
2N/A if (!q.endsWith("/")) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {rq});
2N/A }
2N/A
2N/A query = q.substring(0, q.length()-1);
2N/A
2N/A // Save header char code for parsing.
2N/A
2N/A charCode = hdr.charCode;
2N/A
2N/A // Convert the query into a V2 query.
2N/A
2N/A convertQuery();
2N/A
2N/A // If the query is for "service:directory-agent", then we
2N/A // mark it as having been multicast, because that is the
2N/A // only kind of multicast that we accept for SLPv1. Anybody
2N/A // who unicasts this to us will time out.
2N/A
2N/A if (serviceType.equals(Defaults.DA_SERVICE_TYPE.toString())) {
2N/A hdr.mcast = true;
2N/A
2N/A }
2N/A
2N/A // Construct description.
2N/A
2N/A hdr.constructDescription("SrvRqst",
2N/A " service type=``" +
2N/A serviceType + "''\n" +
2N/A " query=``" +
2N/A query + "''");
2N/A
2N/A } catch (NoSuchElementException ex) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {rq});
2N/A }
2N/A }
2N/A
2N/A // Make a reply message.
2N/A
2N/A SrvLocMsg makeReply(Hashtable urltable,
2N/A Hashtable URLSignatures)
2N/A throws ServiceLocationException {
2N/A
2N/A SLPHeaderV1 hdr =
2N/A ((SLPHeaderV1)getHeader()).makeReplyHeader();
2N/A
2N/A ByteArrayOutputStream baos = new ByteArrayOutputStream();
2N/A
2N/A // Edit out abstract types and nonService: URLs.
2N/A
2N/A Enumeration en = urltable.keys();
2N/A Vector urls = new Vector();
2N/A
2N/A while (en.hasMoreElements()) {
2N/A ServiceURL surl = (ServiceURL)en.nextElement();
2N/A
2N/A // Reject if abstract type or nonservice: URL.
2N/A
2N/A ServiceType type = surl.getServiceType();
2N/A
2N/A if (!type.isAbstractType() && type.isServiceURL()) {
2N/A urls.addElement(surl);
2N/A
2N/A }
2N/A }
2N/A
2N/A hdr.iNumReplies = urls.size();
2N/A // keep this info so SAs can drop 0 replies
2N/A
2N/A int n = urls.size();
2N/A
2N/A // Write out the size of the list.
2N/A
2N/A hdr.putInt(n, baos);
2N/A
2N/A en = urls.elements();
2N/A
2N/A // Write out the size of the list.
2N/A
2N/A while (en.hasMoreElements()) {
2N/A ServiceURL surl = (ServiceURL)en.nextElement();
2N/A
2N/A hdr.parseServiceURLOut(surl, true, baos);
2N/A
2N/A }
2N/A
2N/A // We ignore the signatures because we only do V1 compatibility
2N/A // for nonprotected scopes.
2N/A
2N/A hdr.payload = baos.toByteArray();
2N/A
2N/A hdr.constructDescription("SrvRply",
2N/A " service URLs=``" + urls + "''\n");
2N/A
2N/A return hdr;
2N/A
2N/A }
2N/A
2N/A // Convert the query to a V2 query.
2N/A
2N/A void convertQuery()
2N/A throws ServiceLocationException {
2N/A
2N/A // Check for empty query.
2N/A
2N/A query = query.trim();
2N/A
2N/A if (query.length() <= 0) {
2N/A return;
2N/A
2N/A }
2N/A
2N/A // Check for query join.
2N/A
2N/A if (!(query.startsWith("(") && query.endsWith(")"))) {
2N/A
2N/A // Rewrite to a standard query.
2N/A
2N/A query = rewriteQueryJoin(query);
2N/A
2N/A }
2N/A
2N/A // Now rewrite the query into v2 format.
2N/A
2N/A query = rewriteQuery(query);
2N/A }
2N/A
2N/A
2N/A // Rewrite a query join as a conjunction.
2N/A
2N/A private String rewriteQueryJoin(String query)
2N/A throws ServiceLocationException {
2N/A
2N/A // Turn infix expression into prefix.
2N/A
2N/A StringBuffer sbuf = new StringBuffer();
2N/A StringTokenizer tk = new StringTokenizer(query, ",", true);
2N/A boolean lastTokComma = true;
2N/A int numEx = 0;
2N/A
2N/A while (tk.hasMoreElements()) {
2N/A String exp = tk.nextToken().trim();
2N/A
2N/A if (exp.equals(",")) {
2N/A if (lastTokComma) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A
2N/A } else {
2N/A lastTokComma = true;
2N/A }
2N/A
2N/A } else {
2N/A lastTokComma = false;
2N/A
2N/A if (exp.length() <= 0) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A
2N/A }
2N/A
2N/A // Put in parens
2N/A
2N/A sbuf.append("(");
2N/A sbuf.append(exp);
2N/A sbuf.append(")");
2N/A
2N/A numEx++;
2N/A }
2N/A }
2N/A
2N/A if (lastTokComma || numEx == 0) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A
2N/A }
2N/A
2N/A if (numEx > 1) {
2N/A sbuf.insert(0, "(&");
2N/A sbuf.append(")");
2N/A
2N/A }
2N/A
2N/A return sbuf.toString();
2N/A }
2N/A
2N/A // Rewrite a v1 query into v2 format. This includes character escaping.
2N/A
2N/A private String rewriteQuery(String whereList)
2N/A throws ServiceLocationException {
2N/A
2N/A // Parse a logical expression.
2N/A
2N/A StreamTokenizer tk =
2N/A new StreamTokenizer(new StringReader(whereList));
2N/A
2N/A tk.resetSyntax(); // make all chars ordinary...
2N/A tk.whitespaceChars('\000','\037');
2N/A tk.ordinaryChar(SPACE); // but beware of embedded whites...
2N/A tk.wordChars('!', '%');
2N/A tk.ordinaryChar(AND_OP);
2N/A tk.wordChars('\'', '\'');
2N/A tk.ordinaryChar(OPEN_PAREN);
2N/A tk.ordinaryChar(CLOSE_PAREN);
2N/A tk.wordChars('*', '{');
2N/A tk.ordinaryChar(OR_OP);
2N/A tk.wordChars('}', '~');
2N/A
2N/A // Initialize parse tables in terminal.
2N/A
2N/A tk.ordinaryChar(EQUAL_OP);
2N/A tk.ordinaryChar(NOT_OP);
2N/A tk.ordinaryChar(LESS_OP);
2N/A tk.ordinaryChar(GREATER_OP);
2N/A
2N/A StringBuffer buf = new StringBuffer();
2N/A
2N/A
2N/A // Parse through the expression.
2N/A
2N/A try {
2N/A parseInternal(tk, buf, true);
2N/A
2N/A } catch (IOException ex) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A
2N/A }
2N/A
2N/A return buf.toString();
2N/A }
2N/A
2N/A // Do the actual parsing, using the passed-in stream tokenizer.
2N/A
2N/A private void
2N/A parseInternal(StreamTokenizer tk, StringBuffer buf, boolean start)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A int tok = 0;
2N/A boolean ret = true;
2N/A
2N/A do {
2N/A tok = eatWhite(tk);
2N/A
2N/A // We should be at the beginning a parenthesized
2N/A // where list.
2N/A
2N/A if (tok == OPEN_PAREN) {
2N/A
2N/A // Get the next token. Eat whitespace in the process.
2N/A
2N/A tok = eatWhite(tk);
2N/A
2N/A // If it's a logOp, then process as a logical expression.
2N/A // This handles the following nasty case:
2N/A //
2N/A // (&#44;&#45==the rest of it)
2N/A
2N/A int logOp = tok;
2N/A
2N/A if (logOp == AND_OP) {
2N/A
2N/A // Need to check for escape as first thing.
2N/A
2N/A tok = tk.nextToken();
2N/A String str = tk.sval; // not used if token not a string...
2N/A tk.pushBack();
2N/A
2N/A if (tok == StreamTokenizer.TT_WORD) {
2N/A
2N/A if (str.charAt(0) != HASH) {
2N/A parseLogicalExpression(logOp, tk, buf);
2N/A
2N/A } else {
2N/A parse(tk, buf, true);
2N/A // cause we can't push back twice
2N/A
2N/A }
2N/A
2N/A } else {
2N/A parseLogicalExpression(logOp, tk, buf);
2N/A
2N/A }
2N/A
2N/A break;
2N/A
2N/A } else if (logOp == OR_OP) {
2N/A
2N/A parseLogicalExpression(logOp, tk, buf);
2N/A
2N/A break;
2N/A
2N/A } else {
2N/A
2N/A // It's a terminal expression. Push back the last token
2N/A // and parse the terminal.
2N/A
2N/A tk.pushBack();
2N/A
2N/A parse(tk, buf, false);
2N/A
2N/A break;
2N/A
2N/A }
2N/A
2N/A } else {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A }
2N/A
2N/A } while (true);
2N/A
2N/A // Since terminals are allowed alone at the top level,
2N/A // we need to check here whether anything else is
2N/A // in the query.
2N/A
2N/A if (start) {
2N/A
2N/A tok = eatWhite(tk);
2N/A
2N/A if (tok != StreamTokenizer.TT_EOF) {
2N/A
2N/A // The line should have ended by now.
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A }
2N/A }
2N/A
2N/A }
2N/A
2N/A // Rewrite a logical expression.
2N/A
2N/A private void
2N/A parseLogicalExpression(int logOp, StreamTokenizer tk, StringBuffer buf)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A // Append paren and operator to buffer.
2N/A
2N/A buf.append((char)OPEN_PAREN);
2N/A buf.append((char)logOp);
2N/A
2N/A int tok = 0;
2N/A
2N/A do {
2N/A
2N/A tok = eatWhite(tk);
2N/A
2N/A if (tok == OPEN_PAREN) {
2N/A
2N/A // So parseInternal() sees a parenthesized list.
2N/A
2N/A tk.pushBack();
2N/A
2N/A // Go back to parseInternal.
2N/A
2N/A parseInternal(tk, buf, false);
2N/A
2N/A } else if (tok == CLOSE_PAREN) {
2N/A
2N/A // Append the character to the buffer and return.
2N/A
2N/A buf.append((char)tok);
2N/A
2N/A return;
2N/A
2N/A } else {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A }
2N/A
2N/A } while (tok != StreamTokenizer.TT_EOF);
2N/A
2N/A // Error if we've not caught ourselves before this.
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A }
2N/A
2N/A // Parse a terminal. Opening paren has been got.
2N/A
2N/A private void parse(StreamTokenizer tk,
2N/A StringBuffer buf,
2N/A boolean firstEscaped)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A String tag = "";
2N/A int tok = 0;
2N/A
2N/A tok = eatWhite(tk);
2N/A
2N/A // Gather the tag and value.
2N/A
2N/A if (tok != StreamTokenizer.TT_WORD) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A }
2N/A
2N/A // Parse the tag.
2N/A
2N/A tag = parseTag(tk, firstEscaped);
2N/A
2N/A if (tag.length() <= 0) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A }
2N/A
2N/A // Unescape tag.
2N/A
2N/A tag = ServiceLocationAttributeV1.unescapeAttributeString(tag,
2N/A charCode);
2N/A
2N/A // Now escape in v2 format,
2N/A
2N/A tag = ServiceLocationAttribute.escapeAttributeString(tag, true);
2N/A
2N/A // Parse the operator.
2N/A
2N/A char compOp = parseOperator(tk);
2N/A
2N/A // If this was a keyword operator, then add present
2N/A // operator and closing paren and return.
2N/A
2N/A if (compOp == PRESENT) {
2N/A buf.append(OPEN_PAREN);
2N/A buf.append(tag);
2N/A buf.append(EQUAL_OP);
2N/A buf.append(PRESENT);
2N/A buf.append(CLOSE_PAREN);
2N/A return;
2N/A
2N/A }
2N/A
2N/A // Parse value by reading up to the next close paren.
2N/A // Returned value will be in v2 format.
2N/A
2N/A String valTok = parseValue(tk);
2N/A
2N/A // Construct the comparision depending on the operator.
2N/A
2N/A if (compOp == NOT_OP) {
2N/A
2N/A // If the value is an integer, we can construct a query
2N/A // that will exclude the number.
2N/A
2N/A try {
2N/A
2N/A int n = Integer.parseInt(valTok);
2N/A
2N/A // Bump the integer up and down to catch numbers on both
2N/A // sides of the required number. Be careful not to
2N/A // overstep bounds.
2N/A
2N/A if (n < Integer.MAX_VALUE) {
2N/A buf.append(OPEN_PAREN);
2N/A buf.append(tag);
2N/A buf.append(GREATER_OP);
2N/A buf.append(EQUAL_OP);
2N/A buf.append(n + 1);
2N/A buf.append(CLOSE_PAREN);
2N/A
2N/A }
2N/A
2N/A if (n > Integer.MIN_VALUE) {
2N/A buf.append(OPEN_PAREN);
2N/A buf.append(tag);
2N/A buf.append(LESS_OP);
2N/A buf.append(EQUAL_OP);
2N/A buf.append(n - 1);
2N/A buf.append(CLOSE_PAREN);
2N/A
2N/A }
2N/A
2N/A if ((n < Integer.MAX_VALUE) && (n > Integer.MIN_VALUE)) {
2N/A buf.insert(0, OR_OP);
2N/A buf.insert(0, OPEN_PAREN);
2N/A buf.append(CLOSE_PAREN);
2N/A
2N/A }
2N/A
2N/A } catch (NumberFormatException ex) {
2N/A
2N/A // It's not an integer. We can construct a query expression
2N/A // that will not always work. The query rules out advertisments
2N/A // where the attribute value doesn't match and there are
2N/A // no other attributes or values, and advertisements
2N/A // that don't contain the attribute, but it doesn't rule out
2N/A // a multivalued attribute with other values or if there
2N/A // are other attributes. The format of the query is:
2N/A // "(&(<tag>=*)(!(<tag>=<value>))).
2N/A
2N/A buf.append(OPEN_PAREN);
2N/A buf.append(AND_OP);
2N/A buf.append(OPEN_PAREN);
2N/A buf.append(tag);
2N/A buf.append(EQUAL_OP);
2N/A buf.append(PRESENT);
2N/A buf.append(CLOSE_PAREN);
2N/A buf.append(OPEN_PAREN);
2N/A buf.append(NOT_OP);
2N/A buf.append(OPEN_PAREN);
2N/A buf.append(tag);
2N/A buf.append(EQUAL_OP);
2N/A buf.append(valTok);
2N/A buf.append(CLOSE_PAREN);
2N/A buf.append(CLOSE_PAREN);
2N/A buf.append(CLOSE_PAREN);
2N/A
2N/A }
2N/A
2N/A } else if ((compOp == LESS_OP) || (compOp == GREATER_OP)) {
2N/A
2N/A int n = 0;
2N/A
2N/A try {
2N/A
2N/A n = Integer.parseInt(valTok);
2N/A
2N/A } catch (NumberFormatException ex) {
2N/A
2N/A // It's a parse error here.
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A
2N/A }
2N/A
2N/A // We don't attempt to handle something that would cause
2N/A // arithmetic overflow.
2N/A
2N/A if ((n == Integer.MAX_VALUE) || (n == Integer.MIN_VALUE)) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A
2N/A }
2N/A
2N/A // Construct a query that includes everything
2N/A // to the correct side.
2N/A
2N/A buf.append(OPEN_PAREN);
2N/A buf.append(tag);
2N/A
2N/A if (compOp == LESS_OP) {
2N/A buf.append(LESS_OP);
2N/A buf.append(EQUAL_OP);
2N/A buf.append(n - 1);
2N/A
2N/A } else {
2N/A buf.append(GREATER_OP);
2N/A buf.append(EQUAL_OP);
2N/A buf.append(n + 1);
2N/A
2N/A }
2N/A
2N/A buf.append(CLOSE_PAREN);
2N/A
2N/A } else {
2N/A
2N/A // Simple, single operator. Just add it with the
2N/A // value.
2N/A
2N/A buf.append(OPEN_PAREN);
2N/A buf.append(tag);
2N/A
2N/A // Need to distinguish less and greater equal.
2N/A
2N/A if (compOp == LEQUAL_OP) {
2N/A buf.append(LESS_OP);
2N/A buf.append(EQUAL_OP);
2N/A
2N/A } else if (compOp == GEQUAL_OP) {
2N/A buf.append(GREATER_OP);
2N/A buf.append(EQUAL_OP);
2N/A
2N/A } else {
2N/A buf.append(compOp);
2N/A
2N/A }
2N/A
2N/A buf.append(valTok);
2N/A buf.append(CLOSE_PAREN);
2N/A
2N/A }
2N/A
2N/A }
2N/A
2N/A // Gather tokens with embedded whitespace and return.
2N/A
2N/A private String parseTag(StreamTokenizer tk, boolean ampStart)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A String value = "";
2N/A
2N/A // Take care of corner case here.
2N/A
2N/A if (ampStart) {
2N/A value = value +"&";
2N/A ampStart = false;
2N/A }
2N/A
2N/A do {
2N/A
2N/A if (tk.ttype == StreamTokenizer.TT_WORD) {
2N/A value += tk.sval;
2N/A
2N/A } else if ((char)tk.ttype == SPACE) {
2N/A value = value + " ";
2N/A
2N/A } else if ((char)tk.ttype == AND_OP) {
2N/A value = value + "&";
2N/A
2N/A } else {
2N/A break;
2N/A
2N/A }
2N/A tk.nextToken();
2N/A
2N/A } while (true);
2N/A
2N/A return value.trim(); // removes trailing whitespace...
2N/A }
2N/A
2N/A private char parseOperator(StreamTokenizer tk)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A int tok = tk.ttype;
2N/A
2N/A // If the token is a close paren, then this was a keyword
2N/A // (e.g. "(foo)". Return the present operator.
2N/A
2N/A if ((char)tok == CLOSE_PAREN) {
2N/A return PRESENT;
2N/A
2N/A }
2N/A
2N/A if (tok != EQUAL_OP && tok != NOT_OP &&
2N/A tok != LESS_OP && tok != GREATER_OP) {
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A
2N/A }
2N/A
2N/A char compOp = (char)tok;
2N/A
2N/A // Get the next token.
2N/A
2N/A tok = tk.nextToken();
2N/A
2N/A // Look for dual character operators.
2N/A
2N/A if ((char)tok == EQUAL_OP) {
2N/A
2N/A // Here, we can have either "!=", "<=", ">=", or "==".
2N/A // Anything else is wrong.
2N/A
2N/A if (compOp != LESS_OP && compOp != GREATER_OP &&
2N/A compOp != EQUAL_OP && compOp != NOT_OP) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A }
2N/A
2N/A // Assign the right dual operator.
2N/A
2N/A if (compOp == LESS_OP) {
2N/A compOp = LEQUAL_OP;
2N/A
2N/A } else if (compOp == GREATER_OP) {
2N/A compOp = GEQUAL_OP;
2N/A
2N/A }
2N/A
2N/A } else if (compOp != LESS_OP && compOp != GREATER_OP) {
2N/A
2N/A // Error if the comparison operator was something other
2N/A // than ``<'' or ``>'' and there is no equal. This
2N/A // rules out ``!'' or ``='' alone.
2N/A
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A
2N/A } else {
2N/A
2N/A // Push back the last token if it wasn't a two character operator.
2N/A
2N/A tk.pushBack();
2N/A
2N/A }
2N/A
2N/A return compOp;
2N/A }
2N/A
2N/A
2N/A private String parseValue(StreamTokenizer tk)
2N/A throws ServiceLocationException, IOException {
2N/A
2N/A int tok = 0;
2N/A StringBuffer valTok = new StringBuffer();
2N/A
2N/A // Eat leading whitespace.
2N/A
2N/A tok = eatWhite(tk);
2N/A
2N/A // If the first value is a paren, then we've got an
2N/A // opaque.
2N/A
2N/A if ((char)tok == OPEN_PAREN) {
2N/A
2N/A valTok.append("(");
2N/A
2N/A // Collect all tokens up to the closing paren.
2N/A
2N/A do {
2N/A
2N/A tok = tk.nextToken();
2N/A
2N/A // It's a closing paren. break out of the loop.
2N/A
2N/A if ((char)tok == CLOSE_PAREN) {
2N/A valTok.append(")");
2N/A break;
2N/A
2N/A } else if ((char)tok == EQUAL_OP) {
2N/A valTok.append("=");
2N/A
2N/A } else if (tok == StreamTokenizer.TT_WORD) {
2N/A valTok.append(tk.sval);
2N/A
2N/A } else {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A }
2N/A
2N/A } while (true);
2N/A
2N/A
2N/A // Eat whitespace until closing paren.
2N/A
2N/A tok = eatWhite(tk);
2N/A
2N/A if ((char)tok != CLOSE_PAREN) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A
2N/A }
2N/A
2N/A } else {
2N/A
2N/A // Error if just a closed paren.
2N/A
2N/A if (tok == CLOSE_PAREN) {
2N/A throw
2N/A new ServiceLocationException(
2N/A ServiceLocationException.PARSE_ERROR,
2N/A "v1_query_error",
2N/A new Object[] {query});
2N/A
2N/A }
2N/A
2N/A do {
2N/A
2N/A // Append the token if a WORD
2N/A
2N/A if (tok == StreamTokenizer.TT_WORD) {
2N/A valTok.append(tk.sval);
2N/A
2N/A } else if ((tok != StreamTokenizer.TT_EOF) &&
2N/A (tok != StreamTokenizer.TT_EOL) &&
2N/A (tok != CLOSE_PAREN)) {
2N/A
2N/A // Otherwise, it's a token char, so append.
2N/A
2N/A valTok.append((char)tok);
2N/A
2N/A }
2N/A
2N/A tok = tk.nextToken();
2N/A
2N/A } while (tok != CLOSE_PAREN);
2N/A }
2N/A
2N/A // If a wildcard, remove wildcard stars here for later re-insertion.
2N/A
2N/A String strval = valTok.toString().trim();
2N/A boolean wildstart = false;
2N/A boolean wildend = false;
2N/A
2N/A if (strval.startsWith(WILDCARD)) {
2N/A wildstart = true;
2N/A strval = strval.substring(1, strval.length());
2N/A
2N/A }
2N/A
2N/A if (strval.endsWith(WILDCARD)) {
2N/A wildend = true;
2N/A strval = strval.substring(0, strval.length()-1);
2N/A
2N/A }
2N/A
2N/A // Evaluate the value.
2N/A
2N/A Object val =
2N/A ServiceLocationAttributeV1.evaluate(strval, charCode);
2N/A
2N/A // Now convert to v2 format, and return.
2N/A
2N/A if (val instanceof String) {
2N/A strval =
2N/A ServiceLocationAttribute.escapeAttributeString(val.toString(),
2N/A false);
2N/A
2N/A // Add wildcards back in.
2N/A
2N/A if (wildstart) {
2N/A strval = WILDCARD + strval;
2N/A
2N/A }
2N/A
2N/A if (wildend) {
2N/A strval = strval + WILDCARD;
2N/A
2N/A }
2N/A
2N/A } else {
2N/A strval = val.toString();
2N/A
2N/A }
2N/A
2N/A return strval;
2N/A
2N/A }
2N/A
2N/A // Eat whitespace.
2N/A
2N/A private int eatWhite(StreamTokenizer tk)
2N/A throws IOException {
2N/A
2N/A int tok = tk.nextToken();
2N/A
2N/A while (tok == SPACE) {
2N/A tok = tk.nextToken();
2N/A
2N/A }
2N/A
2N/A return tok;
2N/A }
2N/A}