xref: /onnv-gate/usr/src/lib/libslp/javalib/com/sun/slp/SLPV1SSrvMsg.java (revision 7298:b69e27387f74)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7298SMark.J.Nelson@Sun.COM  * Common Development and Distribution License (the "License").
6*7298SMark.J.Nelson@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
220Sstevel@tonic-gate  * Copyright (c) 1999 by Sun Microsystems, Inc.
230Sstevel@tonic-gate  * All rights reserved.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate //  SLPV1SSrvMsg.java: SLPv1 server side service rqst/reply.
280Sstevel@tonic-gate //  Author:           James Kempf
290Sstevel@tonic-gate //  Created On:       Thu Sep 10 15:33:58 1998
300Sstevel@tonic-gate //  Last Modified By: James Kempf
310Sstevel@tonic-gate //  Last Modified On: Fri Nov  6 14:03:00 1998
320Sstevel@tonic-gate //  Update Count:     41
330Sstevel@tonic-gate //
340Sstevel@tonic-gate 
350Sstevel@tonic-gate 
360Sstevel@tonic-gate package com.sun.slp;
370Sstevel@tonic-gate 
380Sstevel@tonic-gate import java.util.*;
390Sstevel@tonic-gate import java.io.*;
400Sstevel@tonic-gate 
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /**
430Sstevel@tonic-gate  * The SLPV1SSrvMsg class models the SLP server side service request message.
440Sstevel@tonic-gate  *
450Sstevel@tonic-gate  * @author James Kempf
460Sstevel@tonic-gate  */
470Sstevel@tonic-gate 
480Sstevel@tonic-gate class SLPV1SSrvMsg extends SSrvMsg {
490Sstevel@tonic-gate 
500Sstevel@tonic-gate     // For eating whitespace.
510Sstevel@tonic-gate 
520Sstevel@tonic-gate     final static char SPACE = ' ';
530Sstevel@tonic-gate 
540Sstevel@tonic-gate     // Comma for list parsing.
550Sstevel@tonic-gate 
560Sstevel@tonic-gate     final static char COMMA = ',';
570Sstevel@tonic-gate 
580Sstevel@tonic-gate     // Logical operators.
590Sstevel@tonic-gate 
600Sstevel@tonic-gate     final static char OR_OP = '|';
610Sstevel@tonic-gate     final static char AND_OP = '&';
620Sstevel@tonic-gate 
630Sstevel@tonic-gate     // Logical operator corner case needs this.
640Sstevel@tonic-gate 
650Sstevel@tonic-gate     final static char HASH = '#';
660Sstevel@tonic-gate 
670Sstevel@tonic-gate     // Comparison/Assignment operators.
680Sstevel@tonic-gate 
690Sstevel@tonic-gate     final static char EQUAL_OP = '=';
700Sstevel@tonic-gate     final static char NOT_OP = '!';
710Sstevel@tonic-gate     final static char LESS_OP = '<';
720Sstevel@tonic-gate     final static char GREATER_OP = '>';
730Sstevel@tonic-gate     final static char GEQUAL_OP = 'g';
740Sstevel@tonic-gate     final static char LEQUAL_OP = 'l';
750Sstevel@tonic-gate 
760Sstevel@tonic-gate     // Parens.
770Sstevel@tonic-gate 
780Sstevel@tonic-gate     final static char OPEN_PAREN = '(';
790Sstevel@tonic-gate     final static char CLOSE_PAREN = ')';
800Sstevel@tonic-gate 
810Sstevel@tonic-gate     // LDAP present operator
820Sstevel@tonic-gate 
830Sstevel@tonic-gate     final static char PRESENT = '*';
840Sstevel@tonic-gate 
850Sstevel@tonic-gate     // Wildcard operator.
860Sstevel@tonic-gate 
870Sstevel@tonic-gate     final static String WILDCARD = "*";
880Sstevel@tonic-gate 
890Sstevel@tonic-gate     // Character code for parsing.
900Sstevel@tonic-gate 
910Sstevel@tonic-gate     String charCode = IANACharCode.UTF8;
920Sstevel@tonic-gate 
930Sstevel@tonic-gate     // For creating a null reply.
940Sstevel@tonic-gate 
SLPV1SSrvMsg()950Sstevel@tonic-gate     protected SLPV1SSrvMsg() {}
960Sstevel@tonic-gate 
970Sstevel@tonic-gate     // Construct a SLPV1SSrvMsg from the input stream.
980Sstevel@tonic-gate 
SLPV1SSrvMsg(SrvLocHeader hdr, DataInputStream dis)990Sstevel@tonic-gate     SLPV1SSrvMsg(SrvLocHeader hdr, DataInputStream dis)
1000Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
1010Sstevel@tonic-gate 	super(hdr, dis);
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate     }
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate     // Construct an empty SLPV1SSrvMsg, for monolingual off.
1060Sstevel@tonic-gate 
makeEmptyReply(SLPHeaderV1 hdr)1070Sstevel@tonic-gate     static SrvLocMsg makeEmptyReply(SLPHeaderV1 hdr)
1080Sstevel@tonic-gate 	throws ServiceLocationException {
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	SLPV1SSrvMsg msg = new SLPV1SSrvMsg();
1110Sstevel@tonic-gate 	msg.hdr = hdr;
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 	msg.makeReply(new Hashtable(), null);
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	return msg;
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate     }
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate     // Initialize the message from the input stream.
1200Sstevel@tonic-gate 
initialize(DataInputStream dis)1210Sstevel@tonic-gate     void initialize(DataInputStream dis)
1220Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate 	SLPHeaderV1 hdr = (SLPHeaderV1)getHeader();
1250Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	// First get the previous responder.
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 	hdr.parsePreviousRespondersIn(dis);
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 	// Now get the raw query.
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate 	hdr.getString(buf, dis);
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	String rq = buf.toString();
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate 	// Parse the raw query to pull out the service type, scope,
1380Sstevel@tonic-gate 	//  and query.
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 	StringTokenizer st = new StringTokenizer(rq, "/", true);
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate 	try {
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 	    String type =
1450Sstevel@tonic-gate 		Defaults.SERVICE_PREFIX + ":" +
1460Sstevel@tonic-gate 		st.nextToken().trim().toLowerCase() + ":";
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 	    serviceType =
1490Sstevel@tonic-gate 		hdr.checkServiceType(type);
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	    st.nextToken();  // get rid of slash.
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 	    // Get the scope.
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 	    String scope = st.nextToken().trim().toLowerCase();
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate 	    // Special case if scope is empty (meaning the next
1580Sstevel@tonic-gate 	    //  token will be a slash).
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 	    if (scope.equals("/")) {
1610Sstevel@tonic-gate 		scope = "";
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 	    } else {
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 		st.nextToken();  // get rid of slash.
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 		if (scope.length() > 0) {
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate 		    // Validate the scope name.
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 		    hdr.validateScope(scope);
1720Sstevel@tonic-gate 		}
1730Sstevel@tonic-gate 	    }
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 	    // Set up scopes vector.
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	    hdr.scopes = new Vector();
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	    // Substitute default scope here.
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 	    if (scope.length() <= 0) {
1820Sstevel@tonic-gate 		scope = Defaults.DEFAULT_SCOPE;
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	    }
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate 	    hdr.scopes.addElement(scope.toLowerCase().trim());
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	    // Parsing the query is complicated by opaques having slashes.
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 	    String q = "";
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate 	    while (st.hasMoreTokens()) {
1930Sstevel@tonic-gate 		q = q + st.nextToken();
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	    }
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	    // Drop off the final backslash, error if none.
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	    if (!q.endsWith("/")) {
2000Sstevel@tonic-gate 		throw
2010Sstevel@tonic-gate 		    new ServiceLocationException(
2020Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2030Sstevel@tonic-gate 				"v1_query_error",
2040Sstevel@tonic-gate 				new Object[] {rq});
2050Sstevel@tonic-gate 	    }
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	    query = q.substring(0, q.length()-1);
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 	    // Save header char code for parsing.
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	    charCode = hdr.charCode;
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	    // Convert the query into a V2 query.
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 	    convertQuery();
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	    // If the query is for "service:directory-agent", then we
2180Sstevel@tonic-gate 	    //  mark it as having been multicast, because that is the
2190Sstevel@tonic-gate 	    //  only kind of multicast that we accept for SLPv1. Anybody
2200Sstevel@tonic-gate 	    //  who unicasts this to us will time out.
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	    if (serviceType.equals(Defaults.DA_SERVICE_TYPE.toString())) {
2230Sstevel@tonic-gate 		hdr.mcast = true;
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	    }
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 	    // Construct description.
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 	    hdr.constructDescription("SrvRqst",
2300Sstevel@tonic-gate 				     "        service type=``" +
2310Sstevel@tonic-gate 				     serviceType + "''\n" +
2320Sstevel@tonic-gate 				     "        query=``" +
2330Sstevel@tonic-gate 				     query + "''");
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	}  catch (NoSuchElementException ex) {
2360Sstevel@tonic-gate 	    throw
2370Sstevel@tonic-gate 		new ServiceLocationException(
2380Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2390Sstevel@tonic-gate 				"v1_query_error",
2400Sstevel@tonic-gate 				new Object[] {rq});
2410Sstevel@tonic-gate 	}
2420Sstevel@tonic-gate     }
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate     // Make a reply message.
2450Sstevel@tonic-gate 
makeReply(Hashtable urltable, Hashtable URLSignatures)2460Sstevel@tonic-gate     SrvLocMsg makeReply(Hashtable urltable,
2470Sstevel@tonic-gate 			Hashtable URLSignatures)
2480Sstevel@tonic-gate 	throws ServiceLocationException {
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 	SLPHeaderV1 hdr =
2510Sstevel@tonic-gate 	    ((SLPHeaderV1)getHeader()).makeReplyHeader();
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	ByteArrayOutputStream baos = new ByteArrayOutputStream();
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	// Edit out abstract types and nonService: URLs.
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	Enumeration en = urltable.keys();
2580Sstevel@tonic-gate 	Vector urls = new Vector();
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 	while (en.hasMoreElements()) {
2610Sstevel@tonic-gate 	    ServiceURL surl = (ServiceURL)en.nextElement();
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	    // Reject if abstract type or nonservice: URL.
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	    ServiceType type = surl.getServiceType();
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	    if (!type.isAbstractType() && type.isServiceURL()) {
2680Sstevel@tonic-gate 		urls.addElement(surl);
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	    }
2710Sstevel@tonic-gate 	}
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 	hdr.iNumReplies = urls.size();
2740Sstevel@tonic-gate 	// keep this info so SAs can drop 0 replies
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 	int n = urls.size();
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 	// Write out the size of the list.
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	hdr.putInt(n, baos);
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	en = urls.elements();
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 	// Write out the size of the list.
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	while (en.hasMoreElements()) {
2870Sstevel@tonic-gate 	    ServiceURL surl = (ServiceURL)en.nextElement();
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	    hdr.parseServiceURLOut(surl, true, baos);
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	}
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	// We ignore the signatures because we only do V1 compatibility
2940Sstevel@tonic-gate 	//  for nonprotected scopes.
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 	hdr.payload = baos.toByteArray();
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 	hdr.constructDescription("SrvRply",
2990Sstevel@tonic-gate 				 "        service URLs=``" + urls + "''\n");
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 	return hdr;
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate     }
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate     // Convert the query to a V2 query.
3060Sstevel@tonic-gate 
convertQuery()3070Sstevel@tonic-gate     void convertQuery()
3080Sstevel@tonic-gate 	throws ServiceLocationException {
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	// Check for empty query.
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	query = query.trim();
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	if (query.length() <= 0) {
3150Sstevel@tonic-gate 	    return;
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	}
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate 	// Check for query join.
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	if (!(query.startsWith("(") && query.endsWith(")"))) {
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	    // Rewrite to a standard query.
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	    query = rewriteQueryJoin(query);
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	}
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	// Now rewrite the query into v2 format.
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	query = rewriteQuery(query);
3320Sstevel@tonic-gate     }
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate     // Rewrite a query join as a conjunction.
3360Sstevel@tonic-gate 
rewriteQueryJoin(String query)3370Sstevel@tonic-gate     private String rewriteQueryJoin(String query)
3380Sstevel@tonic-gate 	throws ServiceLocationException {
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	// Turn infix expression into prefix.
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	StringBuffer sbuf = new StringBuffer();
3430Sstevel@tonic-gate 	StringTokenizer tk = new StringTokenizer(query, ",", true);
3440Sstevel@tonic-gate 	boolean lastTokComma = true;
3450Sstevel@tonic-gate 	int numEx = 0;
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	while (tk.hasMoreElements()) {
3480Sstevel@tonic-gate 	    String exp = tk.nextToken().trim();
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	    if (exp.equals(",")) {
3510Sstevel@tonic-gate 		if (lastTokComma) {
3520Sstevel@tonic-gate 		    throw
3530Sstevel@tonic-gate 			new ServiceLocationException(
3540Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
3550Sstevel@tonic-gate 				"v1_query_error",
3560Sstevel@tonic-gate 				new Object[] {query});
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 		} else {
3590Sstevel@tonic-gate 		    lastTokComma = true;
3600Sstevel@tonic-gate 		}
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	    } else {
3630Sstevel@tonic-gate 		lastTokComma = false;
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 		if (exp.length() <= 0) {
3660Sstevel@tonic-gate 		    throw
3670Sstevel@tonic-gate 			new ServiceLocationException(
3680Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
3690Sstevel@tonic-gate 				"v1_query_error",
3700Sstevel@tonic-gate 				new Object[] {query});
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 		}
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 		// Put in parens
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 		sbuf.append("(");
3770Sstevel@tonic-gate 		sbuf.append(exp);
3780Sstevel@tonic-gate 		sbuf.append(")");
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 		numEx++;
3810Sstevel@tonic-gate 	    }
3820Sstevel@tonic-gate 	}
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	if (lastTokComma || numEx == 0) {
3850Sstevel@tonic-gate 	    throw
3860Sstevel@tonic-gate 		new ServiceLocationException(
3870Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
3880Sstevel@tonic-gate 				"v1_query_error",
3890Sstevel@tonic-gate 				new Object[] {query});
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 	}
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	if (numEx > 1) {
3940Sstevel@tonic-gate 	    sbuf.insert(0, "(&");
3950Sstevel@tonic-gate 	    sbuf.append(")");
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	}
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 	return sbuf.toString();
4000Sstevel@tonic-gate     }
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate     // Rewrite a v1 query into v2 format. This includes character escaping.
4030Sstevel@tonic-gate 
rewriteQuery(String whereList)4040Sstevel@tonic-gate     private String rewriteQuery(String whereList)
4050Sstevel@tonic-gate 	throws ServiceLocationException {
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	// Parse a logical expression.
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 	StreamTokenizer tk =
4100Sstevel@tonic-gate 	    new StreamTokenizer(new StringReader(whereList));
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 	tk.resetSyntax();  		// make all chars ordinary...
4130Sstevel@tonic-gate 	tk.whitespaceChars('\000','\037');
4140Sstevel@tonic-gate 	tk.ordinaryChar(SPACE);		// but beware of embedded whites...
4150Sstevel@tonic-gate 	tk.wordChars('!', '%');
4160Sstevel@tonic-gate 	tk.ordinaryChar(AND_OP);
4170Sstevel@tonic-gate 	tk.wordChars('\'', '\'');
4180Sstevel@tonic-gate 	tk.ordinaryChar(OPEN_PAREN);
4190Sstevel@tonic-gate 	tk.ordinaryChar(CLOSE_PAREN);
4200Sstevel@tonic-gate 	tk.wordChars('*', '{');
4210Sstevel@tonic-gate 	tk.ordinaryChar(OR_OP);
4220Sstevel@tonic-gate 	tk.wordChars('}', '~');
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	// Initialize parse tables in terminal.
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	tk.ordinaryChar(EQUAL_OP);
4270Sstevel@tonic-gate 	tk.ordinaryChar(NOT_OP);
4280Sstevel@tonic-gate 	tk.ordinaryChar(LESS_OP);
4290Sstevel@tonic-gate 	tk.ordinaryChar(GREATER_OP);
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	// Parse through the expression.
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	try {
4370Sstevel@tonic-gate 	    parseInternal(tk, buf, true);
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 	} catch (IOException ex) {
4400Sstevel@tonic-gate 	    throw
4410Sstevel@tonic-gate 		new ServiceLocationException(
4420Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
4430Sstevel@tonic-gate 				"v1_query_error",
4440Sstevel@tonic-gate 				new Object[] {query});
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 	}
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	return buf.toString();
4490Sstevel@tonic-gate     }
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate     // Do the actual parsing, using the passed-in stream tokenizer.
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate     private void
parseInternal(StreamTokenizer tk, StringBuffer buf, boolean start)4540Sstevel@tonic-gate 	parseInternal(StreamTokenizer tk, StringBuffer buf, boolean start)
4550Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 	int tok = 0;
4580Sstevel@tonic-gate 	boolean ret = true;
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	do {
4610Sstevel@tonic-gate 	    tok = eatWhite(tk);
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 	    // We should be at the beginning a parenthesized
4640Sstevel@tonic-gate 	    //  where list.
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	    if (tok == OPEN_PAREN) {
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 		// Get the next token. Eat whitespace in the process.
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 		tok = eatWhite(tk);
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 		// If it's a logOp, then process as a logical expression.
4730Sstevel@tonic-gate 		//  This handles the following nasty case:
4740Sstevel@tonic-gate 		//
4750Sstevel@tonic-gate 		//  	(&#44;&#45==the rest of it)
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 		int logOp = tok;
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 		if (logOp == AND_OP) {
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate 		    // Need to check for escape as first thing.
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 		    tok = tk.nextToken();
4840Sstevel@tonic-gate 		    String str = tk.sval; // not used if token not a string...
4850Sstevel@tonic-gate 		    tk.pushBack();
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 		    if (tok == StreamTokenizer.TT_WORD) {
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 			if (str.charAt(0) != HASH) {
4900Sstevel@tonic-gate 			    parseLogicalExpression(logOp, tk, buf);
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 			} else {
4930Sstevel@tonic-gate 			    parse(tk, buf, true);
4940Sstevel@tonic-gate 					// cause we can't push back twice
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 			}
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 		    } else {
4990Sstevel@tonic-gate 			parseLogicalExpression(logOp, tk, buf);
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 		    }
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 		    break;
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 		} else if (logOp == OR_OP) {
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 		    parseLogicalExpression(logOp, tk, buf);
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 		    break;
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 		} else {
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 		    // It's a terminal expression. Push back the last token
5140Sstevel@tonic-gate 		    //  and parse the terminal.
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 		    tk.pushBack();
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 		    parse(tk, buf, false);
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 		    break;
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 		}
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 	    } else {
5250Sstevel@tonic-gate 		throw
5260Sstevel@tonic-gate 		    new ServiceLocationException(
5270Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
5280Sstevel@tonic-gate 				"v1_query_error",
5290Sstevel@tonic-gate 				new Object[] {query});
5300Sstevel@tonic-gate 	    }
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate 	} while (true);
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	// Since terminals are allowed alone at the top level,
5350Sstevel@tonic-gate 	//  we need to check here whether anything else is
5360Sstevel@tonic-gate 	//  in the query.
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	if (start) {
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	    tok = eatWhite(tk);
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 	    if (tok != StreamTokenizer.TT_EOF) {
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 		// The line should have ended by now.
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 		throw
5470Sstevel@tonic-gate 		    new ServiceLocationException(
5480Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
5490Sstevel@tonic-gate 				"v1_query_error",
5500Sstevel@tonic-gate 				new Object[] {query});
5510Sstevel@tonic-gate 	    }
5520Sstevel@tonic-gate 	}
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate     }
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate     // Rewrite a logical expression.
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate     private void
parseLogicalExpression(int logOp, StreamTokenizer tk, StringBuffer buf)5590Sstevel@tonic-gate 	parseLogicalExpression(int logOp, StreamTokenizer tk, StringBuffer buf)
5600Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	// Append paren and operator to buffer.
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 	buf.append((char)OPEN_PAREN);
5650Sstevel@tonic-gate 	buf.append((char)logOp);
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 	int tok = 0;
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	do {
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	    tok = eatWhite(tk);
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate 	    if (tok == OPEN_PAREN) {
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 		// So parseInternal() sees a parenthesized list.
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 		tk.pushBack();
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 		// Go back to parseInternal.
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 		parseInternal(tk, buf, false);
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate 	    } else if (tok == CLOSE_PAREN) {
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 		// Append the character to the buffer and return.
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 		buf.append((char)tok);
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 		return;
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	    } else {
5920Sstevel@tonic-gate 		throw
5930Sstevel@tonic-gate 		    new ServiceLocationException(
5940Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
5950Sstevel@tonic-gate 				"v1_query_error",
5960Sstevel@tonic-gate 				new Object[] {query});
5970Sstevel@tonic-gate 	    }
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate 	} while (tok != StreamTokenizer.TT_EOF);
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 	// Error if we've not caught ourselves before this.
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 	throw
6040Sstevel@tonic-gate 	    new ServiceLocationException(
6050Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6060Sstevel@tonic-gate 				"v1_query_error",
6070Sstevel@tonic-gate 				new Object[] {query});
6080Sstevel@tonic-gate     }
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate     // Parse a terminal. Opening paren has been got.
6110Sstevel@tonic-gate 
parse(StreamTokenizer tk, StringBuffer buf, boolean firstEscaped)6120Sstevel@tonic-gate     private void parse(StreamTokenizer tk,
6130Sstevel@tonic-gate 		       StringBuffer buf,
6140Sstevel@tonic-gate 		       boolean firstEscaped)
6150Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 	String tag = "";
6180Sstevel@tonic-gate 	int tok = 0;
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 	tok = eatWhite(tk);
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	// Gather the tag and value.
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	if (tok != StreamTokenizer.TT_WORD) {
6250Sstevel@tonic-gate 	    throw
6260Sstevel@tonic-gate 		new ServiceLocationException(
6270Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6280Sstevel@tonic-gate 				"v1_query_error",
6290Sstevel@tonic-gate 				new Object[] {query});
6300Sstevel@tonic-gate 	}
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 	// Parse the tag.
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate 	tag = parseTag(tk, firstEscaped);
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 	if (tag.length() <= 0) {
6370Sstevel@tonic-gate 	    throw
6380Sstevel@tonic-gate 		new ServiceLocationException(
6390Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6400Sstevel@tonic-gate 				"v1_query_error",
6410Sstevel@tonic-gate 				new Object[] {query});
6420Sstevel@tonic-gate 	}
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	// Unescape tag.
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate 	tag = ServiceLocationAttributeV1.unescapeAttributeString(tag,
6470Sstevel@tonic-gate 								 charCode);
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	// Now escape in v2 format,
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 	tag = ServiceLocationAttribute.escapeAttributeString(tag, true);
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	// Parse the operator.
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 	char compOp = parseOperator(tk);
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	// If this was a keyword operator, then add present
6580Sstevel@tonic-gate 	// operator and closing paren and return.
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 	if (compOp == PRESENT) {
6610Sstevel@tonic-gate 	    buf.append(OPEN_PAREN);
6620Sstevel@tonic-gate 	    buf.append(tag);
6630Sstevel@tonic-gate 	    buf.append(EQUAL_OP);
6640Sstevel@tonic-gate 	    buf.append(PRESENT);
6650Sstevel@tonic-gate 	    buf.append(CLOSE_PAREN);
6660Sstevel@tonic-gate 	    return;
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	}
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	// Parse value by reading up to the next close paren.
6710Sstevel@tonic-gate 	//  Returned value will be in v2 format.
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 	String valTok = parseValue(tk);
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	// Construct the comparision depending on the operator.
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	if (compOp == NOT_OP) {
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 	    // If the value is an integer, we can construct a query
6800Sstevel@tonic-gate 	    //  that will exclude the number.
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 	    try {
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate 		int n = Integer.parseInt(valTok);
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 		// Bump the integer up and down to catch numbers on both
6870Sstevel@tonic-gate 		//  sides of the required number. Be careful not to
6880Sstevel@tonic-gate 		//  overstep bounds.
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 		if (n < Integer.MAX_VALUE) {
6910Sstevel@tonic-gate 		    buf.append(OPEN_PAREN);
6920Sstevel@tonic-gate 		    buf.append(tag);
6930Sstevel@tonic-gate 		    buf.append(GREATER_OP);
6940Sstevel@tonic-gate 		    buf.append(EQUAL_OP);
6950Sstevel@tonic-gate 		    buf.append(n + 1);
6960Sstevel@tonic-gate 		    buf.append(CLOSE_PAREN);
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 		}
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 		if (n > Integer.MIN_VALUE) {
7010Sstevel@tonic-gate 		    buf.append(OPEN_PAREN);
7020Sstevel@tonic-gate 		    buf.append(tag);
7030Sstevel@tonic-gate 		    buf.append(LESS_OP);
7040Sstevel@tonic-gate 		    buf.append(EQUAL_OP);
7050Sstevel@tonic-gate 		    buf.append(n - 1);
7060Sstevel@tonic-gate 		    buf.append(CLOSE_PAREN);
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 		}
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 		if ((n < Integer.MAX_VALUE) && (n > Integer.MIN_VALUE)) {
7110Sstevel@tonic-gate 		    buf.insert(0, OR_OP);
7120Sstevel@tonic-gate 		    buf.insert(0, OPEN_PAREN);
7130Sstevel@tonic-gate 		    buf.append(CLOSE_PAREN);
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate 		}
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	    } catch (NumberFormatException ex) {
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 		// It's not an integer. We can construct a query expression
7200Sstevel@tonic-gate 		// that will not always work. The query rules out advertisments
7210Sstevel@tonic-gate 		// where the attribute value doesn't match and there are
7220Sstevel@tonic-gate 		// no other attributes or values, and advertisements
7230Sstevel@tonic-gate 		// that don't contain the attribute, but it doesn't rule out
7240Sstevel@tonic-gate 		// a multivalued attribute with other values or if there
7250Sstevel@tonic-gate 		// are other attributes. The format of the query is:
7260Sstevel@tonic-gate 		// "(&(<tag>=*)(!(<tag>=<value>))).
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 		buf.append(OPEN_PAREN);
7290Sstevel@tonic-gate 		buf.append(AND_OP);
7300Sstevel@tonic-gate 		buf.append(OPEN_PAREN);
7310Sstevel@tonic-gate 		buf.append(tag);
7320Sstevel@tonic-gate 		buf.append(EQUAL_OP);
7330Sstevel@tonic-gate 		buf.append(PRESENT);
7340Sstevel@tonic-gate 		buf.append(CLOSE_PAREN);
7350Sstevel@tonic-gate 		buf.append(OPEN_PAREN);
7360Sstevel@tonic-gate 		buf.append(NOT_OP);
7370Sstevel@tonic-gate 		buf.append(OPEN_PAREN);
7380Sstevel@tonic-gate 		buf.append(tag);
7390Sstevel@tonic-gate 		buf.append(EQUAL_OP);
7400Sstevel@tonic-gate 		buf.append(valTok);
7410Sstevel@tonic-gate 		buf.append(CLOSE_PAREN);
7420Sstevel@tonic-gate 		buf.append(CLOSE_PAREN);
7430Sstevel@tonic-gate 		buf.append(CLOSE_PAREN);
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate 	    }
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 	} else if ((compOp == LESS_OP) || (compOp == GREATER_OP)) {
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 	    int n = 0;
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 	    try {
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate 		n = Integer.parseInt(valTok);
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 	    } catch (NumberFormatException ex) {
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate 		// It's a parse error here.
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 		throw
7600Sstevel@tonic-gate 		    new ServiceLocationException(
7610Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
7620Sstevel@tonic-gate 				"v1_query_error",
7630Sstevel@tonic-gate 				new Object[] {query});
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate 	    }
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 	    // We don't attempt to handle something that would cause
7680Sstevel@tonic-gate 	    // arithmetic overflow.
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 	    if ((n == Integer.MAX_VALUE) || (n == Integer.MIN_VALUE)) {
7710Sstevel@tonic-gate 		throw
7720Sstevel@tonic-gate 		    new ServiceLocationException(
7730Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
7740Sstevel@tonic-gate 				"v1_query_error",
7750Sstevel@tonic-gate 				new Object[] {query});
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate 	    }
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 	    // Construct a query that includes everything
7800Sstevel@tonic-gate 	    //  to the correct side.
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate 	    buf.append(OPEN_PAREN);
7830Sstevel@tonic-gate 	    buf.append(tag);
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 	    if (compOp == LESS_OP) {
7860Sstevel@tonic-gate 		buf.append(LESS_OP);
7870Sstevel@tonic-gate 		buf.append(EQUAL_OP);
7880Sstevel@tonic-gate 		buf.append(n - 1);
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 	    } else {
7910Sstevel@tonic-gate 		buf.append(GREATER_OP);
7920Sstevel@tonic-gate 		buf.append(EQUAL_OP);
7930Sstevel@tonic-gate 		buf.append(n + 1);
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	    }
7960Sstevel@tonic-gate 
7970Sstevel@tonic-gate 	    buf.append(CLOSE_PAREN);
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 	} else {
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 	    // Simple, single operator. Just add it with the
8020Sstevel@tonic-gate 	    //  value.
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate 	    buf.append(OPEN_PAREN);
8050Sstevel@tonic-gate 	    buf.append(tag);
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	    // Need to distinguish less and greater equal.
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 	    if (compOp == LEQUAL_OP) {
8100Sstevel@tonic-gate 		buf.append(LESS_OP);
8110Sstevel@tonic-gate 		buf.append(EQUAL_OP);
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 	    } else if (compOp == GEQUAL_OP) {
8140Sstevel@tonic-gate 		buf.append(GREATER_OP);
8150Sstevel@tonic-gate 		buf.append(EQUAL_OP);
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 	    } else {
8180Sstevel@tonic-gate 		buf.append(compOp);
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate 	    }
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate 	    buf.append(valTok);
8230Sstevel@tonic-gate 	    buf.append(CLOSE_PAREN);
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate 	}
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate     }
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate     // Gather tokens with embedded whitespace and return.
8300Sstevel@tonic-gate 
parseTag(StreamTokenizer tk, boolean ampStart)8310Sstevel@tonic-gate     private String parseTag(StreamTokenizer tk, boolean ampStart)
8320Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	String value = "";
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 	// Take care of corner case here.
8370Sstevel@tonic-gate 
8380Sstevel@tonic-gate 	if (ampStart) {
8390Sstevel@tonic-gate 	    value = value +"&";
8400Sstevel@tonic-gate 	    ampStart = false;
8410Sstevel@tonic-gate 	}
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 	do {
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 	    if (tk.ttype == StreamTokenizer.TT_WORD) {
8460Sstevel@tonic-gate 		value += tk.sval;
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	    } else if ((char)tk.ttype == SPACE) {
8490Sstevel@tonic-gate 		value = value + " ";
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	    } else if ((char)tk.ttype == AND_OP) {
8520Sstevel@tonic-gate 		value = value + "&";
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 	    } else {
8550Sstevel@tonic-gate 		break;
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	    }
8580Sstevel@tonic-gate 	    tk.nextToken();
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate 	} while (true);
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 	return value.trim();  // removes trailing whitespace...
8630Sstevel@tonic-gate     }
8640Sstevel@tonic-gate 
parseOperator(StreamTokenizer tk)8650Sstevel@tonic-gate     private char parseOperator(StreamTokenizer tk)
8660Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 	int tok = tk.ttype;
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate 	// If the token is a close paren, then this was a keyword
8710Sstevel@tonic-gate 	// (e.g. "(foo)". Return the present operator.
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate 	if ((char)tok == CLOSE_PAREN) {
8740Sstevel@tonic-gate 	    return PRESENT;
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate 	}
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	if (tok != EQUAL_OP && tok != NOT_OP &&
8790Sstevel@tonic-gate 	    tok != LESS_OP && tok != GREATER_OP) {
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate 	    throw
8820Sstevel@tonic-gate 		new ServiceLocationException(
8830Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
8840Sstevel@tonic-gate 				"v1_query_error",
8850Sstevel@tonic-gate 				new Object[] {query});
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate 	}
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 	char compOp = (char)tok;
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 	// Get the next token.
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 	tok = tk.nextToken();
8940Sstevel@tonic-gate 
8950Sstevel@tonic-gate 	// Look for dual character operators.
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 	if ((char)tok == EQUAL_OP) {
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 	    // Here, we can have either "!=", "<=", ">=", or "==".
9000Sstevel@tonic-gate 	    //  Anything else is wrong.
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 	    if (compOp != LESS_OP && compOp != GREATER_OP &&
9030Sstevel@tonic-gate 		compOp != EQUAL_OP && compOp != NOT_OP) {
9040Sstevel@tonic-gate 		throw
9050Sstevel@tonic-gate 		    new ServiceLocationException(
9060Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
9070Sstevel@tonic-gate 				"v1_query_error",
9080Sstevel@tonic-gate 				new Object[] {query});
9090Sstevel@tonic-gate 	    }
9100Sstevel@tonic-gate 
9110Sstevel@tonic-gate 	    // Assign the right dual operator.
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate 	    if (compOp == LESS_OP) {
9140Sstevel@tonic-gate 		compOp = LEQUAL_OP;
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	    } else if (compOp == GREATER_OP) {
9170Sstevel@tonic-gate 		compOp = GEQUAL_OP;
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 	    }
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	} else if (compOp != LESS_OP && compOp != GREATER_OP) {
9220Sstevel@tonic-gate 
9230Sstevel@tonic-gate 	    // Error if the comparison operator was something other
9240Sstevel@tonic-gate 	    //  than ``<'' or ``>'' and there is no equal. This
9250Sstevel@tonic-gate 	    //  rules out ``!'' or ``='' alone.
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 	    throw
9280Sstevel@tonic-gate 		new ServiceLocationException(
9290Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
9300Sstevel@tonic-gate 				"v1_query_error",
9310Sstevel@tonic-gate 				new Object[] {query});
9320Sstevel@tonic-gate 
9330Sstevel@tonic-gate 	} else {
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	    // Push back the last token if it wasn't a two character operator.
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 	    tk.pushBack();
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate 	}
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 	return compOp;
9420Sstevel@tonic-gate     }
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate 
parseValue(StreamTokenizer tk)9450Sstevel@tonic-gate     private String parseValue(StreamTokenizer tk)
9460Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	int tok = 0;
9490Sstevel@tonic-gate 	StringBuffer valTok = new StringBuffer();
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate 	// Eat leading whitespace.
9520Sstevel@tonic-gate 
9530Sstevel@tonic-gate 	tok = eatWhite(tk);
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate 	// If the first value is a paren, then we've got an
9560Sstevel@tonic-gate 	//  opaque.
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 	if ((char)tok == OPEN_PAREN) {
9590Sstevel@tonic-gate 
9600Sstevel@tonic-gate 	    valTok.append("(");
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 	    // Collect all tokens up to the closing paren.
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate 	    do {
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 		tok = tk.nextToken();
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 		// It's a closing paren. break out of the loop.
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 		if ((char)tok == CLOSE_PAREN) {
9710Sstevel@tonic-gate 		    valTok.append(")");
9720Sstevel@tonic-gate 		    break;
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate 		} else if ((char)tok == EQUAL_OP) {
9750Sstevel@tonic-gate 		    valTok.append("=");
9760Sstevel@tonic-gate 
9770Sstevel@tonic-gate 		} else if (tok == StreamTokenizer.TT_WORD) {
9780Sstevel@tonic-gate 		    valTok.append(tk.sval);
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 		} else {
9810Sstevel@tonic-gate 		    throw
9820Sstevel@tonic-gate 			new ServiceLocationException(
9830Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
9840Sstevel@tonic-gate 				"v1_query_error",
9850Sstevel@tonic-gate 				new Object[] {query});
9860Sstevel@tonic-gate 		}
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 	    } while (true);
9890Sstevel@tonic-gate 
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	    // Eat whitespace until closing paren.
9920Sstevel@tonic-gate 
9930Sstevel@tonic-gate 	    tok = eatWhite(tk);
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	    if ((char)tok != CLOSE_PAREN) {
9960Sstevel@tonic-gate 		throw
9970Sstevel@tonic-gate 		    new ServiceLocationException(
9980Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
9990Sstevel@tonic-gate 				"v1_query_error",
10000Sstevel@tonic-gate 				new Object[] {query});
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	    }
10030Sstevel@tonic-gate 
10040Sstevel@tonic-gate 	} else {
10050Sstevel@tonic-gate 
10060Sstevel@tonic-gate 	    // Error if just a closed paren.
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate 	    if (tok == CLOSE_PAREN) {
10090Sstevel@tonic-gate 		throw
10100Sstevel@tonic-gate 		    new ServiceLocationException(
10110Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
10120Sstevel@tonic-gate 				"v1_query_error",
10130Sstevel@tonic-gate 				new Object[] {query});
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate 	    }
10160Sstevel@tonic-gate 
10170Sstevel@tonic-gate 	    do {
10180Sstevel@tonic-gate 
10190Sstevel@tonic-gate 		// Append the token if a WORD
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 		if (tok == StreamTokenizer.TT_WORD) {
10220Sstevel@tonic-gate 		    valTok.append(tk.sval);
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 		} else if ((tok != StreamTokenizer.TT_EOF) &&
10250Sstevel@tonic-gate 			   (tok != StreamTokenizer.TT_EOL) &&
10260Sstevel@tonic-gate 			   (tok != CLOSE_PAREN)) {
10270Sstevel@tonic-gate 
10280Sstevel@tonic-gate 		    // Otherwise, it's a token char, so append.
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate 		    valTok.append((char)tok);
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate 		}
10330Sstevel@tonic-gate 
10340Sstevel@tonic-gate 		tok = tk.nextToken();
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	    } while (tok != CLOSE_PAREN);
10370Sstevel@tonic-gate 	}
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 	// If a wildcard, remove wildcard stars here for later re-insertion.
10400Sstevel@tonic-gate 
10410Sstevel@tonic-gate 	String strval = valTok.toString().trim();
10420Sstevel@tonic-gate 	boolean wildstart = false;
10430Sstevel@tonic-gate 	boolean wildend = false;
10440Sstevel@tonic-gate 
10450Sstevel@tonic-gate 	if (strval.startsWith(WILDCARD)) {
10460Sstevel@tonic-gate 	    wildstart = true;
10470Sstevel@tonic-gate 	    strval = strval.substring(1, strval.length());
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate 	}
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate 	if (strval.endsWith(WILDCARD)) {
10520Sstevel@tonic-gate 	    wildend = true;
10530Sstevel@tonic-gate 	    strval = strval.substring(0, strval.length()-1);
10540Sstevel@tonic-gate 
10550Sstevel@tonic-gate 	}
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate 	// Evaluate the value.
10580Sstevel@tonic-gate 
10590Sstevel@tonic-gate 	Object val =
10600Sstevel@tonic-gate 	    ServiceLocationAttributeV1.evaluate(strval, charCode);
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	// Now convert to v2 format, and return.
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate 	if (val instanceof String) {
10650Sstevel@tonic-gate 	    strval =
10660Sstevel@tonic-gate 		ServiceLocationAttribute.escapeAttributeString(val.toString(),
10670Sstevel@tonic-gate 							       false);
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	    // Add wildcards back in.
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate 	    if (wildstart) {
10720Sstevel@tonic-gate 		strval = WILDCARD + strval;
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate 	    }
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate 	    if (wildend) {
10770Sstevel@tonic-gate 		strval = strval + WILDCARD;
10780Sstevel@tonic-gate 
10790Sstevel@tonic-gate 	    }
10800Sstevel@tonic-gate 
10810Sstevel@tonic-gate 	} else {
10820Sstevel@tonic-gate 	    strval = val.toString();
10830Sstevel@tonic-gate 
10840Sstevel@tonic-gate 	}
10850Sstevel@tonic-gate 
10860Sstevel@tonic-gate 	return strval;
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate     }
10890Sstevel@tonic-gate 
10900Sstevel@tonic-gate     // Eat whitespace.
10910Sstevel@tonic-gate 
eatWhite(StreamTokenizer tk)10920Sstevel@tonic-gate     private int eatWhite(StreamTokenizer tk)
10930Sstevel@tonic-gate 	throws IOException {
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 	int tok = tk.nextToken();
10960Sstevel@tonic-gate 
10970Sstevel@tonic-gate 	while (tok == SPACE) {
10980Sstevel@tonic-gate 	    tok = tk.nextToken();
10990Sstevel@tonic-gate 
11000Sstevel@tonic-gate 	}
11010Sstevel@tonic-gate 
11020Sstevel@tonic-gate 	return tok;
11030Sstevel@tonic-gate     }
11040Sstevel@tonic-gate }
1105