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