001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry.parse;
016
017 import org.apache.commons.logging.Log;
018 import org.apache.commons.logging.LogFactory;
019 import org.apache.hivemind.*;
020 import org.apache.hivemind.impl.DefaultErrorHandler;
021 import org.apache.hivemind.impl.LocationImpl;
022 import org.apache.hivemind.parse.AbstractParser;
023 import org.apache.tapestry.INamespace;
024 import org.apache.tapestry.Tapestry;
025 import org.apache.tapestry.bean.BindingBeanInitializer;
026 import org.apache.tapestry.bean.LightweightBeanInitializer;
027 import org.apache.tapestry.binding.BindingConstants;
028 import org.apache.tapestry.binding.BindingSource;
029 import org.apache.tapestry.coerce.ValueConverter;
030 import org.apache.tapestry.spec.*;
031 import org.apache.tapestry.util.IPropertyHolder;
032 import org.apache.tapestry.util.RegexpMatcher;
033 import org.apache.tapestry.util.xml.DocumentParseException;
034 import org.apache.tapestry.util.xml.InvalidStringException;
035 import org.xml.sax.InputSource;
036 import org.xml.sax.SAXException;
037 import org.xml.sax.SAXParseException;
038
039 import javax.xml.parsers.SAXParser;
040 import javax.xml.parsers.SAXParserFactory;
041 import java.io.BufferedInputStream;
042 import java.io.IOException;
043 import java.io.InputStream;
044 import java.net.URL;
045 import java.util.HashMap;
046 import java.util.Iterator;
047 import java.util.Map;
048
049 /**
050 * Parses the different types of Tapestry specifications.
051 * <p>
052 * Not threadsafe; it is the callers responsibility to ensure thread safety.
053 *
054 * @author Howard Lewis Ship
055 */
056 public class SpecificationParser extends AbstractParser implements ISpecificationParser
057 {
058 /**
059 * Perl5 pattern for asset names. Letter, followed by letter, number or underscore. Also allows
060 * the special "$template" value.
061 *
062 * @since 2.2
063 */
064
065 public static final String ASSET_NAME_PATTERN = "(\\$template)|(" + Tapestry.SIMPLE_PROPERTY_NAME_PATTERN + ")";
066
067 /**
068 * Perl5 pattern for helper bean names. Letter, followed by letter, number or underscore.
069 *
070 * @since 2.2
071 */
072
073 public static final String BEAN_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN;
074
075 public static final String IDENTIFIER_PATTERN = "_?[a-zA-Z]\\w*";
076
077 public static final String EXTENDED_IDENTIFIER_PATTERN = "_?[a-zA-Z](\\w|-)*";
078
079 /**
080 * Perl5 pattern for component type (which was known as an "alias" in earlier versions of
081 * Tapestry). This is either a simple property name, or a series of property names seperated by
082 * slashes (the latter being new in Tapestry 4.0). This defines a literal that can appear in a
083 * library or application specification.
084 *
085 * @since 2.2
086 */
087
088 public static final String COMPONENT_ALIAS_PATTERN = "^(" + IDENTIFIER_PATTERN + "/)*"
089 + IDENTIFIER_PATTERN + "$";
090
091 /**
092 * Perl5 pattern for component ids. Letter, followed by letter, number or underscore.
093 *
094 * @since 2.2
095 */
096
097 public static final String COMPONENT_ID_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN;
098
099 /**
100 * Perl5 pattern for component types (i.e., the type attribute of the <component>
101 * element). Component types are an optional namespace prefix followed by a component type
102 * (within the library defined by the namespace). Starting in 4.0, the type portion is actually
103 * a series of identifiers seperated by slashes.
104 *
105 * @since 2.2
106 */
107
108 public static final String COMPONENT_TYPE_PATTERN = "^(" + IDENTIFIER_PATTERN + ":)?" + "("
109 + IDENTIFIER_PATTERN + "/)*" + IDENTIFIER_PATTERN + "$";
110
111 /**
112 * Extended version of {@link Tapestry#SIMPLE_PROPERTY_NAME_PATTERN}, but allows a series of
113 * individual property names, seperated by periods. In addition, each name within the dotted
114 * sequence is allowed to contain dashes.
115 *
116 * @since 2.2
117 */
118
119 public static final String EXTENDED_PROPERTY_NAME_PATTERN = "^" + EXTENDED_IDENTIFIER_PATTERN
120 + "(\\." + EXTENDED_IDENTIFIER_PATTERN + ")*$";
121
122 /**
123 * Per5 pattern for extension names. Letter followed by letter, number, dash, period or
124 * underscore.
125 *
126 * @since 2.2
127 */
128
129 public static final String EXTENSION_NAME_PATTERN = EXTENDED_PROPERTY_NAME_PATTERN;
130
131 /**
132 * Perl5 pattern for library ids. Letter followed by letter, number or underscore.
133 *
134 * @since 2.2
135 */
136
137 public static final String LIBRARY_ID_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN;
138
139 /**
140 * Perl5 pattern for page names. Page names appear in library and application specifications, in
141 * the <page> element. Starting with 4.0, the page name may look more like a path name,
142 * consisting of a number of ids seperated by slashes. This is used to determine the folder
143 * which contains the page specification or the page's template.
144 *
145 * @since 2.2
146 */
147
148 public static final String PAGE_NAME_PATTERN = "^" + IDENTIFIER_PATTERN + "(/" + EXTENDED_IDENTIFIER_PATTERN + ")*$";
149
150 /**
151 * Perl5 pattern that parameter names must conform to. Letter, followed by letter, number or
152 * underscore.
153 *
154 * @since 2.2
155 */
156
157 public static final String PARAMETER_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN;
158
159 /**
160 * Perl5 pattern that property names (that can be connected to parameters) must conform to.
161 * Letter, followed by letter, number or underscore.
162 *
163 * @since 2.2
164 */
165
166 public static final String PROPERTY_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN;
167
168 /**
169 * Perl5 pattern for service names. Letter followed by letter, number, dash, underscore or
170 * period.
171 *
172 * @since 2.2
173 * @deprecated As of release 4.0, the <service> element (in 3.0 DTDs) is no longer
174 * supported.
175 */
176
177 public static final String SERVICE_NAME_PATTERN = EXTENDED_PROPERTY_NAME_PATTERN;
178
179 /** @since 3.0 */
180
181 public static final String TAPESTRY_DTD_3_0_PUBLIC_ID = "-//Apache Software Foundation//Tapestry Specification 3.0//EN";
182
183 /** @since 4.0 */
184
185 public static final String TAPESTRY_DTD_4_0_PUBLIC_ID = "-//Apache Software Foundation//Tapestry Specification 4.0//EN";
186
187 /** @since 4.1 */
188
189 public static final String TAPESTRY_DTD_4_1_PUBLIC_ID = "-//Apache Software Foundation//Tapestry Specification 4.1//EN";
190
191 private static final int STATE_ALLOW_DESCRIPTION = 2000;
192
193 private static final int STATE_ALLOW_PROPERTY = 2001;
194
195 private static final int STATE_APPLICATION_SPECIFICATION_INITIAL = 1002;
196
197 private static final int STATE_BEAN = 4;
198
199 /** Very different between 3.0 and 4.0 DTD. */
200
201 private static final int STATE_BINDING_3_0 = 7;
202
203 /** @since 4.0 */
204
205 private static final int STATE_BINDING = 100;
206
207 private static final int STATE_COMPONENT = 6;
208
209 private static final int STATE_COMPONENT_SPECIFICATION = 1;
210
211 private static final int STATE_COMPONENT_SPECIFICATION_INITIAL = 1000;
212
213 private static final int STATE_CONFIGURE = 14;
214
215 private static final int STATE_DESCRIPTION = 2;
216
217 private static final int STATE_EXTENSION = 13;
218
219 private static final int STATE_LIBRARY_SPECIFICATION = 12;
220
221 private static final int STATE_LIBRARY_SPECIFICATION_INITIAL = 1003;
222
223 private static final int STATE_LISTENER_BINDING = 8;
224
225 private static final int STATE_NO_CONTENT = 3000;
226
227 private static final int STATE_PAGE_SPECIFICATION = 11;
228
229 private static final int STATE_PAGE_SPECIFICATION_INITIAL = 1001;
230
231 private static final int STATE_META = 3;
232
233 private static final int STATE_PROPERTY = 10;
234
235 private static final int STATE_SET = 5;
236
237 /** 3.0 DTD only. */
238 private static final int STATE_STATIC_BINDING = 9;
239
240 /**
241 * We can share a single map for all the XML attribute to object conversions, since the keys are
242 * unique.
243 */
244
245 private final Map _conversionMap = new HashMap();
246
247 /** @since 4.0 */
248 private final Log _log;
249
250 /** @since 4.0 */
251 private final ErrorHandler _errorHandler;
252
253 /**
254 * Set to true if parsing the 4.0 DTD.
255 *
256 * @since 4.0
257 */
258
259 private boolean _dtd40;
260
261 /**
262 * The attributes of the current element, as a map (string keyed on string).
263 */
264
265 private Map _attributes;
266
267 /**
268 * The name of the current element.
269 */
270
271 private String _elementName;
272
273 /** @since 1.0.9 */
274
275 private final SpecFactory _factory;
276
277 private RegexpMatcher _matcher = new RegexpMatcher();
278
279 private SAXParser _parser;
280
281 private SAXParserFactory _parserFactory = SAXParserFactory.newInstance();
282
283 /**
284 * @since 3.0
285 */
286
287 private final ClassResolver _resolver;
288
289 /** @since 4.0 */
290
291 private BindingSource _bindingSource;
292
293 /**
294 * The root object parsed: a component or page specification, a library specification, or an
295 * application specification.
296 */
297 private Object _rootObject;
298
299 /** @since 4.0 */
300
301 private ValueConverter _valueConverter;
302
303 // Identify all the different acceptible values.
304 // We continue to sneak by with a single map because
305 // there aren't conflicts; when we have 'foo' meaning
306 // different things in different places in the DTD, we'll
307 // need multiple maps.
308
309 {
310
311 _conversionMap.put("true", Boolean.TRUE);
312 _conversionMap.put("t", Boolean.TRUE);
313 _conversionMap.put("1", Boolean.TRUE);
314 _conversionMap.put("y", Boolean.TRUE);
315 _conversionMap.put("yes", Boolean.TRUE);
316 _conversionMap.put("on", Boolean.TRUE);
317 _conversionMap.put("aye", Boolean.TRUE);
318
319 _conversionMap.put("false", Boolean.FALSE);
320 _conversionMap.put("f", Boolean.FALSE);
321 _conversionMap.put("0", Boolean.FALSE);
322 _conversionMap.put("off", Boolean.FALSE);
323 _conversionMap.put("no", Boolean.FALSE);
324 _conversionMap.put("n", Boolean.FALSE);
325 _conversionMap.put("nay", Boolean.FALSE);
326
327 _conversionMap.put("none", BeanLifecycle.NONE);
328 _conversionMap.put("request", BeanLifecycle.REQUEST);
329 _conversionMap.put("page", BeanLifecycle.PAGE);
330 _conversionMap.put("render", BeanLifecycle.RENDER);
331
332 _parserFactory.setNamespaceAware(false);
333 _parserFactory.setValidating(true);
334 }
335
336 /**
337 * This constructor is a convienience used by some tests.
338 */
339 public SpecificationParser(ClassResolver resolver)
340 {
341 this(new DefaultErrorHandler(), LogFactory.getLog(SpecificationParser.class),
342 resolver, new SpecFactory());
343 }
344
345 /**
346 * The full constructor, used within Tapestry.
347 */
348 public SpecificationParser(ErrorHandler errorHandler, Log log, ClassResolver resolver,
349 SpecFactory factory)
350 {
351 _errorHandler = errorHandler;
352 _log = log;
353 _resolver = resolver;
354 _factory = factory;
355 }
356
357 protected void begin(String elementName, Map attributes)
358 {
359 _elementName = elementName;
360 _attributes = attributes;
361
362 switch (getState())
363 {
364 case STATE_COMPONENT_SPECIFICATION_INITIAL:
365
366 beginComponentSpecificationInitial();
367 break;
368
369 case STATE_PAGE_SPECIFICATION_INITIAL:
370
371 beginPageSpecificationInitial();
372 break;
373
374 case STATE_APPLICATION_SPECIFICATION_INITIAL:
375
376 beginApplicationSpecificationInitial();
377 break;
378
379 case STATE_LIBRARY_SPECIFICATION_INITIAL:
380
381 beginLibrarySpecificationInitial();
382 break;
383
384 case STATE_COMPONENT_SPECIFICATION:
385
386 beginComponentSpecification();
387 break;
388
389 case STATE_PAGE_SPECIFICATION:
390
391 beginPageSpecification();
392 break;
393
394 case STATE_ALLOW_DESCRIPTION:
395
396 beginAllowDescription();
397 break;
398
399 case STATE_ALLOW_PROPERTY:
400
401 allowMetaData();
402 break;
403
404 case STATE_BEAN:
405
406 beginBean();
407 break;
408
409 case STATE_COMPONENT:
410
411 beginComponent();
412 break;
413
414 case STATE_LIBRARY_SPECIFICATION:
415
416 beginLibrarySpecification();
417 break;
418
419 case STATE_EXTENSION:
420
421 beginExtension();
422 break;
423
424 default:
425
426 unexpectedElement(_elementName);
427 }
428 }
429
430 /**
431 * Special state for a number of specification types that can support the <description>
432 * element.
433 */
434
435 private void beginAllowDescription()
436 {
437 if (_elementName.equals("description"))
438 {
439 enterDescription();
440 return;
441 }
442
443 unexpectedElement(_elementName);
444 }
445
446 /**
447 * Special state for a number of elements that can support the nested <meta> meta data
448 * element (<property> in 3.0 DTD).
449 */
450
451 private void allowMetaData()
452 {
453 if (_dtd40)
454 {
455 if (_elementName.equals("meta"))
456 {
457 enterMeta();
458 return;
459 }
460 }
461 else if (_elementName.equals("property"))
462 {
463 enterProperty30();
464 return;
465 }
466
467 unexpectedElement(_elementName);
468 }
469
470 private void beginApplicationSpecificationInitial()
471 {
472 expectElement("application");
473
474 String name = getAttribute("name");
475 String engineClassName = getAttribute("engine-class");
476
477 IApplicationSpecification as = _factory.createApplicationSpecification();
478
479 as.setName(name);
480
481 if (HiveMind.isNonBlank(engineClassName))
482 as.setEngineClassName(engineClassName);
483
484 _rootObject = as;
485
486 push(_elementName, as, STATE_LIBRARY_SPECIFICATION);
487 }
488
489 private void beginBean()
490 {
491 if (_elementName.equals("set"))
492 {
493 enterSet();
494 return;
495 }
496
497 if (_elementName.equals("set-property"))
498 {
499 enterSetProperty30();
500 return;
501 }
502
503 if (_elementName.equals("set-message-property"))
504 {
505 enterSetMessage30();
506 return;
507 }
508
509 if (_elementName.equals("description"))
510 {
511 enterDescription();
512 return;
513 }
514
515 allowMetaData();
516 }
517
518 private void beginComponent()
519 {
520 // <binding> has changed between 3.0 and 4.0
521
522 if (_elementName.equals("binding"))
523 {
524 enterBinding();
525 return;
526 }
527
528 if (_elementName.equals("static-binding"))
529 {
530 enterStaticBinding30();
531 return;
532 }
533
534 if (_elementName.equals("message-binding"))
535 {
536 enterMessageBinding30();
537 return;
538 }
539
540 if (_elementName.equals("inherited-binding"))
541 {
542 enterInheritedBinding30();
543 return;
544 }
545
546 if (_elementName.equals("listener-binding"))
547 {
548 enterListenerBinding();
549 return;
550 }
551
552 allowMetaData();
553 }
554
555 private void beginComponentSpecification()
556 {
557 if (_elementName.equals("reserved-parameter"))
558 {
559 enterReservedParameter();
560 return;
561 }
562
563 if (_elementName.equals("parameter"))
564 {
565 enterParameter();
566 return;
567 }
568
569 // The remainder are common to both <component-specification> and
570 // <page-specification>
571
572 beginPageSpecification();
573 }
574
575 private void beginComponentSpecificationInitial()
576 {
577 expectElement("component-specification");
578
579 IComponentSpecification cs = _factory.createComponentSpecification();
580
581 cs.setAllowBody(getBooleanAttribute("allow-body", true));
582 cs.setAllowInformalParameters(getBooleanAttribute("allow-informal-parameters", true));
583 cs.setDeprecated(getBooleanAttribute("deprecated", false));
584
585 String className = getAttribute("class");
586
587 if (className != null)
588 cs.setComponentClassName(className);
589
590 cs.setSpecificationLocation(getResource());
591
592 _rootObject = cs;
593
594 push(_elementName, cs, STATE_COMPONENT_SPECIFICATION);
595 }
596
597 private void beginExtension()
598 {
599 if (_elementName.equals("configure"))
600 {
601 enterConfigure();
602 return;
603 }
604
605 allowMetaData();
606 }
607
608 private void beginLibrarySpecification()
609 {
610 if (_elementName.equals("description"))
611 {
612 enterDescription();
613 return;
614 }
615
616 if (_elementName.equals("page"))
617 {
618 enterPage();
619 return;
620 }
621
622 if (_elementName.equals("component-type"))
623 {
624 enterComponentType();
625 return;
626 }
627
628 // Holdover from the 3.0 DTD, now ignored.
629
630 if (_elementName.equals("service"))
631 {
632 enterService30();
633 return;
634 }
635
636 if (_elementName.equals("library"))
637 {
638 enterLibrary();
639 return;
640 }
641
642 if (_elementName.equals("extension"))
643 {
644 enterExtension();
645 return;
646 }
647
648 allowMetaData();
649 }
650
651 private void beginLibrarySpecificationInitial()
652 {
653 expectElement("library-specification");
654
655 ILibrarySpecification ls = _factory.createLibrarySpecification();
656
657 _rootObject = ls;
658
659 push(_elementName, ls, STATE_LIBRARY_SPECIFICATION);
660 }
661
662 private void beginPageSpecification()
663 {
664 if (_elementName.equals("component"))
665 {
666 enterComponent();
667 return;
668 }
669
670 if (_elementName.equals("bean"))
671 {
672 enterBean();
673 return;
674 }
675
676 // <property-specification> in 3.0, <property> in 4.0
677 // Have to be careful, because <meta> in 4.0 was <property> in 3.0
678
679 if (_elementName.equals("property-specification")
680 || (_dtd40 && _elementName.equals("property")))
681 {
682 enterProperty();
683 return;
684 }
685
686 if (_elementName.equals("inject"))
687 {
688 enterInject();
689 return;
690 }
691
692 // <asset> is new in 4.0
693
694 if (_elementName.equals("asset"))
695 {
696 enterAsset();
697 return;
698 }
699
700 // <context-asset>, <external-asset>, and <private-asset>
701 // are all throwbacks to the 3.0 DTD and don't exist
702 // in the 4.0 DTD.
703
704 if (_elementName.equals("context-asset"))
705 {
706 enterContextAsset30();
707 return;
708 }
709
710 if (_elementName.equals("private-asset"))
711 {
712 enterPrivateAsset30();
713 return;
714 }
715
716 if (_elementName.equals("external-asset"))
717 {
718 enterExternalAsset30();
719 return;
720
721 }
722
723 if (_elementName.equals("description"))
724 {
725 enterDescription();
726 return;
727 }
728
729 allowMetaData();
730 }
731
732 private void beginPageSpecificationInitial()
733 {
734 expectElement("page-specification");
735
736 IComponentSpecification cs = _factory.createComponentSpecification();
737
738 String className = getAttribute("class");
739
740 if (className != null)
741 cs.setComponentClassName(className);
742
743 cs.setSpecificationLocation(getResource());
744 cs.setPageSpecification(true);
745
746 _rootObject = cs;
747
748 push(_elementName, cs, STATE_PAGE_SPECIFICATION);
749 }
750
751 /**
752 * Close a stream (if not null), ignoring any errors.
753 */
754 private void close(InputStream stream)
755 {
756 try
757 {
758 if (stream != null)
759 stream.close();
760 }
761 catch (IOException ex)
762 {
763 // ignore
764 }
765 }
766
767 private void copyBindings(String sourceComponentId, IComponentSpecification cs,
768 IContainedComponent target)
769 {
770 IContainedComponent source = cs.getComponent(sourceComponentId);
771 if (source == null)
772 throw new DocumentParseException(ParseMessages.unableToCopy(sourceComponentId),
773 getLocation());
774
775 Iterator i = source.getBindingNames().iterator();
776 while (i.hasNext())
777 {
778 String bindingName = (String) i.next();
779 IBindingSpecification binding = source.getBinding(bindingName);
780 target.setBinding(bindingName, binding);
781 }
782
783 target.setType(source.getType());
784 }
785
786 protected void end(String elementName)
787 {
788 _elementName = elementName;
789
790 switch (getState())
791 {
792 case STATE_DESCRIPTION:
793
794 endDescription();
795 break;
796
797 case STATE_META:
798
799 endProperty();
800 break;
801
802 case STATE_SET:
803
804 endSetProperty();
805 break;
806
807 case STATE_BINDING_3_0:
808
809 endBinding30();
810 break;
811
812 case STATE_BINDING:
813
814 endBinding();
815 break;
816
817 case STATE_STATIC_BINDING:
818
819 endStaticBinding();
820 break;
821
822 case STATE_PROPERTY:
823
824 endPropertySpecification();
825 break;
826
827 case STATE_LIBRARY_SPECIFICATION:
828
829 endLibrarySpecification();
830 break;
831
832 case STATE_CONFIGURE:
833
834 endConfigure();
835 break;
836
837 default:
838 break;
839 }
840
841 // Pop the top element of the stack and continue processing from there.
842
843 pop();
844 }
845
846 private void endBinding30()
847 {
848 BindingSetter bs = (BindingSetter) peekObject();
849
850 String expression = getExtendedValue(bs.getValue(), "expression", true);
851
852 IBindingSpecification spec = _factory.createBindingSpecification();
853
854 spec.setType(BindingType.PREFIXED);
855 spec.setValue(BindingConstants.OGNL_PREFIX + ":" + expression);
856
857 bs.apply(spec);
858 }
859
860 private void endConfigure()
861 {
862 ExtensionConfigurationSetter setter = (ExtensionConfigurationSetter) peekObject();
863
864 String finalValue = getExtendedValue(setter.getValue(), "value", true);
865
866 setter.apply(finalValue);
867 }
868
869 private void endDescription()
870 {
871 DescriptionSetter setter = (DescriptionSetter) peekObject();
872
873 String description = peekContent();
874
875 setter.apply(description);
876 }
877
878 private void endLibrarySpecification()
879 {
880 ILibrarySpecification spec = (ILibrarySpecification) peekObject();
881
882 spec.setSpecificationLocation(getResource());
883
884 spec.instantiateImmediateExtensions();
885 }
886
887 private void endProperty()
888 {
889 PropertyValueSetter pvs = (PropertyValueSetter) peekObject();
890
891 String finalValue = getExtendedValue(pvs.getPropertyValue(), "value", true);
892
893 pvs.applyValue(finalValue);
894 }
895
896 private void endPropertySpecification()
897 {
898 IPropertySpecification ps = (IPropertySpecification) peekObject();
899
900 String initialValue = getExtendedValue(ps.getInitialValue(), "initial-value", false);
901
902 // In the 3.0 DTD, the initial value was always an OGNL expression.
903 // In the 4.0 DTD, it is a binding reference, qualified with a prefix.
904
905 if (initialValue != null && !_dtd40)
906 initialValue = BindingConstants.OGNL_PREFIX + ":" + initialValue;
907
908 ps.setInitialValue(initialValue);
909 }
910
911 private void endSetProperty()
912 {
913 BeanSetPropertySetter bs = (BeanSetPropertySetter) peekObject();
914
915 String finalValue = getExtendedValue(bs.getBindingReference(), "expression", true);
916
917 bs.applyBindingReference(finalValue);
918 }
919
920 private void endStaticBinding()
921 {
922 BindingSetter bs = (BindingSetter) peekObject();
923
924 String literalValue = getExtendedValue(bs.getValue(), "value", true);
925
926 IBindingSpecification spec = _factory.createBindingSpecification();
927
928 spec.setType(BindingType.PREFIXED);
929 spec.setValue(BindingConstants.LITERAL_PREFIX + ":" + literalValue);
930
931 bs.apply(spec);
932 }
933
934 private void enterAsset(String pathAttributeName, String prefix)
935 {
936 String name = getValidatedAttribute("name", ASSET_NAME_PATTERN, "invalid-asset-name");
937 String path = getAttribute(pathAttributeName);
938 String propertyName = getValidatedAttribute(
939 "property",
940 PROPERTY_NAME_PATTERN,
941 "invalid-property-name");
942
943 IAssetSpecification ia = _factory.createAssetSpecification();
944
945 ia.setPath(prefix == null ? path : prefix + path);
946 ia.setPropertyName(propertyName);
947
948 IComponentSpecification cs = (IComponentSpecification) peekObject();
949
950 cs.addAsset(name, ia);
951
952 push(_elementName, ia, STATE_ALLOW_PROPERTY);
953 }
954
955 private void enterBean()
956 {
957 String name = getValidatedAttribute("name", BEAN_NAME_PATTERN, "invalid-bean-name");
958
959 String classAttribute = getAttribute("class");
960
961 // Look for the lightweight initialization
962
963 int commax = classAttribute.indexOf(',');
964
965 String className = commax < 0 ? classAttribute : classAttribute.substring(0, commax);
966
967 BeanLifecycle lifecycle = (BeanLifecycle) getConvertedAttribute(
968 "lifecycle",
969 BeanLifecycle.REQUEST);
970 String propertyName = getValidatedAttribute(
971 "property",
972 PROPERTY_NAME_PATTERN,
973 "invalid-property-name");
974
975 IBeanSpecification bs = _factory.createBeanSpecification();
976
977 bs.setClassName(className);
978 bs.setLifecycle(lifecycle);
979 bs.setPropertyName(propertyName);
980
981 if (commax > 0)
982 {
983 String initializer = classAttribute.substring(commax + 1);
984 bs.addInitializer(new LightweightBeanInitializer(initializer));
985 }
986
987 IComponentSpecification cs = (IComponentSpecification) peekObject();
988
989 cs.addBeanSpecification(name, bs);
990
991 push(_elementName, bs, STATE_BEAN);
992 }
993
994 private void enterBinding()
995 {
996 if (!_dtd40)
997 {
998 enterBinding30();
999 return;
1000 }
1001
1002 // 4.0 stuff
1003
1004 String name = getValidatedAttribute(
1005 "name",
1006 PARAMETER_NAME_PATTERN,
1007 "invalid-parameter-name");
1008 String value = getAttribute("value");
1009
1010 IContainedComponent cc = (IContainedComponent) peekObject();
1011
1012 BindingSetter bs = new BindingSetter(cc, name, value);
1013
1014 push(_elementName, bs, STATE_BINDING, false);
1015 }
1016
1017 private void endBinding()
1018 {
1019 BindingSetter bs = (BindingSetter) peekObject();
1020
1021 String value = getExtendedValue(bs.getValue(), "value", true);
1022
1023 IBindingSpecification spec = _factory.createBindingSpecification();
1024
1025 spec.setType(BindingType.PREFIXED);
1026 spec.setValue(value);
1027
1028 bs.apply(spec);
1029 }
1030
1031 /**
1032 * Handles a binding in a 3.0 DTD.
1033 */
1034
1035 private void enterBinding30()
1036 {
1037 String name = getAttribute("name");
1038 String expression = getAttribute("expression");
1039
1040 IContainedComponent cc = (IContainedComponent) peekObject();
1041
1042 BindingSetter bs = new BindingSetter(cc, name, expression);
1043
1044 push(_elementName, bs, STATE_BINDING_3_0, false);
1045 }
1046
1047 private void enterComponent()
1048 {
1049 String id = getValidatedAttribute("id", COMPONENT_ID_PATTERN, "invalid-component-id");
1050
1051 String type = getValidatedAttribute(
1052 "type",
1053 COMPONENT_TYPE_PATTERN,
1054 "invalid-component-type");
1055 String copyOf = getAttribute("copy-of");
1056 boolean inherit = getBooleanAttribute("inherit-informal-parameters", false);
1057 String propertyName = getValidatedAttribute(
1058 "property",
1059 PROPERTY_NAME_PATTERN,
1060 "invalid-property-name");
1061
1062 // Check that either copy-of or type, but not both
1063
1064 boolean hasCopyOf = HiveMind.isNonBlank(copyOf);
1065
1066 if (hasCopyOf)
1067 {
1068 if (HiveMind.isNonBlank(type))
1069 throw new DocumentParseException(ParseMessages.bothTypeAndCopyOf(id), getLocation());
1070 }
1071 else
1072 {
1073 if (HiveMind.isBlank(type))
1074 throw new DocumentParseException(ParseMessages.missingTypeOrCopyOf(id),
1075 getLocation());
1076 }
1077
1078 IContainedComponent cc = _factory.createContainedComponent();
1079 cc.setType(type);
1080 cc.setCopyOf(copyOf);
1081 cc.setInheritInformalParameters(inherit);
1082 cc.setPropertyName(propertyName);
1083
1084 IComponentSpecification cs = (IComponentSpecification) peekObject();
1085
1086 cs.addComponent(id, cc);
1087
1088 if (hasCopyOf)
1089 copyBindings(copyOf, cs, cc);
1090
1091 push(_elementName, cc, STATE_COMPONENT);
1092 }
1093
1094 private void enterComponentType()
1095 {
1096 String type = getValidatedAttribute(
1097 "type",
1098 COMPONENT_ALIAS_PATTERN,
1099 "invalid-component-type");
1100 String path = getAttribute("specification-path");
1101
1102 ILibrarySpecification ls = (ILibrarySpecification) peekObject();
1103
1104 ls.setComponentSpecificationPath(type, path);
1105
1106 push(_elementName, null, STATE_NO_CONTENT);
1107 }
1108
1109 private void enterConfigure()
1110 {
1111 String attributeName = _dtd40 ? "property" : "property-name";
1112
1113 String propertyName = getValidatedAttribute(
1114 attributeName,
1115 PROPERTY_NAME_PATTERN,
1116 "invalid-property-name");
1117
1118 String value = getAttribute("value");
1119
1120 IExtensionSpecification es = (IExtensionSpecification) peekObject();
1121
1122 ExtensionConfigurationSetter setter = new ExtensionConfigurationSetter(es, propertyName,
1123 value);
1124
1125 push(_elementName, setter, STATE_CONFIGURE, false);
1126 }
1127
1128 private void enterContextAsset30()
1129 {
1130 enterAsset("path", "context:");
1131 }
1132
1133 /**
1134 * New in the 4.0 DTD. When using the 4.0 DTD, you must explicitly specify prefix if the asset
1135 * is not stored in the same domain as the specification file.
1136 *
1137 * @since 4.0
1138 */
1139
1140 private void enterAsset()
1141 {
1142 enterAsset("path", null);
1143 }
1144
1145 private void enterDescription()
1146 {
1147 push(_elementName, new DescriptionSetter(peekObject()), STATE_DESCRIPTION, false);
1148 }
1149
1150 private void enterExtension()
1151 {
1152 String name = getValidatedAttribute(
1153 "name",
1154 EXTENSION_NAME_PATTERN,
1155 "invalid-extension-name");
1156
1157 boolean immediate = getBooleanAttribute("immediate", false);
1158 String className = getAttribute("class");
1159
1160 IExtensionSpecification es = _factory.createExtensionSpecification(
1161 _resolver,
1162 _valueConverter);
1163
1164 es.setClassName(className);
1165 es.setImmediate(immediate);
1166
1167 ILibrarySpecification ls = (ILibrarySpecification) peekObject();
1168
1169 ls.addExtensionSpecification(name, es);
1170
1171 push(_elementName, es, STATE_EXTENSION);
1172 }
1173
1174 private void enterExternalAsset30()
1175 {
1176 // External URLs get no prefix, but will have a scheme (i.e., "http:") that
1177 // fulfils much the same purpose.
1178
1179 enterAsset("URL", null);
1180 }
1181
1182 /** A throwback to the 3.0 DTD. */
1183
1184 private void enterInheritedBinding30()
1185 {
1186 String name = getAttribute("name");
1187 String parameterName = getAttribute("parameter-name");
1188
1189 IBindingSpecification bs = _factory.createBindingSpecification();
1190 bs.setType(BindingType.INHERITED);
1191 bs.setValue(parameterName);
1192
1193 IContainedComponent cc = (IContainedComponent) peekObject();
1194
1195 cc.setBinding(name, bs);
1196
1197 push(_elementName, null, STATE_NO_CONTENT);
1198 }
1199
1200 private void enterLibrary()
1201 {
1202 String libraryId = getValidatedAttribute("id", LIBRARY_ID_PATTERN, "invalid-library-id");
1203 String path = getAttribute("specification-path");
1204
1205 if (libraryId.equals(INamespace.FRAMEWORK_NAMESPACE)
1206 || libraryId.equals(INamespace.APPLICATION_NAMESPACE))
1207 throw new DocumentParseException(ParseMessages
1208 .frameworkLibraryIdIsReserved(INamespace.FRAMEWORK_NAMESPACE), getLocation());
1209
1210 ILibrarySpecification ls = (ILibrarySpecification) peekObject();
1211
1212 ls.setLibrarySpecificationPath(libraryId, path);
1213
1214 push(_elementName, null, STATE_NO_CONTENT);
1215 }
1216
1217 private void enterListenerBinding()
1218 {
1219 _log.warn(ParseMessages.listenerBindingUnsupported(getLocation()));
1220
1221 push(_elementName, null, STATE_LISTENER_BINDING, false);
1222 }
1223
1224 private void enterMessageBinding30()
1225 {
1226 String name = getAttribute("name");
1227 String key = getAttribute("key");
1228
1229 IBindingSpecification bs = _factory.createBindingSpecification();
1230 bs.setType(BindingType.PREFIXED);
1231 bs.setValue(BindingConstants.MESSAGE_PREFIX + ":" + key);
1232 bs.setLocation(getLocation());
1233
1234 IContainedComponent cc = (IContainedComponent) peekObject();
1235
1236 cc.setBinding(name, bs);
1237
1238 push(_elementName, null, STATE_NO_CONTENT);
1239 }
1240
1241 private void enterPage()
1242 {
1243 String name = getValidatedAttribute("name", PAGE_NAME_PATTERN, "invalid-page-name");
1244 String path = getAttribute("specification-path");
1245
1246 ILibrarySpecification ls = (ILibrarySpecification) peekObject();
1247
1248 ls.setPageSpecificationPath(name, path);
1249
1250 push(_elementName, null, STATE_NO_CONTENT);
1251 }
1252
1253 private void enterParameter()
1254 {
1255 IParameterSpecification ps = _factory.createParameterSpecification();
1256
1257 String name = getValidatedAttribute(
1258 "name",
1259 PARAMETER_NAME_PATTERN,
1260 "invalid-parameter-name");
1261
1262 String attributeName = _dtd40 ? "property" : "property-name";
1263
1264 String propertyName = getValidatedAttribute(
1265 attributeName,
1266 PROPERTY_NAME_PATTERN,
1267 "invalid-property-name");
1268
1269 if (propertyName == null)
1270 propertyName = name;
1271
1272 ps.setParameterName(name);
1273 ps.setPropertyName(propertyName);
1274
1275 ps.setRequired(getBooleanAttribute("required", false));
1276
1277 // In the 3.0 DTD, default-value was always an OGNL expression.
1278 // Starting with 4.0, it's like a binding (prefixed). For a 3.0
1279 // DTD, we supply the "ognl:" prefix.
1280
1281 String defaultValue = getAttribute("default-value");
1282
1283 if (defaultValue != null && !_dtd40)
1284 defaultValue = BindingConstants.OGNL_PREFIX + ":" + defaultValue;
1285
1286 ps.setDefaultValue(defaultValue);
1287
1288 if (!_dtd40)
1289 {
1290 // When direction=auto (in a 3.0 DTD), turn caching off
1291
1292 String direction = getAttribute("direction");
1293 ps.setCache(!"auto".equals(direction));
1294 }
1295 else
1296 {
1297 boolean cache = getBooleanAttribute("cache", true);
1298 ps.setCache(cache);
1299 }
1300
1301 // type will only be specified in a 3.0 DTD.
1302
1303 String type = getAttribute("type");
1304
1305 if (type != null)
1306 ps.setType(type);
1307
1308 // aliases is new in the 4.0 DTD
1309
1310 String aliases = getAttribute("aliases");
1311
1312 ps.setAliases(aliases);
1313 ps.setDeprecated(getBooleanAttribute("deprecated", false));
1314
1315 IComponentSpecification cs = (IComponentSpecification) peekObject();
1316
1317 cs.addParameter(ps);
1318
1319 push(_elementName, ps, STATE_ALLOW_DESCRIPTION);
1320 }
1321
1322 private void enterPrivateAsset30()
1323 {
1324 enterAsset("resource-path", "classpath:");
1325 }
1326
1327 /** @since 4.0 */
1328 private void enterMeta()
1329 {
1330 String key = getAttribute("key");
1331 String value = getAttribute("value");
1332
1333 // Value may be null, in which case the value is set from the element content
1334
1335 IPropertyHolder ph = (IPropertyHolder) peekObject();
1336
1337 push(_elementName, new PropertyValueSetter(ph, key, value), STATE_META, false);
1338 }
1339
1340 private void enterProperty30()
1341 {
1342 String name = getAttribute("name");
1343 String value = getAttribute("value");
1344
1345 // Value may be null, in which case the value is set from the element content
1346
1347 IPropertyHolder ph = (IPropertyHolder) peekObject();
1348
1349 push(_elementName, new PropertyValueSetter(ph, name, value), STATE_META, false);
1350 }
1351
1352 /**
1353 * &tl;property> in 4.0, or <property-specification> in 3.0.
1354 */
1355
1356 private void enterProperty()
1357 {
1358 String name = getValidatedAttribute("name", PROPERTY_NAME_PATTERN, "invalid-property-name");
1359 String type = getAttribute("type");
1360
1361 String persistence = null;
1362
1363 if (_dtd40)
1364 persistence = getAttribute("persist");
1365 else
1366 persistence = getBooleanAttribute("persistent", false) ? "session" : null;
1367
1368 String initialValue = getAttribute("initial-value");
1369
1370 IPropertySpecification ps = _factory.createPropertySpecification();
1371 ps.setName(name);
1372
1373 if (HiveMind.isNonBlank(type))
1374 ps.setType(type);
1375
1376 ps.setPersistence(persistence);
1377 ps.setInitialValue(initialValue);
1378
1379 IComponentSpecification cs = (IComponentSpecification) peekObject();
1380 cs.addPropertySpecification(ps);
1381
1382 push(_elementName, ps, STATE_PROPERTY, false);
1383 }
1384
1385 /**
1386 * @since 4.0
1387 */
1388
1389 private void enterInject()
1390 {
1391 String property = getValidatedAttribute(
1392 "property",
1393 PROPERTY_NAME_PATTERN,
1394 "invalid-property-name");
1395 String type = getAttribute("type");
1396 String objectReference = getAttribute("object");
1397
1398 InjectSpecification spec = _factory.createInjectSpecification();
1399
1400 spec.setProperty(property);
1401 spec.setType(type);
1402 spec.setObject(objectReference);
1403 IComponentSpecification cs = (IComponentSpecification) peekObject();
1404
1405 cs.addInjectSpecification(spec);
1406
1407 push(_elementName, spec, STATE_NO_CONTENT);
1408 }
1409
1410 private void enterReservedParameter()
1411 {
1412 String name = getAttribute("name");
1413 IComponentSpecification cs = (IComponentSpecification) peekObject();
1414
1415 cs.addReservedParameterName(name);
1416
1417 push(_elementName, null, STATE_NO_CONTENT);
1418 }
1419
1420 private void enterService30()
1421 {
1422 _errorHandler.error(_log, ParseMessages.serviceElementNotSupported(), getLocation(), null);
1423
1424 push(_elementName, null, STATE_NO_CONTENT);
1425 }
1426
1427 private void enterSetMessage30()
1428 {
1429 String name = getAttribute("name");
1430 String key = getAttribute("key");
1431
1432 BindingBeanInitializer bi = _factory.createBindingBeanInitializer(_bindingSource);
1433
1434 bi.setPropertyName(name);
1435 bi.setBindingReference(BindingConstants.MESSAGE_PREFIX + ":" + key);
1436 bi.setLocation(getLocation());
1437
1438 IBeanSpecification bs = (IBeanSpecification) peekObject();
1439
1440 bs.addInitializer(bi);
1441
1442 push(_elementName, null, STATE_NO_CONTENT);
1443 }
1444
1445 private void enterSet()
1446 {
1447 String name = getAttribute("name");
1448 String reference = getAttribute("value");
1449
1450 BindingBeanInitializer bi = _factory.createBindingBeanInitializer(_bindingSource);
1451
1452 bi.setPropertyName(name);
1453
1454 IBeanSpecification bs = (IBeanSpecification) peekObject();
1455
1456 push(_elementName, new BeanSetPropertySetter(bs, bi, null, reference), STATE_SET, false);
1457 }
1458
1459 private void enterSetProperty30()
1460 {
1461 String name = getAttribute("name");
1462 String expression = getAttribute("expression");
1463
1464 BindingBeanInitializer bi = _factory.createBindingBeanInitializer(_bindingSource);
1465
1466 bi.setPropertyName(name);
1467
1468 IBeanSpecification bs = (IBeanSpecification) peekObject();
1469
1470 push(_elementName, new BeanSetPropertySetter(bs, bi, BindingConstants.OGNL_PREFIX + ":",
1471 expression), STATE_SET, false);
1472 }
1473
1474 private void enterStaticBinding30()
1475 {
1476 String name = getAttribute("name");
1477 String expression = getAttribute("value");
1478
1479 IContainedComponent cc = (IContainedComponent) peekObject();
1480
1481 BindingSetter bs = new BindingSetter(cc, name, expression);
1482
1483 push(_elementName, bs, STATE_STATIC_BINDING, false);
1484 }
1485
1486 private void expectElement(String elementName)
1487 {
1488 if (_elementName.equals(elementName))
1489 return;
1490
1491 throw new DocumentParseException(ParseMessages.incorrectDocumentType(
1492 _elementName,
1493 elementName), getLocation(), null);
1494
1495 }
1496
1497 private String getAttribute(String name)
1498 {
1499 return (String) _attributes.get(name);
1500 }
1501
1502 private boolean getBooleanAttribute(String name, boolean defaultValue)
1503 {
1504 String value = getAttribute(name);
1505
1506 if (value == null)
1507 return defaultValue;
1508
1509 Boolean b = (Boolean) _conversionMap.get(value);
1510
1511 return b.booleanValue();
1512 }
1513
1514 private Object getConvertedAttribute(String name, Object defaultValue)
1515 {
1516 String key = getAttribute(name);
1517
1518 if (key == null)
1519 return defaultValue;
1520
1521 return _conversionMap.get(key);
1522 }
1523
1524 private InputSource getDTDInputSource(String name)
1525 {
1526 InputStream stream = getClass().getResourceAsStream(name);
1527
1528 return new InputSource(stream);
1529 }
1530
1531 private String getExtendedValue(String attributeValue, String attributeName, boolean required)
1532 {
1533 String contentValue = peekContent();
1534
1535 boolean asAttribute = HiveMind.isNonBlank(attributeValue);
1536 boolean asContent = HiveMind.isNonBlank(contentValue);
1537
1538 if (asAttribute && asContent)
1539 {
1540 throw new DocumentParseException(ParseMessages.noAttributeAndBody(
1541 attributeName,
1542 _elementName), getLocation(), null);
1543 }
1544
1545 if (required && !(asAttribute || asContent))
1546 {
1547 throw new DocumentParseException(ParseMessages.requiredExtendedAttribute(
1548 _elementName,
1549 attributeName), getLocation(), null);
1550 }
1551
1552 if (asAttribute)
1553 return attributeValue;
1554
1555 return contentValue;
1556 }
1557
1558 private String getValidatedAttribute(String name, String pattern, String errorKey)
1559 {
1560 String value = getAttribute(name);
1561
1562 if (value == null)
1563 return null;
1564
1565 if (_matcher.matches(pattern, value))
1566 return value;
1567
1568 throw new InvalidStringException(ParseMessages.invalidAttribute(errorKey, value), value,
1569 getLocation());
1570 }
1571
1572 protected void initializeParser(Resource resource, int startState)
1573 {
1574 super.initializeParser(resource, startState);
1575
1576 _rootObject = null;
1577 _attributes = new HashMap();
1578 }
1579
1580 public IApplicationSpecification parseApplicationSpecification(Resource resource)
1581 {
1582 initializeParser(resource, STATE_APPLICATION_SPECIFICATION_INITIAL);
1583
1584 try
1585 {
1586 parseDocument();
1587
1588 return (IApplicationSpecification) _rootObject;
1589 }
1590 finally
1591 {
1592 resetParser();
1593 }
1594 }
1595
1596 public IComponentSpecification parseComponentSpecification(Resource resource)
1597 {
1598 initializeParser(resource, STATE_COMPONENT_SPECIFICATION_INITIAL);
1599
1600 try
1601 {
1602 parseDocument();
1603
1604 return (IComponentSpecification) _rootObject;
1605 }
1606 finally
1607 {
1608 resetParser();
1609 }
1610 }
1611
1612 private void parseDocument()
1613 {
1614 InputStream stream = null;
1615
1616 Resource resource = getResource();
1617
1618 boolean success = false;
1619
1620 try
1621 {
1622 if (_parser == null)
1623 _parser = _parserFactory.newSAXParser();
1624
1625 URL resourceURL = resource.getResourceURL();
1626
1627 if (resourceURL == null)
1628 throw new DocumentParseException(ParseMessages.missingResource(resource), resource);
1629
1630 InputStream rawStream = resourceURL.openStream();
1631 stream = new BufferedInputStream(rawStream);
1632
1633 _parser.parse(stream, this, resourceURL.toExternalForm());
1634
1635 stream.close();
1636 stream = null;
1637
1638 success = true;
1639 }
1640 catch (SAXParseException ex)
1641 {
1642 _parser = null;
1643
1644 Location location = new LocationImpl(resource, ex.getLineNumber(), ex.getColumnNumber());
1645
1646 throw new DocumentParseException(ParseMessages.errorReadingResource(resource, ex),
1647 location, ex);
1648 }
1649 catch (Exception ex)
1650 {
1651 _parser = null;
1652
1653 throw new DocumentParseException(ParseMessages.errorReadingResource(resource, ex),
1654 resource, ex);
1655 }
1656 finally
1657 {
1658 if (!success)
1659 _parser = null;
1660
1661 close(stream);
1662 }
1663 }
1664
1665 public ILibrarySpecification parseLibrarySpecification(Resource resource)
1666 {
1667 initializeParser(resource, STATE_LIBRARY_SPECIFICATION_INITIAL);
1668
1669 try
1670 {
1671 parseDocument();
1672
1673 return (ILibrarySpecification) _rootObject;
1674 }
1675 finally
1676 {
1677 resetParser();
1678 }
1679 }
1680
1681 public IComponentSpecification parsePageSpecification(Resource resource)
1682 {
1683 initializeParser(resource, STATE_PAGE_SPECIFICATION_INITIAL);
1684
1685 try
1686 {
1687 parseDocument();
1688
1689 return (IComponentSpecification) _rootObject;
1690 }
1691 finally
1692 {
1693 resetParser();
1694 }
1695 }
1696
1697 protected String peekContent()
1698 {
1699 String content = super.peekContent();
1700
1701 if (content == null)
1702 return null;
1703
1704 return content.trim();
1705 }
1706
1707 protected void resetParser()
1708 {
1709 _rootObject = null;
1710 _dtd40 = false;
1711
1712 _attributes.clear();
1713 }
1714
1715 /**
1716 * Resolved an external entity, which is assumed to be the doctype. Might need a check to ensure
1717 * that specs without a doctype fail.
1718 */
1719 public InputSource resolveEntity(String publicId, String systemId) throws SAXException
1720 {
1721 if (TAPESTRY_DTD_4_0_PUBLIC_ID.equals(publicId))
1722 {
1723 _dtd40 = true;
1724 return getDTDInputSource("Tapestry_4_0.dtd");
1725 }
1726
1727 if (TAPESTRY_DTD_4_1_PUBLIC_ID.equals(publicId))
1728 {
1729 _dtd40 = true;
1730 return getDTDInputSource("Tapestry_4_1.dtd");
1731 }
1732
1733 if (TAPESTRY_DTD_3_0_PUBLIC_ID.equals(publicId))
1734 return getDTDInputSource("Tapestry_3_0.dtd");
1735
1736 throw new DocumentParseException(ParseMessages.unknownPublicId(getResource(), publicId),
1737 new LocationImpl(getResource()), null);
1738 }
1739
1740 /** @since 4.0 */
1741 public void setBindingSource(BindingSource bindingSource)
1742 {
1743 _bindingSource = bindingSource;
1744 }
1745
1746 /** @since 4.0 */
1747 public void setValueConverter(ValueConverter valueConverter)
1748 {
1749 _valueConverter = valueConverter;
1750 }
1751 }