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 Float.valueOf(version); 952 } catch (NumberFormatException ex) { 953 954 throw 955 new ServiceLocationException( 956 ServiceLocationException.PARSE_ERROR, 957 "template_vers_err", 958 new Object[] {Integer.toString(tk.lineno())}); 959 960 } 961 962 this.version = version; 963 964 // Check for extra stuff. 965 966 if ((tt = stk.nextToken()) != StreamTokenizer.TT_EOF) { 967 throw 968 new ServiceLocationException( 969 ServiceLocationException.PARSE_ERROR, 970 "template_vers_err", 971 new Object[] {Integer.toString(tk.lineno())}); 972 } 973 974 mask = VERSION_MASK; 975 } else { 976 977 duplicate = true; 978 } 979 } else if (id.equalsIgnoreCase( 980 SLPTemplateRegistry.DESCRIPTION_ATTR_ID)) { 981 982 // Make sure there is nothing else on that line. 983 984 if (stk.nextToken() != StreamTokenizer.TT_EOF) { 985 986 throw 987 new ServiceLocationException( 988 ServiceLocationException.PARSE_ERROR, 989 "template_attr_syntax", 990 new Object[] {Integer.toString(tk.lineno())}); 991 } 992 993 if ((found & DESCRIPTION_MASK) == 0) { 994 995 // Need to continue parsing help text until we reach a blank 996 // line. 997 998 String helpText = ""; 999 1000 do { 1001 int ptt = tt; 1002 tt = tk.nextToken(); 1003 1004 if (tt == StreamTokenizer.TT_WORD) { 1005 1006 helpText = helpText + tk.sval + "\n"; 1007 1008 } else if (tt == StreamTokenizer.TT_EOL) { 1009 1010 // If previous token was end of line, quit. 1011 1012 if (ptt == StreamTokenizer.TT_EOL) { 1013 1014 // Store any text first. 1015 1016 if (helpText.length() > 0) { 1017 description = helpText; 1018 1019 } 1020 1021 tk.pushBack(); // so same as above 1022 1023 break; 1024 } 1025 } else if (tt == StreamTokenizer.TT_EOF) { 1026 throw 1027 new ServiceLocationException( 1028 ServiceLocationException.PARSE_ERROR, 1029 "template_end_error", 1030 new Object[] {Integer.toString(tk.lineno())}); 1031 1032 } else { 1033 1034 throw 1035 new ServiceLocationException( 1036 ServiceLocationException.PARSE_ERROR, 1037 "template_unk_token", 1038 new Object[] {Integer.toString(tk.lineno())}); 1039 1040 } 1041 1042 } while (true); 1043 1044 mask = DESCRIPTION_MASK; 1045 } else { 1046 1047 duplicate = true; 1048 } 1049 } else if (id.equalsIgnoreCase( 1050 SLPTemplateRegistry.SERVICE_URL_ATTR_ID)) { 1051 1052 if ((found & URL_PATH_RULES_MASK) == 0) { 1053 1054 String serviceURLGrammer = ""; 1055 1056 // Pull everything out of the rdr StringReader until empty. 1057 1058 int ic; 1059 1060 while ((ic = rdr.read()) != -1) { 1061 serviceURLGrammer += (char)ic; 1062 1063 } 1064 1065 serviceURLGrammer += "\n"; 1066 1067 // Need to continue parsing service URL syntax until we 1068 // reach a blank line. 1069 1070 tt = StreamTokenizer.TT_EOL; 1071 1072 do { 1073 int ptt = tt; 1074 tt = tk.nextToken(); 1075 1076 if (tt == StreamTokenizer.TT_WORD) { 1077 1078 serviceURLGrammer = serviceURLGrammer + tk.sval + "\n"; 1079 1080 } else if (tt == StreamTokenizer.TT_EOL) { 1081 1082 // If previous token was end of line, quit. 1083 1084 if (ptt == StreamTokenizer.TT_EOL) { 1085 1086 // Store any text first. 1087 1088 if (serviceURLGrammer.length() > 0) { 1089 URLSyntax = serviceURLGrammer; 1090 1091 } 1092 1093 tk.pushBack(); // so same as above. 1094 1095 break; 1096 } 1097 } else if (tt == StreamTokenizer.TT_EOF) { 1098 throw 1099 new ServiceLocationException( 1100 ServiceLocationException.PARSE_ERROR, 1101 "template_end_error", 1102 new Object[] {Integer.toString(tk.lineno())}); 1103 1104 } else { 1105 1106 throw 1107 new ServiceLocationException( 1108 ServiceLocationException.PARSE_ERROR, 1109 "template_unk_token", 1110 new Object[] {Integer.toString(tk.lineno())}); 1111 1112 } 1113 1114 } while (true); 1115 1116 mask = URL_PATH_RULES_MASK; 1117 } else { 1118 1119 duplicate = true; 1120 } 1121 } else { 1122 1123 throw 1124 new ServiceLocationException( 1125 ServiceLocationException.PARSE_ERROR, 1126 "template_nontattribute_err", 1127 new Object[] {Integer.toString(tk.lineno())}); 1128 1129 } 1130 1131 // Throw exception if a duplicate definition was detected. 1132 1133 if (duplicate) { 1134 1135 throw 1136 new ServiceLocationException( 1137 ServiceLocationException.PARSE_ERROR, 1138 "template_dup_def", 1139 new Object[] {Integer.toString(tk.lineno())}); 1140 1141 } 1142 1143 1144 // Make sure the assignment ends with a blank line. 1145 1146 if ((tt = tk.nextToken()) != StreamTokenizer.TT_EOL) { 1147 1148 throw 1149 new ServiceLocationException( 1150 ServiceLocationException.PARSE_ERROR, 1151 "template_attr_syntax", 1152 new Object[] {Integer.toString(tk.lineno())}); 1153 1154 } 1155 1156 return mask; 1157 1158 } 1159 1160 1161 // Parse the attributes from the tokenizer. 1162 1163 private void parseAttributes(StreamTokenizer tk) 1164 throws ServiceLocationException { 1165 1166 try { 1167 1168 do { 1169 1170 // Check if at end of file yet. 1171 1172 int tt = tk.nextToken(); 1173 1174 if (tt == StreamTokenizer.TT_EOF) { 1175 break; 1176 } 1177 1178 // If not, push token back so we can get it next time. 1179 1180 tk.pushBack(); 1181 1182 // Parse off the attribute descriptor. 1183 1184 AttributeDescriptor attDesc = parseAttribute(tk); 1185 1186 // Check whether default values, if any, are correct. 1187 1188 checkDefaultValues(attDesc); 1189 1190 // If the attribute already exists, then throw exception. 1191 // We could arguably replace existing, but it might 1192 // suprise the user. 1193 1194 String attrId = attDesc.getId().toLowerCase(); 1195 1196 if (attributeDescriptors.get(attrId) != null) { 1197 1198 throw 1199 new ServiceLocationException( 1200 ServiceLocationException.PARSE_ERROR, 1201 "template_dup_def", 1202 new Object[] {Integer.toString(tk.lineno())}); 1203 1204 } 1205 1206 // Add the attribute to the descriptor table. 1207 1208 attributeDescriptors.put(attrId, attDesc); 1209 1210 } while (true); 1211 1212 } catch (IOException ex) { 1213 1214 throw 1215 new ServiceLocationException( 1216 ServiceLocationException.INTERNAL_SYSTEM_ERROR, 1217 "template_io_error", 1218 new Object[] {Integer.toString(tk.lineno())}); 1219 } 1220 1221 } 1222 1223 // Parse a single attribute description from the tokenizer. 1224 1225 private AttributeDescriptor 1226 parseAttribute(StreamTokenizer tk) throws ServiceLocationException { 1227 1228 AttributeDescriptor attDesc = new AttributeDescriptor(); 1229 int lineno = 0; 1230 1231 try { 1232 1233 // Parse the string for attribute id, type, and flags. 1234 1235 lineno = tk.lineno(); 1236 1237 int tt = tk.nextToken(); 1238 1239 if (tt != StreamTokenizer.TT_WORD) { 1240 throw 1241 new ServiceLocationException( 1242 ServiceLocationException.PARSE_ERROR, 1243 "template_attr_syntax", 1244 new Object[] {Integer.toString(tk.lineno())}); 1245 } 1246 1247 StreamTokenizer stk = 1248 new StreamTokenizer(new StringReader(tk.sval)); 1249 1250 initIdChar(stk); 1251 1252 // Parse the attribute id. 1253 1254 parseId(stk, attDesc, lineno); 1255 1256 // Parse the type and flags. 1257 1258 parseTypeAndFlags(stk, attDesc, lineno); 1259 1260 tt = tk.nextToken(); 1261 1262 if (tt == StreamTokenizer.TT_EOF) { 1263 1264 throw 1265 new ServiceLocationException( 1266 ServiceLocationException.PARSE_ERROR, 1267 "template_end_error", 1268 new Object[] {Integer.toString(tk.lineno())}); 1269 1270 } 1271 1272 if (tt != StreamTokenizer.TT_EOL) { 1273 1274 throw 1275 new ServiceLocationException( 1276 ServiceLocationException.PARSE_ERROR, 1277 "template_unk_token", 1278 new Object[] {Integer.toString(tk.lineno())}); 1279 1280 } 1281 1282 // Parse initial values. 1283 1284 if (!attDesc.getIsKeyword()) { 1285 1286 String tok = ""; 1287 1288 // Read in entire list. 1289 1290 do { 1291 int ptt = tt; 1292 lineno = tk.lineno(); 1293 tt = tk.nextToken(); 1294 1295 if (tt == StreamTokenizer.TT_WORD) { 1296 1297 // Trim line, check for '#', indicating end of list. 1298 1299 String line = tk.sval.trim(); 1300 1301 if (line.charAt(0) == TT_FIELD) { 1302 // it's help text already. 1303 1304 if (tok.length() > 0) { 1305 stk = 1306 new StreamTokenizer(new StringReader(tok)); 1307 parseDefaultValues(stk, attDesc, lineno); 1308 } 1309 1310 tk.pushBack(); 1311 break; 1312 1313 } else { 1314 1315 // Otherwise concatenate onto growing list. 1316 1317 tok = tok + line; 1318 1319 } 1320 1321 } else if (tt == StreamTokenizer.TT_EOL) { 1322 1323 if (ptt == StreamTokenizer.TT_EOL) { 1324 // end of attribute definition. 1325 1326 // Process any accumulated list. 1327 1328 if (tok.length() > 0) { 1329 stk = 1330 new StreamTokenizer(new StringReader(tok)); 1331 parseDefaultValues(stk, attDesc, lineno); 1332 } 1333 1334 return attDesc; 1335 1336 } 1337 } else if (tt == StreamTokenizer.TT_EOF) { 1338 throw 1339 new ServiceLocationException( 1340 ServiceLocationException.PARSE_ERROR, 1341 "template_end_error", 1342 new Object[] {Integer.toString(tk.lineno())}); 1343 1344 } else { 1345 1346 throw 1347 new ServiceLocationException( 1348 ServiceLocationException.PARSE_ERROR, 1349 "template_unk_token", 1350 new Object[] {Integer.toString(tk.lineno())}); 1351 1352 } 1353 1354 } while (true); 1355 1356 } else { 1357 attDesc.setDefaultValues(null); 1358 attDesc.setAllowedValues(null); 1359 1360 // Check for end of definition. 1361 1362 if ((tt = tk.nextToken()) == StreamTokenizer.TT_EOL) { 1363 return attDesc; 1364 1365 } else if (tt == StreamTokenizer.TT_WORD) { 1366 1367 // Check for start of help text. 1368 1369 String line = tk.sval.trim(); 1370 1371 if (line.charAt(0) != TT_FIELD) { 1372 throw 1373 new ServiceLocationException( 1374 ServiceLocationException.PARSE_ERROR, 1375 "template_attr_syntax", 1376 new Object[] {Integer.toString(tk.lineno())}); 1377 1378 } else { 1379 1380 tk.pushBack(); 1381 1382 } 1383 1384 } else if (tt == StreamTokenizer.TT_EOF) { 1385 throw 1386 new ServiceLocationException( 1387 ServiceLocationException.PARSE_ERROR, 1388 "template_end_error", 1389 new Object[] {Integer.toString(tk.lineno())}); 1390 1391 } else { 1392 1393 throw 1394 new ServiceLocationException( 1395 ServiceLocationException.PARSE_ERROR, 1396 "template_unk_token", 1397 new Object[] {Integer.toString(tk.lineno())}); 1398 1399 } 1400 } 1401 1402 1403 // Parse help text. 1404 1405 String helpText = ""; 1406 1407 do { 1408 int ptt = tt; 1409 lineno = tk.lineno(); 1410 tt = tk.nextToken(); 1411 1412 if (tt == StreamTokenizer.TT_WORD) { 1413 1414 // Check for end of help text. 1415 1416 String line = tk.sval.trim(); 1417 1418 if (line.charAt(0) == TT_FIELD) { 1419 1420 // Help text is collected verbatim after '#'. 1421 1422 helpText = 1423 helpText + line.substring(1) + "\n"; 1424 1425 } else { 1426 1427 // We've reached the end of the help text. Store it 1428 // and break out of the loop. 1429 1430 if (helpText.length() > 0) { 1431 attDesc.setDescription(helpText); 1432 } 1433 1434 tk.pushBack(); 1435 break; 1436 1437 } 1438 1439 } else if (tt == StreamTokenizer.TT_EOL || 1440 tt == StreamTokenizer.TT_EOF) { 1441 1442 // If previous token was end of line, quit. 1443 1444 if (ptt == StreamTokenizer.TT_EOL) { 1445 1446 // Store any text first. 1447 1448 if (helpText.length() > 0) { 1449 attDesc.setDescription(helpText); 1450 } 1451 1452 // If this is a keyword attribute, set the allowed 1453 // values list to null. 1454 1455 if (attDesc.getIsKeyword()) { 1456 attDesc.setAllowedValues(null); 1457 } 1458 1459 return attDesc; 1460 1461 } else if (tt == StreamTokenizer.TT_EOF) { 1462 1463 // Error if previous token wasn't EOL. 1464 1465 throw 1466 new ServiceLocationException( 1467 ServiceLocationException.PARSE_ERROR, 1468 "template_end_error", 1469 new Object[] {Integer.toString(tk.lineno())}); 1470 } 1471 1472 } else { 1473 1474 throw 1475 new ServiceLocationException( 1476 ServiceLocationException.PARSE_ERROR, 1477 "template_unk_token", 1478 new Object[] {Integer.toString(tk.lineno())}); 1479 } 1480 1481 } while (true); 1482 1483 // Parse allowed values. 1484 1485 if (!attDesc.getIsKeyword()) { 1486 1487 String tok = ""; 1488 1489 // Read in entire list. 1490 1491 do { 1492 int ptt = tt; 1493 lineno = tk.lineno(); 1494 tt = tk.nextToken(); 1495 1496 if (tt == StreamTokenizer.TT_WORD) { 1497 1498 // Concatenate onto growing list. 1499 1500 tok = tok + tk.sval; 1501 1502 } else if (tt == StreamTokenizer.TT_EOL) { 1503 1504 if (ptt == StreamTokenizer.TT_EOL) { 1505 // end of attribute definition. 1506 1507 // Process any accumulated list. 1508 1509 if (tok.length() > 0) { 1510 stk = 1511 new StreamTokenizer(new StringReader(tok)); 1512 parseAllowedValues(stk, attDesc, lineno); 1513 } 1514 1515 return attDesc; 1516 1517 } 1518 } else if (tt == StreamTokenizer.TT_EOF) { 1519 throw 1520 new ServiceLocationException( 1521 ServiceLocationException.PARSE_ERROR, 1522 "template_end_error", 1523 new Object[] {Integer.toString(tk.lineno())}); 1524 1525 } else { 1526 1527 throw 1528 new ServiceLocationException( 1529 ServiceLocationException.PARSE_ERROR, 1530 "template_unk_token", 1531 new Object[] {Integer.toString(tk.lineno())}); 1532 } 1533 1534 } while (true); 1535 1536 } else { 1537 1538 // Error. Keyword attribute should have ended during help text 1539 // parsing or before. 1540 1541 throw 1542 new ServiceLocationException( 1543 ServiceLocationException.PARSE_ERROR, 1544 "template_attr_syntax", 1545 new Object[] {Integer.toString(tk.lineno())}); 1546 } 1547 1548 } catch (IOException ex) { 1549 1550 throw 1551 new ServiceLocationException( 1552 ServiceLocationException.INTERNAL_SYSTEM_ERROR, 1553 "template_io_error", 1554 new Object[] { 1555 Integer.toString(tk.lineno()), 1556 ex.getMessage()}); 1557 } 1558 1559 } 1560 1561 // Check whether the default values, if any, are correct. 1562 1563 private void checkDefaultValues(AttributeDescriptor attDesc) 1564 throws ServiceLocationException { 1565 1566 // Don't bother if it's a keyword attribute, parsing has checked. 1567 1568 if (attDesc.getIsKeyword()) { 1569 return; 1570 } 1571 1572 Enumeration init = attDesc.getDefaultValues(); 1573 Enumeration en = attDesc.getAllowedValues(); 1574 Vector allowed = new Vector(); 1575 String attDescType = attDesc.getValueType(); 1576 1577 // First, collect the allowed values. 1578 1579 while (en.hasMoreElements()) { 1580 Object allval = en.nextElement(); 1581 1582 // Lower case strings and create opaques for comparison 1583 // if type is opaque. 1584 1585 if (attDescType.equals(JAVA_STRING_TYPE)) { 1586 allval = ((String)allval).toLowerCase(); 1587 1588 } else if (attDescType.equals(JAVA_OPAQUE_TYPE)) { 1589 allval = new Opaque((byte[])allval); 1590 1591 } 1592 1593 allowed.addElement(allval); 1594 } 1595 1596 // Now compare the allowed with the initial. 1597 1598 if (allowed.size() > 0) { 1599 1600 // Error if allowed is restricted but no initializers. 1601 1602 if (!init.hasMoreElements()) { 1603 1604 throw 1605 new ServiceLocationException( 1606 ServiceLocationException.PARSE_ERROR, 1607 "template_no_init", 1608 new Object[] {attDesc.getId()}); 1609 1610 } 1611 1612 Object val = null; 1613 1614 // Compare init values with allowed. 1615 1616 while (init.hasMoreElements()) { 1617 Object test = init.nextElement(); 1618 val = test; // for exception.. 1619 1620 if (attDescType.equals(JAVA_STRING_TYPE)) { 1621 test = ((String)test).toLowerCase(); 1622 1623 } else if (attDescType.equals(JAVA_OPAQUE_TYPE)) { 1624 test = new Opaque((byte[])test); 1625 1626 } 1627 1628 if (allowed.indexOf(test) != -1) { 1629 return; // found it! 1630 } 1631 } 1632 // Initializer wasn't found. 1633 1634 throw 1635 new ServiceLocationException( 1636 ServiceLocationException.PARSE_ERROR, 1637 "template_wrong_init", 1638 new Object[] { 1639 val.toString(), attDesc.getId()}); 1640 } 1641 } 1642 1643 // Parse the attribute's id string. 1644 1645 private void parseId(StreamTokenizer tk, 1646 AttributeDescriptor attDesc, 1647 int baseLineno) 1648 throws ServiceLocationException, IOException { 1649 1650 // Parse the attribute's identifier tag. 1651 1652 String id = parseWord(tk, baseLineno); 1653 1654 int tt = tk.nextToken(); 1655 1656 // Parse the seperator. 1657 1658 if (tt != TT_EQUALS) { 1659 throw 1660 new ServiceLocationException( 1661 ServiceLocationException.PARSE_ERROR, 1662 "template_attr_syntax", 1663 new Object[] { 1664 Integer.toString(tk.lineno() + baseLineno)}); 1665 1666 } 1667 1668 // Expand out any escaped ``#''. It won't be handled by 1669 // SLA. 1670 1671 id = unescapeHash(id); 1672 1673 // Expand out character escapes. 1674 1675 id = 1676 ServiceLocationAttribute.unescapeAttributeString(id, true); 1677 1678 1679 attDesc.setId(id); 1680 } 1681 1682 // Parse the attribute's type and flags. 1683 1684 private void 1685 parseTypeAndFlags(StreamTokenizer tk, 1686 AttributeDescriptor attDesc, 1687 int baseLineno) 1688 throws ServiceLocationException, IOException { 1689 1690 int existingFlags = 0; 1691 1692 // Parse the attribute's type. 1693 1694 String type = parseWord(tk, baseLineno); 1695 1696 checkAndAddType(type, attDesc, tk.lineno() + baseLineno); 1697 1698 // Parse the flags. 1699 1700 do { 1701 1702 // Check if any flags are left. 1703 1704 if (tk.nextToken() == StreamTokenizer.TT_EOF) { 1705 break; 1706 1707 } else { 1708 tk.pushBack(); 1709 } 1710 1711 int lineno = tk.lineno(); 1712 1713 // Parse the flag. 1714 1715 String flag = parseWord(tk, baseLineno); 1716 1717 // Error if flags with keyword. 1718 1719 if (attDesc.getIsKeyword()) { 1720 throw 1721 new ServiceLocationException( 1722 ServiceLocationException.PARSE_ERROR, 1723 "template_attr_syntax", 1724 new Object[] { 1725 Integer.toString(tk.lineno() + baseLineno)}); 1726 } 1727 1728 1729 // Check and assign it to the attribute. 1730 1731 existingFlags = 1732 existingFlags | checkAndAddFlag(flag, 1733 existingFlags, 1734 attDesc, 1735 baseLineno + lineno); 1736 1737 } while (true); 1738 } 1739 1740 // Parse the attribute's initial value(s). 1741 1742 private void parseDefaultValues(StreamTokenizer tk, 1743 AttributeDescriptor attDesc, 1744 int baseLineno) 1745 throws ServiceLocationException, IOException { 1746 1747 // First get the vector of initial values. 1748 1749 Vector vals = parseValueList(tk, attDesc, baseLineno); 1750 1751 // Check whether it works for this attribute. Type 1752 // checking will be done by value list parsing. 1753 1754 if (!attDesc.getIsMultivalued() && vals.size() > 1) { 1755 throw 1756 new ServiceLocationException( 1757 ServiceLocationException.PARSE_ERROR, 1758 "template_attr_syntax", 1759 new Object[] { 1760 Integer.toString(tk.lineno() + baseLineno)}); 1761 } 1762 1763 attDesc.setDefaultValues(vals); 1764 } 1765 1766 // Parse the attribute's allowed values. 1767 1768 private void 1769 parseAllowedValues(StreamTokenizer tk, 1770 AttributeDescriptor attDesc, 1771 int baseLineno) 1772 throws ServiceLocationException, IOException { 1773 1774 // First get the vector of all allowed values. 1775 1776 Vector vals = parseValueList(tk, attDesc, baseLineno); 1777 1778 // Now set the allowed value vector. 1779 1780 attDesc.setAllowedValues(vals); 1781 } 1782 1783 // Parse a value list. 1784 1785 private Vector parseValueList(StreamTokenizer stk, 1786 AttributeDescriptor attDesc, 1787 int baseLineno) 1788 throws ServiceLocationException, IOException { 1789 1790 Vector req = new Vector(); 1791 1792 // Set up the tokenizer according to the type of the 1793 // attribute. 1794 1795 String type = attDesc.getValueType(); 1796 1797 if (type.equals(JAVA_STRING_TYPE) || type.equals(JAVA_OPAQUE_TYPE)) { 1798 initStringItemChar(stk); 1799 } else if (type.equals(JAVA_INTEGER_TYPE)) { 1800 initIntItemChar(stk); 1801 } else if (type.equals(JAVA_BOOLEAN_TYPE)) { 1802 initIdChar(stk); 1803 } 1804 1805 // Parse through a potentially multivalued value list. 1806 1807 boolean wordRequired = true; // true when a word is required, 1808 // false when a comma required. 1809 boolean syntaxError = false; 1810 String reqTok = ""; 1811 int lineno = 0; 1812 1813 do { 1814 int tt = stk.nextToken(); 1815 lineno = stk.lineno() + baseLineno; 1816 1817 if (tt == StreamTokenizer.TT_WORD) { 1818 1819 // If a word isn't required, then the case is 1820 // "token token" and is an error. 1821 1822 if (!wordRequired) { 1823 syntaxError = true; 1824 } 1825 1826 reqTok = stk.sval.trim(); 1827 1828 // Convert the value to the proper object. 1829 1830 Object reqVal = convertValue(type, reqTok, baseLineno); 1831 req.addElement(reqVal); 1832 1833 wordRequired = false; 1834 1835 } else if (tt == StreamTokenizer.TT_EOF) { 1836 1837 // If a word is required, then list ends with 1838 // a comma, so error. 1839 1840 if (wordRequired) { 1841 syntaxError = true; 1842 } 1843 1844 break; 1845 1846 } else if (tt == TT_COMMA) { 1847 1848 // If a word is required, then error. The case is ",,". 1849 1850 if (wordRequired) { 1851 syntaxError = true; 1852 break; 1853 } 1854 1855 // Otherwise, the next token must be a word. 1856 1857 wordRequired = true; 1858 1859 } else { 1860 1861 // No other tokens are allowed. 1862 1863 syntaxError = true; 1864 break; 1865 } 1866 1867 } while (true); 1868 1869 if (syntaxError) { 1870 1871 throw 1872 new ServiceLocationException( 1873 ServiceLocationException.PARSE_ERROR, 1874 "template_attr_syntax", 1875 new Object[] {Integer.toString(lineno)}); 1876 } 1877 1878 return req; 1879 1880 } 1881 1882 // Check the type and add it to the attribute descriptor. 1883 1884 private void checkAndAddType(String type, 1885 AttributeDescriptor attDesc, 1886 int lineno) 1887 throws ServiceLocationException { 1888 1889 // Check token against recognized types. 1890 1891 if (type.equalsIgnoreCase(STRING_TYPE)) { 1892 attDesc.setValueType(JAVA_STRING_TYPE); 1893 1894 } else if (type.equalsIgnoreCase(INTEGER_TYPE)) { 1895 attDesc.setValueType(JAVA_INTEGER_TYPE); 1896 1897 } else if (type.equalsIgnoreCase(BOOLEAN_TYPE)) { 1898 attDesc.setValueType(JAVA_BOOLEAN_TYPE); 1899 1900 } else if (type.equalsIgnoreCase(OPAQUE_TYPE)) { 1901 attDesc.setValueType(JAVA_OPAQUE_TYPE); 1902 1903 } else if (type.equalsIgnoreCase(KEYWORD_TYPE)) { 1904 attDesc.setIsKeyword(true); 1905 1906 } else { 1907 1908 throw 1909 new ServiceLocationException( 1910 ServiceLocationException.PARSE_ERROR, 1911 "template_not_slp_type", 1912 new Object[] {Integer.toString(lineno)}); 1913 } 1914 1915 } 1916 1917 // Check the flag and add it to the attribute descriptor. 1918 1919 private int checkAndAddFlag(String flag, 1920 int matched, 1921 AttributeDescriptor attDesc, 1922 int lineno) 1923 throws ServiceLocationException { 1924 1925 boolean duplicate = false; 1926 1927 // We depend on the attribute descriptor being initialized to 1928 // nothing, i.e. false for all flags and for keyword. 1929 1930 if (flag.equalsIgnoreCase(MULTIPLE_FLAG)) { 1931 1932 if ((matched & MULTIPLE_MASK) != 0) { 1933 duplicate = true; 1934 1935 } else { 1936 1937 // Check for boolean. Booleans may not have 1938 // multiple values. 1939 1940 if (attDesc.getValueType().equals(JAVA_BOOLEAN_TYPE)) { 1941 1942 throw 1943 new ServiceLocationException( 1944 ServiceLocationException.PARSE_ERROR, 1945 "template_boolean_multi", 1946 new Object[] {Integer.toString(lineno)}); 1947 } 1948 1949 attDesc.setIsMultivalued(true); 1950 return MULTIPLE_MASK; 1951 1952 } 1953 1954 } else if (flag.equalsIgnoreCase(LITERAL_FLAG)) { 1955 1956 if ((matched & LITERAL_MASK) != 0) { 1957 duplicate = true; 1958 1959 } else { 1960 attDesc.setIsLiteral(true); 1961 return LITERAL_MASK; 1962 } 1963 1964 } else if (flag.equalsIgnoreCase(EXPLICIT_FLAG)) { 1965 1966 if ((matched & EXPLICIT_MASK) != 0) { 1967 duplicate = true; 1968 1969 } else { 1970 attDesc.setRequiresExplicitMatch(true); 1971 return EXPLICIT_MASK; 1972 } 1973 1974 } else if (flag.equalsIgnoreCase(OPTIONAL_FLAG)) { 1975 1976 if ((matched & OPTIONAL_MASK) != 0) { 1977 duplicate = true; 1978 1979 } else { 1980 attDesc.setIsOptional(true); 1981 return OPTIONAL_MASK; 1982 } 1983 1984 } else { 1985 1986 throw 1987 new ServiceLocationException( 1988 ServiceLocationException.PARSE_ERROR, 1989 "template_invalid_attr_flag", 1990 new Object[] {Integer.toString(lineno)}); 1991 } 1992 1993 1994 if (duplicate) { 1995 throw 1996 new ServiceLocationException( 1997 ServiceLocationException.PARSE_ERROR, 1998 "template_dup_attr_flag", 1999 new Object[] {Integer.toString(lineno)}); 2000 } 2001 2002 return 0; // never happens. 2003 } 2004 2005 // Parse a word out of the tokenizer. The exact characters 2006 // will depend on what the syntax tables have been set to. 2007 2008 private String parseWord(StreamTokenizer tk, int baseLineno) 2009 throws ServiceLocationException, IOException { 2010 2011 int tt = tk.nextToken(); 2012 2013 if (tt == StreamTokenizer.TT_WORD) { 2014 return (tk.sval); 2015 2016 } else { 2017 2018 String errorToken = ""; 2019 2020 // Report the erroneous characters. 2021 2022 if (tt == StreamTokenizer.TT_NUMBER) { 2023 errorToken = Double.toString(tk.nval); 2024 } else if (tt == StreamTokenizer.TT_EOL) { 2025 errorToken = "<end of line>"; 2026 } else if (tt == StreamTokenizer.TT_EOF) { 2027 errorToken = "<end of file>"; 2028 } else { 2029 errorToken = (Character.valueOf((char)tt)).toString(); 2030 } 2031 2032 throw 2033 new ServiceLocationException( 2034 ServiceLocationException.PARSE_ERROR, 2035 "template_invalid_tok", 2036 new Object[] { 2037 Integer.toString(tk.lineno() + baseLineno)}); 2038 2039 } 2040 2041 } 2042 2043 // Convert a value list token to the value. 2044 2045 private Object convertValue(String type, 2046 String reqTok, 2047 int lineno) 2048 throws ServiceLocationException, 2049 IOException { 2050 2051 Object reqVal = null; 2052 2053 if (type.equals(JAVA_STRING_TYPE)) { 2054 2055 // Expand out any escaped ``#''. It won't be handled by 2056 // SLA. 2057 2058 reqTok = unescapeHash(reqTok); 2059 2060 // Expand out character escapes. 2061 2062 reqVal = 2063 ServiceLocationAttribute.unescapeAttributeString(reqTok, 2064 false); 2065 2066 } else if (type.equals(JAVA_INTEGER_TYPE)) { 2067 2068 try { 2069 2070 reqVal = Integer.valueOf(reqTok); 2071 2072 } catch (NumberFormatException ex) { 2073 2074 throw 2075 new ServiceLocationException( 2076 ServiceLocationException.PARSE_ERROR, 2077 "template_expect_int", 2078 new Object[] { 2079 Integer.toString(lineno), reqTok }); 2080 } 2081 } else if (type.equals(JAVA_BOOLEAN_TYPE)) { 2082 2083 // Boolean.valueOf() doesn't handle this properly. 2084 2085 if (reqTok.equalsIgnoreCase(TRUE_TOKEN)) { 2086 2087 reqVal = Boolean.valueOf(true); 2088 2089 } else if (reqTok.equalsIgnoreCase(FALSE_TOKEN)) { 2090 2091 reqVal = Boolean.valueOf(false); 2092 2093 } else { 2094 2095 throw 2096 new ServiceLocationException( 2097 ServiceLocationException.PARSE_ERROR, 2098 "template_expect_bool", 2099 new Object[] { 2100 Integer.toString(lineno), reqTok}); 2101 } 2102 } else if (type.equals(JAVA_OPAQUE_TYPE)) { 2103 2104 reqVal = Opaque.unescapeByteArray(reqTok); 2105 2106 } else { 2107 2108 Assert.slpassert(false, 2109 "template_attr_desc", 2110 new Object[0]); 2111 } 2112 2113 return reqVal; 2114 } 2115 2116 // Expand out any escaped hashes. Not handled by SLA. 2117 2118 private String unescapeHash(String str) { 2119 2120 StringBuffer buf = new StringBuffer(); 2121 int len = ESC_HASH.length(); 2122 int i, j = 0; 2123 2124 for (i = str.indexOf(ESC_HASH, j); 2125 i != -1; 2126 i = str.indexOf(ESC_HASH, j)) { 2127 2128 buf.append(str.substring(j, i)); 2129 buf.append(HASH); 2130 j = i + len; 2131 } 2132 2133 len = str.length(); 2134 2135 if (j < len) { 2136 buf.append(str.substring(j, len)); 2137 2138 } 2139 2140 return buf.toString(); 2141 } 2142 2143 } 2144