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