17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*9a70fc3bSMark J. Nelson  * Common Development and Distribution License (the "License").
6*9a70fc3bSMark J. Nelson  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
227c478bd9Sstevel@tonic-gate  * Copyright 2001,2003 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  *
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate //  Parser.java:      LDAP Parser for those service stores that need it.
287c478bd9Sstevel@tonic-gate //  Author:           James Kempf
297c478bd9Sstevel@tonic-gate //  Created On:       Mon Apr 27 08:11:08 1998
307c478bd9Sstevel@tonic-gate //  Last Modified By: James Kempf
317c478bd9Sstevel@tonic-gate //  Last Modified On: Mon Mar  1 08:29:36 1999
327c478bd9Sstevel@tonic-gate //  Update Count:     45
337c478bd9Sstevel@tonic-gate //
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate package com.sun.slp;
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate import java.util.*;
387c478bd9Sstevel@tonic-gate import java.io.*;
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate /**
417c478bd9Sstevel@tonic-gate  * The Parser class implements LDAP query parsing for ServiceStoreInMemory.
427c478bd9Sstevel@tonic-gate  * It is an internal class because it must know about the internal
437c478bd9Sstevel@tonic-gate  * structure of the hashtables.
447c478bd9Sstevel@tonic-gate  *
457c478bd9Sstevel@tonic-gate  * @author James Kempf
467c478bd9Sstevel@tonic-gate  */
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate abstract class Parser extends Object {
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate     final private static char NONASCII_LOWER = '\u0080';
517c478bd9Sstevel@tonic-gate     final private static char NONASCII_UPPER = '\uffff';
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate     final static char EQUAL = '=';
547c478bd9Sstevel@tonic-gate     final static char LESS = '<';
557c478bd9Sstevel@tonic-gate     final static char GREATER = '>';
567c478bd9Sstevel@tonic-gate     private final static char STAR = '*';
577c478bd9Sstevel@tonic-gate     final static char PRESENT = STAR;
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate     private final static char OPAREN = '(';
607c478bd9Sstevel@tonic-gate     private final static char CPAREN = ')';
617c478bd9Sstevel@tonic-gate     private final static char APPROX = '~';
627c478bd9Sstevel@tonic-gate     private final static char NOT = '!';
637c478bd9Sstevel@tonic-gate     private final static char AND = '&';
647c478bd9Sstevel@tonic-gate     private final static char OR = '|';
657c478bd9Sstevel@tonic-gate     private final static char SPACE = ' ';
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate     /**
687c478bd9Sstevel@tonic-gate      * Record for returning stuff to the service store.
697c478bd9Sstevel@tonic-gate      *
707c478bd9Sstevel@tonic-gate      * @author James Kempf
717c478bd9Sstevel@tonic-gate      */
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate     static final class ParserRecord extends Object {
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate 	Hashtable services = new Hashtable();
767c478bd9Sstevel@tonic-gate 	Hashtable signatures = new Hashtable();
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate     }
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate     /**
827c478bd9Sstevel@tonic-gate      * The QueryEvaluator interface evaluates a term in a query, given
837c478bd9Sstevel@tonic-gate      * the attribute id, the operator, the object, and whether the
847c478bd9Sstevel@tonic-gate      * term is currently under negation from a not operator. Only those
857c478bd9Sstevel@tonic-gate      * ServiceStore implemenations that want to use the Parser
867c478bd9Sstevel@tonic-gate      * class to perform query parsing must provide this.
877c478bd9Sstevel@tonic-gate      *
887c478bd9Sstevel@tonic-gate      * @author James Kempf
897c478bd9Sstevel@tonic-gate      */
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate     interface QueryEvaluator {
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	/**
947c478bd9Sstevel@tonic-gate 	 * Evaluate the query, storing away the services that match.
957c478bd9Sstevel@tonic-gate 	 *
967c478bd9Sstevel@tonic-gate 	 * @param tag The attribute tag for the term.
977c478bd9Sstevel@tonic-gate 	 * @param op The term operator.
987c478bd9Sstevel@tonic-gate 	 * @param pattern the operand of the term.
997c478bd9Sstevel@tonic-gate 	 * @param invert True if the results of the comparison should be
1007c478bd9Sstevel@tonic-gate 	 *		     inverted due to a not operator.
1017c478bd9Sstevel@tonic-gate 	 * @param returns Hashtable for the returns. The returns are
1027c478bd9Sstevel@tonic-gate 	 *		      structured exactly like the hashtable
1037c478bd9Sstevel@tonic-gate 	 *		      returned from findServices().
1047c478bd9Sstevel@tonic-gate 	 * @return True if the term matched, false if not.
1057c478bd9Sstevel@tonic-gate 	 */
1067c478bd9Sstevel@tonic-gate 
evaluate(AttributeString tag, char op, Object pattern, boolean invert, ParserRecord returns)1077c478bd9Sstevel@tonic-gate 	boolean evaluate(AttributeString tag,
1087c478bd9Sstevel@tonic-gate 			 char op,
1097c478bd9Sstevel@tonic-gate 			 Object pattern,
1107c478bd9Sstevel@tonic-gate 			 boolean invert,
1117c478bd9Sstevel@tonic-gate 			 ParserRecord returns)
1127c478bd9Sstevel@tonic-gate 	    throws ServiceLocationException;
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate     }
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate     /**
1177c478bd9Sstevel@tonic-gate      * Parse a query and incrementally evaluate.
1187c478bd9Sstevel@tonic-gate      *
1197c478bd9Sstevel@tonic-gate      * @param urlLevel Hashtable of langlevel hashtables containing
1207c478bd9Sstevel@tonic-gate      *                 registrations for the service type and scope.
1217c478bd9Sstevel@tonic-gate      * @param query The query. Escapes have not yet been processed.
1227c478bd9Sstevel@tonic-gate      * @param ret   Vector for returned records.
1237c478bd9Sstevel@tonic-gate      * @param locale Locale in which to interpret query strings.
1247c478bd9Sstevel@tonic-gate      * @param ret ParserRecord in which to return the results.
1257c478bd9Sstevel@tonic-gate      */
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate     static void
parseAndEvaluateQuery(String query, Parser.QueryEvaluator ev, Locale locale, ParserRecord ret)1287c478bd9Sstevel@tonic-gate 	parseAndEvaluateQuery(String query,
1297c478bd9Sstevel@tonic-gate 			      Parser.QueryEvaluator ev,
1307c478bd9Sstevel@tonic-gate 			      Locale locale,
1317c478bd9Sstevel@tonic-gate 			      ParserRecord ret)
1327c478bd9Sstevel@tonic-gate 	throws ServiceLocationException {
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	// Create and initialize lexical analyzer.
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	StreamTokenizer tk = new StreamTokenizer(new StringReader(query));
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 	tk.resetSyntax();  		 // make all chars ordinary...
1397c478bd9Sstevel@tonic-gate 	tk.wordChars('\177','\177');	 // treat controls as part of tokens
1407c478bd9Sstevel@tonic-gate 	tk.wordChars('\000', SPACE);
1417c478bd9Sstevel@tonic-gate 	tk.ordinaryChar(NOT);              // 'NOT' operator
1427c478bd9Sstevel@tonic-gate 	tk.wordChars('"', '%');
1437c478bd9Sstevel@tonic-gate 	tk.ordinaryChar(AND);              // 'AND' operator
1447c478bd9Sstevel@tonic-gate 	tk.wordChars('\'', '\'');
1457c478bd9Sstevel@tonic-gate 	tk.ordinaryChar(OPAREN);           // filter grouping
1467c478bd9Sstevel@tonic-gate 	tk.ordinaryChar(CPAREN);
1477c478bd9Sstevel@tonic-gate 	tk.ordinaryChar(STAR);             // present operator
1487c478bd9Sstevel@tonic-gate 	tk.wordChars('+', '{');
1497c478bd9Sstevel@tonic-gate 	tk.ordinaryChar(OR);               // 'OR' operator
1507c478bd9Sstevel@tonic-gate 	tk.wordChars('}', '~');
1517c478bd9Sstevel@tonic-gate 	tk.ordinaryChar(EQUAL);            // comparision operator
1527c478bd9Sstevel@tonic-gate 	tk.ordinaryChar(LESS);             // less operator
1537c478bd9Sstevel@tonic-gate 	tk.ordinaryChar(GREATER);          // greater operator
1547c478bd9Sstevel@tonic-gate 	tk.ordinaryChar(APPROX);           // approx operator
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	// Begin parsing.
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	try {
1597c478bd9Sstevel@tonic-gate 	    ParserRecord rec = parseFilter(tk, ev, locale, false, true);
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	    // Throw exception if anything occurs after the
1627c478bd9Sstevel@tonic-gate 	    //  parsed expression.
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	    if (tk.nextToken() != StreamTokenizer.TT_EOF) {
1657c478bd9Sstevel@tonic-gate 		throw
1667c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
1677c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1687c478bd9Sstevel@tonic-gate 				"par_char_closing",
1697c478bd9Sstevel@tonic-gate 				new Object[] {query});
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 	    }
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	    // Merge in returns. Use OR operator so all returned
1747c478bd9Sstevel@tonic-gate 	    //  values are merged in.
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	    mergeQueryReturns(ret, rec, OR);
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 	} catch (IOException ex) {
1797c478bd9Sstevel@tonic-gate 	    throw
1807c478bd9Sstevel@tonic-gate 		new ServiceLocationException(
1817c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
1827c478bd9Sstevel@tonic-gate 				"par_syn_err",
1837c478bd9Sstevel@tonic-gate 				new Object[] {query});
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 	}
1867c478bd9Sstevel@tonic-gate     }
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate     //
1897c478bd9Sstevel@tonic-gate     // Routines for dealing with parse returns record.
1907c478bd9Sstevel@tonic-gate     //
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate     // Merge source to target. The target has already
1937c478bd9Sstevel@tonic-gate     //  been precharged with ones that must match
1947c478bd9Sstevel@tonic-gate     //  if the op is AND. If it's OR, then simply
1957c478bd9Sstevel@tonic-gate     //  stuff them in.
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate     private static boolean
mergeQueryReturns(ParserRecord target, ParserRecord source, char op)1987c478bd9Sstevel@tonic-gate 	mergeQueryReturns(ParserRecord target,
1997c478bd9Sstevel@tonic-gate 			  ParserRecord source,
2007c478bd9Sstevel@tonic-gate 			  char op) {
2017c478bd9Sstevel@tonic-gate 	Hashtable targetServices = target.services;
2027c478bd9Sstevel@tonic-gate 	Hashtable sourceServices = source.services;
2037c478bd9Sstevel@tonic-gate 	boolean eval;
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	if (op == AND) {
2067c478bd9Sstevel@tonic-gate 	    eval = mergeTablesWithAnd(targetServices, sourceServices);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	} else {
2097c478bd9Sstevel@tonic-gate 	    eval = mergeTablesWithOr(targetServices, sourceServices);
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	}
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	Hashtable targetSigs = target.signatures;
2147c478bd9Sstevel@tonic-gate 	Hashtable sourceSigs = source.signatures;
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	if (op == AND) {
2177c478bd9Sstevel@tonic-gate 	    mergeTablesWithAnd(targetSigs, sourceSigs);
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	} else {
2207c478bd9Sstevel@tonic-gate 	    mergeTablesWithOr(targetSigs, sourceSigs);
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	}
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 	return eval;
2257c478bd9Sstevel@tonic-gate     }
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate     // Merge tables by removing anything from target that isn't in source.
2297c478bd9Sstevel@tonic-gate 
mergeTablesWithAnd(Hashtable target, Hashtable source)2307c478bd9Sstevel@tonic-gate     private static boolean mergeTablesWithAnd(Hashtable target,
2317c478bd9Sstevel@tonic-gate 					      Hashtable source) {
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	Enumeration en = target.keys();
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 	// Remove any from target that aren't in source.
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	while (en.hasMoreElements()) {
2387c478bd9Sstevel@tonic-gate 	    Object tkey = en.nextElement();
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	    if (source.get(tkey) == null) {
2417c478bd9Sstevel@tonic-gate 		target.remove(tkey);
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	    }
2447c478bd9Sstevel@tonic-gate 	}
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	// If there's nothing left, return false to indicate no further
2477c478bd9Sstevel@tonic-gate 	//  evaluation needed.
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 	if (target.size() <= 0) {
2507c478bd9Sstevel@tonic-gate 	    return false;
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	}
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 	return true;
2557c478bd9Sstevel@tonic-gate     }
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate     // Merge tables by adding everything from source into target.
2587c478bd9Sstevel@tonic-gate 
mergeTablesWithOr(Hashtable target, Hashtable source)2597c478bd9Sstevel@tonic-gate     private static boolean mergeTablesWithOr(Hashtable target,
2607c478bd9Sstevel@tonic-gate 					     Hashtable source) {
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	Enumeration en = source.keys();
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	while (en.hasMoreElements()) {
2657c478bd9Sstevel@tonic-gate 	    Object skey = en.nextElement();
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	    target.put(skey, source.get(skey));
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	}
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	return true;
2727c478bd9Sstevel@tonic-gate     }
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate     //
2757c478bd9Sstevel@tonic-gate     // Parsing for various productions.
2767c478bd9Sstevel@tonic-gate     //
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate     // Parse the filter production.
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate     private static ParserRecord
parseFilter(StreamTokenizer tk, Parser.QueryEvaluator ev, Locale locale, boolean invert, boolean eval)2827c478bd9Sstevel@tonic-gate 	parseFilter(StreamTokenizer tk,
2837c478bd9Sstevel@tonic-gate 		    Parser.QueryEvaluator ev,
2847c478bd9Sstevel@tonic-gate 		    Locale locale,
2857c478bd9Sstevel@tonic-gate 		    boolean invert,
2867c478bd9Sstevel@tonic-gate 		    boolean eval)
2877c478bd9Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	ParserRecord ret = null;
2907c478bd9Sstevel@tonic-gate 	int tok = tk.nextToken();
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	// Check for opening paren.
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	if (tok != OPAREN) {
2957c478bd9Sstevel@tonic-gate 	    throw
2967c478bd9Sstevel@tonic-gate 		new ServiceLocationException(
2977c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
2987c478bd9Sstevel@tonic-gate 				"par_init_par",
2997c478bd9Sstevel@tonic-gate 				new Object[0]);
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 	}
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	// Parse inside.
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	tok = tk.nextToken();
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	// Check for a logical operator.
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	if (tok == AND || tok == OR) {
3107c478bd9Sstevel@tonic-gate 	    ret = parseFilterlist(tk, ev, locale, (char)tok, invert, eval);
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	} else if (tok == NOT) {
3137c478bd9Sstevel@tonic-gate 	    ret =  parseFilter(tk, ev, locale, !invert, eval);
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	} else if (tok == StreamTokenizer.TT_WORD) {
3167c478bd9Sstevel@tonic-gate 	    tk.pushBack();
3177c478bd9Sstevel@tonic-gate 	    ret =  parseItem(tk, ev, locale, invert, eval);
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	} else {
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate 	    // Since we've covered the ASCII character set, the only other
3227c478bd9Sstevel@tonic-gate 	    //  thing that could be here is a nonASCII character. We push it
3237c478bd9Sstevel@tonic-gate 	    //  back and deal with it in parseItem().
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	    tk.pushBack();
3267c478bd9Sstevel@tonic-gate 	    ret = parseItem(tk, ev, locale, invert, eval);
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	}
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 	tok = tk.nextToken();
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	// Check for closing paren.
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	if (tok != CPAREN) {
3357c478bd9Sstevel@tonic-gate 	    throw
3367c478bd9Sstevel@tonic-gate 		new ServiceLocationException(
3377c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
3387c478bd9Sstevel@tonic-gate 				"par_final_par",
3397c478bd9Sstevel@tonic-gate 				new Object[0]);
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	}
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 	return ret;
3447c478bd9Sstevel@tonic-gate     }
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate     // Parse a filterlist production.
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate     private static ParserRecord
parseFilterlist(StreamTokenizer tk, Parser.QueryEvaluator ev, Locale locale, char op, boolean invert, boolean eval)3497c478bd9Sstevel@tonic-gate 	parseFilterlist(StreamTokenizer tk,
3507c478bd9Sstevel@tonic-gate 			Parser.QueryEvaluator ev,
3517c478bd9Sstevel@tonic-gate 			Locale locale,
3527c478bd9Sstevel@tonic-gate 			char op,
3537c478bd9Sstevel@tonic-gate 			boolean invert,
3547c478bd9Sstevel@tonic-gate 			boolean eval)
3557c478bd9Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
3567c478bd9Sstevel@tonic-gate 	boolean match;
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	ParserRecord mrex = null;
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 	// Parse through the list of filters.
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	do {
3637c478bd9Sstevel@tonic-gate 	    ParserRecord prex = null;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	    if (op == AND) {
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 		prex = parseFilter(tk, ev, locale, invert, eval);
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 	    } else {
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 		prex = parseFilter(tk, ev, locale, invert, eval);
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 	    }
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	    // We need to start off with something.
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	    if (mrex == null) {
3787c478bd9Sstevel@tonic-gate 		mrex = prex;
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	    } else {
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 		// Merge in returns.
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 		eval = mergeQueryReturns(mrex, prex, op);
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	    }
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	    // Look for ending paren.
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	    int tok = tk.nextToken();
3917c478bd9Sstevel@tonic-gate 	    tk.pushBack();
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	    if (tok == CPAREN) {
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 		return mrex;
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	    }
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	} while (true);
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate     }
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate     // Parse item.
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate     private static ParserRecord
parseItem(StreamTokenizer tk, Parser.QueryEvaluator ev, Locale locale, boolean invert, boolean eval)4067c478bd9Sstevel@tonic-gate 	parseItem(StreamTokenizer tk,
4077c478bd9Sstevel@tonic-gate 		  Parser.QueryEvaluator ev,
4087c478bd9Sstevel@tonic-gate 		  Locale locale,
4097c478bd9Sstevel@tonic-gate 		  boolean invert,
4107c478bd9Sstevel@tonic-gate 		  boolean eval)
4117c478bd9Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	ParserRecord prex = new ParserRecord();
4147c478bd9Sstevel@tonic-gate 	AttributeString attr = parseAttr(tk, locale);
4157c478bd9Sstevel@tonic-gate 	char op = parseOp(tk);
4167c478bd9Sstevel@tonic-gate 	Object value = null;
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	// If operator is PRESENT, then check whether
4197c478bd9Sstevel@tonic-gate 	//  it's not really a wildcarded value. If the next
4207c478bd9Sstevel@tonic-gate 	//  token isn't a closing paren, then it's
4217c478bd9Sstevel@tonic-gate 	//  a wildcarded value.
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	if (op == PRESENT) {
4247c478bd9Sstevel@tonic-gate 	    int tok = tk.nextToken();
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	    tk.pushBack();  // ...in any event...
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 	    if ((char)tok != CPAREN) { // It's a wildcarded pattern...
4297c478bd9Sstevel@tonic-gate 		op = EQUAL;
4307c478bd9Sstevel@tonic-gate 		value = parseValue(tk, locale);
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 		// Need to convert to a wildcarded pattern. Regardless
4337c478bd9Sstevel@tonic-gate 		//  of type, since wildcard makes the type be a
4347c478bd9Sstevel@tonic-gate 		//  string.
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 		value =
4377c478bd9Sstevel@tonic-gate 		    new AttributePattern(PRESENT + value.toString(), locale);
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	    }
4407c478bd9Sstevel@tonic-gate 	} else {
4417c478bd9Sstevel@tonic-gate 	    value = parseValue(tk, locale);
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 	}
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	// Check for inappropriate pattern.
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	if (value instanceof AttributePattern &&
4487c478bd9Sstevel@tonic-gate 	    ((AttributePattern)value).isWildcarded() &&
4497c478bd9Sstevel@tonic-gate 	    op != EQUAL) {
4507c478bd9Sstevel@tonic-gate 	    throw
4517c478bd9Sstevel@tonic-gate 		new ServiceLocationException(
4527c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
4537c478bd9Sstevel@tonic-gate 				"par_wild_op",
4547c478bd9Sstevel@tonic-gate 				new Object[] {new Character(op)});
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	}
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	// Check for inappropriate boolean.
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 	if ((value instanceof Boolean ||
4617c478bd9Sstevel@tonic-gate 	    value instanceof Opaque) &&
4627c478bd9Sstevel@tonic-gate 	    (op == GREATER || op == LESS)) {
4637c478bd9Sstevel@tonic-gate 	    throw
4647c478bd9Sstevel@tonic-gate 		new ServiceLocationException(
4657c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
4667c478bd9Sstevel@tonic-gate 				"par_bool_op",
4677c478bd9Sstevel@tonic-gate 				new Object[] {new Character(op)});
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	}
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 	// Check for wrong operator with keyword.
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	if ((value == null || value.toString().length() <= 0) &&
4747c478bd9Sstevel@tonic-gate 	    op != PRESENT) {
4757c478bd9Sstevel@tonic-gate 	    throw
4767c478bd9Sstevel@tonic-gate 		new ServiceLocationException(
4777c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
4787c478bd9Sstevel@tonic-gate 				"par_key_op",
4797c478bd9Sstevel@tonic-gate 				new Object[] {new Character(op)});
4807c478bd9Sstevel@tonic-gate 	}
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	if (eval) {
4837c478bd9Sstevel@tonic-gate 	    /*
4847c478bd9Sstevel@tonic-gate 	     * Try and evaluate the query. If the evaluation failed and the
4857c478bd9Sstevel@tonic-gate 	     * value was an Integer or Boolean try again after converting the
4867c478bd9Sstevel@tonic-gate 	     * value to a String. This is because the value in the query will
4877c478bd9Sstevel@tonic-gate 	     * be converted to an Integer or Boolean in preference to a String
4887c478bd9Sstevel@tonic-gate 	     * even though the query starts out as a String.  Hence when an
4897c478bd9Sstevel@tonic-gate 	     * attribute is registered with a String value that can equally be
4907c478bd9Sstevel@tonic-gate 	     * parsed as a valid Integer or Boolean value the String will
4917c478bd9Sstevel@tonic-gate 	     * almost always be parsed as an Integer or Boolean. This results
4927c478bd9Sstevel@tonic-gate 	     * in the failing of the initial type check when performing the
4937c478bd9Sstevel@tonic-gate 	     * query. By converting the value to a String there is another shot
4947c478bd9Sstevel@tonic-gate 	     * at fulfulling the query.
4957c478bd9Sstevel@tonic-gate 	     */
4967c478bd9Sstevel@tonic-gate 	    if (!ev.evaluate(attr, op, value, invert, prex) &&
4977c478bd9Sstevel@tonic-gate 		    !(value instanceof AttributeString)) {
4987c478bd9Sstevel@tonic-gate 		ev.evaluate(attr,
4997c478bd9Sstevel@tonic-gate 			    op,
5007c478bd9Sstevel@tonic-gate 			    new AttributeString(
5017c478bd9Sstevel@tonic-gate 				value.toString().trim(),
5027c478bd9Sstevel@tonic-gate 				locale),
5037c478bd9Sstevel@tonic-gate 			    invert,
5047c478bd9Sstevel@tonic-gate 			    prex);
5057c478bd9Sstevel@tonic-gate 	    }
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	}
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 	return prex;
5107c478bd9Sstevel@tonic-gate     }
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate     // Parse attribute tag.
5137c478bd9Sstevel@tonic-gate 
parseAttr(StreamTokenizer tk, Locale locale)5147c478bd9Sstevel@tonic-gate     private static AttributeString parseAttr(StreamTokenizer tk, Locale locale)
5157c478bd9Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 	String str  = parsePotentialNonASCII(tk);
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	str =
5207c478bd9Sstevel@tonic-gate 	    ServiceLocationAttribute.unescapeAttributeString(str, true);
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 	return new AttributeString(str, locale);
5237c478bd9Sstevel@tonic-gate     }
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate     // Parse attribute operator.
5267c478bd9Sstevel@tonic-gate 
parseOp(StreamTokenizer tk)5277c478bd9Sstevel@tonic-gate     private static char parseOp(StreamTokenizer tk)
5287c478bd9Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate 	int tok = tk.nextToken();
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	// Identify operator
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 	switch (tok) {
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	case EQUAL:
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 	    // Is it present?
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 	    tok = tk.nextToken();
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	    if (tok == STAR) {
5437c478bd9Sstevel@tonic-gate 		return PRESENT;
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 	    } else {
5467c478bd9Sstevel@tonic-gate 		tk.pushBack();
5477c478bd9Sstevel@tonic-gate 		return EQUAL;
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	    }
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	case APPROX: case GREATER: case LESS:
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	    // Need equals.
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	    if (tk.nextToken() != EQUAL) {
5567c478bd9Sstevel@tonic-gate 		break;
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 	    }
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	    if (tok == APPROX) {
5617c478bd9Sstevel@tonic-gate 		tok = EQUAL;
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate 	    }
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	    return (char)tok;
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 	default:
5687c478bd9Sstevel@tonic-gate 	    break;
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 	}
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 	throw
5737c478bd9Sstevel@tonic-gate 	    new ServiceLocationException(
5747c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
5757c478bd9Sstevel@tonic-gate 				"par_comp_op",
5767c478bd9Sstevel@tonic-gate 				new Object[0]);
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate     }
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate     // Parse expression value.
5817c478bd9Sstevel@tonic-gate 
parseValue(StreamTokenizer tk, Locale locale)5827c478bd9Sstevel@tonic-gate     private static Object parseValue(StreamTokenizer tk, Locale locale)
5837c478bd9Sstevel@tonic-gate 	throws ServiceLocationException, IOException {
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 	// Parse until the next closing paren.
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	do {
5907c478bd9Sstevel@tonic-gate 	    int tok = tk.nextToken();
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	    if (tok == CPAREN) {
5937c478bd9Sstevel@tonic-gate 		tk.pushBack();
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 		Object o =
5967c478bd9Sstevel@tonic-gate 		    ServiceLocationAttribute.evaluate(buf.toString().trim());
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 		if (o instanceof String) {
5997c478bd9Sstevel@tonic-gate 		    o = new AttributePattern((String)o, locale);
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 		} else if (o instanceof byte[]) {
6027c478bd9Sstevel@tonic-gate 		    o = new Opaque((byte[])o);
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 		}
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 		return o;
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 	    } else if (tok != StreamTokenizer.TT_EOF) {
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 		if (tok == StreamTokenizer.TT_WORD) {
6117c478bd9Sstevel@tonic-gate 		    buf.append(tk.sval);
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 		} else if (tok == StreamTokenizer.TT_NUMBER) {
6147c478bd9Sstevel@tonic-gate 		    Assert.slpassert(false,
6157c478bd9Sstevel@tonic-gate 				  "par_ntok",
6167c478bd9Sstevel@tonic-gate 				  new Object[0]);
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 		} else {
6197c478bd9Sstevel@tonic-gate 		    buf.append((char)tok);
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 		}
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	    } else {
6247c478bd9Sstevel@tonic-gate 		throw
6257c478bd9Sstevel@tonic-gate 		    new ServiceLocationException(
6267c478bd9Sstevel@tonic-gate 				ServiceLocationException.PARSE_ERROR,
6277c478bd9Sstevel@tonic-gate 				"par_qend",
6287c478bd9Sstevel@tonic-gate 				new Object[0]);
6297c478bd9Sstevel@tonic-gate 	    }
6307c478bd9Sstevel@tonic-gate 	} while (true);
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate     }
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate     // NonASCII characters may be in the string. StreamTokenizer
6357c478bd9Sstevel@tonic-gate     //  can't handle them as part of words, so we need to resort to
6367c478bd9Sstevel@tonic-gate     //  this loop to handle it.
6377c478bd9Sstevel@tonic-gate 
parsePotentialNonASCII(StreamTokenizer tk)6387c478bd9Sstevel@tonic-gate     private static String parsePotentialNonASCII(StreamTokenizer tk)
6397c478bd9Sstevel@tonic-gate 	throws IOException {
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	StringBuffer buf = new StringBuffer();
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 	do {
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	    int tok = tk.nextToken();
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 	    if (tok == StreamTokenizer.TT_WORD) {
6487c478bd9Sstevel@tonic-gate 		buf.append(tk.sval);
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 	    } else if (((char)tok >= NONASCII_LOWER) &&
6517c478bd9Sstevel@tonic-gate 		       ((char)tok <= NONASCII_UPPER)) {
6527c478bd9Sstevel@tonic-gate 		buf.append((char)tok);
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	    } else {
6557c478bd9Sstevel@tonic-gate 		tk.pushBack();
6567c478bd9Sstevel@tonic-gate 		break;
6577c478bd9Sstevel@tonic-gate 
6587c478bd9Sstevel@tonic-gate 	    }
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	} while (true);
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	return buf.toString();
6637c478bd9Sstevel@tonic-gate     }
6647c478bd9Sstevel@tonic-gate }
665