1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * ident "%Z%%M% %I% %E% SMI" 24*7c478bd9Sstevel@tonic-gate * 25*7c478bd9Sstevel@tonic-gate * Copyright 2001,2003 Sun Microsystems, Inc. All rights reserved. 26*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 27*7c478bd9Sstevel@tonic-gate * 28*7c478bd9Sstevel@tonic-gate */ 29*7c478bd9Sstevel@tonic-gate 30*7c478bd9Sstevel@tonic-gate // SCCS Status: %W% %G% 31*7c478bd9Sstevel@tonic-gate // Parser.java: LDAP Parser for those service stores that need it. 32*7c478bd9Sstevel@tonic-gate // Author: James Kempf 33*7c478bd9Sstevel@tonic-gate // Created On: Mon Apr 27 08:11:08 1998 34*7c478bd9Sstevel@tonic-gate // Last Modified By: James Kempf 35*7c478bd9Sstevel@tonic-gate // Last Modified On: Mon Mar 1 08:29:36 1999 36*7c478bd9Sstevel@tonic-gate // Update Count: 45 37*7c478bd9Sstevel@tonic-gate // 38*7c478bd9Sstevel@tonic-gate 39*7c478bd9Sstevel@tonic-gate package com.sun.slp; 40*7c478bd9Sstevel@tonic-gate 41*7c478bd9Sstevel@tonic-gate import java.util.*; 42*7c478bd9Sstevel@tonic-gate import java.io.*; 43*7c478bd9Sstevel@tonic-gate 44*7c478bd9Sstevel@tonic-gate /** 45*7c478bd9Sstevel@tonic-gate * The Parser class implements LDAP query parsing for ServiceStoreInMemory. 46*7c478bd9Sstevel@tonic-gate * It is an internal class because it must know about the internal 47*7c478bd9Sstevel@tonic-gate * structure of the hashtables. 48*7c478bd9Sstevel@tonic-gate * 49*7c478bd9Sstevel@tonic-gate * @version %R%.%L% %D% 50*7c478bd9Sstevel@tonic-gate * @author James Kempf 51*7c478bd9Sstevel@tonic-gate */ 52*7c478bd9Sstevel@tonic-gate 53*7c478bd9Sstevel@tonic-gate abstract class Parser extends Object { 54*7c478bd9Sstevel@tonic-gate 55*7c478bd9Sstevel@tonic-gate final private static char NONASCII_LOWER = '\u0080'; 56*7c478bd9Sstevel@tonic-gate final private static char NONASCII_UPPER = '\uffff'; 57*7c478bd9Sstevel@tonic-gate 58*7c478bd9Sstevel@tonic-gate final static char EQUAL = '='; 59*7c478bd9Sstevel@tonic-gate final static char LESS = '<'; 60*7c478bd9Sstevel@tonic-gate final static char GREATER = '>'; 61*7c478bd9Sstevel@tonic-gate private final static char STAR = '*'; 62*7c478bd9Sstevel@tonic-gate final static char PRESENT = STAR; 63*7c478bd9Sstevel@tonic-gate 64*7c478bd9Sstevel@tonic-gate private final static char OPAREN = '('; 65*7c478bd9Sstevel@tonic-gate private final static char CPAREN = ')'; 66*7c478bd9Sstevel@tonic-gate private final static char APPROX = '~'; 67*7c478bd9Sstevel@tonic-gate private final static char NOT = '!'; 68*7c478bd9Sstevel@tonic-gate private final static char AND = '&'; 69*7c478bd9Sstevel@tonic-gate private final static char OR = '|'; 70*7c478bd9Sstevel@tonic-gate private final static char SPACE = ' '; 71*7c478bd9Sstevel@tonic-gate 72*7c478bd9Sstevel@tonic-gate /** 73*7c478bd9Sstevel@tonic-gate * Record for returning stuff to the service store. 74*7c478bd9Sstevel@tonic-gate * 75*7c478bd9Sstevel@tonic-gate * @version %R%.%L% %D% 76*7c478bd9Sstevel@tonic-gate * @author James Kempf 77*7c478bd9Sstevel@tonic-gate */ 78*7c478bd9Sstevel@tonic-gate 79*7c478bd9Sstevel@tonic-gate static final class ParserRecord extends Object { 80*7c478bd9Sstevel@tonic-gate 81*7c478bd9Sstevel@tonic-gate Hashtable services = new Hashtable(); 82*7c478bd9Sstevel@tonic-gate Hashtable signatures = new Hashtable(); 83*7c478bd9Sstevel@tonic-gate 84*7c478bd9Sstevel@tonic-gate } 85*7c478bd9Sstevel@tonic-gate 86*7c478bd9Sstevel@tonic-gate 87*7c478bd9Sstevel@tonic-gate /** 88*7c478bd9Sstevel@tonic-gate * The QueryEvaluator interface evaluates a term in a query, given 89*7c478bd9Sstevel@tonic-gate * the attribute id, the operator, the object, and whether the 90*7c478bd9Sstevel@tonic-gate * term is currently under negation from a not operator. Only those 91*7c478bd9Sstevel@tonic-gate * ServiceStore implemenations that want to use the Parser 92*7c478bd9Sstevel@tonic-gate * class to perform query parsing must provide this. 93*7c478bd9Sstevel@tonic-gate * 94*7c478bd9Sstevel@tonic-gate * @version %R%.%L% %D% 95*7c478bd9Sstevel@tonic-gate * @author James Kempf 96*7c478bd9Sstevel@tonic-gate */ 97*7c478bd9Sstevel@tonic-gate 98*7c478bd9Sstevel@tonic-gate interface QueryEvaluator { 99*7c478bd9Sstevel@tonic-gate 100*7c478bd9Sstevel@tonic-gate /** 101*7c478bd9Sstevel@tonic-gate * Evaluate the query, storing away the services that match. 102*7c478bd9Sstevel@tonic-gate * 103*7c478bd9Sstevel@tonic-gate * @param tag The attribute tag for the term. 104*7c478bd9Sstevel@tonic-gate * @param op The term operator. 105*7c478bd9Sstevel@tonic-gate * @param pattern the operand of the term. 106*7c478bd9Sstevel@tonic-gate * @param invert True if the results of the comparison should be 107*7c478bd9Sstevel@tonic-gate * inverted due to a not operator. 108*7c478bd9Sstevel@tonic-gate * @param returns Hashtable for the returns. The returns are 109*7c478bd9Sstevel@tonic-gate * structured exactly like the hashtable 110*7c478bd9Sstevel@tonic-gate * returned from findServices(). 111*7c478bd9Sstevel@tonic-gate * @return True if the term matched, false if not. 112*7c478bd9Sstevel@tonic-gate */ 113*7c478bd9Sstevel@tonic-gate 114*7c478bd9Sstevel@tonic-gate boolean evaluate(AttributeString tag, 115*7c478bd9Sstevel@tonic-gate char op, 116*7c478bd9Sstevel@tonic-gate Object pattern, 117*7c478bd9Sstevel@tonic-gate boolean invert, 118*7c478bd9Sstevel@tonic-gate ParserRecord returns) 119*7c478bd9Sstevel@tonic-gate throws ServiceLocationException; 120*7c478bd9Sstevel@tonic-gate 121*7c478bd9Sstevel@tonic-gate } 122*7c478bd9Sstevel@tonic-gate 123*7c478bd9Sstevel@tonic-gate /** 124*7c478bd9Sstevel@tonic-gate * Parse a query and incrementally evaluate. 125*7c478bd9Sstevel@tonic-gate * 126*7c478bd9Sstevel@tonic-gate * @param urlLevel Hashtable of langlevel hashtables containing 127*7c478bd9Sstevel@tonic-gate * registrations for the service type and scope. 128*7c478bd9Sstevel@tonic-gate * @param query The query. Escapes have not yet been processed. 129*7c478bd9Sstevel@tonic-gate * @param ret Vector for returned records. 130*7c478bd9Sstevel@tonic-gate * @param locale Locale in which to interpret query strings. 131*7c478bd9Sstevel@tonic-gate * @param ret ParserRecord in which to return the results. 132*7c478bd9Sstevel@tonic-gate */ 133*7c478bd9Sstevel@tonic-gate 134*7c478bd9Sstevel@tonic-gate static void 135*7c478bd9Sstevel@tonic-gate parseAndEvaluateQuery(String query, 136*7c478bd9Sstevel@tonic-gate Parser.QueryEvaluator ev, 137*7c478bd9Sstevel@tonic-gate Locale locale, 138*7c478bd9Sstevel@tonic-gate ParserRecord ret) 139*7c478bd9Sstevel@tonic-gate throws ServiceLocationException { 140*7c478bd9Sstevel@tonic-gate 141*7c478bd9Sstevel@tonic-gate // Create and initialize lexical analyzer. 142*7c478bd9Sstevel@tonic-gate 143*7c478bd9Sstevel@tonic-gate StreamTokenizer tk = new StreamTokenizer(new StringReader(query)); 144*7c478bd9Sstevel@tonic-gate 145*7c478bd9Sstevel@tonic-gate tk.resetSyntax(); // make all chars ordinary... 146*7c478bd9Sstevel@tonic-gate tk.wordChars('\177','\177'); // treat controls as part of tokens 147*7c478bd9Sstevel@tonic-gate tk.wordChars('\000', SPACE); 148*7c478bd9Sstevel@tonic-gate tk.ordinaryChar(NOT); // 'NOT' operator 149*7c478bd9Sstevel@tonic-gate tk.wordChars('"', '%'); 150*7c478bd9Sstevel@tonic-gate tk.ordinaryChar(AND); // 'AND' operator 151*7c478bd9Sstevel@tonic-gate tk.wordChars('\'', '\''); 152*7c478bd9Sstevel@tonic-gate tk.ordinaryChar(OPAREN); // filter grouping 153*7c478bd9Sstevel@tonic-gate tk.ordinaryChar(CPAREN); 154*7c478bd9Sstevel@tonic-gate tk.ordinaryChar(STAR); // present operator 155*7c478bd9Sstevel@tonic-gate tk.wordChars('+', '{'); 156*7c478bd9Sstevel@tonic-gate tk.ordinaryChar(OR); // 'OR' operator 157*7c478bd9Sstevel@tonic-gate tk.wordChars('}', '~'); 158*7c478bd9Sstevel@tonic-gate tk.ordinaryChar(EQUAL); // comparision operator 159*7c478bd9Sstevel@tonic-gate tk.ordinaryChar(LESS); // less operator 160*7c478bd9Sstevel@tonic-gate tk.ordinaryChar(GREATER); // greater operator 161*7c478bd9Sstevel@tonic-gate tk.ordinaryChar(APPROX); // approx operator 162*7c478bd9Sstevel@tonic-gate 163*7c478bd9Sstevel@tonic-gate // Begin parsing. 164*7c478bd9Sstevel@tonic-gate 165*7c478bd9Sstevel@tonic-gate try { 166*7c478bd9Sstevel@tonic-gate ParserRecord rec = parseFilter(tk, ev, locale, false, true); 167*7c478bd9Sstevel@tonic-gate 168*7c478bd9Sstevel@tonic-gate // Throw exception if anything occurs after the 169*7c478bd9Sstevel@tonic-gate // parsed expression. 170*7c478bd9Sstevel@tonic-gate 171*7c478bd9Sstevel@tonic-gate if (tk.nextToken() != StreamTokenizer.TT_EOF) { 172*7c478bd9Sstevel@tonic-gate throw 173*7c478bd9Sstevel@tonic-gate new ServiceLocationException( 174*7c478bd9Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 175*7c478bd9Sstevel@tonic-gate "par_char_closing", 176*7c478bd9Sstevel@tonic-gate new Object[] {query}); 177*7c478bd9Sstevel@tonic-gate 178*7c478bd9Sstevel@tonic-gate } 179*7c478bd9Sstevel@tonic-gate 180*7c478bd9Sstevel@tonic-gate // Merge in returns. Use OR operator so all returned 181*7c478bd9Sstevel@tonic-gate // values are merged in. 182*7c478bd9Sstevel@tonic-gate 183*7c478bd9Sstevel@tonic-gate mergeQueryReturns(ret, rec, OR); 184*7c478bd9Sstevel@tonic-gate 185*7c478bd9Sstevel@tonic-gate } catch (IOException ex) { 186*7c478bd9Sstevel@tonic-gate throw 187*7c478bd9Sstevel@tonic-gate new ServiceLocationException( 188*7c478bd9Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 189*7c478bd9Sstevel@tonic-gate "par_syn_err", 190*7c478bd9Sstevel@tonic-gate new Object[] {query}); 191*7c478bd9Sstevel@tonic-gate 192*7c478bd9Sstevel@tonic-gate } 193*7c478bd9Sstevel@tonic-gate } 194*7c478bd9Sstevel@tonic-gate 195*7c478bd9Sstevel@tonic-gate // 196*7c478bd9Sstevel@tonic-gate // Routines for dealing with parse returns record. 197*7c478bd9Sstevel@tonic-gate // 198*7c478bd9Sstevel@tonic-gate 199*7c478bd9Sstevel@tonic-gate // Merge source to target. The target has already 200*7c478bd9Sstevel@tonic-gate // been precharged with ones that must match 201*7c478bd9Sstevel@tonic-gate // if the op is AND. If it's OR, then simply 202*7c478bd9Sstevel@tonic-gate // stuff them in. 203*7c478bd9Sstevel@tonic-gate 204*7c478bd9Sstevel@tonic-gate private static boolean 205*7c478bd9Sstevel@tonic-gate mergeQueryReturns(ParserRecord target, 206*7c478bd9Sstevel@tonic-gate ParserRecord source, 207*7c478bd9Sstevel@tonic-gate char op) { 208*7c478bd9Sstevel@tonic-gate Hashtable targetServices = target.services; 209*7c478bd9Sstevel@tonic-gate Hashtable sourceServices = source.services; 210*7c478bd9Sstevel@tonic-gate boolean eval; 211*7c478bd9Sstevel@tonic-gate 212*7c478bd9Sstevel@tonic-gate if (op == AND) { 213*7c478bd9Sstevel@tonic-gate eval = mergeTablesWithAnd(targetServices, sourceServices); 214*7c478bd9Sstevel@tonic-gate 215*7c478bd9Sstevel@tonic-gate } else { 216*7c478bd9Sstevel@tonic-gate eval = mergeTablesWithOr(targetServices, sourceServices); 217*7c478bd9Sstevel@tonic-gate 218*7c478bd9Sstevel@tonic-gate } 219*7c478bd9Sstevel@tonic-gate 220*7c478bd9Sstevel@tonic-gate Hashtable targetSigs = target.signatures; 221*7c478bd9Sstevel@tonic-gate Hashtable sourceSigs = source.signatures; 222*7c478bd9Sstevel@tonic-gate 223*7c478bd9Sstevel@tonic-gate if (op == AND) { 224*7c478bd9Sstevel@tonic-gate mergeTablesWithAnd(targetSigs, sourceSigs); 225*7c478bd9Sstevel@tonic-gate 226*7c478bd9Sstevel@tonic-gate } else { 227*7c478bd9Sstevel@tonic-gate mergeTablesWithOr(targetSigs, sourceSigs); 228*7c478bd9Sstevel@tonic-gate 229*7c478bd9Sstevel@tonic-gate } 230*7c478bd9Sstevel@tonic-gate 231*7c478bd9Sstevel@tonic-gate return eval; 232*7c478bd9Sstevel@tonic-gate } 233*7c478bd9Sstevel@tonic-gate 234*7c478bd9Sstevel@tonic-gate 235*7c478bd9Sstevel@tonic-gate // Merge tables by removing anything from target that isn't in source. 236*7c478bd9Sstevel@tonic-gate 237*7c478bd9Sstevel@tonic-gate private static boolean mergeTablesWithAnd(Hashtable target, 238*7c478bd9Sstevel@tonic-gate Hashtable source) { 239*7c478bd9Sstevel@tonic-gate 240*7c478bd9Sstevel@tonic-gate Enumeration en = target.keys(); 241*7c478bd9Sstevel@tonic-gate 242*7c478bd9Sstevel@tonic-gate // Remove any from target that aren't in source. 243*7c478bd9Sstevel@tonic-gate 244*7c478bd9Sstevel@tonic-gate while (en.hasMoreElements()) { 245*7c478bd9Sstevel@tonic-gate Object tkey = en.nextElement(); 246*7c478bd9Sstevel@tonic-gate 247*7c478bd9Sstevel@tonic-gate if (source.get(tkey) == null) { 248*7c478bd9Sstevel@tonic-gate target.remove(tkey); 249*7c478bd9Sstevel@tonic-gate 250*7c478bd9Sstevel@tonic-gate } 251*7c478bd9Sstevel@tonic-gate } 252*7c478bd9Sstevel@tonic-gate 253*7c478bd9Sstevel@tonic-gate // If there's nothing left, return false to indicate no further 254*7c478bd9Sstevel@tonic-gate // evaluation needed. 255*7c478bd9Sstevel@tonic-gate 256*7c478bd9Sstevel@tonic-gate if (target.size() <= 0) { 257*7c478bd9Sstevel@tonic-gate return false; 258*7c478bd9Sstevel@tonic-gate 259*7c478bd9Sstevel@tonic-gate } 260*7c478bd9Sstevel@tonic-gate 261*7c478bd9Sstevel@tonic-gate return true; 262*7c478bd9Sstevel@tonic-gate } 263*7c478bd9Sstevel@tonic-gate 264*7c478bd9Sstevel@tonic-gate // Merge tables by adding everything from source into target. 265*7c478bd9Sstevel@tonic-gate 266*7c478bd9Sstevel@tonic-gate private static boolean mergeTablesWithOr(Hashtable target, 267*7c478bd9Sstevel@tonic-gate Hashtable source) { 268*7c478bd9Sstevel@tonic-gate 269*7c478bd9Sstevel@tonic-gate Enumeration en = source.keys(); 270*7c478bd9Sstevel@tonic-gate 271*7c478bd9Sstevel@tonic-gate while (en.hasMoreElements()) { 272*7c478bd9Sstevel@tonic-gate Object skey = en.nextElement(); 273*7c478bd9Sstevel@tonic-gate 274*7c478bd9Sstevel@tonic-gate target.put(skey, source.get(skey)); 275*7c478bd9Sstevel@tonic-gate 276*7c478bd9Sstevel@tonic-gate } 277*7c478bd9Sstevel@tonic-gate 278*7c478bd9Sstevel@tonic-gate return true; 279*7c478bd9Sstevel@tonic-gate } 280*7c478bd9Sstevel@tonic-gate 281*7c478bd9Sstevel@tonic-gate // 282*7c478bd9Sstevel@tonic-gate // Parsing for various productions. 283*7c478bd9Sstevel@tonic-gate // 284*7c478bd9Sstevel@tonic-gate 285*7c478bd9Sstevel@tonic-gate 286*7c478bd9Sstevel@tonic-gate // Parse the filter production. 287*7c478bd9Sstevel@tonic-gate 288*7c478bd9Sstevel@tonic-gate private static ParserRecord 289*7c478bd9Sstevel@tonic-gate parseFilter(StreamTokenizer tk, 290*7c478bd9Sstevel@tonic-gate Parser.QueryEvaluator ev, 291*7c478bd9Sstevel@tonic-gate Locale locale, 292*7c478bd9Sstevel@tonic-gate boolean invert, 293*7c478bd9Sstevel@tonic-gate boolean eval) 294*7c478bd9Sstevel@tonic-gate throws ServiceLocationException, IOException { 295*7c478bd9Sstevel@tonic-gate 296*7c478bd9Sstevel@tonic-gate ParserRecord ret = null; 297*7c478bd9Sstevel@tonic-gate int tok = tk.nextToken(); 298*7c478bd9Sstevel@tonic-gate 299*7c478bd9Sstevel@tonic-gate // Check for opening paren. 300*7c478bd9Sstevel@tonic-gate 301*7c478bd9Sstevel@tonic-gate if (tok != OPAREN) { 302*7c478bd9Sstevel@tonic-gate throw 303*7c478bd9Sstevel@tonic-gate new ServiceLocationException( 304*7c478bd9Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 305*7c478bd9Sstevel@tonic-gate "par_init_par", 306*7c478bd9Sstevel@tonic-gate new Object[0]); 307*7c478bd9Sstevel@tonic-gate 308*7c478bd9Sstevel@tonic-gate } 309*7c478bd9Sstevel@tonic-gate 310*7c478bd9Sstevel@tonic-gate // Parse inside. 311*7c478bd9Sstevel@tonic-gate 312*7c478bd9Sstevel@tonic-gate tok = tk.nextToken(); 313*7c478bd9Sstevel@tonic-gate 314*7c478bd9Sstevel@tonic-gate // Check for a logical operator. 315*7c478bd9Sstevel@tonic-gate 316*7c478bd9Sstevel@tonic-gate if (tok == AND || tok == OR) { 317*7c478bd9Sstevel@tonic-gate ret = parseFilterlist(tk, ev, locale, (char)tok, invert, eval); 318*7c478bd9Sstevel@tonic-gate 319*7c478bd9Sstevel@tonic-gate } else if (tok == NOT) { 320*7c478bd9Sstevel@tonic-gate ret = parseFilter(tk, ev, locale, !invert, eval); 321*7c478bd9Sstevel@tonic-gate 322*7c478bd9Sstevel@tonic-gate } else if (tok == StreamTokenizer.TT_WORD) { 323*7c478bd9Sstevel@tonic-gate tk.pushBack(); 324*7c478bd9Sstevel@tonic-gate ret = parseItem(tk, ev, locale, invert, eval); 325*7c478bd9Sstevel@tonic-gate 326*7c478bd9Sstevel@tonic-gate } else { 327*7c478bd9Sstevel@tonic-gate 328*7c478bd9Sstevel@tonic-gate // Since we've covered the ASCII character set, the only other 329*7c478bd9Sstevel@tonic-gate // thing that could be here is a nonASCII character. We push it 330*7c478bd9Sstevel@tonic-gate // back and deal with it in parseItem(). 331*7c478bd9Sstevel@tonic-gate 332*7c478bd9Sstevel@tonic-gate tk.pushBack(); 333*7c478bd9Sstevel@tonic-gate ret = parseItem(tk, ev, locale, invert, eval); 334*7c478bd9Sstevel@tonic-gate 335*7c478bd9Sstevel@tonic-gate } 336*7c478bd9Sstevel@tonic-gate 337*7c478bd9Sstevel@tonic-gate tok = tk.nextToken(); 338*7c478bd9Sstevel@tonic-gate 339*7c478bd9Sstevel@tonic-gate // Check for closing paren. 340*7c478bd9Sstevel@tonic-gate 341*7c478bd9Sstevel@tonic-gate if (tok != CPAREN) { 342*7c478bd9Sstevel@tonic-gate throw 343*7c478bd9Sstevel@tonic-gate new ServiceLocationException( 344*7c478bd9Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 345*7c478bd9Sstevel@tonic-gate "par_final_par", 346*7c478bd9Sstevel@tonic-gate new Object[0]); 347*7c478bd9Sstevel@tonic-gate 348*7c478bd9Sstevel@tonic-gate } 349*7c478bd9Sstevel@tonic-gate 350*7c478bd9Sstevel@tonic-gate return ret; 351*7c478bd9Sstevel@tonic-gate } 352*7c478bd9Sstevel@tonic-gate 353*7c478bd9Sstevel@tonic-gate // Parse a filterlist production. 354*7c478bd9Sstevel@tonic-gate 355*7c478bd9Sstevel@tonic-gate private static ParserRecord 356*7c478bd9Sstevel@tonic-gate parseFilterlist(StreamTokenizer tk, 357*7c478bd9Sstevel@tonic-gate Parser.QueryEvaluator ev, 358*7c478bd9Sstevel@tonic-gate Locale locale, 359*7c478bd9Sstevel@tonic-gate char op, 360*7c478bd9Sstevel@tonic-gate boolean invert, 361*7c478bd9Sstevel@tonic-gate boolean eval) 362*7c478bd9Sstevel@tonic-gate throws ServiceLocationException, IOException { 363*7c478bd9Sstevel@tonic-gate boolean match; 364*7c478bd9Sstevel@tonic-gate 365*7c478bd9Sstevel@tonic-gate ParserRecord mrex = null; 366*7c478bd9Sstevel@tonic-gate 367*7c478bd9Sstevel@tonic-gate // Parse through the list of filters. 368*7c478bd9Sstevel@tonic-gate 369*7c478bd9Sstevel@tonic-gate do { 370*7c478bd9Sstevel@tonic-gate ParserRecord prex = null; 371*7c478bd9Sstevel@tonic-gate 372*7c478bd9Sstevel@tonic-gate if (op == AND) { 373*7c478bd9Sstevel@tonic-gate 374*7c478bd9Sstevel@tonic-gate prex = parseFilter(tk, ev, locale, invert, eval); 375*7c478bd9Sstevel@tonic-gate 376*7c478bd9Sstevel@tonic-gate } else { 377*7c478bd9Sstevel@tonic-gate 378*7c478bd9Sstevel@tonic-gate prex = parseFilter(tk, ev, locale, invert, eval); 379*7c478bd9Sstevel@tonic-gate 380*7c478bd9Sstevel@tonic-gate } 381*7c478bd9Sstevel@tonic-gate 382*7c478bd9Sstevel@tonic-gate // We need to start off with something. 383*7c478bd9Sstevel@tonic-gate 384*7c478bd9Sstevel@tonic-gate if (mrex == null) { 385*7c478bd9Sstevel@tonic-gate mrex = prex; 386*7c478bd9Sstevel@tonic-gate 387*7c478bd9Sstevel@tonic-gate } else { 388*7c478bd9Sstevel@tonic-gate 389*7c478bd9Sstevel@tonic-gate // Merge in returns. 390*7c478bd9Sstevel@tonic-gate 391*7c478bd9Sstevel@tonic-gate eval = mergeQueryReturns(mrex, prex, op); 392*7c478bd9Sstevel@tonic-gate 393*7c478bd9Sstevel@tonic-gate } 394*7c478bd9Sstevel@tonic-gate 395*7c478bd9Sstevel@tonic-gate // Look for ending paren. 396*7c478bd9Sstevel@tonic-gate 397*7c478bd9Sstevel@tonic-gate int tok = tk.nextToken(); 398*7c478bd9Sstevel@tonic-gate tk.pushBack(); 399*7c478bd9Sstevel@tonic-gate 400*7c478bd9Sstevel@tonic-gate if (tok == CPAREN) { 401*7c478bd9Sstevel@tonic-gate 402*7c478bd9Sstevel@tonic-gate return mrex; 403*7c478bd9Sstevel@tonic-gate 404*7c478bd9Sstevel@tonic-gate } 405*7c478bd9Sstevel@tonic-gate 406*7c478bd9Sstevel@tonic-gate } while (true); 407*7c478bd9Sstevel@tonic-gate 408*7c478bd9Sstevel@tonic-gate } 409*7c478bd9Sstevel@tonic-gate 410*7c478bd9Sstevel@tonic-gate // Parse item. 411*7c478bd9Sstevel@tonic-gate 412*7c478bd9Sstevel@tonic-gate private static ParserRecord 413*7c478bd9Sstevel@tonic-gate parseItem(StreamTokenizer tk, 414*7c478bd9Sstevel@tonic-gate Parser.QueryEvaluator ev, 415*7c478bd9Sstevel@tonic-gate Locale locale, 416*7c478bd9Sstevel@tonic-gate boolean invert, 417*7c478bd9Sstevel@tonic-gate boolean eval) 418*7c478bd9Sstevel@tonic-gate throws ServiceLocationException, IOException { 419*7c478bd9Sstevel@tonic-gate 420*7c478bd9Sstevel@tonic-gate ParserRecord prex = new ParserRecord(); 421*7c478bd9Sstevel@tonic-gate AttributeString attr = parseAttr(tk, locale); 422*7c478bd9Sstevel@tonic-gate char op = parseOp(tk); 423*7c478bd9Sstevel@tonic-gate Object value = null; 424*7c478bd9Sstevel@tonic-gate 425*7c478bd9Sstevel@tonic-gate // If operator is PRESENT, then check whether 426*7c478bd9Sstevel@tonic-gate // it's not really a wildcarded value. If the next 427*7c478bd9Sstevel@tonic-gate // token isn't a closing paren, then it's 428*7c478bd9Sstevel@tonic-gate // a wildcarded value. 429*7c478bd9Sstevel@tonic-gate 430*7c478bd9Sstevel@tonic-gate if (op == PRESENT) { 431*7c478bd9Sstevel@tonic-gate int tok = tk.nextToken(); 432*7c478bd9Sstevel@tonic-gate 433*7c478bd9Sstevel@tonic-gate tk.pushBack(); // ...in any event... 434*7c478bd9Sstevel@tonic-gate 435*7c478bd9Sstevel@tonic-gate if ((char)tok != CPAREN) { // It's a wildcarded pattern... 436*7c478bd9Sstevel@tonic-gate op = EQUAL; 437*7c478bd9Sstevel@tonic-gate value = parseValue(tk, locale); 438*7c478bd9Sstevel@tonic-gate 439*7c478bd9Sstevel@tonic-gate // Need to convert to a wildcarded pattern. Regardless 440*7c478bd9Sstevel@tonic-gate // of type, since wildcard makes the type be a 441*7c478bd9Sstevel@tonic-gate // string. 442*7c478bd9Sstevel@tonic-gate 443*7c478bd9Sstevel@tonic-gate value = 444*7c478bd9Sstevel@tonic-gate new AttributePattern(PRESENT + value.toString(), locale); 445*7c478bd9Sstevel@tonic-gate 446*7c478bd9Sstevel@tonic-gate } 447*7c478bd9Sstevel@tonic-gate } else { 448*7c478bd9Sstevel@tonic-gate value = parseValue(tk, locale); 449*7c478bd9Sstevel@tonic-gate 450*7c478bd9Sstevel@tonic-gate } 451*7c478bd9Sstevel@tonic-gate 452*7c478bd9Sstevel@tonic-gate // Check for inappropriate pattern. 453*7c478bd9Sstevel@tonic-gate 454*7c478bd9Sstevel@tonic-gate if (value instanceof AttributePattern && 455*7c478bd9Sstevel@tonic-gate ((AttributePattern)value).isWildcarded() && 456*7c478bd9Sstevel@tonic-gate op != EQUAL) { 457*7c478bd9Sstevel@tonic-gate throw 458*7c478bd9Sstevel@tonic-gate new ServiceLocationException( 459*7c478bd9Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 460*7c478bd9Sstevel@tonic-gate "par_wild_op", 461*7c478bd9Sstevel@tonic-gate new Object[] {new Character(op)}); 462*7c478bd9Sstevel@tonic-gate 463*7c478bd9Sstevel@tonic-gate } 464*7c478bd9Sstevel@tonic-gate 465*7c478bd9Sstevel@tonic-gate // Check for inappropriate boolean. 466*7c478bd9Sstevel@tonic-gate 467*7c478bd9Sstevel@tonic-gate if ((value instanceof Boolean || 468*7c478bd9Sstevel@tonic-gate value instanceof Opaque) && 469*7c478bd9Sstevel@tonic-gate (op == GREATER || op == LESS)) { 470*7c478bd9Sstevel@tonic-gate throw 471*7c478bd9Sstevel@tonic-gate new ServiceLocationException( 472*7c478bd9Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 473*7c478bd9Sstevel@tonic-gate "par_bool_op", 474*7c478bd9Sstevel@tonic-gate new Object[] {new Character(op)}); 475*7c478bd9Sstevel@tonic-gate 476*7c478bd9Sstevel@tonic-gate } 477*7c478bd9Sstevel@tonic-gate 478*7c478bd9Sstevel@tonic-gate // Check for wrong operator with keyword. 479*7c478bd9Sstevel@tonic-gate 480*7c478bd9Sstevel@tonic-gate if ((value == null || value.toString().length() <= 0) && 481*7c478bd9Sstevel@tonic-gate op != PRESENT) { 482*7c478bd9Sstevel@tonic-gate throw 483*7c478bd9Sstevel@tonic-gate new ServiceLocationException( 484*7c478bd9Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 485*7c478bd9Sstevel@tonic-gate "par_key_op", 486*7c478bd9Sstevel@tonic-gate new Object[] {new Character(op)}); 487*7c478bd9Sstevel@tonic-gate } 488*7c478bd9Sstevel@tonic-gate 489*7c478bd9Sstevel@tonic-gate if (eval) { 490*7c478bd9Sstevel@tonic-gate /* 491*7c478bd9Sstevel@tonic-gate * Try and evaluate the query. If the evaluation failed and the 492*7c478bd9Sstevel@tonic-gate * value was an Integer or Boolean try again after converting the 493*7c478bd9Sstevel@tonic-gate * value to a String. This is because the value in the query will 494*7c478bd9Sstevel@tonic-gate * be converted to an Integer or Boolean in preference to a String 495*7c478bd9Sstevel@tonic-gate * even though the query starts out as a String. Hence when an 496*7c478bd9Sstevel@tonic-gate * attribute is registered with a String value that can equally be 497*7c478bd9Sstevel@tonic-gate * parsed as a valid Integer or Boolean value the String will 498*7c478bd9Sstevel@tonic-gate * almost always be parsed as an Integer or Boolean. This results 499*7c478bd9Sstevel@tonic-gate * in the failing of the initial type check when performing the 500*7c478bd9Sstevel@tonic-gate * query. By converting the value to a String there is another shot 501*7c478bd9Sstevel@tonic-gate * at fulfulling the query. 502*7c478bd9Sstevel@tonic-gate */ 503*7c478bd9Sstevel@tonic-gate if (!ev.evaluate(attr, op, value, invert, prex) && 504*7c478bd9Sstevel@tonic-gate !(value instanceof AttributeString)) { 505*7c478bd9Sstevel@tonic-gate ev.evaluate(attr, 506*7c478bd9Sstevel@tonic-gate op, 507*7c478bd9Sstevel@tonic-gate new AttributeString( 508*7c478bd9Sstevel@tonic-gate value.toString().trim(), 509*7c478bd9Sstevel@tonic-gate locale), 510*7c478bd9Sstevel@tonic-gate invert, 511*7c478bd9Sstevel@tonic-gate prex); 512*7c478bd9Sstevel@tonic-gate } 513*7c478bd9Sstevel@tonic-gate 514*7c478bd9Sstevel@tonic-gate } 515*7c478bd9Sstevel@tonic-gate 516*7c478bd9Sstevel@tonic-gate return prex; 517*7c478bd9Sstevel@tonic-gate } 518*7c478bd9Sstevel@tonic-gate 519*7c478bd9Sstevel@tonic-gate // Parse attribute tag. 520*7c478bd9Sstevel@tonic-gate 521*7c478bd9Sstevel@tonic-gate private static AttributeString parseAttr(StreamTokenizer tk, Locale locale) 522*7c478bd9Sstevel@tonic-gate throws ServiceLocationException, IOException { 523*7c478bd9Sstevel@tonic-gate 524*7c478bd9Sstevel@tonic-gate String str = parsePotentialNonASCII(tk); 525*7c478bd9Sstevel@tonic-gate 526*7c478bd9Sstevel@tonic-gate str = 527*7c478bd9Sstevel@tonic-gate ServiceLocationAttribute.unescapeAttributeString(str, true); 528*7c478bd9Sstevel@tonic-gate 529*7c478bd9Sstevel@tonic-gate return new AttributeString(str, locale); 530*7c478bd9Sstevel@tonic-gate } 531*7c478bd9Sstevel@tonic-gate 532*7c478bd9Sstevel@tonic-gate // Parse attribute operator. 533*7c478bd9Sstevel@tonic-gate 534*7c478bd9Sstevel@tonic-gate private static char parseOp(StreamTokenizer tk) 535*7c478bd9Sstevel@tonic-gate throws ServiceLocationException, IOException { 536*7c478bd9Sstevel@tonic-gate 537*7c478bd9Sstevel@tonic-gate int tok = tk.nextToken(); 538*7c478bd9Sstevel@tonic-gate 539*7c478bd9Sstevel@tonic-gate // Identify operator 540*7c478bd9Sstevel@tonic-gate 541*7c478bd9Sstevel@tonic-gate switch (tok) { 542*7c478bd9Sstevel@tonic-gate 543*7c478bd9Sstevel@tonic-gate case EQUAL: 544*7c478bd9Sstevel@tonic-gate 545*7c478bd9Sstevel@tonic-gate // Is it present? 546*7c478bd9Sstevel@tonic-gate 547*7c478bd9Sstevel@tonic-gate tok = tk.nextToken(); 548*7c478bd9Sstevel@tonic-gate 549*7c478bd9Sstevel@tonic-gate if (tok == STAR) { 550*7c478bd9Sstevel@tonic-gate return PRESENT; 551*7c478bd9Sstevel@tonic-gate 552*7c478bd9Sstevel@tonic-gate } else { 553*7c478bd9Sstevel@tonic-gate tk.pushBack(); 554*7c478bd9Sstevel@tonic-gate return EQUAL; 555*7c478bd9Sstevel@tonic-gate 556*7c478bd9Sstevel@tonic-gate } 557*7c478bd9Sstevel@tonic-gate 558*7c478bd9Sstevel@tonic-gate case APPROX: case GREATER: case LESS: 559*7c478bd9Sstevel@tonic-gate 560*7c478bd9Sstevel@tonic-gate // Need equals. 561*7c478bd9Sstevel@tonic-gate 562*7c478bd9Sstevel@tonic-gate if (tk.nextToken() != EQUAL) { 563*7c478bd9Sstevel@tonic-gate break; 564*7c478bd9Sstevel@tonic-gate 565*7c478bd9Sstevel@tonic-gate } 566*7c478bd9Sstevel@tonic-gate 567*7c478bd9Sstevel@tonic-gate if (tok == APPROX) { 568*7c478bd9Sstevel@tonic-gate tok = EQUAL; 569*7c478bd9Sstevel@tonic-gate 570*7c478bd9Sstevel@tonic-gate } 571*7c478bd9Sstevel@tonic-gate 572*7c478bd9Sstevel@tonic-gate return (char)tok; 573*7c478bd9Sstevel@tonic-gate 574*7c478bd9Sstevel@tonic-gate default: 575*7c478bd9Sstevel@tonic-gate break; 576*7c478bd9Sstevel@tonic-gate 577*7c478bd9Sstevel@tonic-gate } 578*7c478bd9Sstevel@tonic-gate 579*7c478bd9Sstevel@tonic-gate throw 580*7c478bd9Sstevel@tonic-gate new ServiceLocationException( 581*7c478bd9Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 582*7c478bd9Sstevel@tonic-gate "par_comp_op", 583*7c478bd9Sstevel@tonic-gate new Object[0]); 584*7c478bd9Sstevel@tonic-gate 585*7c478bd9Sstevel@tonic-gate } 586*7c478bd9Sstevel@tonic-gate 587*7c478bd9Sstevel@tonic-gate // Parse expression value. 588*7c478bd9Sstevel@tonic-gate 589*7c478bd9Sstevel@tonic-gate private static Object parseValue(StreamTokenizer tk, Locale locale) 590*7c478bd9Sstevel@tonic-gate throws ServiceLocationException, IOException { 591*7c478bd9Sstevel@tonic-gate 592*7c478bd9Sstevel@tonic-gate StringBuffer buf = new StringBuffer(); 593*7c478bd9Sstevel@tonic-gate 594*7c478bd9Sstevel@tonic-gate // Parse until the next closing paren. 595*7c478bd9Sstevel@tonic-gate 596*7c478bd9Sstevel@tonic-gate do { 597*7c478bd9Sstevel@tonic-gate int tok = tk.nextToken(); 598*7c478bd9Sstevel@tonic-gate 599*7c478bd9Sstevel@tonic-gate if (tok == CPAREN) { 600*7c478bd9Sstevel@tonic-gate tk.pushBack(); 601*7c478bd9Sstevel@tonic-gate 602*7c478bd9Sstevel@tonic-gate Object o = 603*7c478bd9Sstevel@tonic-gate ServiceLocationAttribute.evaluate(buf.toString().trim()); 604*7c478bd9Sstevel@tonic-gate 605*7c478bd9Sstevel@tonic-gate if (o instanceof String) { 606*7c478bd9Sstevel@tonic-gate o = new AttributePattern((String)o, locale); 607*7c478bd9Sstevel@tonic-gate 608*7c478bd9Sstevel@tonic-gate } else if (o instanceof byte[]) { 609*7c478bd9Sstevel@tonic-gate o = new Opaque((byte[])o); 610*7c478bd9Sstevel@tonic-gate 611*7c478bd9Sstevel@tonic-gate } 612*7c478bd9Sstevel@tonic-gate 613*7c478bd9Sstevel@tonic-gate return o; 614*7c478bd9Sstevel@tonic-gate 615*7c478bd9Sstevel@tonic-gate } else if (tok != StreamTokenizer.TT_EOF) { 616*7c478bd9Sstevel@tonic-gate 617*7c478bd9Sstevel@tonic-gate if (tok == StreamTokenizer.TT_WORD) { 618*7c478bd9Sstevel@tonic-gate buf.append(tk.sval); 619*7c478bd9Sstevel@tonic-gate 620*7c478bd9Sstevel@tonic-gate } else if (tok == StreamTokenizer.TT_NUMBER) { 621*7c478bd9Sstevel@tonic-gate Assert.slpassert(false, 622*7c478bd9Sstevel@tonic-gate "par_ntok", 623*7c478bd9Sstevel@tonic-gate new Object[0]); 624*7c478bd9Sstevel@tonic-gate 625*7c478bd9Sstevel@tonic-gate } else { 626*7c478bd9Sstevel@tonic-gate buf.append((char)tok); 627*7c478bd9Sstevel@tonic-gate 628*7c478bd9Sstevel@tonic-gate } 629*7c478bd9Sstevel@tonic-gate 630*7c478bd9Sstevel@tonic-gate } else { 631*7c478bd9Sstevel@tonic-gate throw 632*7c478bd9Sstevel@tonic-gate new ServiceLocationException( 633*7c478bd9Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 634*7c478bd9Sstevel@tonic-gate "par_qend", 635*7c478bd9Sstevel@tonic-gate new Object[0]); 636*7c478bd9Sstevel@tonic-gate } 637*7c478bd9Sstevel@tonic-gate } while (true); 638*7c478bd9Sstevel@tonic-gate 639*7c478bd9Sstevel@tonic-gate } 640*7c478bd9Sstevel@tonic-gate 641*7c478bd9Sstevel@tonic-gate // NonASCII characters may be in the string. StreamTokenizer 642*7c478bd9Sstevel@tonic-gate // can't handle them as part of words, so we need to resort to 643*7c478bd9Sstevel@tonic-gate // this loop to handle it. 644*7c478bd9Sstevel@tonic-gate 645*7c478bd9Sstevel@tonic-gate private static String parsePotentialNonASCII(StreamTokenizer tk) 646*7c478bd9Sstevel@tonic-gate throws IOException { 647*7c478bd9Sstevel@tonic-gate 648*7c478bd9Sstevel@tonic-gate StringBuffer buf = new StringBuffer(); 649*7c478bd9Sstevel@tonic-gate 650*7c478bd9Sstevel@tonic-gate do { 651*7c478bd9Sstevel@tonic-gate 652*7c478bd9Sstevel@tonic-gate int tok = tk.nextToken(); 653*7c478bd9Sstevel@tonic-gate 654*7c478bd9Sstevel@tonic-gate if (tok == StreamTokenizer.TT_WORD) { 655*7c478bd9Sstevel@tonic-gate buf.append(tk.sval); 656*7c478bd9Sstevel@tonic-gate 657*7c478bd9Sstevel@tonic-gate } else if (((char)tok >= NONASCII_LOWER) && 658*7c478bd9Sstevel@tonic-gate ((char)tok <= NONASCII_UPPER)) { 659*7c478bd9Sstevel@tonic-gate buf.append((char)tok); 660*7c478bd9Sstevel@tonic-gate 661*7c478bd9Sstevel@tonic-gate } else { 662*7c478bd9Sstevel@tonic-gate tk.pushBack(); 663*7c478bd9Sstevel@tonic-gate break; 664*7c478bd9Sstevel@tonic-gate 665*7c478bd9Sstevel@tonic-gate } 666*7c478bd9Sstevel@tonic-gate 667*7c478bd9Sstevel@tonic-gate } while (true); 668*7c478bd9Sstevel@tonic-gate 669*7c478bd9Sstevel@tonic-gate return buf.toString(); 670*7c478bd9Sstevel@tonic-gate } 671*7c478bd9Sstevel@tonic-gate } 672