1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * ident "%Z%%M% %I% %E% SMI" 24 * 25 * Copyright (c) 1999 by Sun Microsystems, Inc. 26 * All rights reserved. 27 * 28 */ 29 30 // SCCS Status: %W% %G% 31 // SLPV1SSrvMsg.java: SLPv1 server side service rqst/reply. 32 // Author: James Kempf 33 // Created On: Thu Sep 10 15:33:58 1998 34 // Last Modified By: James Kempf 35 // Last Modified On: Fri Nov 6 14:03:00 1998 36 // Update Count: 41 37 // 38 39 40 package com.sun.slp; 41 42 import java.util.*; 43 import java.io.*; 44 45 46 /** 47 * The SLPV1SSrvMsg class models the SLP server side service request message. 48 * 49 * @version %R%.%L% %D% 50 * @author James Kempf 51 */ 52 53 class SLPV1SSrvMsg extends SSrvMsg { 54 55 // For eating whitespace. 56 57 final static char SPACE = ' '; 58 59 // Comma for list parsing. 60 61 final static char COMMA = ','; 62 63 // Logical operators. 64 65 final static char OR_OP = '|'; 66 final static char AND_OP = '&'; 67 68 // Logical operator corner case needs this. 69 70 final static char HASH = '#'; 71 72 // Comparison/Assignment operators. 73 74 final static char EQUAL_OP = '='; 75 final static char NOT_OP = '!'; 76 final static char LESS_OP = '<'; 77 final static char GREATER_OP = '>'; 78 final static char GEQUAL_OP = 'g'; 79 final static char LEQUAL_OP = 'l'; 80 81 // Parens. 82 83 final static char OPEN_PAREN = '('; 84 final static char CLOSE_PAREN = ')'; 85 86 // LDAP present operator 87 88 final static char PRESENT = '*'; 89 90 // Wildcard operator. 91 92 final static String WILDCARD = "*"; 93 94 // Character code for parsing. 95 96 String charCode = IANACharCode.UTF8; 97 98 // For creating a null reply. 99 100 protected SLPV1SSrvMsg() {} 101 102 // Construct a SLPV1SSrvMsg from the input stream. 103 104 SLPV1SSrvMsg(SrvLocHeader hdr, DataInputStream dis) 105 throws ServiceLocationException, IOException { 106 super(hdr, dis); 107 108 } 109 110 // Construct an empty SLPV1SSrvMsg, for monolingual off. 111 112 static SrvLocMsg makeEmptyReply(SLPHeaderV1 hdr) 113 throws ServiceLocationException { 114 115 SLPV1SSrvMsg msg = new SLPV1SSrvMsg(); 116 msg.hdr = hdr; 117 118 msg.makeReply(new Hashtable(), null); 119 120 return msg; 121 122 } 123 124 // Initialize the message from the input stream. 125 126 void initialize(DataInputStream dis) 127 throws ServiceLocationException, IOException { 128 129 SLPHeaderV1 hdr = (SLPHeaderV1)getHeader(); 130 StringBuffer buf = new StringBuffer(); 131 132 // First get the previous responder. 133 134 hdr.parsePreviousRespondersIn(dis); 135 136 // Now get the raw query. 137 138 hdr.getString(buf, dis); 139 140 String rq = buf.toString(); 141 142 // Parse the raw query to pull out the service type, scope, 143 // and query. 144 145 StringTokenizer st = new StringTokenizer(rq, "/", true); 146 147 try { 148 149 String type = 150 Defaults.SERVICE_PREFIX + ":" + 151 st.nextToken().trim().toLowerCase() + ":"; 152 153 serviceType = 154 hdr.checkServiceType(type); 155 156 st.nextToken(); // get rid of slash. 157 158 // Get the scope. 159 160 String scope = st.nextToken().trim().toLowerCase(); 161 162 // Special case if scope is empty (meaning the next 163 // token will be a slash). 164 165 if (scope.equals("/")) { 166 scope = ""; 167 168 } else { 169 170 st.nextToken(); // get rid of slash. 171 172 if (scope.length() > 0) { 173 174 // Validate the scope name. 175 176 hdr.validateScope(scope); 177 } 178 } 179 180 // Set up scopes vector. 181 182 hdr.scopes = new Vector(); 183 184 // Substitute default scope here. 185 186 if (scope.length() <= 0) { 187 scope = Defaults.DEFAULT_SCOPE; 188 189 } 190 191 hdr.scopes.addElement(scope.toLowerCase().trim()); 192 193 // Parsing the query is complicated by opaques having slashes. 194 195 String q = ""; 196 197 while (st.hasMoreTokens()) { 198 q = q + st.nextToken(); 199 200 } 201 202 // Drop off the final backslash, error if none. 203 204 if (!q.endsWith("/")) { 205 throw 206 new ServiceLocationException( 207 ServiceLocationException.PARSE_ERROR, 208 "v1_query_error", 209 new Object[] {rq}); 210 } 211 212 query = q.substring(0, q.length()-1); 213 214 // Save header char code for parsing. 215 216 charCode = hdr.charCode; 217 218 // Convert the query into a V2 query. 219 220 convertQuery(); 221 222 // If the query is for "service:directory-agent", then we 223 // mark it as having been multicast, because that is the 224 // only kind of multicast that we accept for SLPv1. Anybody 225 // who unicasts this to us will time out. 226 227 if (serviceType.equals(Defaults.DA_SERVICE_TYPE.toString())) { 228 hdr.mcast = true; 229 230 } 231 232 // Construct description. 233 234 hdr.constructDescription("SrvRqst", 235 " service type=``" + 236 serviceType + "''\n" + 237 " query=``" + 238 query + "''"); 239 240 } catch (NoSuchElementException ex) { 241 throw 242 new ServiceLocationException( 243 ServiceLocationException.PARSE_ERROR, 244 "v1_query_error", 245 new Object[] {rq}); 246 } 247 } 248 249 // Make a reply message. 250 251 SrvLocMsg makeReply(Hashtable urltable, 252 Hashtable URLSignatures) 253 throws ServiceLocationException { 254 255 SLPHeaderV1 hdr = 256 ((SLPHeaderV1)getHeader()).makeReplyHeader(); 257 258 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 259 260 // Edit out abstract types and nonService: URLs. 261 262 Enumeration en = urltable.keys(); 263 Vector urls = new Vector(); 264 265 while (en.hasMoreElements()) { 266 ServiceURL surl = (ServiceURL)en.nextElement(); 267 268 // Reject if abstract type or nonservice: URL. 269 270 ServiceType type = surl.getServiceType(); 271 272 if (!type.isAbstractType() && type.isServiceURL()) { 273 urls.addElement(surl); 274 275 } 276 } 277 278 hdr.iNumReplies = urls.size(); 279 // keep this info so SAs can drop 0 replies 280 281 int n = urls.size(); 282 283 // Write out the size of the list. 284 285 hdr.putInt(n, baos); 286 287 en = urls.elements(); 288 289 // Write out the size of the list. 290 291 while (en.hasMoreElements()) { 292 ServiceURL surl = (ServiceURL)en.nextElement(); 293 294 hdr.parseServiceURLOut(surl, true, baos); 295 296 } 297 298 // We ignore the signatures because we only do V1 compatibility 299 // for nonprotected scopes. 300 301 hdr.payload = baos.toByteArray(); 302 303 hdr.constructDescription("SrvRply", 304 " service URLs=``" + urls + "''\n"); 305 306 return hdr; 307 308 } 309 310 // Convert the query to a V2 query. 311 312 void convertQuery() 313 throws ServiceLocationException { 314 315 // Check for empty query. 316 317 query = query.trim(); 318 319 if (query.length() <= 0) { 320 return; 321 322 } 323 324 // Check for query join. 325 326 if (!(query.startsWith("(") && query.endsWith(")"))) { 327 328 // Rewrite to a standard query. 329 330 query = rewriteQueryJoin(query); 331 332 } 333 334 // Now rewrite the query into v2 format. 335 336 query = rewriteQuery(query); 337 } 338 339 340 // Rewrite a query join as a conjunction. 341 342 private String rewriteQueryJoin(String query) 343 throws ServiceLocationException { 344 345 // Turn infix expression into prefix. 346 347 StringBuffer sbuf = new StringBuffer(); 348 StringTokenizer tk = new StringTokenizer(query, ",", true); 349 boolean lastTokComma = true; 350 int numEx = 0; 351 352 while (tk.hasMoreElements()) { 353 String exp = tk.nextToken().trim(); 354 355 if (exp.equals(",")) { 356 if (lastTokComma) { 357 throw 358 new ServiceLocationException( 359 ServiceLocationException.PARSE_ERROR, 360 "v1_query_error", 361 new Object[] {query}); 362 363 } else { 364 lastTokComma = true; 365 } 366 367 } else { 368 lastTokComma = false; 369 370 if (exp.length() <= 0) { 371 throw 372 new ServiceLocationException( 373 ServiceLocationException.PARSE_ERROR, 374 "v1_query_error", 375 new Object[] {query}); 376 377 } 378 379 // Put in parens 380 381 sbuf.append("("); 382 sbuf.append(exp); 383 sbuf.append(")"); 384 385 numEx++; 386 } 387 } 388 389 if (lastTokComma || numEx == 0) { 390 throw 391 new ServiceLocationException( 392 ServiceLocationException.PARSE_ERROR, 393 "v1_query_error", 394 new Object[] {query}); 395 396 } 397 398 if (numEx > 1) { 399 sbuf.insert(0, "(&"); 400 sbuf.append(")"); 401 402 } 403 404 return sbuf.toString(); 405 } 406 407 // Rewrite a v1 query into v2 format. This includes character escaping. 408 409 private String rewriteQuery(String whereList) 410 throws ServiceLocationException { 411 412 // Parse a logical expression. 413 414 StreamTokenizer tk = 415 new StreamTokenizer(new StringReader(whereList)); 416 417 tk.resetSyntax(); // make all chars ordinary... 418 tk.whitespaceChars('\000','\037'); 419 tk.ordinaryChar(SPACE); // but beware of embedded whites... 420 tk.wordChars('!', '%'); 421 tk.ordinaryChar(AND_OP); 422 tk.wordChars('\'', '\''); 423 tk.ordinaryChar(OPEN_PAREN); 424 tk.ordinaryChar(CLOSE_PAREN); 425 tk.wordChars('*', '{'); 426 tk.ordinaryChar(OR_OP); 427 tk.wordChars('}', '~'); 428 429 // Initialize parse tables in terminal. 430 431 tk.ordinaryChar(EQUAL_OP); 432 tk.ordinaryChar(NOT_OP); 433 tk.ordinaryChar(LESS_OP); 434 tk.ordinaryChar(GREATER_OP); 435 436 StringBuffer buf = new StringBuffer(); 437 438 439 // Parse through the expression. 440 441 try { 442 parseInternal(tk, buf, true); 443 444 } catch (IOException ex) { 445 throw 446 new ServiceLocationException( 447 ServiceLocationException.PARSE_ERROR, 448 "v1_query_error", 449 new Object[] {query}); 450 451 } 452 453 return buf.toString(); 454 } 455 456 // Do the actual parsing, using the passed-in stream tokenizer. 457 458 private void 459 parseInternal(StreamTokenizer tk, StringBuffer buf, boolean start) 460 throws ServiceLocationException, IOException { 461 462 int tok = 0; 463 boolean ret = true; 464 465 do { 466 tok = eatWhite(tk); 467 468 // We should be at the beginning a parenthesized 469 // where list. 470 471 if (tok == OPEN_PAREN) { 472 473 // Get the next token. Eat whitespace in the process. 474 475 tok = eatWhite(tk); 476 477 // If it's a logOp, then process as a logical expression. 478 // This handles the following nasty case: 479 // 480 // (,-==the rest of it) 481 482 int logOp = tok; 483 484 if (logOp == AND_OP) { 485 486 // Need to check for escape as first thing. 487 488 tok = tk.nextToken(); 489 String str = tk.sval; // not used if token not a string... 490 tk.pushBack(); 491 492 if (tok == StreamTokenizer.TT_WORD) { 493 494 if (str.charAt(0) != HASH) { 495 parseLogicalExpression(logOp, tk, buf); 496 497 } else { 498 parse(tk, buf, true); 499 // cause we can't push back twice 500 501 } 502 503 } else { 504 parseLogicalExpression(logOp, tk, buf); 505 506 } 507 508 break; 509 510 } else if (logOp == OR_OP) { 511 512 parseLogicalExpression(logOp, tk, buf); 513 514 break; 515 516 } else { 517 518 // It's a terminal expression. Push back the last token 519 // and parse the terminal. 520 521 tk.pushBack(); 522 523 parse(tk, buf, false); 524 525 break; 526 527 } 528 529 } else { 530 throw 531 new ServiceLocationException( 532 ServiceLocationException.PARSE_ERROR, 533 "v1_query_error", 534 new Object[] {query}); 535 } 536 537 } while (true); 538 539 // Since terminals are allowed alone at the top level, 540 // we need to check here whether anything else is 541 // in the query. 542 543 if (start) { 544 545 tok = eatWhite(tk); 546 547 if (tok != StreamTokenizer.TT_EOF) { 548 549 // The line should have ended by now. 550 551 throw 552 new ServiceLocationException( 553 ServiceLocationException.PARSE_ERROR, 554 "v1_query_error", 555 new Object[] {query}); 556 } 557 } 558 559 } 560 561 // Rewrite a logical expression. 562 563 private void 564 parseLogicalExpression(int logOp, StreamTokenizer tk, StringBuffer buf) 565 throws ServiceLocationException, IOException { 566 567 // Append paren and operator to buffer. 568 569 buf.append((char)OPEN_PAREN); 570 buf.append((char)logOp); 571 572 int tok = 0; 573 574 do { 575 576 tok = eatWhite(tk); 577 578 if (tok == OPEN_PAREN) { 579 580 // So parseInternal() sees a parenthesized list. 581 582 tk.pushBack(); 583 584 // Go back to parseInternal. 585 586 parseInternal(tk, buf, false); 587 588 } else if (tok == CLOSE_PAREN) { 589 590 // Append the character to the buffer and return. 591 592 buf.append((char)tok); 593 594 return; 595 596 } else { 597 throw 598 new ServiceLocationException( 599 ServiceLocationException.PARSE_ERROR, 600 "v1_query_error", 601 new Object[] {query}); 602 } 603 604 } while (tok != StreamTokenizer.TT_EOF); 605 606 // Error if we've not caught ourselves before this. 607 608 throw 609 new ServiceLocationException( 610 ServiceLocationException.PARSE_ERROR, 611 "v1_query_error", 612 new Object[] {query}); 613 } 614 615 // Parse a terminal. Opening paren has been got. 616 617 private void parse(StreamTokenizer tk, 618 StringBuffer buf, 619 boolean firstEscaped) 620 throws ServiceLocationException, IOException { 621 622 String tag = ""; 623 int tok = 0; 624 625 tok = eatWhite(tk); 626 627 // Gather the tag and value. 628 629 if (tok != StreamTokenizer.TT_WORD) { 630 throw 631 new ServiceLocationException( 632 ServiceLocationException.PARSE_ERROR, 633 "v1_query_error", 634 new Object[] {query}); 635 } 636 637 // Parse the tag. 638 639 tag = parseTag(tk, firstEscaped); 640 641 if (tag.length() <= 0) { 642 throw 643 new ServiceLocationException( 644 ServiceLocationException.PARSE_ERROR, 645 "v1_query_error", 646 new Object[] {query}); 647 } 648 649 // Unescape tag. 650 651 tag = ServiceLocationAttributeV1.unescapeAttributeString(tag, 652 charCode); 653 654 // Now escape in v2 format, 655 656 tag = ServiceLocationAttribute.escapeAttributeString(tag, true); 657 658 // Parse the operator. 659 660 char compOp = parseOperator(tk); 661 662 // If this was a keyword operator, then add present 663 // operator and closing paren and return. 664 665 if (compOp == PRESENT) { 666 buf.append(OPEN_PAREN); 667 buf.append(tag); 668 buf.append(EQUAL_OP); 669 buf.append(PRESENT); 670 buf.append(CLOSE_PAREN); 671 return; 672 673 } 674 675 // Parse value by reading up to the next close paren. 676 // Returned value will be in v2 format. 677 678 String valTok = parseValue(tk); 679 680 // Construct the comparision depending on the operator. 681 682 if (compOp == NOT_OP) { 683 684 // If the value is an integer, we can construct a query 685 // that will exclude the number. 686 687 try { 688 689 int n = Integer.parseInt(valTok); 690 691 // Bump the integer up and down to catch numbers on both 692 // sides of the required number. Be careful not to 693 // overstep bounds. 694 695 if (n < Integer.MAX_VALUE) { 696 buf.append(OPEN_PAREN); 697 buf.append(tag); 698 buf.append(GREATER_OP); 699 buf.append(EQUAL_OP); 700 buf.append(n + 1); 701 buf.append(CLOSE_PAREN); 702 703 } 704 705 if (n > Integer.MIN_VALUE) { 706 buf.append(OPEN_PAREN); 707 buf.append(tag); 708 buf.append(LESS_OP); 709 buf.append(EQUAL_OP); 710 buf.append(n - 1); 711 buf.append(CLOSE_PAREN); 712 713 } 714 715 if ((n < Integer.MAX_VALUE) && (n > Integer.MIN_VALUE)) { 716 buf.insert(0, OR_OP); 717 buf.insert(0, OPEN_PAREN); 718 buf.append(CLOSE_PAREN); 719 720 } 721 722 } catch (NumberFormatException ex) { 723 724 // It's not an integer. We can construct a query expression 725 // that will not always work. The query rules out advertisments 726 // where the attribute value doesn't match and there are 727 // no other attributes or values, and advertisements 728 // that don't contain the attribute, but it doesn't rule out 729 // a multivalued attribute with other values or if there 730 // are other attributes. The format of the query is: 731 // "(&(<tag>=*)(!(<tag>=<value>))). 732 733 buf.append(OPEN_PAREN); 734 buf.append(AND_OP); 735 buf.append(OPEN_PAREN); 736 buf.append(tag); 737 buf.append(EQUAL_OP); 738 buf.append(PRESENT); 739 buf.append(CLOSE_PAREN); 740 buf.append(OPEN_PAREN); 741 buf.append(NOT_OP); 742 buf.append(OPEN_PAREN); 743 buf.append(tag); 744 buf.append(EQUAL_OP); 745 buf.append(valTok); 746 buf.append(CLOSE_PAREN); 747 buf.append(CLOSE_PAREN); 748 buf.append(CLOSE_PAREN); 749 750 } 751 752 } else if ((compOp == LESS_OP) || (compOp == GREATER_OP)) { 753 754 int n = 0; 755 756 try { 757 758 n = Integer.parseInt(valTok); 759 760 } catch (NumberFormatException ex) { 761 762 // It's a parse error here. 763 764 throw 765 new ServiceLocationException( 766 ServiceLocationException.PARSE_ERROR, 767 "v1_query_error", 768 new Object[] {query}); 769 770 } 771 772 // We don't attempt to handle something that would cause 773 // arithmetic overflow. 774 775 if ((n == Integer.MAX_VALUE) || (n == Integer.MIN_VALUE)) { 776 throw 777 new ServiceLocationException( 778 ServiceLocationException.PARSE_ERROR, 779 "v1_query_error", 780 new Object[] {query}); 781 782 } 783 784 // Construct a query that includes everything 785 // to the correct side. 786 787 buf.append(OPEN_PAREN); 788 buf.append(tag); 789 790 if (compOp == LESS_OP) { 791 buf.append(LESS_OP); 792 buf.append(EQUAL_OP); 793 buf.append(n - 1); 794 795 } else { 796 buf.append(GREATER_OP); 797 buf.append(EQUAL_OP); 798 buf.append(n + 1); 799 800 } 801 802 buf.append(CLOSE_PAREN); 803 804 } else { 805 806 // Simple, single operator. Just add it with the 807 // value. 808 809 buf.append(OPEN_PAREN); 810 buf.append(tag); 811 812 // Need to distinguish less and greater equal. 813 814 if (compOp == LEQUAL_OP) { 815 buf.append(LESS_OP); 816 buf.append(EQUAL_OP); 817 818 } else if (compOp == GEQUAL_OP) { 819 buf.append(GREATER_OP); 820 buf.append(EQUAL_OP); 821 822 } else { 823 buf.append(compOp); 824 825 } 826 827 buf.append(valTok); 828 buf.append(CLOSE_PAREN); 829 830 } 831 832 } 833 834 // Gather tokens with embedded whitespace and return. 835 836 private String parseTag(StreamTokenizer tk, boolean ampStart) 837 throws ServiceLocationException, IOException { 838 839 String value = ""; 840 841 // Take care of corner case here. 842 843 if (ampStart) { 844 value = value +"&"; 845 ampStart = false; 846 } 847 848 do { 849 850 if (tk.ttype == StreamTokenizer.TT_WORD) { 851 value += tk.sval; 852 853 } else if ((char)tk.ttype == SPACE) { 854 value = value + " "; 855 856 } else if ((char)tk.ttype == AND_OP) { 857 value = value + "&"; 858 859 } else { 860 break; 861 862 } 863 tk.nextToken(); 864 865 } while (true); 866 867 return value.trim(); // removes trailing whitespace... 868 } 869 870 private char parseOperator(StreamTokenizer tk) 871 throws ServiceLocationException, IOException { 872 873 int tok = tk.ttype; 874 875 // If the token is a close paren, then this was a keyword 876 // (e.g. "(foo)". Return the present operator. 877 878 if ((char)tok == CLOSE_PAREN) { 879 return PRESENT; 880 881 } 882 883 if (tok != EQUAL_OP && tok != NOT_OP && 884 tok != LESS_OP && tok != GREATER_OP) { 885 886 throw 887 new ServiceLocationException( 888 ServiceLocationException.PARSE_ERROR, 889 "v1_query_error", 890 new Object[] {query}); 891 892 } 893 894 char compOp = (char)tok; 895 896 // Get the next token. 897 898 tok = tk.nextToken(); 899 900 // Look for dual character operators. 901 902 if ((char)tok == EQUAL_OP) { 903 904 // Here, we can have either "!=", "<=", ">=", or "==". 905 // Anything else is wrong. 906 907 if (compOp != LESS_OP && compOp != GREATER_OP && 908 compOp != EQUAL_OP && compOp != NOT_OP) { 909 throw 910 new ServiceLocationException( 911 ServiceLocationException.PARSE_ERROR, 912 "v1_query_error", 913 new Object[] {query}); 914 } 915 916 // Assign the right dual operator. 917 918 if (compOp == LESS_OP) { 919 compOp = LEQUAL_OP; 920 921 } else if (compOp == GREATER_OP) { 922 compOp = GEQUAL_OP; 923 924 } 925 926 } else if (compOp != LESS_OP && compOp != GREATER_OP) { 927 928 // Error if the comparison operator was something other 929 // than ``<'' or ``>'' and there is no equal. This 930 // rules out ``!'' or ``='' alone. 931 932 throw 933 new ServiceLocationException( 934 ServiceLocationException.PARSE_ERROR, 935 "v1_query_error", 936 new Object[] {query}); 937 938 } else { 939 940 // Push back the last token if it wasn't a two character operator. 941 942 tk.pushBack(); 943 944 } 945 946 return compOp; 947 } 948 949 950 private String parseValue(StreamTokenizer tk) 951 throws ServiceLocationException, IOException { 952 953 int tok = 0; 954 StringBuffer valTok = new StringBuffer(); 955 956 // Eat leading whitespace. 957 958 tok = eatWhite(tk); 959 960 // If the first value is a paren, then we've got an 961 // opaque. 962 963 if ((char)tok == OPEN_PAREN) { 964 965 valTok.append("("); 966 967 // Collect all tokens up to the closing paren. 968 969 do { 970 971 tok = tk.nextToken(); 972 973 // It's a closing paren. break out of the loop. 974 975 if ((char)tok == CLOSE_PAREN) { 976 valTok.append(")"); 977 break; 978 979 } else if ((char)tok == EQUAL_OP) { 980 valTok.append("="); 981 982 } else if (tok == StreamTokenizer.TT_WORD) { 983 valTok.append(tk.sval); 984 985 } else { 986 throw 987 new ServiceLocationException( 988 ServiceLocationException.PARSE_ERROR, 989 "v1_query_error", 990 new Object[] {query}); 991 } 992 993 } while (true); 994 995 996 // Eat whitespace until closing paren. 997 998 tok = eatWhite(tk); 999 1000 if ((char)tok != CLOSE_PAREN) { 1001 throw 1002 new ServiceLocationException( 1003 ServiceLocationException.PARSE_ERROR, 1004 "v1_query_error", 1005 new Object[] {query}); 1006 1007 } 1008 1009 } else { 1010 1011 // Error if just a closed paren. 1012 1013 if (tok == CLOSE_PAREN) { 1014 throw 1015 new ServiceLocationException( 1016 ServiceLocationException.PARSE_ERROR, 1017 "v1_query_error", 1018 new Object[] {query}); 1019 1020 } 1021 1022 do { 1023 1024 // Append the token if a WORD 1025 1026 if (tok == StreamTokenizer.TT_WORD) { 1027 valTok.append(tk.sval); 1028 1029 } else if ((tok != StreamTokenizer.TT_EOF) && 1030 (tok != StreamTokenizer.TT_EOL) && 1031 (tok != CLOSE_PAREN)) { 1032 1033 // Otherwise, it's a token char, so append. 1034 1035 valTok.append((char)tok); 1036 1037 } 1038 1039 tok = tk.nextToken(); 1040 1041 } while (tok != CLOSE_PAREN); 1042 } 1043 1044 // If a wildcard, remove wildcard stars here for later re-insertion. 1045 1046 String strval = valTok.toString().trim(); 1047 boolean wildstart = false; 1048 boolean wildend = false; 1049 1050 if (strval.startsWith(WILDCARD)) { 1051 wildstart = true; 1052 strval = strval.substring(1, strval.length()); 1053 1054 } 1055 1056 if (strval.endsWith(WILDCARD)) { 1057 wildend = true; 1058 strval = strval.substring(0, strval.length()-1); 1059 1060 } 1061 1062 // Evaluate the value. 1063 1064 Object val = 1065 ServiceLocationAttributeV1.evaluate(strval, charCode); 1066 1067 // Now convert to v2 format, and return. 1068 1069 if (val instanceof String) { 1070 strval = 1071 ServiceLocationAttribute.escapeAttributeString(val.toString(), 1072 false); 1073 1074 // Add wildcards back in. 1075 1076 if (wildstart) { 1077 strval = WILDCARD + strval; 1078 1079 } 1080 1081 if (wildend) { 1082 strval = strval + WILDCARD; 1083 1084 } 1085 1086 } else { 1087 strval = val.toString(); 1088 1089 } 1090 1091 return strval; 1092 1093 } 1094 1095 // Eat whitespace. 1096 1097 private int eatWhite(StreamTokenizer tk) 1098 throws IOException { 1099 1100 int tok = tk.nextToken(); 1101 1102 while (tok == SPACE) { 1103 tok = tk.nextToken(); 1104 1105 } 1106 1107 return tok; 1108 } 1109 } 1110