xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/AttributeVerifier.java (revision f5505c7d459abfaefd2fe69228d6cb3259cd231a)
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 		    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 
parseAttributes(StreamTokenizer tk)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
parseAttribute(StreamTokenizer tk)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 
checkDefaultValues(AttributeDescriptor attDesc)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 
parseId(StreamTokenizer tk, AttributeDescriptor attDesc, int baseLineno)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
parseTypeAndFlags(StreamTokenizer tk, AttributeDescriptor attDesc, int baseLineno)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 
parseDefaultValues(StreamTokenizer tk, AttributeDescriptor attDesc, int baseLineno)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
parseAllowedValues(StreamTokenizer tk, AttributeDescriptor attDesc, int baseLineno)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 
parseValueList(StreamTokenizer stk, AttributeDescriptor attDesc, int baseLineno)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 
checkAndAddType(String type, AttributeDescriptor attDesc, int lineno)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 
checkAndAddFlag(String flag, int matched, AttributeDescriptor attDesc, int lineno)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 
parseWord(StreamTokenizer tk, int baseLineno)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 
convertValue(String type, String reqTok, int lineno)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 
unescapeHash(String str)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