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-2001 by Sun Microsystems, Inc. 26 * All rights reserved. 27 * 28 */ 29 30 // SCCS Status: @(#)AttributeVerifier.java 1.3 06/25/98 31 // AttributeVerifier.java: An attribute verifier for SLP attributes. 32 // Author: James Kempf 33 // Created On: Thu Jun 19 10:51:32 1997 34 // Last Modified By: James Kempf 35 // Last Modified On: Mon Nov 9 10:21:02 1998 36 // Update Count: 200 37 // 38 39 package com.sun.slp; 40 41 import java.util.*; 42 import java.io.*; 43 44 /** 45 * The AttributeVerifier class implements the ServiceLocationAttributeVerifier 46 * interface, but without committment to a particular mechanism for 47 * obtaining the template defintion. Subclasses provide the mechanism, 48 * and pass in the template to the parent as a Reader during object 49 * creation. The AttributeVerifier class parses tokens from the Reader and 50 * constructs the attribute descriptor objects describing the attribute. These 51 * are used during verification of the attribute. The AttributeVerifier 52 * and implementations of the attribute descriptors are free to optimize 53 * space utilization by lazily evaluating portions of the attribute 54 * template. 55 * 56 * @version 1.3 98/10/14 57 * @author James Kempf 58 * 59 */ 60 61 class AttributeVerifier 62 extends Object 63 implements ServiceLocationAttributeVerifier { 64 65 // Template specific escape. 66 67 private static final String ESC_HASH = "\\23"; 68 private static final String HASH = "#"; 69 70 // Number of template attributes. 71 72 private static final int TEMPLATE_ATTR_NO = 5; 73 74 // Bitfields for found template attributes. 75 76 private static final int SERVICE_MASK = 0x01; 77 private static final int VERSION_MASK = 0x02; 78 private static final int DESCRIPTION_MASK = 0x08; 79 private static final int URL_PATH_RULES_MASK = 0x10; 80 81 // When all template attribute assignments are found. 82 83 private static final int TEMPLATE_FOUND = (SERVICE_MASK | 84 VERSION_MASK | 85 DESCRIPTION_MASK | 86 URL_PATH_RULES_MASK); 87 88 // These are the valid SLP types. 89 90 private static final String INTEGER_TYPE = "integer"; 91 private static final String STRING_TYPE = "string"; 92 private static final String BOOLEAN_TYPE = "boolean"; 93 private static final String OPAQUE_TYPE = "opaque"; 94 private static final String KEYWORD_TYPE = "keyword"; 95 96 // These are the corresponding Java types. Package public so 97 // others (SLPConfig for example) can get at them. 98 99 static final String JAVA_STRING_TYPE = 100 "java.lang.String"; 101 static final String JAVA_INTEGER_TYPE = 102 "java.lang.Integer"; 103 static final String JAVA_BOOLEAN_TYPE = 104 "java.lang.Boolean"; 105 static final String JAVA_OPAQUE_TYPE = 106 "[B"; 107 108 // Tokens for boolean values. 109 110 private static final String TRUE_TOKEN = "true"; 111 private static final String FALSE_TOKEN = "false"; 112 113 // This is the number of flags. 114 115 private static final int FLAG_NO = 4; 116 117 // These are the flags. 118 119 private static final String MULTIPLE_FLAG = "m"; 120 private static final String LITERAL_FLAG = "l"; 121 private static final String EXPLICIT_FLAG = "x"; 122 private static final String OPTIONAL_FLAG = "o"; 123 124 // These masks help determine whether the flags have been duplicated. 125 126 private static final byte MULTIPLE_MASK = 0x01; 127 private static final byte LITERAL_MASK = 0x02; 128 private static final byte EXPLICIT_MASK = 0x04; 129 private static final byte OPTIONAL_MASK = 0x08; 130 131 // These are tokens for separator characters. 132 133 private static final char TT_COMMA = ','; 134 private static final char TT_EQUALS = '='; 135 private static final char TT_FIELD = '#'; 136 private static final char TT_ESCAPE = '\\'; 137 138 // This token is for checking version number 139 // attribute assignment. 140 141 private static final char TT_PERIOD = '.'; 142 143 // Radix64 code characters. 144 145 private static final char UPPER_START_CODE = 'A'; 146 private static final char UPPER_END_CODE = 'Z'; 147 private static final char LOWER_START_CODE = 'a'; 148 private static final char LOWER_END_CODE = 'z'; 149 private static final char NUMBER_START_CODE = '0'; 150 private static final char NUMBER_END_CODE = '9'; 151 private static final char EXTRA_CODE1 = '+'; 152 private static final char EXTRA_CODE2 = '/'; 153 private static final char PAD_CODE = '='; 154 private static final char LENGTH_SEPERATOR = ':'; 155 156 // The SLP service type of this template. 157 158 private ServiceType serviceType; 159 160 // The template's language locale. 161 162 private Locale locale; 163 164 // The template's version. 165 166 private String version; 167 168 // The template's URL syntax. 169 170 private String URLSyntax; 171 172 // The template's description. 173 174 private String description; 175 176 // The attribute descriptors. 177 178 private Hashtable attributeDescriptors = new Hashtable(); 179 180 // 181 // Constructors. 182 183 AttributeVerifier() { 184 185 } 186 187 // Initialize the attribute verifier with a reader. Subclasses or clients 188 // pass in a Reader on the template that is used for parsing. This 189 // method is used when the template includes the template attributes 190 // and URL rules. 191 192 void initialize(Reader r) throws ServiceLocationException { 193 194 // Use a StreamTokenizer to parse. 195 196 StreamTokenizer tk = new StreamTokenizer(r); 197 198 // Initialize tokenizer for parsing main. 199 200 initFieldChar(tk); 201 202 // Now parse the attribute template, including template attributes. 203 204 parseTemplate(tk); 205 } 206 207 // Initialize with this method when no template attributes are involved. 208 209 void initializeAttributesOnly(Reader r) 210 throws ServiceLocationException { 211 212 // Use a StreamTokenizer to parse. 213 214 StreamTokenizer tk = new StreamTokenizer(r); 215 216 // Initialize tokenizer for parsing main. 217 218 initFieldChar(tk); 219 220 // Now parse the attribute templates, but no template attributes. 221 222 parseAttributes(tk); 223 } 224 225 // 226 // ServiceLocationAttributeVerifier interface implementation. 227 // 228 229 /** 230 * Returns the SLP service type for which this is the verifier. 231 * 232 * @return The SLP service type name. 233 */ 234 235 public ServiceType getServiceType() { 236 237 return serviceType; 238 } 239 240 /** 241 * Returns the SLP language locale of this is the verifier. 242 * 243 * @return The SLP language locale. 244 */ 245 246 public Locale getLocale() { 247 248 return locale; 249 } 250 251 /** 252 * Returns the SLP version of this is the verifier. 253 * 254 * @return The SLP version. 255 */ 256 257 public String getVersion() { 258 259 return version; 260 } 261 262 /** 263 * Returns the SLP URL syntax of this is the verifier. 264 * 265 * @return The SLP URL syntax. 266 */ 267 268 public String getURLSyntax() { 269 270 return URLSyntax; 271 } 272 273 /** 274 * Returns the SLP description of this is the verifier. 275 * 276 * @return The SLP description. 277 */ 278 279 public String getDescription() { 280 281 return description; 282 } 283 284 /** 285 * Returns the ServiceLocationAttributeDescriptor object for the 286 * attribute having the named id. IF no such attribute exists in the 287 * template, returns null. This method is primarily for GUI tools to 288 * display attribute information. Programmatic verification of attributes 289 * should use the verifyAttribute() method. 290 * 291 * @param attrId Id of attribute to return. 292 * @return The ServiceLocationAttributeDescriptor object corresponding 293 * to the parameter, or null if none. 294 */ 295 296 public ServiceLocationAttributeDescriptor 297 getAttributeDescriptor(String attrId) { 298 299 return 300 (ServiceLocationAttributeDescriptor) 301 attributeDescriptors.get(attrId.toLowerCase()); 302 303 } 304 305 /** 306 * Returns an Enumeration of 307 * ServiceLocationAttributeDescriptors for the template. This method 308 * is primarily for GUI tools to display attribute information. 309 * Programmatic verification of attributes should use the 310 * verifyAttribute() method. Note that small memory implementations 311 * may want to implement the Enumeration so that attributes are 312 * parsed on demand rather than at creation time. 313 * 314 * @return A Dictionary with attribute id's as the keys and 315 * ServiceLocationAttributeDescriptor objects for the 316 * attributes as the values. 317 */ 318 319 public Enumeration getAttributeDescriptors() { 320 321 return ((Hashtable)attributeDescriptors.clone()).elements(); 322 323 } 324 325 /** 326 * Verify that the attribute parameter is a valid SLP attribute. 327 * 328 * @param attribute The ServiceLocationAttribute to be verified. 329 */ 330 331 public void verifyAttribute(ServiceLocationAttribute attribute) 332 throws ServiceLocationException { 333 334 String id = attribute.getId().toLowerCase(); 335 ServiceLocationAttributeDescriptor des = 336 (ServiceLocationAttributeDescriptor)attributeDescriptors.get(id); 337 338 if (des == null) { 339 340 throw 341 new ServiceLocationException( 342 ServiceLocationException.PARSE_ERROR, 343 "template_no_attribute", 344 new Object[] { id }); 345 } 346 347 348 String type = des.getValueType(); 349 Vector vals = attribute.getValues(); 350 351 // If keyword, check that no values were specified. 352 353 if (des.getIsKeyword()) { 354 355 if (vals != null) { 356 throw 357 new ServiceLocationException( 358 ServiceLocationException.PARSE_ERROR, 359 "template_not_null", 360 new Object[] { id }); 361 } 362 } else { 363 364 int i, n; 365 366 // Check that a values vector exists, and, if the attribute is 367 // not multivalued, only one element is in it. 368 369 if (vals == null) { 370 throw 371 new ServiceLocationException( 372 ServiceLocationException.PARSE_ERROR, 373 "template_null", 374 new Object[] { id }); 375 376 } 377 378 n = vals.size(); 379 380 if (n > 1 && !des.getIsMultivalued()) { 381 throw 382 new ServiceLocationException( 383 ServiceLocationException.PARSE_ERROR, 384 "template_not_multi", 385 new Object[] { id }); 386 } 387 388 // Get allowed values. 389 390 Vector av = null; 391 Enumeration en = des.getAllowedValues(); 392 393 if (en.hasMoreElements()) { 394 av = new Vector(); 395 396 while (en.hasMoreElements()) { 397 Object v = en.nextElement(); 398 399 // Lower case if string, convert to Opaque if byte array. 400 401 if (type.equals(JAVA_STRING_TYPE)) { 402 v = ((String)v).toLowerCase(); 403 404 } else if (type.equals(JAVA_OPAQUE_TYPE)) { 405 v = new Opaque((byte[])v); 406 407 } 408 av.addElement(v); 409 410 } 411 } 412 413 // Check that the types of the values vector match the attribute 414 // type. Also, if any allowed values, that attribute values 415 // match. 416 417 String attTypeName = des.getValueType(); 418 419 for (i = 0; i < n; i++) { 420 Object val = vals.elementAt(i); 421 422 String typeName = val.getClass().getName(); 423 424 if (!typeName.equals(attTypeName)) { 425 throw 426 new ServiceLocationException( 427 ServiceLocationException.PARSE_ERROR, 428 "template_type_mismatch", 429 new Object[] { id, typeName, attTypeName }); 430 431 } 432 433 // Convert value for comparison, if necessary. 434 435 if (type.equals(JAVA_STRING_TYPE)) { 436 val = ((String)val).toLowerCase(); 437 438 } else if (type.equals(JAVA_OPAQUE_TYPE)) { 439 val = new Opaque((byte[])val); 440 441 } 442 443 if (av != null && !av.contains(val)) { 444 throw 445 new ServiceLocationException( 446 ServiceLocationException.PARSE_ERROR, 447 "template_not_allowed_value", 448 new Object[] {id, val}); 449 450 } 451 } 452 453 } 454 455 // No way to verify `X' because that's a search property. We 456 // must verify `O' in the context of an attribute set. 457 } 458 459 /** 460 * Verify that the set of registration attributes matches the 461 * required attributes for the service. 462 * 463 * @param attributeVector A Vector of ServiceLocationAttribute objects 464 * for the registration. 465 * @exception ServiceLocationException Thrown if the 466 * attribute set is not valid. The message contains information 467 * on the attribute name and problem. 468 */ 469 470 public void verifyRegistration(Vector attributeVector) 471 throws ServiceLocationException { 472 473 Assert.nonNullParameter(attributeVector, "attributeVector"); 474 475 476 if (attributeVector.size() <= 0) { 477 478 // Check whether any attributes are required. If so, then 479 // there's an error. 480 481 Enumeration en = attributeDescriptors.elements(); 482 483 while (en.hasMoreElements()) { 484 ServiceLocationAttributeDescriptor attDesc = 485 (ServiceLocationAttributeDescriptor)en.nextElement(); 486 487 if (!attDesc.getIsOptional()) { 488 489 throw 490 new ServiceLocationException( 491 ServiceLocationException.PARSE_ERROR, 492 "template_missing_required", 493 new Object[] { attDesc.getId() }); 494 } 495 } 496 } else { 497 498 // Construct a hashtable of incoming objects, verifying them 499 // while doing so. 500 501 int i, n = attributeVector.size(); 502 Hashtable incoming = new Hashtable(); 503 504 for (i = 0; i < n; i++) { 505 ServiceLocationAttribute attribute = 506 (ServiceLocationAttribute)attributeVector.elementAt(i); 507 String id = attribute.getId().toLowerCase(); 508 509 // If we already have it, signal a duplicate. 510 511 if (incoming.get(id) != null) { 512 throw 513 new ServiceLocationException( 514 ServiceLocationException.PARSE_ERROR, 515 "template_dup", 516 new Object[] { attribute.getId() }); 517 518 } 519 520 verifyAttribute(attribute); 521 522 incoming.put(id, attribute); 523 } 524 525 // Now check that all required attributes are present. 526 527 Enumeration en = attributeDescriptors.elements(); 528 529 while (en.hasMoreElements()) { 530 ServiceLocationAttributeDescriptor attDesc = 531 (ServiceLocationAttributeDescriptor)en.nextElement(); 532 String attrId = attDesc.getId(); 533 534 if (!attDesc.getIsOptional() && 535 incoming.get(attrId.toLowerCase()) == null) { 536 537 throw 538 new ServiceLocationException( 539 ServiceLocationException.PARSE_ERROR, 540 "template_missing_required", 541 new Object[] { attrId }); 542 } 543 } 544 } 545 546 } 547 548 // 549 // Private implementation. This is the template attribute parser. 550 // 551 552 // 553 // Tokenizer initializers. 554 555 // Base initialization. Resets syntax tables, sets up EOL parsing, 556 // and makes word case significant. 557 558 private void initForBase(StreamTokenizer tk) { 559 560 // Each part of an attribute production must specify which 561 // characters belong to words. 562 563 tk.resetSyntax(); 564 565 // Note that we have to make EOL be whitespace even if significant 566 // because otherwise the line number won't be correctly incremented. 567 568 tk.whitespaceChars((int)'\n', (int)'\n'); 569 570 // Don't lower case tokens. 571 572 tk.lowerCaseMode(false); 573 } 574 575 // Initialize those token characters that appear in all 576 // productions. 577 578 private void initCommonToken(StreamTokenizer tk) { 579 580 // These characters are recognized as parts of tokens. 581 582 tk.wordChars((int)'A', (int)'Z'); 583 tk.wordChars((int)'a', (int)'z'); 584 tk.wordChars((int)'0', (int)'9'); 585 tk.wordChars((int)'&', (int)'&'); 586 tk.wordChars((int)'*', (int)'*'); 587 tk.wordChars((int)':', (int)':'); 588 tk.wordChars((int)'-', (int)'-'); 589 tk.wordChars((int)'_', (int)'_'); 590 tk.wordChars((int)'$', (int)'$'); 591 tk.wordChars((int)'+', (int)'+'); 592 tk.wordChars((int)'@', (int)'@'); 593 tk.wordChars((int)'.', (int)'.'); 594 tk.wordChars((int)'|', (int)'|'); 595 tk.wordChars((int)'<', (int)'<'); 596 tk.wordChars((int)'>', (int)'>'); 597 tk.wordChars((int)'~', (int)'~'); 598 599 } 600 601 // Initialize tokenizer for parsing attribute name, 602 // attribute type and flags, 603 // and for boolean initializer lists. 604 605 private void initIdChar(StreamTokenizer tk) { 606 607 initForBase(tk); 608 initCommonToken(tk); 609 610 // Need backslash for escaping. 611 612 tk.wordChars((int)'\\', (int)'\\'); 613 614 // Attribute id, Type, flags, and boolean initialzers 615 // all ignore white space. 616 617 tk.whitespaceChars((int)' ', (int)' '); 618 tk.whitespaceChars((int)'\t', (int)'\t'); 619 620 // Attribute part won't view newline as being significant. 621 622 tk.eolIsSignificant(false); 623 624 // Case is not folded. 625 626 tk.lowerCaseMode(false); 627 } 628 629 // Initialize tokenizer for parsing service type name. 630 // need to restrict characters. 631 632 private void initSchemeIdChar(StreamTokenizer tk) { 633 634 initForBase(tk); 635 636 tk.wordChars((int)'A', (int)'Z'); 637 tk.wordChars((int)'a', (int)'z'); 638 tk.wordChars((int)'0', (int)'9'); 639 tk.wordChars((int)'-', (int)'-'); 640 tk.wordChars((int)'+', (int)'+'); 641 tk.wordChars((int)'.', (int)'.'); // allows naming authority. 642 tk.wordChars((int)':', (int)':'); // for abstract and concrete type. 643 644 // Scheme name, type, flags, and boolean initialzers 645 // all ignore white space. 646 647 tk.whitespaceChars((int)' ', (int)' '); 648 tk.whitespaceChars((int)'\t', (int)'\t'); 649 650 // Scheme part won't view newline as being significant. 651 652 tk.eolIsSignificant(false); 653 654 // Case is not folded. 655 656 tk.lowerCaseMode(false); 657 658 } 659 660 // Initialize tokenizer for string list parsing. 661 // Everything except '#' and ',' is recognized. 662 // Note that whitespace is significant, but 663 // EOL is ignored. 664 665 private void initStringItemChar(StreamTokenizer tk) { 666 667 initForBase(tk); 668 669 tk.wordChars((int)'\t', (int)'\t'); 670 tk.wordChars((int)' ', (int)'"'); 671 // '#' goes here 672 tk.wordChars((int)'$', (int)'+'); 673 // ',' goes here 674 tk.wordChars((int)'-', (int)'/'); 675 tk.wordChars((int)'0', (int)'9'); 676 tk.wordChars((int)':', (int)':'); 677 // ';' goes here 678 tk.wordChars((int)'<', (int)'@'); 679 tk.wordChars((int)'A', (int)'Z'); 680 tk.wordChars((int)'[', (int)'`'); 681 tk.wordChars((int)'a', (int)'z'); 682 tk.wordChars((int)'{', (int)'~'); 683 684 // '%' is also reserved, but it is postprocessed 685 // after the string is collected. 686 687 // Parse by lines to check when we've reached the end of the list. 688 689 tk.whitespaceChars((int)'\r', (int)'\r'); 690 tk.whitespaceChars((int)'\n', (int)'\n'); 691 tk.eolIsSignificant(true); 692 693 } 694 695 // Initialize tokenizer for integer list parsing. 696 697 private void initIntItemChar(StreamTokenizer tk) { 698 699 initForBase(tk); 700 701 tk.wordChars((int)'0', (int)'9'); 702 tk.wordChars((int)'-', (int)'-'); 703 tk.wordChars((int)'+', (int)'+'); 704 705 // Integer value list parsing ignores white space. 706 707 tk.whitespaceChars((int)' ', (int)' '); 708 tk.whitespaceChars((int)'\t', (int)'\t'); 709 710 // Parse by lines so we can find the end. 711 712 tk.whitespaceChars((int)'\r', (int)'\r'); 713 tk.whitespaceChars((int)'\n', (int)'\n'); 714 tk.eolIsSignificant(true); 715 716 } 717 718 // Boolean lists have same item syntax as scheme char. 719 720 // Initialize main production parsing. The only 721 // significant token character is <NL> because 722 // parsing is done on a line-oriented basis. 723 724 private void initFieldChar(StreamTokenizer tk) { 725 726 initForBase(tk); 727 728 tk.wordChars((int)'\t', (int)'\t'); 729 tk.wordChars((int)' ', (int)'/'); 730 tk.wordChars((int)'0', (int)'9'); 731 tk.wordChars((int)':', (int)'@'); 732 tk.wordChars((int)'A', (int)'Z'); 733 tk.wordChars((int)'[', (int)'`'); 734 tk.wordChars((int)'a', (int)'z'); 735 tk.wordChars((int)'{', (int)'~'); 736 737 tk.whitespaceChars((int)'\r', (int)'\r'); 738 tk.whitespaceChars((int)'\n', (int)'\n'); 739 tk.eolIsSignificant(true); 740 } 741 742 // 743 // Parsing methods. 744 // 745 746 // Parse a template from the tokenizer. 747 748 private void parseTemplate(StreamTokenizer tk) 749 throws ServiceLocationException { 750 751 // First parse past the template attributes. 752 753 parseTemplateAttributes(tk); 754 755 // Finally, parse the attributes. 756 757 parseAttributes(tk); 758 759 } 760 761 // Parse the template attributes from the tokenizer. 762 763 private void parseTemplateAttributes(StreamTokenizer tk) 764 throws ServiceLocationException { 765 766 int found = 0; 767 768 // Parse each of the template attributes. Note that we are parsing 769 // the attribute value assignments, not definitions. 770 771 try { 772 773 do { 774 775 found = found | parseTemplateAttribute(tk, found); 776 777 } while (found != TEMPLATE_FOUND); 778 779 } catch (IOException ex) { 780 781 throw 782 new ServiceLocationException( 783 ServiceLocationException.INTERNAL_SYSTEM_ERROR, 784 "template_io_error", 785 new Object[] {Integer.toString(tk.lineno())}); 786 787 } 788 } 789 790 // Parse a template attribute. 791 792 private int parseTemplateAttribute(StreamTokenizer tk, int found) 793 throws ServiceLocationException, IOException { 794 795 // Get line including id and equals. 796 797 int tt = tk.nextToken(); 798 799 if (tt != StreamTokenizer.TT_WORD) { 800 801 throw 802 new ServiceLocationException( 803 ServiceLocationException.PARSE_ERROR, 804 "template_assign_error", 805 new Object[] {Integer.toString(tk.lineno())}); 806 } 807 808 // Get tokenizer for id and potential value line. 809 810 StringReader rdr = new StringReader(tk.sval); 811 StreamTokenizer stk = new StreamTokenizer(rdr); 812 813 initIdChar(stk); 814 815 // Make sure newline is there. 816 817 if ((tt = tk.nextToken()) == StreamTokenizer.TT_EOF) { 818 819 throw 820 new ServiceLocationException( 821 ServiceLocationException.PARSE_ERROR, 822 "template_end_error", 823 new Object[] {Integer.toString(tk.lineno())}); 824 825 } 826 827 if (tt != StreamTokenizer.TT_EOL) { 828 829 throw 830 new ServiceLocationException( 831 ServiceLocationException.PARSE_ERROR, 832 "template_unk_token", 833 new Object[] {Integer.toString(tk.lineno())}); 834 835 } 836 837 838 // Parse off id. 839 840 if ((tt = stk.nextToken()) != StreamTokenizer.TT_WORD) { 841 842 throw 843 new ServiceLocationException( 844 ServiceLocationException.PARSE_ERROR, 845 "template_missing_id", 846 new Object[] {Integer.toString(tk.lineno())}); 847 } 848 849 String id = stk.sval; 850 boolean duplicate = false; 851 int mask = 0; 852 853 // Check for the equals. 854 855 if ((tt = stk.nextToken()) != TT_EQUALS) { 856 857 throw 858 new ServiceLocationException( 859 ServiceLocationException.PARSE_ERROR, 860 "template_missing_eq ", 861 new Object[] {Integer.toString(tk.lineno())}); 862 863 } 864 865 // Depending on the id, parse the rest. 866 867 if (id.equalsIgnoreCase(SLPTemplateRegistry.SERVICE_ATTR_ID)) { 868 869 if ((found & SERVICE_MASK) == 0) { 870 871 // Just need to parse off the service type. 872 873 if ((tt = stk.nextToken()) != StreamTokenizer.TT_WORD) { 874 throw 875 new ServiceLocationException( 876 ServiceLocationException.PARSE_ERROR, 877 "template_srv_type_err", 878 new Object[] {Integer.toString(tk.lineno())}); 879 } 880 881 // Check for characters which are not alphanumerics, + and -. 882 // Service type names are more heavily restricted. 883 884 StreamTokenizer sttk = 885 new StreamTokenizer(new StringReader(stk.sval)); 886 887 initSchemeIdChar(sttk); 888 889 if (sttk.nextToken() != StreamTokenizer.TT_WORD || 890 !stk.sval.equals(sttk.sval)) { 891 throw 892 new ServiceLocationException( 893 ServiceLocationException.PARSE_ERROR, 894 "template_srv_type_err", 895 new Object[] {Integer.toString(tk.lineno())}); 896 897 } 898 899 // Need to prefix with "serivce:". 900 901 String typeName = sttk.sval; 902 903 if (!typeName.startsWith(Defaults.SERVICE_PREFIX+":")) { 904 typeName = Defaults.SERVICE_PREFIX+":"+typeName; 905 906 } 907 908 // Set service type instance variable. 909 910 serviceType = new ServiceType(typeName); 911 912 // Check for extra stuff. 913 914 if ((tt = stk.nextToken()) != StreamTokenizer.TT_EOF) { 915 throw 916 new ServiceLocationException( 917 ServiceLocationException.PARSE_ERROR, 918 "template_srv_type_err", 919 new Object[] {Integer.toString(tk.lineno())}); 920 } 921 922 mask = SERVICE_MASK; 923 } else { 924 925 duplicate = true; 926 } 927 } else if (id.equalsIgnoreCase(SLPTemplateRegistry.VERSION_ATTR_ID)) { 928 929 if ((found & VERSION_MASK) == 0) { 930 931 // Just need to parse off the version number. 932 933 if ((tt = stk.nextToken()) != StreamTokenizer.TT_WORD) { 934 throw 935 new ServiceLocationException( 936 ServiceLocationException.PARSE_ERROR, 937 "template_vers_err", 938 new Object[] {Integer.toString(tk.lineno())}); 939 } 940 941 // Make sure it's a valid version number. 942 943 String version = stk.sval; 944 945 if (version.indexOf(TT_PERIOD) == -1) { 946 947 throw 948 new ServiceLocationException( 949 ServiceLocationException.PARSE_ERROR, 950 "template_vers_mssing", 951 new Object[] {Integer.toString(tk.lineno())}); 952 953 } 954 955 try { 956 957 new Float(version); 958 } catch (NumberFormatException ex) { 959 960 throw 961 new ServiceLocationException( 962 ServiceLocationException.PARSE_ERROR, 963 "template_vers_err", 964 new Object[] {Integer.toString(tk.lineno())}); 965 966 } 967 968 this.version = version; 969 970 // Check for extra stuff. 971 972 if ((tt = stk.nextToken()) != StreamTokenizer.TT_EOF) { 973 throw 974 new ServiceLocationException( 975 ServiceLocationException.PARSE_ERROR, 976 "template_vers_err", 977 new Object[] {Integer.toString(tk.lineno())}); 978 } 979 980 mask = VERSION_MASK; 981 } else { 982 983 duplicate = true; 984 } 985 } else if (id.equalsIgnoreCase( 986 SLPTemplateRegistry.DESCRIPTION_ATTR_ID)) { 987 988 // Make sure there is nothing else on that line. 989 990 if (stk.nextToken() != StreamTokenizer.TT_EOF) { 991 992 throw 993 new ServiceLocationException( 994 ServiceLocationException.PARSE_ERROR, 995 "template_attr_syntax", 996 new Object[] {Integer.toString(tk.lineno())}); 997 } 998 999 if ((found & DESCRIPTION_MASK) == 0) { 1000 1001 // Need to continue parsing help text until we reach a blank 1002 // line. 1003 1004 String helpText = ""; 1005 1006 do { 1007 int ptt = tt; 1008 tt = tk.nextToken(); 1009 1010 if (tt == StreamTokenizer.TT_WORD) { 1011 1012 helpText = helpText + tk.sval + "\n"; 1013 1014 } else if (tt == StreamTokenizer.TT_EOL) { 1015 1016 // If previous token was end of line, quit. 1017 1018 if (ptt == StreamTokenizer.TT_EOL) { 1019 1020 // Store any text first. 1021 1022 if (helpText.length() > 0) { 1023 description = helpText; 1024 1025 } 1026 1027 tk.pushBack(); // so same as above 1028 1029 break; 1030 } 1031 } else if (tt == StreamTokenizer.TT_EOF) { 1032 throw 1033 new ServiceLocationException( 1034 ServiceLocationException.PARSE_ERROR, 1035 "template_end_error", 1036 new Object[] {Integer.toString(tk.lineno())}); 1037 1038 } else { 1039 1040 throw 1041 new ServiceLocationException( 1042 ServiceLocationException.PARSE_ERROR, 1043 "template_unk_token", 1044 new Object[] {Integer.toString(tk.lineno())}); 1045 1046 } 1047 1048 } while (true); 1049 1050 mask = DESCRIPTION_MASK; 1051 } else { 1052 1053 duplicate = true; 1054 } 1055 } else if (id.equalsIgnoreCase( 1056 SLPTemplateRegistry.SERVICE_URL_ATTR_ID)) { 1057 1058 if ((found & URL_PATH_RULES_MASK) == 0) { 1059 1060 String serviceURLGrammer = ""; 1061 1062 // Pull everything out of the rdr StringReader until empty. 1063 1064 int ic; 1065 1066 while ((ic = rdr.read()) != -1) { 1067 serviceURLGrammer += (char)ic; 1068 1069 } 1070 1071 serviceURLGrammer += "\n"; 1072 1073 // Need to continue parsing service URL syntax until we 1074 // reach a blank line. 1075 1076 tt = StreamTokenizer.TT_EOL; 1077 1078 do { 1079 int ptt = tt; 1080 tt = tk.nextToken(); 1081 1082 if (tt == StreamTokenizer.TT_WORD) { 1083 1084 serviceURLGrammer = serviceURLGrammer + tk.sval + "\n"; 1085 1086 } else if (tt == StreamTokenizer.TT_EOL) { 1087 1088 // If previous token was end of line, quit. 1089 1090 if (ptt == StreamTokenizer.TT_EOL) { 1091 1092 // Store any text first. 1093 1094 if (serviceURLGrammer.length() > 0) { 1095 URLSyntax = serviceURLGrammer; 1096 1097 } 1098 1099 tk.pushBack(); // so same as above. 1100 1101 break; 1102 } 1103 } else if (tt == StreamTokenizer.TT_EOF) { 1104 throw 1105 new ServiceLocationException( 1106 ServiceLocationException.PARSE_ERROR, 1107 "template_end_error", 1108 new Object[] {Integer.toString(tk.lineno())}); 1109 1110 } else { 1111 1112 throw 1113 new ServiceLocationException( 1114 ServiceLocationException.PARSE_ERROR, 1115 "template_unk_token", 1116 new Object[] {Integer.toString(tk.lineno())}); 1117 1118 } 1119 1120 } while (true); 1121 1122 mask = URL_PATH_RULES_MASK; 1123 } else { 1124 1125 duplicate = true; 1126 } 1127 } else { 1128 1129 throw 1130 new ServiceLocationException( 1131 ServiceLocationException.PARSE_ERROR, 1132 "template_nontattribute_err", 1133 new Object[] {Integer.toString(tk.lineno())}); 1134 1135 } 1136 1137 // Throw exception if a duplicate definition was detected. 1138 1139 if (duplicate) { 1140 1141 throw 1142 new ServiceLocationException( 1143 ServiceLocationException.PARSE_ERROR, 1144 "template_dup_def", 1145 new Object[] {Integer.toString(tk.lineno())}); 1146 1147 } 1148 1149 1150 // Make sure the assignment ends with a blank line. 1151 1152 if ((tt = tk.nextToken()) != StreamTokenizer.TT_EOL) { 1153 1154 throw 1155 new ServiceLocationException( 1156 ServiceLocationException.PARSE_ERROR, 1157 "template_attr_syntax", 1158 new Object[] {Integer.toString(tk.lineno())}); 1159 1160 } 1161 1162 return mask; 1163 1164 } 1165 1166 1167 // Parse the attributes from the tokenizer. 1168 1169 private void parseAttributes(StreamTokenizer tk) 1170 throws ServiceLocationException { 1171 1172 try { 1173 1174 do { 1175 1176 // Check if at end of file yet. 1177 1178 int tt = tk.nextToken(); 1179 1180 if (tt == StreamTokenizer.TT_EOF) { 1181 break; 1182 } 1183 1184 // If not, push token back so we can get it next time. 1185 1186 tk.pushBack(); 1187 1188 // Parse off the attribute descriptor. 1189 1190 AttributeDescriptor attDesc = parseAttribute(tk); 1191 1192 // Check whether default values, if any, are correct. 1193 1194 checkDefaultValues(attDesc); 1195 1196 // If the attribute already exists, then throw exception. 1197 // We could arguably replace existing, but it might 1198 // suprise the user. 1199 1200 String attrId = attDesc.getId().toLowerCase(); 1201 1202 if (attributeDescriptors.get(attrId) != null) { 1203 1204 throw 1205 new ServiceLocationException( 1206 ServiceLocationException.PARSE_ERROR, 1207 "template_dup_def", 1208 new Object[] {Integer.toString(tk.lineno())}); 1209 1210 } 1211 1212 // Add the attribute to the descriptor table. 1213 1214 attributeDescriptors.put(attrId, attDesc); 1215 1216 } while (true); 1217 1218 } catch (IOException ex) { 1219 1220 throw 1221 new ServiceLocationException( 1222 ServiceLocationException.INTERNAL_SYSTEM_ERROR, 1223 "template_io_error", 1224 new Object[] {Integer.toString(tk.lineno())}); 1225 } 1226 1227 } 1228 1229 // Parse a single attribute description from the tokenizer. 1230 1231 private AttributeDescriptor 1232 parseAttribute(StreamTokenizer tk) throws ServiceLocationException { 1233 1234 AttributeDescriptor attDesc = new AttributeDescriptor(); 1235 int lineno = 0; 1236 1237 try { 1238 1239 // Parse the string for attribute id, type, and flags. 1240 1241 lineno = tk.lineno(); 1242 1243 int tt = tk.nextToken(); 1244 1245 if (tt != StreamTokenizer.TT_WORD) { 1246 throw 1247 new ServiceLocationException( 1248 ServiceLocationException.PARSE_ERROR, 1249 "template_attr_syntax", 1250 new Object[] {Integer.toString(tk.lineno())}); 1251 } 1252 1253 StreamTokenizer stk = 1254 new StreamTokenizer(new StringReader(tk.sval)); 1255 1256 initIdChar(stk); 1257 1258 // Parse the attribute id. 1259 1260 parseId(stk, attDesc, lineno); 1261 1262 // Parse the type and flags. 1263 1264 parseTypeAndFlags(stk, attDesc, lineno); 1265 1266 tt = tk.nextToken(); 1267 1268 if (tt == StreamTokenizer.TT_EOF) { 1269 1270 throw 1271 new ServiceLocationException( 1272 ServiceLocationException.PARSE_ERROR, 1273 "template_end_error", 1274 new Object[] {Integer.toString(tk.lineno())}); 1275 1276 } 1277 1278 if (tt != StreamTokenizer.TT_EOL) { 1279 1280 throw 1281 new ServiceLocationException( 1282 ServiceLocationException.PARSE_ERROR, 1283 "template_unk_token", 1284 new Object[] {Integer.toString(tk.lineno())}); 1285 1286 } 1287 1288 // Parse initial values. 1289 1290 if (!attDesc.getIsKeyword()) { 1291 1292 String tok = ""; 1293 1294 // Read in entire list. 1295 1296 do { 1297 int ptt = tt; 1298 lineno = tk.lineno(); 1299 tt = tk.nextToken(); 1300 1301 if (tt == StreamTokenizer.TT_WORD) { 1302 1303 // Trim line, check for '#', indicating end of list. 1304 1305 String line = tk.sval.trim(); 1306 1307 if (line.charAt(0) == TT_FIELD) { 1308 // it's help text already. 1309 1310 if (tok.length() > 0) { 1311 stk = 1312 new StreamTokenizer(new StringReader(tok)); 1313 parseDefaultValues(stk, attDesc, lineno); 1314 } 1315 1316 tk.pushBack(); 1317 break; 1318 1319 } else { 1320 1321 // Otherwise concatenate onto growing list. 1322 1323 tok = tok + line; 1324 1325 } 1326 1327 } else if (tt == StreamTokenizer.TT_EOL) { 1328 1329 if (ptt == StreamTokenizer.TT_EOL) { 1330 // end of attribute definition. 1331 1332 // Process any accumulated list. 1333 1334 if (tok.length() > 0) { 1335 stk = 1336 new StreamTokenizer(new StringReader(tok)); 1337 parseDefaultValues(stk, attDesc, lineno); 1338 } 1339 1340 return attDesc; 1341 1342 } 1343 } else if (tt == StreamTokenizer.TT_EOF) { 1344 throw 1345 new ServiceLocationException( 1346 ServiceLocationException.PARSE_ERROR, 1347 "template_end_error", 1348 new Object[] {Integer.toString(tk.lineno())}); 1349 1350 } else { 1351 1352 throw 1353 new ServiceLocationException( 1354 ServiceLocationException.PARSE_ERROR, 1355 "template_unk_token", 1356 new Object[] {Integer.toString(tk.lineno())}); 1357 1358 } 1359 1360 } while (true); 1361 1362 } else { 1363 attDesc.setDefaultValues(null); 1364 attDesc.setAllowedValues(null); 1365 1366 // Check for end of definition. 1367 1368 if ((tt = tk.nextToken()) == StreamTokenizer.TT_EOL) { 1369 return attDesc; 1370 1371 } else if (tt == StreamTokenizer.TT_WORD) { 1372 1373 // Check for start of help text. 1374 1375 String line = tk.sval.trim(); 1376 1377 if (line.charAt(0) != TT_FIELD) { 1378 throw 1379 new ServiceLocationException( 1380 ServiceLocationException.PARSE_ERROR, 1381 "template_attr_syntax", 1382 new Object[] {Integer.toString(tk.lineno())}); 1383 1384 } else { 1385 1386 tk.pushBack(); 1387 1388 } 1389 1390 } else if (tt == StreamTokenizer.TT_EOF) { 1391 throw 1392 new ServiceLocationException( 1393 ServiceLocationException.PARSE_ERROR, 1394 "template_end_error", 1395 new Object[] {Integer.toString(tk.lineno())}); 1396 1397 } else { 1398 1399 throw 1400 new ServiceLocationException( 1401 ServiceLocationException.PARSE_ERROR, 1402 "template_unk_token", 1403 new Object[] {Integer.toString(tk.lineno())}); 1404 1405 } 1406 } 1407 1408 1409 // Parse help text. 1410 1411 String helpText = ""; 1412 1413 do { 1414 int ptt = tt; 1415 lineno = tk.lineno(); 1416 tt = tk.nextToken(); 1417 1418 if (tt == StreamTokenizer.TT_WORD) { 1419 1420 // Check for end of help text. 1421 1422 String line = tk.sval.trim(); 1423 1424 if (line.charAt(0) == TT_FIELD) { 1425 1426 // Help text is collected verbatim after '#'. 1427 1428 helpText = 1429 helpText + line.substring(1) + "\n"; 1430 1431 } else { 1432 1433 // We've reached the end of the help text. Store it 1434 // and break out of the loop. 1435 1436 if (helpText.length() > 0) { 1437 attDesc.setDescription(helpText); 1438 } 1439 1440 tk.pushBack(); 1441 break; 1442 1443 } 1444 1445 } else if (tt == StreamTokenizer.TT_EOL || 1446 tt == StreamTokenizer.TT_EOF) { 1447 1448 // If previous token was end of line, quit. 1449 1450 if (ptt == StreamTokenizer.TT_EOL) { 1451 1452 // Store any text first. 1453 1454 if (helpText.length() > 0) { 1455 attDesc.setDescription(helpText); 1456 } 1457 1458 // If this is a keyword attribute, set the allowed 1459 // values list to null. 1460 1461 if (attDesc.getIsKeyword()) { 1462 attDesc.setAllowedValues(null); 1463 } 1464 1465 return attDesc; 1466 1467 } else if (tt == StreamTokenizer.TT_EOF) { 1468 1469 // Error if previous token wasn't EOL. 1470 1471 throw 1472 new ServiceLocationException( 1473 ServiceLocationException.PARSE_ERROR, 1474 "template_end_error", 1475 new Object[] {Integer.toString(tk.lineno())}); 1476 } 1477 1478 } else { 1479 1480 throw 1481 new ServiceLocationException( 1482 ServiceLocationException.PARSE_ERROR, 1483 "template_unk_token", 1484 new Object[] {Integer.toString(tk.lineno())}); 1485 } 1486 1487 } while (true); 1488 1489 // Parse allowed values. 1490 1491 if (!attDesc.getIsKeyword()) { 1492 1493 String tok = ""; 1494 1495 // Read in entire list. 1496 1497 do { 1498 int ptt = tt; 1499 lineno = tk.lineno(); 1500 tt = tk.nextToken(); 1501 1502 if (tt == StreamTokenizer.TT_WORD) { 1503 1504 // Concatenate onto growing list. 1505 1506 tok = tok + tk.sval; 1507 1508 } else if (tt == StreamTokenizer.TT_EOL) { 1509 1510 if (ptt == StreamTokenizer.TT_EOL) { 1511 // end of attribute definition. 1512 1513 // Process any accumulated list. 1514 1515 if (tok.length() > 0) { 1516 stk = 1517 new StreamTokenizer(new StringReader(tok)); 1518 parseAllowedValues(stk, attDesc, lineno); 1519 } 1520 1521 return attDesc; 1522 1523 } 1524 } else if (tt == StreamTokenizer.TT_EOF) { 1525 throw 1526 new ServiceLocationException( 1527 ServiceLocationException.PARSE_ERROR, 1528 "template_end_error", 1529 new Object[] {Integer.toString(tk.lineno())}); 1530 1531 } else { 1532 1533 throw 1534 new ServiceLocationException( 1535 ServiceLocationException.PARSE_ERROR, 1536 "template_unk_token", 1537 new Object[] {Integer.toString(tk.lineno())}); 1538 } 1539 1540 } while (true); 1541 1542 } else { 1543 1544 // Error. Keyword attribute should have ended during help text 1545 // parsing or before. 1546 1547 throw 1548 new ServiceLocationException( 1549 ServiceLocationException.PARSE_ERROR, 1550 "template_attr_syntax", 1551 new Object[] {Integer.toString(tk.lineno())}); 1552 } 1553 1554 } catch (IOException ex) { 1555 1556 throw 1557 new ServiceLocationException( 1558 ServiceLocationException.INTERNAL_SYSTEM_ERROR, 1559 "template_io_error", 1560 new Object[] { 1561 Integer.toString(tk.lineno()), 1562 ex.getMessage()}); 1563 } 1564 1565 } 1566 1567 // Check whether the default values, if any, are correct. 1568 1569 private void checkDefaultValues(AttributeDescriptor attDesc) 1570 throws ServiceLocationException { 1571 1572 // Don't bother if it's a keyword attribute, parsing has checked. 1573 1574 if (attDesc.getIsKeyword()) { 1575 return; 1576 } 1577 1578 Enumeration init = attDesc.getDefaultValues(); 1579 Enumeration en = attDesc.getAllowedValues(); 1580 Vector allowed = new Vector(); 1581 String attDescType = attDesc.getValueType(); 1582 1583 // First, collect the allowed values. 1584 1585 while (en.hasMoreElements()) { 1586 Object allval = en.nextElement(); 1587 1588 // Lower case strings and create opaques for comparison 1589 // if type is opaque. 1590 1591 if (attDescType.equals(JAVA_STRING_TYPE)) { 1592 allval = ((String)allval).toLowerCase(); 1593 1594 } else if (attDescType.equals(JAVA_OPAQUE_TYPE)) { 1595 allval = new Opaque((byte[])allval); 1596 1597 } 1598 1599 allowed.addElement(allval); 1600 } 1601 1602 // Now compare the allowed with the initial. 1603 1604 if (allowed.size() > 0) { 1605 1606 // Error if allowed is restricted but no initializers. 1607 1608 if (!init.hasMoreElements()) { 1609 1610 throw 1611 new ServiceLocationException( 1612 ServiceLocationException.PARSE_ERROR, 1613 "template_no_init", 1614 new Object[] {attDesc.getId()}); 1615 1616 } 1617 1618 Object val = null; 1619 1620 // Compare init values with allowed. 1621 1622 while (init.hasMoreElements()) { 1623 Object test = init.nextElement(); 1624 val = test; // for exception.. 1625 1626 if (attDescType.equals(JAVA_STRING_TYPE)) { 1627 test = ((String)test).toLowerCase(); 1628 1629 } else if (attDescType.equals(JAVA_OPAQUE_TYPE)) { 1630 test = new Opaque((byte[])test); 1631 1632 } 1633 1634 if (allowed.indexOf(test) != -1) { 1635 return; // found it! 1636 } 1637 } 1638 // Initializer wasn't found. 1639 1640 throw 1641 new ServiceLocationException( 1642 ServiceLocationException.PARSE_ERROR, 1643 "template_wrong_init", 1644 new Object[] { 1645 val.toString(), attDesc.getId()}); 1646 } 1647 } 1648 1649 // Parse the attribute's id string. 1650 1651 private void parseId(StreamTokenizer tk, 1652 AttributeDescriptor attDesc, 1653 int baseLineno) 1654 throws ServiceLocationException, IOException { 1655 1656 // Parse the attribute's identifier tag. 1657 1658 String id = parseWord(tk, baseLineno); 1659 1660 int tt = tk.nextToken(); 1661 1662 // Parse the seperator. 1663 1664 if (tt != TT_EQUALS) { 1665 throw 1666 new ServiceLocationException( 1667 ServiceLocationException.PARSE_ERROR, 1668 "template_attr_syntax", 1669 new Object[] { 1670 Integer.toString(tk.lineno() + baseLineno)}); 1671 1672 } 1673 1674 // Expand out any escaped ``#''. It won't be handled by 1675 // SLA. 1676 1677 id = unescapeHash(id); 1678 1679 // Expand out character escapes. 1680 1681 id = 1682 ServiceLocationAttribute.unescapeAttributeString(id, true); 1683 1684 1685 attDesc.setId(id); 1686 } 1687 1688 // Parse the attribute's type and flags. 1689 1690 private void 1691 parseTypeAndFlags(StreamTokenizer tk, 1692 AttributeDescriptor attDesc, 1693 int baseLineno) 1694 throws ServiceLocationException, IOException { 1695 1696 int existingFlags = 0; 1697 1698 // Parse the attribute's type. 1699 1700 String type = parseWord(tk, baseLineno); 1701 1702 checkAndAddType(type, attDesc, tk.lineno() + baseLineno); 1703 1704 // Parse the flags. 1705 1706 do { 1707 1708 // Check if any flags are left. 1709 1710 if (tk.nextToken() == StreamTokenizer.TT_EOF) { 1711 break; 1712 1713 } else { 1714 tk.pushBack(); 1715 } 1716 1717 int lineno = tk.lineno(); 1718 1719 // Parse the flag. 1720 1721 String flag = parseWord(tk, baseLineno); 1722 1723 // Error if flags with keyword. 1724 1725 if (attDesc.getIsKeyword()) { 1726 throw 1727 new ServiceLocationException( 1728 ServiceLocationException.PARSE_ERROR, 1729 "template_attr_syntax", 1730 new Object[] { 1731 Integer.toString(tk.lineno() + baseLineno)}); 1732 } 1733 1734 1735 // Check and assign it to the attribute. 1736 1737 existingFlags = 1738 existingFlags | checkAndAddFlag(flag, 1739 existingFlags, 1740 attDesc, 1741 baseLineno + lineno); 1742 1743 } while (true); 1744 } 1745 1746 // Parse the attribute's initial value(s). 1747 1748 private void parseDefaultValues(StreamTokenizer tk, 1749 AttributeDescriptor attDesc, 1750 int baseLineno) 1751 throws ServiceLocationException, IOException { 1752 1753 // First get the vector of initial values. 1754 1755 Vector vals = parseValueList(tk, attDesc, baseLineno); 1756 1757 // Check whether it works for this attribute. Type 1758 // checking will be done by value list parsing. 1759 1760 if (!attDesc.getIsMultivalued() && vals.size() > 1) { 1761 throw 1762 new ServiceLocationException( 1763 ServiceLocationException.PARSE_ERROR, 1764 "template_attr_syntax", 1765 new Object[] { 1766 Integer.toString(tk.lineno() + baseLineno)}); 1767 } 1768 1769 attDesc.setDefaultValues(vals); 1770 } 1771 1772 // Parse the attribute's allowed values. 1773 1774 private void 1775 parseAllowedValues(StreamTokenizer tk, 1776 AttributeDescriptor attDesc, 1777 int baseLineno) 1778 throws ServiceLocationException, IOException { 1779 1780 // First get the vector of all allowed values. 1781 1782 Vector vals = parseValueList(tk, attDesc, baseLineno); 1783 1784 // Now set the allowed value vector. 1785 1786 attDesc.setAllowedValues(vals); 1787 } 1788 1789 // Parse a value list. 1790 1791 private Vector parseValueList(StreamTokenizer stk, 1792 AttributeDescriptor attDesc, 1793 int baseLineno) 1794 throws ServiceLocationException, IOException { 1795 1796 Vector req = new Vector(); 1797 1798 // Set up the tokenizer according to the type of the 1799 // attribute. 1800 1801 String type = attDesc.getValueType(); 1802 1803 if (type.equals(JAVA_STRING_TYPE) || type.equals(JAVA_OPAQUE_TYPE)) { 1804 initStringItemChar(stk); 1805 } else if (type.equals(JAVA_INTEGER_TYPE)) { 1806 initIntItemChar(stk); 1807 } else if (type.equals(JAVA_BOOLEAN_TYPE)) { 1808 initIdChar(stk); 1809 } 1810 1811 // Parse through a potentially multivalued value list. 1812 1813 boolean wordRequired = true; // true when a word is required, 1814 // false when a comma required. 1815 boolean syntaxError = false; 1816 String reqTok = ""; 1817 int lineno = 0; 1818 1819 do { 1820 int tt = stk.nextToken(); 1821 lineno = stk.lineno() + baseLineno; 1822 1823 if (tt == StreamTokenizer.TT_WORD) { 1824 1825 // If a word isn't required, then the case is 1826 // "token token" and is an error. 1827 1828 if (!wordRequired) { 1829 syntaxError = true; 1830 } 1831 1832 reqTok = stk.sval.trim(); 1833 1834 // Convert the value to the proper object. 1835 1836 Object reqVal = convertValue(type, reqTok, baseLineno); 1837 req.addElement(reqVal); 1838 1839 wordRequired = false; 1840 1841 } else if (tt == StreamTokenizer.TT_EOF) { 1842 1843 // If a word is required, then list ends with 1844 // a comma, so error. 1845 1846 if (wordRequired) { 1847 syntaxError = true; 1848 } 1849 1850 break; 1851 1852 } else if (tt == TT_COMMA) { 1853 1854 // If a word is required, then error. The case is ",,". 1855 1856 if (wordRequired) { 1857 syntaxError = true; 1858 break; 1859 } 1860 1861 // Otherwise, the next token must be a word. 1862 1863 wordRequired = true; 1864 1865 } else { 1866 1867 // No other tokens are allowed. 1868 1869 syntaxError = true; 1870 break; 1871 } 1872 1873 } while (true); 1874 1875 if (syntaxError) { 1876 1877 throw 1878 new ServiceLocationException( 1879 ServiceLocationException.PARSE_ERROR, 1880 "template_attr_syntax", 1881 new Object[] {Integer.toString(lineno)}); 1882 } 1883 1884 return req; 1885 1886 } 1887 1888 // Check the type and add it to the attribute descriptor. 1889 1890 private void checkAndAddType(String type, 1891 AttributeDescriptor attDesc, 1892 int lineno) 1893 throws ServiceLocationException { 1894 1895 // Check token against recognized types. 1896 1897 if (type.equalsIgnoreCase(STRING_TYPE)) { 1898 attDesc.setValueType(JAVA_STRING_TYPE); 1899 1900 } else if (type.equalsIgnoreCase(INTEGER_TYPE)) { 1901 attDesc.setValueType(JAVA_INTEGER_TYPE); 1902 1903 } else if (type.equalsIgnoreCase(BOOLEAN_TYPE)) { 1904 attDesc.setValueType(JAVA_BOOLEAN_TYPE); 1905 1906 } else if (type.equalsIgnoreCase(OPAQUE_TYPE)) { 1907 attDesc.setValueType(JAVA_OPAQUE_TYPE); 1908 1909 } else if (type.equalsIgnoreCase(KEYWORD_TYPE)) { 1910 attDesc.setIsKeyword(true); 1911 1912 } else { 1913 1914 throw 1915 new ServiceLocationException( 1916 ServiceLocationException.PARSE_ERROR, 1917 "template_not_slp_type", 1918 new Object[] {Integer.toString(lineno)}); 1919 } 1920 1921 } 1922 1923 // Check the flag and add it to the attribute descriptor. 1924 1925 private int checkAndAddFlag(String flag, 1926 int matched, 1927 AttributeDescriptor attDesc, 1928 int lineno) 1929 throws ServiceLocationException { 1930 1931 boolean duplicate = false; 1932 1933 // We depend on the attribute descriptor being initialized to 1934 // nothing, i.e. false for all flags and for keyword. 1935 1936 if (flag.equalsIgnoreCase(MULTIPLE_FLAG)) { 1937 1938 if ((matched & MULTIPLE_MASK) != 0) { 1939 duplicate = true; 1940 1941 } else { 1942 1943 // Check for boolean. Booleans may not have 1944 // multiple values. 1945 1946 if (attDesc.getValueType().equals(JAVA_BOOLEAN_TYPE)) { 1947 1948 throw 1949 new ServiceLocationException( 1950 ServiceLocationException.PARSE_ERROR, 1951 "template_boolean_multi", 1952 new Object[] {Integer.toString(lineno)}); 1953 } 1954 1955 attDesc.setIsMultivalued(true); 1956 return MULTIPLE_MASK; 1957 1958 } 1959 1960 } else if (flag.equalsIgnoreCase(LITERAL_FLAG)) { 1961 1962 if ((matched & LITERAL_MASK) != 0) { 1963 duplicate = true; 1964 1965 } else { 1966 attDesc.setIsLiteral(true); 1967 return LITERAL_MASK; 1968 } 1969 1970 } else if (flag.equalsIgnoreCase(EXPLICIT_FLAG)) { 1971 1972 if ((matched & EXPLICIT_MASK) != 0) { 1973 duplicate = true; 1974 1975 } else { 1976 attDesc.setRequiresExplicitMatch(true); 1977 return EXPLICIT_MASK; 1978 } 1979 1980 } else if (flag.equalsIgnoreCase(OPTIONAL_FLAG)) { 1981 1982 if ((matched & OPTIONAL_MASK) != 0) { 1983 duplicate = true; 1984 1985 } else { 1986 attDesc.setIsOptional(true); 1987 return OPTIONAL_MASK; 1988 } 1989 1990 } else { 1991 1992 throw 1993 new ServiceLocationException( 1994 ServiceLocationException.PARSE_ERROR, 1995 "template_invalid_attr_flag", 1996 new Object[] {Integer.toString(lineno)}); 1997 } 1998 1999 2000 if (duplicate) { 2001 throw 2002 new ServiceLocationException( 2003 ServiceLocationException.PARSE_ERROR, 2004 "template_dup_attr_flag", 2005 new Object[] {Integer.toString(lineno)}); 2006 } 2007 2008 return 0; // never happens. 2009 } 2010 2011 // Parse a word out of the tokenizer. The exact characters 2012 // will depend on what the syntax tables have been set to. 2013 2014 private String parseWord(StreamTokenizer tk, int baseLineno) 2015 throws ServiceLocationException, IOException { 2016 2017 int tt = tk.nextToken(); 2018 2019 if (tt == StreamTokenizer.TT_WORD) { 2020 return (tk.sval); 2021 2022 } else { 2023 2024 String errorToken = ""; 2025 2026 // Report the erroneous characters. 2027 2028 if (tt == StreamTokenizer.TT_NUMBER) { 2029 errorToken = Double.toString(tk.nval); 2030 } else if (tt == StreamTokenizer.TT_EOL) { 2031 errorToken = "<end of line>"; 2032 } else if (tt == StreamTokenizer.TT_EOF) { 2033 errorToken = "<end of file>"; 2034 } else { 2035 errorToken = (new Character((char)tt)).toString(); 2036 } 2037 2038 throw 2039 new ServiceLocationException( 2040 ServiceLocationException.PARSE_ERROR, 2041 "template_invalid_tok", 2042 new Object[] { 2043 Integer.toString(tk.lineno() + baseLineno)}); 2044 2045 } 2046 2047 } 2048 2049 // Convert a value list token to the value. 2050 2051 private Object convertValue(String type, 2052 String reqTok, 2053 int lineno) 2054 throws ServiceLocationException, 2055 IOException { 2056 2057 Object reqVal = null; 2058 2059 if (type.equals(JAVA_STRING_TYPE)) { 2060 2061 // Expand out any escaped ``#''. It won't be handled by 2062 // SLA. 2063 2064 reqTok = unescapeHash(reqTok); 2065 2066 // Expand out character escapes. 2067 2068 reqVal = 2069 ServiceLocationAttribute.unescapeAttributeString(reqTok, 2070 false); 2071 2072 } else if (type.equals(JAVA_INTEGER_TYPE)) { 2073 2074 try { 2075 2076 reqVal = Integer.valueOf(reqTok); 2077 2078 } catch (NumberFormatException ex) { 2079 2080 throw 2081 new ServiceLocationException( 2082 ServiceLocationException.PARSE_ERROR, 2083 "template_expect_int", 2084 new Object[] { 2085 Integer.toString(lineno), reqTok }); 2086 } 2087 } else if (type.equals(JAVA_BOOLEAN_TYPE)) { 2088 2089 // Boolean.valueOf() doesn't handle this properly. 2090 2091 if (reqTok.equalsIgnoreCase(TRUE_TOKEN)) { 2092 2093 reqVal = new Boolean(true); 2094 2095 } else if (reqTok.equalsIgnoreCase(FALSE_TOKEN)) { 2096 2097 reqVal = new Boolean(false); 2098 2099 } else { 2100 2101 throw 2102 new ServiceLocationException( 2103 ServiceLocationException.PARSE_ERROR, 2104 "template_expect_bool", 2105 new Object[] { 2106 Integer.toString(lineno), reqTok}); 2107 } 2108 } else if (type.equals(JAVA_OPAQUE_TYPE)) { 2109 2110 reqVal = Opaque.unescapeByteArray(reqTok); 2111 2112 } else { 2113 2114 Assert.slpassert(false, 2115 "template_attr_desc", 2116 new Object[0]); 2117 } 2118 2119 return reqVal; 2120 } 2121 2122 // Expand out any escaped hashes. Not handled by SLA. 2123 2124 private String unescapeHash(String str) { 2125 2126 StringBuffer buf = new StringBuffer(); 2127 int len = ESC_HASH.length(); 2128 int i, j = 0; 2129 2130 for (i = str.indexOf(ESC_HASH, j); 2131 i != -1; 2132 i = str.indexOf(ESC_HASH, j)) { 2133 2134 buf.append(str.substring(j, i)); 2135 buf.append(HASH); 2136 j = i + len; 2137 } 2138 2139 len = str.length(); 2140 2141 if (j < len) { 2142 buf.append(str.substring(j, len)); 2143 2144 } 2145 2146 return buf.toString(); 2147 } 2148 2149 } 2150