xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/AttributeVerifier.java (revision 55fea89dcaa64928bed4327112404dcb3e07b79f)
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 
AttributeVerifier()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 
initialize(Reader r)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 
initializeAttributesOnly(Reader r)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 
getServiceType()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 
getLocale()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 
getVersion()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 
getURLSyntax()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 
getDescription()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
getAttributeDescriptor(String attrId)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 
getAttributeDescriptors()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 
verifyAttribute(ServiceLocationAttribute attribute)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 
verifyRegistration(Vector attributeVector)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 
initForBase(StreamTokenizer tk)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 
initCommonToken(StreamTokenizer tk)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 
initIdChar(StreamTokenizer tk)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 
initSchemeIdChar(StreamTokenizer tk)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 
initStringItemChar(StreamTokenizer tk)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 
initIntItemChar(StreamTokenizer tk)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 
initFieldChar(StreamTokenizer tk)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 
parseTemplate(StreamTokenizer tk)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 
parseTemplateAttributes(StreamTokenizer tk)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 
parseTemplateAttribute(StreamTokenizer tk, int found)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 
parseAttributes(StreamTokenizer tk)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
parseAttribute(StreamTokenizer tk)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 
checkDefaultValues(AttributeDescriptor attDesc)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 
parseId(StreamTokenizer tk, AttributeDescriptor attDesc, int baseLineno)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
parseTypeAndFlags(StreamTokenizer tk, AttributeDescriptor attDesc, int baseLineno)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 
parseDefaultValues(StreamTokenizer tk, AttributeDescriptor attDesc, int baseLineno)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
parseAllowedValues(StreamTokenizer tk, AttributeDescriptor attDesc, int baseLineno)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 
parseValueList(StreamTokenizer stk, AttributeDescriptor attDesc, int baseLineno)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 
checkAndAddType(String type, AttributeDescriptor attDesc, int lineno)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 
checkAndAddFlag(String flag, int matched, AttributeDescriptor attDesc, int lineno)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 
parseWord(StreamTokenizer tk, int baseLineno)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 
convertValue(String type, String reqTok, int lineno)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 
unescapeHash(String str)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