You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2025/05/04 14:38:29 UTC
(commons-jelly) 01/02: Sort members
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jelly.git
commit fb088290c3ebd6a82aa65e98cfe0748b28ea504e
Author: Gary D. Gregory <ga...@gmail.com>
AuthorDate: Sun May 4 10:33:20 2025 -0400
Sort members
---
.../apache/commons/jelly/DynaBeanTagSupport.java | 38 +-
.../java/org/apache/commons/jelly/DynaTag.java | 8 +-
.../main/java/org/apache/commons/jelly/Jelly.java | 230 +--
.../org/apache/commons/jelly/JellyContext.java | 1078 ++++++-------
.../org/apache/commons/jelly/JellyException.java | 130 +-
.../apache/commons/jelly/JellyTagException.java | 16 +-
.../org/apache/commons/jelly/LocationAware.java | 32 +-
.../org/apache/commons/jelly/MapTagSupport.java | 20 +-
.../main/java/org/apache/commons/jelly/Tag.java | 60 +-
.../java/org/apache/commons/jelly/TagLibrary.java | 84 +-
.../java/org/apache/commons/jelly/TagSupport.java | 272 ++--
.../java/org/apache/commons/jelly/XMLOutput.java | 1284 +++++++--------
.../jelly/expression/CompositeExpression.java | 98 +-
.../jelly/expression/ConstantExpression.java | 20 +-
.../commons/jelly/expression/Expression.java | 22 +-
.../jelly/expression/ExpressionSupport.java | 52 +-
.../jelly/expression/jexl/JexlExpression.java | 140 +-
.../expression/jexl/JexlExpressionFactory.java | 82 +-
.../jelly/expression/xpath/XPathExpression.java | 50 +-
.../org/apache/commons/jelly/impl/Attribute.java | 48 +-
.../jelly/impl/DefaultTagLibraryResolver.java | 172 +-
.../apache/commons/jelly/impl/DynamicBeanTag.java | 68 +-
.../commons/jelly/impl/DynamicDynaBeanTag.java | 54 +-
.../org/apache/commons/jelly/impl/DynamicTag.java | 16 +-
.../commons/jelly/impl/DynamicTagLibrary.java | 88 +-
.../org/apache/commons/jelly/impl/Embedded.java | 214 +--
.../commons/jelly/impl/ExpressionScript.java | 28 +-
.../org/apache/commons/jelly/impl/ScriptBlock.java | 42 +-
.../org/apache/commons/jelly/impl/StaticTag.java | 60 +-
.../apache/commons/jelly/impl/StaticTagScript.java | 36 +-
.../org/apache/commons/jelly/impl/TagScript.java | 844 +++++-----
.../org/apache/commons/jelly/impl/TextScript.java | 88 +-
.../jelly/parser/DefaultNamespaceFilter.java | 36 +-
.../org/apache/commons/jelly/parser/XMLParser.java | 1698 ++++++++++----------
.../apache/commons/jelly/servlet/JellyServlet.java | 100 +-
.../commons/jelly/servlet/JellyServletContext.java | 16 +-
.../org/apache/commons/jelly/tags/Resources.java | 14 +-
.../org/apache/commons/jelly/tags/core/ArgTag.java | 286 ++--
.../jelly/tags/core/BaseClassLoaderTag.java | 14 +-
.../apache/commons/jelly/tags/core/CaseTag.java | 24 +-
.../apache/commons/jelly/tags/core/CatchTag.java | 112 +-
.../apache/commons/jelly/tags/core/DefaultTag.java | 14 +-
.../apache/commons/jelly/tags/core/FileTag.java | 128 +-
.../apache/commons/jelly/tags/core/ForEachTag.java | 262 +--
.../commons/jelly/tags/core/GetStaticTag.java | 66 +-
.../apache/commons/jelly/tags/core/ImportTag.java | 16 +-
.../apache/commons/jelly/tags/core/IncludeTag.java | 76 +-
.../commons/jelly/tags/core/InvokeStaticTag.java | 90 +-
.../apache/commons/jelly/tags/core/InvokeTag.java | 40 +-
.../apache/commons/jelly/tags/core/MuteTag.java | 30 +-
.../org/apache/commons/jelly/tags/core/NewTag.java | 20 +-
.../apache/commons/jelly/tags/core/ParseTag.java | 116 +-
.../org/apache/commons/jelly/tags/core/SetTag.java | 90 +-
.../apache/commons/jelly/tags/core/SwitchTag.java | 70 +-
.../apache/commons/jelly/tags/core/ThreadTag.java | 22 +-
.../apache/commons/jelly/tags/core/UseBeanTag.java | 168 +-
.../apache/commons/jelly/tags/core/UseListTag.java | 20 +-
.../apache/commons/jelly/test/BaseJellyTest.java | 50 +-
.../commons/jelly/util/ClassLoaderUtils.java | 26 +-
.../commons/jelly/util/CommandLineParser.java | 4 +-
.../commons/jelly/util/NestedRuntimeException.java | 18 +-
.../commons/jelly/util/SafeContentHandler.java | 34 +-
.../org/apache/commons/jelly/util/TagUtils.java | 8 +-
.../commons/jelly/xpath/XPathComparator.java | 48 +-
.../test/java/org/apache/commons/jelly/TJEcho.java | 24 +-
.../org/apache/commons/jelly/TestCoreTags.java | 38 +-
.../commons/jelly/TestDoctypeDefinitionXXE.java | 14 +-
.../java/org/apache/commons/jelly/TestTrim.java | 24 +-
.../org/apache/commons/jelly/TestXMLOutput.java | 20 +-
.../commons/jelly/core/BaseMemoryLeakTest.java | 8 +-
.../org/apache/commons/jelly/core/Customer.java | 54 +-
.../apache/commons/jelly/core/ExceptionBean.java | 8 +-
.../commons/jelly/core/ModularExceptionBean.java | 26 +-
.../java/org/apache/commons/jelly/core/Order.java | 26 +-
.../org/apache/commons/jelly/core/Product.java | 10 +-
.../org/apache/commons/jelly/core/TestArgTag.java | 222 +--
.../apache/commons/jelly/core/TestBreakTag.java | 20 +-
.../apache/commons/jelly/core/TestChooseTag.java | 8 +-
.../org/apache/commons/jelly/core/TestFileTag.java | 34 +-
.../apache/commons/jelly/core/TestForEachTag.java | 8 +-
.../commons/jelly/core/TestGetStaticTag.java | 8 +-
.../apache/commons/jelly/core/TestIncludeTag.java | 92 +-
.../commons/jelly/core/TestInvokeStaticTag.java | 104 +-
.../apache/commons/jelly/core/TestInvokeTag.java | 84 +-
.../commons/jelly/core/TestNamespacePrefixes.java | 8 +-
.../org/apache/commons/jelly/core/TestNewTag.java | 84 +-
.../apache/commons/jelly/core/TestSwitchTag.java | 172 +-
.../jelly/core/TestUnexpectedTagCaching.java | 74 +-
.../apache/commons/jelly/core/TestUnknownTags.java | 8 +-
.../apache/commons/jelly/core/TestUseBeanTag.java | 44 +-
.../expression/TestCustomExpressionFactory.java | 20 +-
.../commons/jelly/expression/TestDynaBeans.java | 40 +-
.../commons/jelly/expression/TestExpressions.java | 60 +-
.../commons/jelly/tags/junit/AssertThrowsTag.java | 58 +-
.../apache/commons/jelly/tags/junit/CaseTag.java | 20 +-
.../tags/junit/JellyAssertionFailedError.java | 94 +-
.../apache/commons/jelly/tags/junit/RunTag.java | 164 +-
.../apache/commons/jelly/tags/junit/SuiteTag.java | 38 +-
.../commons/jelly/test/impl/TestEmbedded.java | 58 +-
.../jelly/test/xml/TestDefaultNamespaceFilter.java | 8 +-
.../commons/jelly/test/xml/TestDummyXMLOutput.java | 8 +-
.../jelly/test/xml/TestNonexistentTags.java | 10 +-
.../commons/jelly/test/xml/TestXMLParserCache.java | 8 +-
.../commons/jelly/test/xml/TestXMLValidation.java | 8 +-
.../org/apache/commons/jelly/tags/ant/AntTag.java | 418 ++---
.../commons/jelly/tags/ant/AntTagLibrary.java | 76 +-
.../jelly/tags/ant/DefaultPropsHandler.java | 132 +-
.../apache/commons/jelly/tags/ant/FileScanner.java | 30 +-
.../commons/jelly/tags/ant/FileScannerTag.java | 20 +-
.../commons/jelly/tags/ant/GrantProject.java | 162 +-
.../commons/jelly/tags/ant/JellyPropsHandler.java | 34 +-
.../commons/jelly/tags/ant/PropsHandler.java | 92 +-
.../commons/jelly/tags/ant/SetPropertyTag.java | 30 +-
.../apache/commons/jelly/task/AntJellyContext.java | 66 +-
.../org/apache/commons/jelly/task/JellyTask.java | 148 +-
.../org/apache/commons/jelly/ant/tag/DummyTag.java | 18 +-
.../apache/commons/jelly/ant/task/DummyTask.java | 38 +-
.../apache/commons/jelly/tags/antlr/AntlrTag.java | 20 +-
.../apache/commons/jelly/avalon/JellyService.java | 22 +-
.../commons/jelly/avalon/JellyServiceImpl.java | 170 +-
.../commons/jelly/tags/bean/BeanPropertyTag.java | 62 +-
.../apache/commons/jelly/tags/bean/BeanTag.java | 48 +-
.../commons/jelly/tags/bean/BeanTagLibrary.java | 86 +-
.../apache/commons/jelly/tags/bean/BeandefTag.java | 50 +-
.../apache/commons/jelly/tags/bean/Customer.java | 52 +-
.../apache/commons/jelly/tags/bean/Manager.java | 34 +-
.../commons/jelly/tags/bean/MyContainerTag.java | 14 +-
.../commons/jelly/tags/bean/MyTagLibrary.java | 34 +-
.../org/apache/commons/jelly/tags/bean/Order.java | 26 +-
.../apache/commons/jelly/tags/bean/Product.java | 10 +-
.../jelly/tags/beanshell/BeanShellExpression.java | 14 +-
.../jelly/tags/beanshell/JellyInterpreter.java | 48 +-
.../jelly/tags/beanshell/TestBeanShellEL.java | 26 +-
.../jelly/tags/betwixt/IntrospectorTag.java | 144 +-
.../commons/jelly/tags/betwixt/ParseTag.java | 58 +-
.../org/apache/commons/digester/rss/Channel.java | 310 ++--
.../org/apache/commons/digester/rss/Image.java | 88 +-
.../java/org/apache/commons/digester/rss/Item.java | 40 +-
.../apache/commons/digester/rss/RSSDigester.java | 256 +--
.../org/apache/commons/digester/rss/TextInput.java | 56 +-
.../commons/jelly/tags/bsf/BSFExpression.java | 14 +-
.../jelly/tags/bsf/BSFExpressionFactory.java | 56 +-
.../commons/jelly/tags/bsf/BSFTagLibrary.java | 16 +-
.../jelly/tags/bsf/JellyContextRegistry.java | 8 +-
.../apache/commons/jelly/tags/bsf/ScriptTag.java | 10 +-
.../commons/jelly/tags/define/AttributeTag.java | 14 +-
.../apache/commons/jelly/tags/define/BeanTag.java | 36 +-
.../commons/jelly/tags/define/ClassLoaderTag.java | 62 +-
.../commons/jelly/tags/define/DynaBeanTag.java | 30 +-
.../commons/jelly/tags/define/ExtendTag.java | 12 +-
.../commons/jelly/tags/define/InvokeTag.java | 16 +-
.../commons/jelly/tags/define/JellyBeanTag.java | 32 +-
.../commons/jelly/tags/define/TaglibTag.java | 22 +-
.../commons/jelly/tags/define/MyRunnable.java | 20 +-
.../commons/jelly/tags/define/TestDynamicTags.java | 28 +-
.../commons/jelly/tags/dynabean/DynabeanTag.java | 8 +-
.../commons/jelly/tags/dynabean/DynaclassTag.java | 20 +-
.../commons/jelly/tags/dynabean/PropertyTag.java | 24 +-
.../apache/commons/jelly/tags/dynabean/SetTag.java | 54 +-
.../apache/commons/jelly/tags/email/EmailTag.java | 124 +-
.../apache/commons/jelly/tags/fmt/BundleTag.java | 200 +--
.../commons/jelly/tags/fmt/FormatDateTag.java | 164 +-
.../jelly/tags/fmt/LocalizationContext.java | 38 +-
.../apache/commons/jelly/tags/fmt/MessageTag.java | 24 +-
.../commons/jelly/tags/fmt/SetBundleTag.java | 8 +-
.../commons/jelly/tags/fmt/SetLocaleTag.java | 330 ++--
.../commons/jelly/tags/fmt/SetTimeZoneTag.java | 8 +-
.../apache/commons/jelly/tags/fmt/TimeZoneTag.java | 90 +-
.../apache/commons/jelly/tags/html/ParseTag.java | 46 +-
.../apache/commons/jelly/tags/http/HeaderTag.java | 16 +-
.../commons/jelly/tags/http/HttpTagSupport.java | 188 +--
.../commons/jelly/tags/http/MultipartPostTag.java | 18 +-
.../commons/jelly/tags/http/ParameterTag.java | 16 +-
.../apache/commons/jelly/tags/http/PartTag.java | 64 +-
.../org/apache/commons/jelly/tags/http/Proxy.java | 20 +-
.../org/apache/commons/jelly/tags/http/PutTag.java | 22 +-
.../apache/commons/jelly/tags/http/SessionTag.java | 126 +-
.../commons/jelly/tags/interaction/AskTag.java | 126 +-
.../commons/jelly/tags/jaxme/GeneratorTag.java | 66 +-
.../commons/jelly/tags/jaxme/MarshallTag.java | 44 +-
.../commons/jelly/tags/jaxme/UnmarshallTag.java | 46 +-
.../jaxme/examples/misc/address/AddressType.java | 14 +-
.../jaxme/examples/misc/address/ObjectFactory.java | 20 +-
.../misc/address/impl/AddressTypeHandler.java | 242 +--
.../misc/address/impl/AddressTypeImpl.java | 30 +-
.../misc/address/impl/AddressTypeSerializer.java | 4 +-
.../jelly/tags/jetty/AbstractMethodHandlerTag.java | 6 +-
.../commons/jelly/tags/jetty/HttpContextTag.java | 84 +-
.../jelly/tags/jetty/JellyResourceHttpHandler.java | 52 +-
.../jelly/tags/jetty/JettyHttpServerTag.java | 82 +-
.../apache/commons/jelly/tags/jetty/RealmTag.java | 32 +-
.../jelly/tags/jetty/ResponseHeaderTag.java | 16 +-
.../jelly/tags/jetty/SecurityHandlerTag.java | 136 +-
.../jelly/tags/jetty/SocketListenerTag.java | 34 +-
.../jelly/jetty/TestJettyHttpServerTags.java | 92 +-
.../apache/commons/jelly/tags/jface/ActionTag.java | 24 +-
.../jelly/tags/jface/DoubleClickListenerTag.java | 34 +-
.../commons/jelly/tags/jface/JFaceImageTag.java | 42 +-
.../jelly/tags/jface/JFaceLayoutDataTag.java | 24 +-
.../commons/jelly/tags/jface/JFaceLayoutTag.java | 24 +-
.../commons/jelly/tags/jface/JFaceTagLibrary.java | 120 +-
.../commons/jelly/tags/jface/MenuManagerTag.java | 30 +-
.../tags/jface/SelectionChangedListenerTag.java | 22 +-
.../apache/commons/jelly/tags/jface/ViewerTag.java | 22 +-
.../tags/jface/window/ApplicationWindowTag.java | 22 +-
.../commons/jelly/tags/jms/ConnectionTag.java | 14 +-
.../commons/jelly/tags/jms/MapMessageTag.java | 22 +-
.../jelly/tags/jms/MessageOperationTag.java | 56 +-
.../apache/commons/jelly/tags/jms/MessageTag.java | 66 +-
.../commons/jelly/tags/jms/ObjectMessageTag.java | 18 +-
.../apache/commons/jelly/tags/jms/ReceiveTag.java | 34 +-
.../commons/jelly/tags/jms/StopwatchTag.java | 24 +-
.../commons/jelly/tags/jms/SubscribeTag.java | 14 +-
.../commons/jelly/tags/jms/TextMessageTag.java | 16 +-
.../commons/jelly/tags/jmx/OperationTag.java | 52 +-
.../apache/commons/jelly/tags/jmx/RegisterTag.java | 32 +-
.../apache/commons/jelly/tags/jmx/ServerTag.java | 22 +-
.../org/apache/commons/jelly/tags/jmx/Dummy.java | 16 +-
.../apache/commons/jelly/tags/jmx/DummyMBean.java | 4 +-
.../commons/jelly/tags/jsl/ApplyTemplatesTag.java | 8 +-
.../apache/commons/jelly/tags/jsl/StyleTag.java | 28 +-
.../commons/jelly/tags/jsl/StylesheetTag.java | 100 +-
.../apache/commons/jelly/tags/jsl/TemplateTag.java | 114 +-
.../jelly/tags/jsl/XPathPatternExpression.java | 14 +-
.../java/org/apache/commons/jelly/jsl/TestJSL.java | 20 +-
.../commons/jelly/tags/junit/AssertTagSupport.java | 305 ++--
.../commons/jelly/tags/junit/AssertThrowsTag.java | 58 +-
.../apache/commons/jelly/tags/junit/CaseTag.java | 20 +-
.../tags/junit/JellyAssertionFailedError.java | 94 +-
.../apache/commons/jelly/tags/junit/RunTag.java | 164 +-
.../apache/commons/jelly/tags/junit/SuiteTag.java | 42 +-
.../commons/jelly/tags/log/LogTagSupport.java | 24 +-
.../org/apache/commons/jelly/tags/log/MockLog.java | 162 +-
.../commons/jelly/tags/memory/FreeMemoryTag.java | 22 +-
.../apache/commons/jelly/tags/ojb/BrokerTag.java | 16 +-
.../apache/commons/jelly/tags/ojb/StoreTag.java | 10 +-
.../commons/jelly/tags/quartz/CronTriggerTag.java | 174 +-
.../apache/commons/jelly/tags/quartz/JobTag.java | 80 +-
.../commons/jelly/tags/regexp/RegexpTag.java | 70 +-
.../commons/jelly/tags/regexp/ContainsTagTest.java | 10 +-
.../commons/jelly/tags/regexp/MatchTagTest.java | 10 +-
.../commons/jelly/tags/soap/InvokeRawTag.java | 12 +-
.../apache/commons/jelly/tags/soap/InvokeTag.java | 90 +-
.../commons/jelly/tags/soap/StringInputStream.java | 44 +-
.../java/javax/servlet/jsp/jstl/sql/Result.java | 28 +-
.../javax/servlet/jsp/jstl/sql/ResultImpl.java | 52 +-
.../commons/jelly/tags/sql/DataSourceWrapper.java | 70 +-
.../commons/jelly/tags/sql/DateParamTag.java | 58 +-
.../apache/commons/jelly/tags/sql/DriverTag.java | 70 +-
.../apache/commons/jelly/tags/sql/ParamTag.java | 14 +-
.../apache/commons/jelly/tags/sql/QueryTag.java | 40 +-
.../apache/commons/jelly/tags/sql/ResultImpl.java | 130 +-
.../commons/jelly/tags/sql/SetDataSourceTag.java | 74 +-
.../commons/jelly/tags/sql/SqlTagSupport.java | 96 +-
.../commons/jelly/tags/sql/TransactionTag.java | 74 +-
.../apache/commons/jelly/tags/swing/ActionTag.java | 42 +-
.../commons/jelly/tags/swing/BorderAlignTag.java | 22 +-
.../commons/jelly/tags/swing/BorderLayoutTag.java | 22 +-
.../commons/jelly/tags/swing/BorderTagSupport.java | 18 +-
.../commons/jelly/tags/swing/ButtonGroupTag.java | 8 +-
.../commons/jelly/tags/swing/CardLayoutTag.java | 14 +-
.../commons/jelly/tags/swing/ComponentTag.java | 332 ++--
.../commons/jelly/tags/swing/ConstraintTag.java | 82 +-
.../apache/commons/jelly/tags/swing/DialogTag.java | 30 +-
.../commons/jelly/tags/swing/EmptyBorderTag.java | 32 +-
.../commons/jelly/tags/swing/EtchedBorderTag.java | 34 +-
.../commons/jelly/tags/swing/FocusListenerTag.java | 48 +-
.../apache/commons/jelly/tags/swing/FontTag.java | 30 +-
.../apache/commons/jelly/tags/swing/GbcTag.java | 30 +-
.../commons/jelly/tags/swing/KeyListenerTag.java | 52 +-
.../commons/jelly/tags/swing/LayoutTagSupport.java | 28 +-
.../commons/jelly/tags/swing/SwingTagLibrary.java | 36 +-
.../commons/jelly/tags/swing/TableLayoutTag.java | 14 +-
.../jelly/tags/swing/TableModelColumnTag.java | 18 +-
.../commons/jelly/tags/swing/TableModelTag.java | 10 +-
.../org/apache/commons/jelly/tags/swing/TdTag.java | 142 +-
.../commons/jelly/tags/swing/TitledBorderTag.java | 156 +-
.../jelly/tags/swing/WindowListenerTag.java | 50 +-
.../tags/swing/impl/GridBagConstraintBean.java | 278 ++--
.../tags/swing/model/ExpressionTableColumn.java | 16 +-
.../tags/swing/model/ExpressionTableModel.java | 104 +-
.../apache/commons/jelly/core/BaseJellyTest.java | 34 +-
.../commons/jelly/demos/HomepageBuilder.java | 46 +-
.../apache/commons/jelly/swing/MyTableModel.java | 34 +-
.../apache/commons/jelly/swing/TestConverters.java | 58 +-
.../apache/commons/jelly/swing/TestSwingTags.java | 156 +-
.../apache/commons/jelly/tags/swt/ColorTag.java | 100 +-
.../apache/commons/jelly/tags/swt/DialogTag.java | 10 +-
.../org/apache/commons/jelly/tags/swt/FontTag.java | 168 +-
.../org/apache/commons/jelly/tags/swt/GCTag.java | 78 +-
.../apache/commons/jelly/tags/swt/ImageTag.java | 122 +-
.../commons/jelly/tags/swt/LayoutDataTag.java | 38 +-
.../apache/commons/jelly/tags/swt/LayoutTag.java | 38 +-
.../commons/jelly/tags/swt/LayoutTagSupport.java | 38 +-
.../apache/commons/jelly/tags/swt/OnEventTag.java | 52 +-
.../apache/commons/jelly/tags/swt/SwtHelper.java | 38 +-
.../commons/jelly/tags/swt/SwtTagLibrary.java | 82 +-
.../apache/commons/jelly/tags/swt/WidgetTag.java | 248 +--
.../jelly/tags/swt/converters/ColorConverter.java | 92 +-
.../jelly/tags/swt/converters/PointConverter.java | 30 +-
.../commons/jelly/tags/threads/GroupTag.java | 10 +-
.../commons/jelly/tags/threads/JellyThread.java | 20 +-
.../apache/commons/jelly/tags/threads/JoinTag.java | 24 +-
.../apache/commons/jelly/tags/threads/Mutex.java | 10 +-
.../commons/jelly/tags/threads/NotifyTag.java | 14 +-
.../commons/jelly/tags/threads/RunnableStatus.java | 130 +-
.../commons/jelly/tags/threads/ThreadTag.java | 66 +-
.../commons/jelly/tags/threads/UseMutexTag.java | 6 +-
.../commons/jelly/tags/threads/UseThreadTag.java | 42 +-
.../commons/jelly/tags/threads/WaitForTag.java | 62 +-
.../apache/commons/jelly/tags/threads/WaitTag.java | 18 +-
.../commons/jelly/tags/util/LoadTextTag.java | 78 +-
.../apache/commons/jelly/tags/util/ReplaceTag.java | 58 +-
.../apache/commons/jelly/tags/util/SortTag.java | 10 +-
.../commons/jelly/tags/util/TokenizeTag.java | 12 +-
.../apache/commons/jelly/tags/util/Customer.java | 22 +-
.../commons/jelly/tags/validate/ValidateTag.java | 84 +-
.../commons/jelly/tags/validate/VerifierTag.java | 58 +-
.../jelly/tags/velocity/JellyContextAdapter.java | 52 +-
.../commons/jelly/tags/velocity/MergeTag.java | 118 +-
.../tags/velocity/JellyContextAdapterTest.java | 162 +-
.../commons/jelly/tags/xml/AttributeTag.java | 12 +-
.../apache/commons/jelly/tags/xml/CopyOfTag.java | 8 +-
.../org/apache/commons/jelly/tags/xml/CopyTag.java | 6 +-
.../apache/commons/jelly/tags/xml/ElementTag.java | 142 +-
.../apache/commons/jelly/tags/xml/ForEachTag.java | 38 +-
.../org/apache/commons/jelly/tags/xml/IfTag.java | 10 +-
.../apache/commons/jelly/tags/xml/ParamTag.java | 12 +-
.../apache/commons/jelly/tags/xml/ParseTag.java | 50 +-
.../commons/jelly/tags/xml/ParseTagSupport.java | 138 +-
.../jelly/tags/xml/ReplaceNamespaceTag.java | 56 +-
.../org/apache/commons/jelly/tags/xml/SetTag.java | 180 +--
.../org/apache/commons/jelly/tags/xml/SortTag.java | 16 +-
.../commons/jelly/tags/xml/TransformTag.java | 936 +++++------
.../apache/commons/jelly/tags/xml/TestParser.java | 30 +-
.../apache/commons/jelly/tags/xml/TestXMLTags.java | 380 ++---
.../commons/jelly/tags/xmlunit/ActualTag.java | 10 +-
.../tags/xmlunit/AssertDocumentsEqualTag.java | 10 +-
.../commons/jelly/tags/xmlunit/ExpectedTag.java | 10 +-
.../jelly/tags/xmlunit/XMLUnitTagSupport.java | 54 +-
340 files changed, 13230 insertions(+), 13233 deletions(-)
diff --git a/core/src/main/java/org/apache/commons/jelly/DynaBeanTagSupport.java b/core/src/main/java/org/apache/commons/jelly/DynaBeanTagSupport.java
index 575ccaf2..db889582 100644
--- a/core/src/main/java/org/apache/commons/jelly/DynaBeanTagSupport.java
+++ b/core/src/main/java/org/apache/commons/jelly/DynaBeanTagSupport.java
@@ -39,20 +39,12 @@ public abstract class DynaBeanTagSupport extends DynaTagSupport {
this.dynaBean = dynaBean;
}
- /** Sets the context in which the tag will be run. */
- @Override
- public void setContext(JellyContext context) throws JellyTagException {
- this.context = context;
- beforeSetAttributes();
- }
-
- /** Sets an attribute value of this tag before the tag is invoked
+ /**
+ * Callback to allow processing to occur before the attributes are about to be set
*/
- @Override
- public void setAttribute(String name, Object value) throws JellyTagException {
- getDynaBean().set(name, value);
+ public void beforeSetAttributes() throws JellyTagException {
}
-
+
/**
* @return the type of the given attribute. By default just return
* Object.class if this is not known.
@@ -65,7 +57,7 @@ public abstract class DynaBeanTagSupport extends DynaTagSupport {
}
return Object.class;
}
-
+
/**
* @return the DynaBean which is used to store the
* attributes of this tag
@@ -74,6 +66,20 @@ public abstract class DynaBeanTagSupport extends DynaTagSupport {
return dynaBean;
}
+ /** Sets an attribute value of this tag before the tag is invoked
+ */
+ @Override
+ public void setAttribute(String name, Object value) throws JellyTagException {
+ getDynaBean().set(name, value);
+ }
+
+ /** Sets the context in which the tag will be run. */
+ @Override
+ public void setContext(JellyContext context) throws JellyTagException {
+ this.context = context;
+ beforeSetAttributes();
+ }
+
/**
* Sets the DynaBean which is used to store the
* attributes of this tag
@@ -81,11 +87,5 @@ public abstract class DynaBeanTagSupport extends DynaTagSupport {
public void setDynaBean(DynaBean dynaBean) {
this.dynaBean = dynaBean;
}
-
- /**
- * Callback to allow processing to occur before the attributes are about to be set
- */
- public void beforeSetAttributes() throws JellyTagException {
- }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/DynaTag.java b/core/src/main/java/org/apache/commons/jelly/DynaTag.java
index 39aa8eac..537e87ea 100644
--- a/core/src/main/java/org/apache/commons/jelly/DynaTag.java
+++ b/core/src/main/java/org/apache/commons/jelly/DynaTag.java
@@ -31,10 +31,6 @@ package org.apache.commons.jelly;
public interface DynaTag extends Tag {
- /** Sets an attribute value of this tag before the tag is invoked
- */
- public void setAttribute(String name, Object value) throws JellyTagException;
-
/**
* @return the type of the given attribute. By default just return
* Object.class if this is not known.
@@ -42,4 +38,8 @@ public interface DynaTag extends Tag {
* be evaluated and just passed in as the attribute value.
*/
public Class getAttributeType(String name) throws JellyTagException;
+
+ /** Sets an attribute value of this tag before the tag is invoked
+ */
+ public void setAttribute(String name, Object value) throws JellyTagException;
}
diff --git a/core/src/main/java/org/apache/commons/jelly/Jelly.java b/core/src/main/java/org/apache/commons/jelly/Jelly.java
index ed3abc39..6b0846a3 100644
--- a/core/src/main/java/org/apache/commons/jelly/Jelly.java
+++ b/core/src/main/java/org/apache/commons/jelly/Jelly.java
@@ -47,31 +47,14 @@ public class Jelly {
/** The Log to which logging calls will be made. */
private static final Log log = LogFactory.getLog(Jelly.class);
- /** The JellyContext to use */
- private JellyContext context;
-
- /** The URL of the script to execute */
- private URL url;
-
- /** The URL of the root context for other scripts */
- private URL rootContext;
+ public static String getJellyBuildDate() {
+ return readBuildTimestampResource("jelly-build-date.txt");
+ }
- /** Whether we have loaded the properties yet */
- private boolean loadedProperties = false;
-
- /**
- * whether to override the default namespace
- */
- private String defaultNamespaceURI = null;
-
- /**
- * whether or not to validate the Jelly script
- */
- private boolean validateXML = false;
-
- public Jelly() {
+ public static String getJellyVersion() {
+ return readBuildTimestampResource("jelly-version.txt");
}
-
+
/**
* Usage: jelly [scriptFile] [-script scriptFile -o outputFile -Dsysprop=syspropval]
*/
@@ -99,15 +82,6 @@ public class Jelly {
}
}
-
- public static String getJellyVersion() {
- return readBuildTimestampResource("jelly-version.txt");
- }
-
- public static String getJellyBuildDate() {
- return readBuildTimestampResource("jelly-build-date.txt");
- }
-
private static String readBuildTimestampResource(String name) {
java.io.Reader in = null;
try {
@@ -127,6 +101,32 @@ public class Jelly {
throw new IllegalStateException("Resource \"" + name + "\" not found.");
}
}
+
+ /** The JellyContext to use */
+ private JellyContext context;
+
+ /** The URL of the script to execute */
+ private URL url;
+
+ /** The URL of the root context for other scripts */
+ private URL rootContext;
+
+ /** Whether we have loaded the properties yet */
+ private boolean loadedProperties = false;
+
+
+ /**
+ * whether to override the default namespace
+ */
+ private String defaultNamespaceURI = null;
+
+ /**
+ * whether or not to validate the Jelly script
+ */
+ private boolean validateXML = false;
+
+ public Jelly() {
+ }
@@ -168,41 +168,6 @@ public class Jelly {
// Properties
//-------------------------------------------------------------------------
- /**
- * Sets the script URL to use as an absolute URL or a relative file name
- */
- public void setScript(String script) throws MalformedURLException {
- setUrl(resolveURL(script));
- }
-
- public URL getUrl() {
- return url;
- }
-
- /**
- * Sets the script URL to use
- */
- public void setUrl(URL url) {
- this.url = url;
- }
-
- /**
- * Gets the root context
- */
- public URL getRootContext() throws MalformedURLException {
- if (rootContext == null) {
- rootContext = new File(System.getProperty("user.dir")).toURL();
- }
- return rootContext;
- }
-
- /**
- * Sets the root context
- */
- public void setRootContext(URL rootContext) {
- this.rootContext = rootContext;
- }
-
/**
* The context to use
*/
@@ -217,56 +182,20 @@ public class Jelly {
return context;
}
- /**
- * Allows the Jelly context to be explicitly set; note that it is the caller's
- * responsibility to make sure that the URLs etc are properly configured
- * @param context
- */
- public void setJellyContext(JellyContext context) {
- this.context = context;
- }
-
- /**
- * Sets the jelly namespace to use for unprefixed elements.
- * Will be overridden by an explicit namespace in the
- * XML document.
- *
- * @param namespace jelly namespace to use (e.g. 'jelly:core')
- */
- public void setDefaultNamespaceURI(String namespace) {
- this.defaultNamespaceURI = namespace;
- }
-
- /**
- * When set to true, the XML parser will attempt to validate
- * the Jelly XML before converting it into a Script.
- *
- * @param validate whether or not to validate
+ /**
+ * Gets the root context
*/
- public void setValidateXML(boolean validate) {
- this.validateXML = validate;
+ public URL getRootContext() throws MalformedURLException {
+ if (rootContext == null) {
+ rootContext = new File(System.getProperty("user.dir")).toURL();
+ }
+ return rootContext;
}
- // Implementation methods
- //-------------------------------------------------------------------------
- /**
- * @return the URL for the relative file name or absolute URL
- */
- protected URL resolveURL(String name) throws MalformedURLException {
-
- URL resourceUrl = ClassLoaderUtils.getClassLoader(getClass()).getResource(name);
- if (resourceUrl == null)
- {
- File file = new File(name);
- if (file.exists()) {
- return file.toURL();
- }
- return new URL(name);
- } else {
- return resourceUrl;
- }
+ public URL getUrl() {
+ return url;
}
-
+
/**
* Attempts to load jelly.properties from the current directory,
* the users home directory or from the classpath
@@ -292,7 +221,7 @@ public class Jelly {
}
}
}
-
+
/**
* Load properties from a file into the context
* @param f
@@ -316,7 +245,7 @@ public class Jelly {
}
}
}
-
+
/**
* Loads the properties from the given input stream
*/
@@ -333,4 +262,75 @@ public class Jelly {
theContext.setVariable(key, value);
}
}
+
+ // Implementation methods
+ //-------------------------------------------------------------------------
+ /**
+ * @return the URL for the relative file name or absolute URL
+ */
+ protected URL resolveURL(String name) throws MalformedURLException {
+
+ URL resourceUrl = ClassLoaderUtils.getClassLoader(getClass()).getResource(name);
+ if (resourceUrl == null)
+ {
+ File file = new File(name);
+ if (file.exists()) {
+ return file.toURL();
+ }
+ return new URL(name);
+ } else {
+ return resourceUrl;
+ }
+ }
+
+ /**
+ * Sets the jelly namespace to use for unprefixed elements.
+ * Will be overridden by an explicit namespace in the
+ * XML document.
+ *
+ * @param namespace jelly namespace to use (e.g. 'jelly:core')
+ */
+ public void setDefaultNamespaceURI(String namespace) {
+ this.defaultNamespaceURI = namespace;
+ }
+
+ /**
+ * Allows the Jelly context to be explicitly set; note that it is the caller's
+ * responsibility to make sure that the URLs etc are properly configured
+ * @param context
+ */
+ public void setJellyContext(JellyContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Sets the root context
+ */
+ public void setRootContext(URL rootContext) {
+ this.rootContext = rootContext;
+ }
+
+ /**
+ * Sets the script URL to use as an absolute URL or a relative file name
+ */
+ public void setScript(String script) throws MalformedURLException {
+ setUrl(resolveURL(script));
+ }
+
+ /**
+ * Sets the script URL to use
+ */
+ public void setUrl(URL url) {
+ this.url = url;
+ }
+
+ /**
+ * When set to true, the XML parser will attempt to validate
+ * the Jelly XML before converting it into a Script.
+ *
+ * @param validate whether or not to validate
+ */
+ public void setValidateXML(boolean validate) {
+ this.validateXML = validate;
+ }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/JellyContext.java b/core/src/main/java/org/apache/commons/jelly/JellyContext.java
index 704ab54d..5a308e3e 100644
--- a/core/src/main/java/org/apache/commons/jelly/JellyContext.java
+++ b/core/src/main/java/org/apache/commons/jelly/JellyContext.java
@@ -112,25 +112,6 @@ public class JellyContext {
init();
}
- /**
- * Create a new context with the given rootURL
- * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
- */
- public JellyContext(URL rootURL) {
- this( rootURL, rootURL );
- }
-
- /**
- * Create a new context with the given rootURL and currentURL
- * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
- * @param currentURL the root URL used in resolving relative resources
- */
- public JellyContext(URL rootURL, URL currentURL) {
- this.rootURL = rootURL;
- this.currentURL = currentURL;
- init();
- }
-
/**
* Create a new context with the given parent context.
* The parent's rootURL and currentURL are set on the child, and the parent's variables are
@@ -175,36 +156,138 @@ public class JellyContext {
}
/**
- * Initialize the context.
- * This includes adding the context to itself under the name <code>context</code> and
- * making the System Properties available as <code>systemScope</code>
+ * Create a new context with the given rootURL
+ * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
*/
- private void init() {
- variables.put("context", this);
+ public JellyContext(URL rootURL) {
+ this( rootURL, rootURL );
+ }
+
+ /**
+ * Create a new context with the given rootURL and currentURL
+ * @param rootURL the root URL used in resolving absolute resources i.e. those starting with '/'
+ * @param currentURL the root URL used in resolving relative resources
+ */
+ public JellyContext(URL rootURL, URL currentURL) {
+ this.rootURL = rootURL;
+ this.currentURL = currentURL;
+ init();
+ }
+
+ /** Clears variables set by Tags.
+ * @see #clearVariables()
+ */
+ public void clear() {
+ clearVariables();
+ }
+
+ /** Clears variables set by Tags (variables set while running a Jelly script)
+ * @see #clear()
+ */
+ protected void clearVariables() {
+ variables.clear();
+ }
+
+ /**
+ * Attempts to parse the script from the given InputSource using the
+ * {@link #getResource} method then returns the compiled script.
+ */
+ public Script compileScript(InputSource source) throws JellyException {
+ XMLParser parser = getXMLParser();
+ parser.setContext(this);
+
+ Script script = null;
try {
- variables.put("systemScope", System.getProperties());
- } catch (SecurityException e) {
- log.debug("security exception accessing system properties", e);
+ script = parser.parse(source);
+ } catch (IOException e) {
+ throw new JellyException(JellyContext.BAD_PARSE, e);
+ } catch (SAXException e) {
+ throw new JellyException(JellyContext.BAD_PARSE, e);
}
+
+ return script.compile();
}
/**
- * @return the parent context for this context
+ * Attempts to parse the script from the given uri using the
+ * {@link #getResource} method then returns the compiled script.
*/
- public JellyContext getParent() {
- return parent;
+ public Script compileScript(String uri) throws JellyException {
+ XMLParser parser = getXMLParser();
+ parser.setContext(this);
+ InputStream in = getResourceAsStream(uri);
+ if (in == null) {
+ throw new JellyException("Could not find Jelly script: " + uri);
+ }
+ Script script = null;
+ try {
+ script = parser.parse(in);
+ } catch (IOException e) {
+ throw new JellyException(JellyContext.BAD_PARSE, e);
+ } catch (SAXException e) {
+ throw new JellyException(JellyContext.BAD_PARSE, e);
+ }
+
+ return script.compile();
}
/**
- * @return the scope of the given name, such as the 'parent' scope.
- * If Jelly is used in a Servlet situation then 'request', 'session' and 'application' are other names
- * for scopes
+ * Attempts to parse the script from the given URL using the
+ * {@link #getResource} method then returns the compiled script.
*/
- public JellyContext getScope(String name) {
- if ( "parent".equals( name ) ) {
- return getParent();
+ public Script compileScript(URL url) throws JellyException {
+ XMLParser parser = getXMLParser();
+ parser.setContext(this);
+
+ Script script = null;
+ try {
+ script = parser.parse(url.toString());
+ } catch (IOException e) {
+ throw new JellyException(JellyContext.BAD_PARSE, e);
+ } catch (SAXException e) {
+ throw new JellyException(JellyContext.BAD_PARSE, e);
}
- return null;
+
+ return script.compile();
+ }
+
+ /**
+ * Factory method to create a new child of this context
+ */
+ protected JellyContext createChildContext() {
+ return new JellyContext(this);
+ }
+
+ // Implementation methods
+ //-------------------------------------------------------------------------
+ /**
+ * @return a new relative URL from the given root and with the addition of the
+ * extra relative URI
+ *
+ * @param rootURL is the root context from which the relative URI will be applied
+ * @param relativeURI is the relative URI (without a leading "/")
+ * @throws MalformedURLException if the URL is invalid.
+ */
+ protected URL createRelativeURL(URL rootURL, String relativeURI)
+ throws MalformedURLException {
+ URL url = rootURL;
+ if (url == null) {
+ File file = new File(System.getProperty("user.dir"));
+ url = file.toURL();
+ }
+ String urlText = url.toString() + relativeURI;
+ if ( log.isDebugEnabled() ) {
+ log.debug("Attempting to open url: " + urlText);
+ }
+ return new URL(urlText);
+ }
+
+ /**
+ * Factory method to allow JellyContext implementations to overload how an XMLParser
+ * is created - such as to overload what the default ExpressionFactory should be.
+ */
+ protected XMLParser createXMLParser() {
+ return new XMLParser(allowDtdToCallExternalEntities);
}
/**
@@ -235,214 +318,149 @@ public class JellyContext {
return answer;
}
- /** @return the value of the given variable name */
- public Object getVariable(String name) {
- Object value = variables.get(name);
- boolean definedHere = value != null || variables.containsKey(name);
-
- if (definedHere) return value;
-
- if ( value == null && isInherit() ) {
- JellyContext parentContext = getParent();
- if (parentContext != null) {
- value = parentContext.getVariable( name );
- }
- }
-
- // ### this is a hack - remove this when we have support for pluggable Scopes
- if ( value == null ) {
- value = getSystemProperty(name);
- }
-
- return value;
- }
-
/**
- * Gets a system property and handle security exceptions
- * @param name the name of the property to retrieve
- * @return the value of the property, or null if a SecurityException occurs
+ * Gets the class loader to be used for instantiating application objects
+ * when required. This is determined based upon the following rules:
+ * <ul>
+ * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
+ * <li>The thread context class loader, if it exists and the
+ * <code>useContextClassLoader</code> property is set to true</li>
+ * <li>The class loader used to load the XMLParser class itself.
+ * </ul>
*/
- private Object getSystemProperty(String name) {
- try {
- return System.getProperty(name);
- }
- catch (SecurityException e) {
- log.debug("security exception accessing system properties", e);
- }
- return null;
+ public ClassLoader getClassLoader() {
+ return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass());
}
/**
- * @return the value of the given variable name in the given variable scope
- * @param name is the name of the variable
- * @param scopeName is the optional scope name such as 'parent'. For servlet environments
- * this could be 'application', 'session' or 'request'.
+ * @return the current URL context of the current script that is executing.
+ * This URL context is used to deduce relative scripts when relative URIs are
+ * used in calls to {@link #getResource} to process relative scripts.
*/
- public Object getVariable(String name, String scopeName) {
- JellyContext scope = getScope(scopeName);
- if ( scope != null ) {
- return scope.getVariable(name);
- }
- return null;
+ public URL getCurrentURL() {
+ return currentURL;
}
- /** Sets the value of the named variable */
- public void setVariable(String name, Object value) {
- if ( isExport() ) {
- getParent().setVariable( name, value );
- return;
- }
- if (value == null) {
- variables.remove(name);
- }
- else {
- variables.put(name, value);
+ /**
+ * Strips off the name of a script to create a new context URL
+ */
+ protected URL getJellyContextURL(InputSource source) throws MalformedURLException {
+ String text = source.getSystemId();
+ if (text != null) {
+ int idx = text.lastIndexOf('/');
+ text = text.substring(0, idx + 1);
+ return new URL(text);
+ } else {
+ return null;
}
+
}
/**
- * Sets the value of the given variable name in the given variable scope
- * @param name is the name of the variable
- * @param scopeName is the optional scope name such as 'parent'. For servlet environments
- * this could be 'application', 'session' or 'request'.
- * @param value is the value of the attribute
+ * Strips off the name of a script to create a new context URL
*/
- public void setVariable(String name, String scopeName, Object value) {
- JellyContext scope = getScope(scopeName);
- if ( scope != null ) {
- scope.setVariable(name, value);
- }
- }
-
- /** Removes the given variable */
- public void removeVariable(String name) {
- variables.remove(name);
- }
-
- /**
- * Removes the given variable in the specified scope.
- *
- * @param name is the name of the variable
- * @param scopeName is the optional scope name such as 'parent'. For servlet environments
- * this could be 'application', 'session' or 'request'.
- */
- public void removeVariable(String name, String scopeName) {
- JellyContext scope = getScope(scopeName);
- if ( scope != null ) {
- scope.removeVariable(name);
- }
- }
-
- /**
- * @return an Iterator over the current variable names in this
- * context
- */
- public Iterator getVariableNames() {
- return variables.keySet().iterator();
+ protected URL getJellyContextURL(URL url) throws MalformedURLException {
+ String text = url.toString();
+ int idx = text.lastIndexOf('/');
+ text = text.substring(0, idx + 1);
+ return new URL(text);
}
/**
- * @return the Map of variables in this scope
+ * @return the parent context for this context
*/
- public Map getVariables() {
- return variables;
+ public JellyContext getParent() {
+ return parent;
}
/**
- * Sets the Map of variables to use
+ * Returns a URL for the given resource from the specified path.
+ * If the uri starts with "/" then the path is taken as relative to
+ * the current context root.
+ * If the uri is a well formed URL then it is used.
+ * If the uri is a file that exists and can be read then it is used.
+ * Otherwise the uri is interpreted as relative to the current context (the
+ * location of the current script).
*/
- public void setVariables(Map variables) {
- // I have seen this fail when the passed Map contains a key, value
- // pair where the value is null
- for (Iterator iter = variables.entrySet().iterator(); iter.hasNext();) {
- Map.Entry element = (Map.Entry) iter.next();
- if (element.getValue() != null) {
- this.variables.put(element.getKey(), element.getValue());
+ public URL getResource(String uri) throws MalformedURLException {
+ if (uri.startsWith("/")) {
+ // append this uri to the context root
+ return createRelativeURL(rootURL, uri.substring(1));
+ }
+ else {
+ try {
+ return new URL(uri);
+ }
+ catch (MalformedURLException e) {
+ // lets try find a relative resource
+ try {
+ return createRelativeURL(currentURL, uri);
+ } catch (MalformedURLException e2) {
+ throw e;
+ }
}
}
- //this.variables.putAll( variables );
- }
-
- /**
- * A factory method to create a new child context of the
- * current context.
- */
- public JellyContext newJellyContext(Map newVariables) {
- // XXXX: should allow this new context to
- // XXXX: inherit parent contexts?
- // XXXX: Or at least publish the parent scope
- // XXXX: as a Map in this new variable scope?
- newVariables.put("parentScope", variables);
- JellyContext answer = createChildContext();
- answer.setVariables(newVariables);
- return answer;
}
/**
- * A factory method to create a new child context of the
- * current context.
+ * Attempts to open an InputStream to the given resource at the specified path.
+ * If the uri starts with "/" then the path is taken as relative to
+ * the current context root. If the uri is a well formed URL then it
+ * is used. Otherwise the uri is interpreted as relative to the current
+ * context (the location of the current script).
+ *
+ * @return null if this resource could not be loaded, otherwise the resources
+ * input stream is returned.
*/
- public JellyContext newJellyContext() {
- return createChildContext();
- }
-
- /** Clears variables set by Tags.
- * @see #clearVariables()
- */
- public void clear() {
- clearVariables();
+ public InputStream getResourceAsStream(String uri) {
+ try {
+ URL url = getResource(uri);
+ return url.openStream();
+ }
+ catch (Exception e) {
+ if (log.isTraceEnabled()) {
+ log.trace(
+ "Caught exception attempting to open: " + uri + ". Exception: " + e,
+ e);
+ }
+ return null;
+ }
}
- /** Clears variables set by Tags (variables set while running a Jelly script)
- * @see #clear()
+ /**
+ * @return the current root context URL from which all absolute resource URIs
+ * will be relative to. For example in a web application the root URL will
+ * map to the web directory which contains the WEB-INF directory.
*/
- protected void clearVariables() {
- variables.clear();
+ public URL getRootURL() {
+ return rootURL;
}
- /** Registers the given tag library against the given namespace URI.
- * This should be called before the parser is used.
+ /**
+ * @return the scope of the given name, such as the 'parent' scope.
+ * If Jelly is used in a Servlet situation then 'request', 'session' and 'application' are other names
+ * for scopes
*/
- public void registerTagLibrary(String namespaceURI, TagLibrary taglib) {
- if (log.isDebugEnabled()) {
- log.debug("Registering tag library to: " + namespaceURI + " taglib: " + taglib);
- }
- taglibs.put(namespaceURI, taglib);
-
- if (isExportLibraries() && parent != null) {
- parent.registerTagLibrary( namespaceURI, taglib );
+ public JellyContext getScope(String name) {
+ if ( "parent".equals( name ) ) {
+ return getParent();
}
+ return null;
}
-
- /** Registers the given tag library class name against the given namespace URI.
- * The class will be loaded via the given ClassLoader
- * This should be called before the parser is used.
+
+ /**
+ * Gets a system property and handle security exceptions
+ * @param name the name of the property to retrieve
+ * @return the value of the property, or null if a SecurityException occurs
*/
- public void registerTagLibrary(
- String namespaceURI,
- String className) {
-
- if (log.isDebugEnabled()) {
- log.debug("Registering tag library to: " + namespaceURI + " taglib: " + className);
- }
- taglibs.put(namespaceURI, className);
-
- if (isExportLibraries() && parent != null) {
- parent.registerTagLibrary( namespaceURI, className );
- }
- }
-
- public boolean isTagLibraryRegistered(String namespaceURI) {
- boolean answer = taglibs.containsKey( namespaceURI );
- if (answer) {
- return true;
- }
- else if ( parent != null ) {
- return parent.isTagLibraryRegistered(namespaceURI);
+ private Object getSystemProperty(String name) {
+ try {
+ return System.getProperty(name);
}
- else {
- return false;
+ catch (SecurityException e) {
+ log.debug("security exception accessing system properties", e);
}
+ return null;
}
/**
@@ -498,67 +516,62 @@ public class JellyContext {
}
/**
- * Attempts to parse the script from the given uri using the
- * {@link #getResource} method then returns the compiled script.
+ * Gets the boolean as to whether the context classloader should be used.
*/
- public Script compileScript(String uri) throws JellyException {
- XMLParser parser = getXMLParser();
- parser.setContext(this);
- InputStream in = getResourceAsStream(uri);
- if (in == null) {
- throw new JellyException("Could not find Jelly script: " + uri);
+ public boolean getUseContextClassLoader() {
+ return useContextClassLoader;
+ }
+
+ /** @return the value of the given variable name */
+ public Object getVariable(String name) {
+ Object value = variables.get(name);
+ boolean definedHere = value != null || variables.containsKey(name);
+
+ if (definedHere) return value;
+
+ if ( value == null && isInherit() ) {
+ JellyContext parentContext = getParent();
+ if (parentContext != null) {
+ value = parentContext.getVariable( name );
+ }
}
- Script script = null;
- try {
- script = parser.parse(in);
- } catch (IOException e) {
- throw new JellyException(JellyContext.BAD_PARSE, e);
- } catch (SAXException e) {
- throw new JellyException(JellyContext.BAD_PARSE, e);
+
+ // ### this is a hack - remove this when we have support for pluggable Scopes
+ if ( value == null ) {
+ value = getSystemProperty(name);
}
- return script.compile();
+ return value;
}
/**
- * Attempts to parse the script from the given URL using the
- * {@link #getResource} method then returns the compiled script.
+ * @return the value of the given variable name in the given variable scope
+ * @param name is the name of the variable
+ * @param scopeName is the optional scope name such as 'parent'. For servlet environments
+ * this could be 'application', 'session' or 'request'.
*/
- public Script compileScript(URL url) throws JellyException {
- XMLParser parser = getXMLParser();
- parser.setContext(this);
-
- Script script = null;
- try {
- script = parser.parse(url.toString());
- } catch (IOException e) {
- throw new JellyException(JellyContext.BAD_PARSE, e);
- } catch (SAXException e) {
- throw new JellyException(JellyContext.BAD_PARSE, e);
+ public Object getVariable(String name, String scopeName) {
+ JellyContext scope = getScope(scopeName);
+ if ( scope != null ) {
+ return scope.getVariable(name);
}
+ return null;
+ }
- return script.compile();
+ /**
+ * @return an Iterator over the current variable names in this
+ * context
+ */
+ public Iterator getVariableNames() {
+ return variables.keySet().iterator();
}
/**
- * Attempts to parse the script from the given InputSource using the
- * {@link #getResource} method then returns the compiled script.
+ * @return the Map of variables in this scope
*/
- public Script compileScript(InputSource source) throws JellyException {
- XMLParser parser = getXMLParser();
- parser.setContext(this);
-
- Script script = null;
- try {
- script = parser.parse(source);
- } catch (IOException e) {
- throw new JellyException(JellyContext.BAD_PARSE, e);
- } catch (SAXException e) {
- throw new JellyException(JellyContext.BAD_PARSE, e);
- }
-
- return script.compile();
- }
+ public Map getVariables() {
+ return variables;
+ }
/**
* @return a thread pooled XMLParser to avoid the startup overhead
@@ -570,88 +583,170 @@ public class JellyContext {
}
/**
- * Factory method to allow JellyContext implementations to overload how an XMLParser
- * is created - such as to overload what the default ExpressionFactory should be.
+ * Initialize the context.
+ * This includes adding the context to itself under the name <code>context</code> and
+ * making the System Properties available as <code>systemScope</code>
*/
- protected XMLParser createXMLParser() {
- return new XMLParser(allowDtdToCallExternalEntities);
+ private void init() {
+ variables.put("context", this);
+ try {
+ variables.put("systemScope", System.getProperties());
+ } catch (SecurityException e) {
+ log.debug("security exception accessing system properties", e);
+ }
}
/**
- * Parses the script from the given File then compiles it and runs it.
- *
- * @return the new child context that was used to run the script
+ * @return whether we should allow our doctype definitions to call out to external entities.
*/
- public JellyContext runScript(File file, XMLOutput output) throws JellyException {
- try {
- return runScript(file.toURL(), output, JellyContext.DEFAULT_EXPORT,
- JellyContext.DEFAULT_INHERIT);
- } catch (MalformedURLException e) {
- throw new JellyException(e.toString());
- }
+ public boolean isAllowDtdToCallExternalEntities() {
+ return this.allowDtdToCallExternalEntities;
}
/**
- * Parses the script from the given URL then compiles it and runs it.
+ * Returns whether caching of Tag instances, per thread, is enabled.
+ * Caching Tags can boost performance, on some JVMs, by reducing the cost of
+ * object construction when running Jelly inside a multi-threaded application server
+ * such as a Servlet engine.
*
- * @return the new child context that was used to run the script
+ * @return whether caching of Tag instances is enabled.
*/
- public JellyContext runScript(URL url, XMLOutput output) throws JellyException {
- return runScript(url, output, JellyContext.DEFAULT_EXPORT,
- JellyContext.DEFAULT_INHERIT);
+ public boolean isCacheTags() {
+ return cacheTags;
}
/**
- * Parses the script from the given InputSource then compiles it and runs it.
- *
- * @return the new child context that was used to run the script
+ * @return whether we should export variable definitions to our parent context
*/
- public JellyContext runScript(InputSource source, XMLOutput output) throws JellyException {
- return runScript(source, output, JellyContext.DEFAULT_EXPORT,
- JellyContext.DEFAULT_INHERIT);
+ public boolean isExport() {
+ return this.export;
}
/**
- * Parses the script from the given uri using the
- * JellyContext.getResource() API then compiles it and runs it.
- *
- * @return the new child context that was used to run the script
+ * Returns whether we export tag libraries to our parents context
+ * @return boolean
*/
- public JellyContext runScript(String uri, XMLOutput output) throws JellyException {
- URL url = null;
- try {
- url = getResource(uri);
- } catch (MalformedURLException e) {
- throw new JellyException(e.toString());
+ public boolean isExportLibraries() {
+ return exportLibraries;
+ }
+
+ /**
+ * @return whether we should inherit variables from our parent context
+ */
+ public boolean isInherit() {
+ return this.inherit;
+ }
+
+ /**
+ * @return the suppressExpressionExceptions
+ * @deprecated after v1.1, exceptions will never be suppressed
+ */
+ public boolean isSuppressExpressionExceptions() {
+ return suppressExpressionExceptions;
+ }
+
+ public boolean isTagLibraryRegistered(String namespaceURI) {
+ boolean answer = taglibs.containsKey( namespaceURI );
+ if (answer) {
+ return true;
+ }
+ else if ( parent != null ) {
+ return parent.isTagLibraryRegistered(namespaceURI);
+ }
+ else {
+ return false;
}
+ }
- if (url == null) {
- throw new JellyException("Could not find Jelly script: " + url);
+ /**
+ * A factory method to create a new child context of the
+ * current context.
+ */
+ public JellyContext newJellyContext() {
+ return createChildContext();
+ }
+
+ /**
+ * A factory method to create a new child context of the
+ * current context.
+ */
+ public JellyContext newJellyContext(Map newVariables) {
+ // XXXX: should allow this new context to
+ // XXXX: inherit parent contexts?
+ // XXXX: Or at least publish the parent scope
+ // XXXX: as a Map in this new variable scope?
+ newVariables.put("parentScope", variables);
+ JellyContext answer = createChildContext();
+ answer.setVariables(newVariables);
+ return answer;
+ }
+
+ /** Registers the given tag library class name against the given namespace URI.
+ * The class will be loaded via the given ClassLoader
+ * This should be called before the parser is used.
+ */
+ public void registerTagLibrary(
+ String namespaceURI,
+ String className) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Registering tag library to: " + namespaceURI + " taglib: " + className);
}
- return runScript(url, output, JellyContext.DEFAULT_EXPORT,
- JellyContext.DEFAULT_INHERIT);
+ taglibs.put(namespaceURI, className);
+
+ if (isExportLibraries() && parent != null) {
+ parent.registerTagLibrary( namespaceURI, className );
+ }
+ }
+
+ // Properties
+ //-------------------------------------------------------------------------
+
+ /** Registers the given tag library against the given namespace URI.
+ * This should be called before the parser is used.
+ */
+ public void registerTagLibrary(String namespaceURI, TagLibrary taglib) {
+ if (log.isDebugEnabled()) {
+ log.debug("Registering tag library to: " + namespaceURI + " taglib: " + taglib);
+ }
+ taglibs.put(namespaceURI, taglib);
+
+ if (isExportLibraries() && parent != null) {
+ parent.registerTagLibrary( namespaceURI, taglib );
+ }
+ }
+
+ /** Removes the given variable */
+ public void removeVariable(String name) {
+ variables.remove(name);
}
/**
- * Parses the script from the given uri using the
- * JellyContext.getResource() API then compiles it and runs it.
+ * Removes the given variable in the specified scope.
+ *
+ * @param name is the name of the variable
+ * @param scopeName is the optional scope name such as 'parent'. For servlet environments
+ * this could be 'application', 'session' or 'request'.
+ */
+ public void removeVariable(String name, String scopeName) {
+ JellyContext scope = getScope(scopeName);
+ if ( scope != null ) {
+ scope.removeVariable(name);
+ }
+ }
+
+ /**
+ * Parses the script from the given File then compiles it and runs it.
*
* @return the new child context that was used to run the script
*/
- public JellyContext runScript(String uri, XMLOutput output,
- boolean export, boolean inherit) throws JellyException {
- URL url = null;
+ public JellyContext runScript(File file, XMLOutput output) throws JellyException {
try {
- url = getResource(uri);
+ return runScript(file.toURL(), output, JellyContext.DEFAULT_EXPORT,
+ JellyContext.DEFAULT_INHERIT);
} catch (MalformedURLException e) {
throw new JellyException(e.toString());
}
-
- if (url == null) {
- throw new JellyException("Could not find Jelly script: " + url);
- }
-
- return runScript(url, output, export, inherit);
}
/**
@@ -669,13 +764,13 @@ public class JellyContext {
}
/**
- * Parses the script from the given URL then compiles it and runs it.
+ * Parses the script from the given InputSource then compiles it and runs it.
*
* @return the new child context that was used to run the script
*/
- public JellyContext runScript(URL url, XMLOutput output,
- boolean export, boolean inherit) throws JellyException {
- return runScript(new InputSource(url.toString()), output, export, inherit);
+ public JellyContext runScript(InputSource source, XMLOutput output) throws JellyException {
+ return runScript(source, output, JellyContext.DEFAULT_EXPORT,
+ JellyContext.DEFAULT_INHERIT);
}
/**
@@ -710,171 +805,73 @@ public class JellyContext {
log.debug( "root context URL: " + newJellyContext.rootURL );
log.debug( "current context URL: " + newJellyContext.currentURL );
}
-
- script.run(newJellyContext, output);
-
- return newJellyContext;
- }
-
- /**
- * Returns a URL for the given resource from the specified path.
- * If the uri starts with "/" then the path is taken as relative to
- * the current context root.
- * If the uri is a well formed URL then it is used.
- * If the uri is a file that exists and can be read then it is used.
- * Otherwise the uri is interpreted as relative to the current context (the
- * location of the current script).
- */
- public URL getResource(String uri) throws MalformedURLException {
- if (uri.startsWith("/")) {
- // append this uri to the context root
- return createRelativeURL(rootURL, uri.substring(1));
- }
- else {
- try {
- return new URL(uri);
- }
- catch (MalformedURLException e) {
- // lets try find a relative resource
- try {
- return createRelativeURL(currentURL, uri);
- } catch (MalformedURLException e2) {
- throw e;
- }
- }
- }
- }
-
- /**
- * Attempts to open an InputStream to the given resource at the specified path.
- * If the uri starts with "/" then the path is taken as relative to
- * the current context root. If the uri is a well formed URL then it
- * is used. Otherwise the uri is interpreted as relative to the current
- * context (the location of the current script).
- *
- * @return null if this resource could not be loaded, otherwise the resources
- * input stream is returned.
- */
- public InputStream getResourceAsStream(String uri) {
- try {
- URL url = getResource(uri);
- return url.openStream();
- }
- catch (Exception e) {
- if (log.isTraceEnabled()) {
- log.trace(
- "Caught exception attempting to open: " + uri + ". Exception: " + e,
- e);
- }
- return null;
- }
- }
-
- // Properties
- //-------------------------------------------------------------------------
-
- /**
- * @return the current root context URL from which all absolute resource URIs
- * will be relative to. For example in a web application the root URL will
- * map to the web directory which contains the WEB-INF directory.
- */
- public URL getRootURL() {
- return rootURL;
- }
-
- /**
- * Sets the current root context URL from which all absolute resource URIs
- * will be relative to. For example in a web application the root URL will
- * map to the web directory which contains the WEB-INF directory.
- */
- public void setRootURL(URL rootURL) {
- this.rootURL = rootURL;
- }
-
- /**
- * @return the current URL context of the current script that is executing.
- * This URL context is used to deduce relative scripts when relative URIs are
- * used in calls to {@link #getResource} to process relative scripts.
- */
- public URL getCurrentURL() {
- return currentURL;
- }
-
- /**
- * Sets the current URL context of the current script that is executing.
- * This URL context is used to deduce relative scripts when relative URIs are
- * used in calls to {@link #getResource} to process relative scripts.
- */
- public void setCurrentURL(URL currentURL) {
- this.currentURL = currentURL;
- }
-
- /**
- * Returns whether caching of Tag instances, per thread, is enabled.
- * Caching Tags can boost performance, on some JVMs, by reducing the cost of
- * object construction when running Jelly inside a multi-threaded application server
- * such as a Servlet engine.
- *
- * @return whether caching of Tag instances is enabled.
- */
- public boolean isCacheTags() {
- return cacheTags;
+
+ script.run(newJellyContext, output);
+
+ return newJellyContext;
}
/**
- * Sets whether caching of Tag instances, per thread, is enabled.
- * Caching Tags can boost performance, on some JVMs, by reducing the cost of
- * object construction when running Jelly inside a multi-threaded application server
- * such as a Servlet engine.
+ * Parses the script from the given uri using the
+ * JellyContext.getResource() API then compiles it and runs it.
*
- * @param cacheTags Whether caching should be enabled or disabled.
+ * @return the new child context that was used to run the script
*/
- public void setCacheTags(boolean cacheTags) {
- this.cacheTags = cacheTags;
- }
+ public JellyContext runScript(String uri, XMLOutput output) throws JellyException {
+ URL url = null;
+ try {
+ url = getResource(uri);
+ } catch (MalformedURLException e) {
+ throw new JellyException(e.toString());
+ }
- /**
- * Returns whether we export tag libraries to our parents context
- * @return boolean
- */
- public boolean isExportLibraries() {
- return exportLibraries;
+ if (url == null) {
+ throw new JellyException("Could not find Jelly script: " + url);
+ }
+ return runScript(url, output, JellyContext.DEFAULT_EXPORT,
+ JellyContext.DEFAULT_INHERIT);
}
/**
- * Sets whether we export tag libraries to our parents context
- * @param exportLibraries The exportLibraries to set
+ * Parses the script from the given uri using the
+ * JellyContext.getResource() API then compiles it and runs it.
+ *
+ * @return the new child context that was used to run the script
*/
- public void setExportLibraries(boolean exportLibraries) {
- this.exportLibraries = exportLibraries;
- }
+ public JellyContext runScript(String uri, XMLOutput output,
+ boolean export, boolean inherit) throws JellyException {
+ URL url = null;
+ try {
+ url = getResource(uri);
+ } catch (MalformedURLException e) {
+ throw new JellyException(e.toString());
+ }
- /**
- * Sets whether we should export variable definitions to our parent context
- */
- public void setExport(boolean export) {
- this.export = export;
- }
+ if (url == null) {
+ throw new JellyException("Could not find Jelly script: " + url);
+ }
- /**
- * @return whether we should export variable definitions to our parent context
- */
- public boolean isExport() {
- return this.export;
+ return runScript(url, output, export, inherit);
}
/**
- * Sets whether we should inherit variables from our parent context
+ * Parses the script from the given URL then compiles it and runs it.
+ *
+ * @return the new child context that was used to run the script
*/
- public void setInherit(boolean inherit) {
- this.inherit = inherit;
+ public JellyContext runScript(URL url, XMLOutput output) throws JellyException {
+ return runScript(url, output, JellyContext.DEFAULT_EXPORT,
+ JellyContext.DEFAULT_INHERIT);
}
/**
- * @return whether we should inherit variables from our parent context
+ * Parses the script from the given URL then compiles it and runs it.
+ *
+ * @return the new child context that was used to run the script
*/
- public boolean isInherit() {
- return this.inherit;
+ public JellyContext runScript(URL url, XMLOutput output,
+ boolean export, boolean inherit) throws JellyException {
+ return runScript(new InputSource(url.toString()), output, export, inherit);
}
/**
@@ -885,24 +882,15 @@ public class JellyContext {
}
/**
- * @return whether we should allow our doctype definitions to call out to external entities.
- */
- public boolean isAllowDtdToCallExternalEntities() {
- return this.allowDtdToCallExternalEntities;
- }
-
- /**
- * Gets the class loader to be used for instantiating application objects
- * when required. This is determined based upon the following rules:
- * <ul>
- * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
- * <li>The thread context class loader, if it exists and the
- * <code>useContextClassLoader</code> property is set to true</li>
- * <li>The class loader used to load the XMLParser class itself.
- * </ul>
+ * Sets whether caching of Tag instances, per thread, is enabled.
+ * Caching Tags can boost performance, on some JVMs, by reducing the cost of
+ * object construction when running Jelly inside a multi-threaded application server
+ * such as a Servlet engine.
+ *
+ * @param cacheTags Whether caching should be enabled or disabled.
*/
- public ClassLoader getClassLoader() {
- return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass());
+ public void setCacheTags(boolean cacheTags) {
+ this.cacheTags = cacheTags;
}
/**
@@ -917,79 +905,34 @@ public class JellyContext {
}
/**
- * Gets the boolean as to whether the context classloader should be used.
- */
- public boolean getUseContextClassLoader() {
- return useContextClassLoader;
- }
-
- /**
- * Determine whether to use the Context ClassLoader (the one found by
- * calling <code>Thread.currentThread().getContextClassLoader()</code>)
- * to resolve/load classes. If not
- * using Context ClassLoader, then the class-loading defaults to
- * using the calling-class' ClassLoader.
- *
- * @param use determines whether to use JellyContext ClassLoader.
- */
- public void setUseContextClassLoader(boolean use) {
- useContextClassLoader = use;
- }
-
- // Implementation methods
- //-------------------------------------------------------------------------
- /**
- * @return a new relative URL from the given root and with the addition of the
- * extra relative URI
- *
- * @param rootURL is the root context from which the relative URI will be applied
- * @param relativeURI is the relative URI (without a leading "/")
- * @throws MalformedURLException if the URL is invalid.
+ * Sets the current URL context of the current script that is executing.
+ * This URL context is used to deduce relative scripts when relative URIs are
+ * used in calls to {@link #getResource} to process relative scripts.
*/
- protected URL createRelativeURL(URL rootURL, String relativeURI)
- throws MalformedURLException {
- URL url = rootURL;
- if (url == null) {
- File file = new File(System.getProperty("user.dir"));
- url = file.toURL();
- }
- String urlText = url.toString() + relativeURI;
- if ( log.isDebugEnabled() ) {
- log.debug("Attempting to open url: " + urlText);
- }
- return new URL(urlText);
+ public void setCurrentURL(URL currentURL) {
+ this.currentURL = currentURL;
}
/**
- * Strips off the name of a script to create a new context URL
+ * Sets whether we should export variable definitions to our parent context
*/
- protected URL getJellyContextURL(URL url) throws MalformedURLException {
- String text = url.toString();
- int idx = text.lastIndexOf('/');
- text = text.substring(0, idx + 1);
- return new URL(text);
+ public void setExport(boolean export) {
+ this.export = export;
}
/**
- * Strips off the name of a script to create a new context URL
+ * Sets whether we export tag libraries to our parents context
+ * @param exportLibraries The exportLibraries to set
*/
- protected URL getJellyContextURL(InputSource source) throws MalformedURLException {
- String text = source.getSystemId();
- if (text != null) {
- int idx = text.lastIndexOf('/');
- text = text.substring(0, idx + 1);
- return new URL(text);
- } else {
- return null;
- }
-
+ public void setExportLibraries(boolean exportLibraries) {
+ this.exportLibraries = exportLibraries;
}
/**
- * Factory method to create a new child of this context
+ * Sets whether we should inherit variables from our parent context
*/
- protected JellyContext createChildContext() {
- return new JellyContext(this);
+ public void setInherit(boolean inherit) {
+ this.inherit = inherit;
}
/**
@@ -1019,15 +962,16 @@ public class JellyContext {
}
- /**
- * @return the suppressExpressionExceptions
- * @deprecated after v1.1, exceptions will never be suppressed
- */
- public boolean isSuppressExpressionExceptions() {
- return suppressExpressionExceptions;
- }
+ /**
+ * Sets the current root context URL from which all absolute resource URIs
+ * will be relative to. For example in a web application the root URL will
+ * map to the web directory which contains the WEB-INF directory.
+ */
+ public void setRootURL(URL rootURL) {
+ this.rootURL = rootURL;
+ }
- /**
+ /**
* @param suppressExpressionExceptions the suppressExpressionExceptions to set
* @deprecated after v1.1, exceptions will never be suppressed
*/
@@ -1035,4 +979,60 @@ public class JellyContext {
this.suppressExpressionExceptions = suppressExpressionExceptions;
}
+ /**
+ * Determine whether to use the Context ClassLoader (the one found by
+ * calling <code>Thread.currentThread().getContextClassLoader()</code>)
+ * to resolve/load classes. If not
+ * using Context ClassLoader, then the class-loading defaults to
+ * using the calling-class' ClassLoader.
+ *
+ * @param use determines whether to use JellyContext ClassLoader.
+ */
+ public void setUseContextClassLoader(boolean use) {
+ useContextClassLoader = use;
+ }
+
+ /** Sets the value of the named variable */
+ public void setVariable(String name, Object value) {
+ if ( isExport() ) {
+ getParent().setVariable( name, value );
+ return;
+ }
+ if (value == null) {
+ variables.remove(name);
+ }
+ else {
+ variables.put(name, value);
+ }
+ }
+
+ /**
+ * Sets the value of the given variable name in the given variable scope
+ * @param name is the name of the variable
+ * @param scopeName is the optional scope name such as 'parent'. For servlet environments
+ * this could be 'application', 'session' or 'request'.
+ * @param value is the value of the attribute
+ */
+ public void setVariable(String name, String scopeName, Object value) {
+ JellyContext scope = getScope(scopeName);
+ if ( scope != null ) {
+ scope.setVariable(name, value);
+ }
+ }
+
+ /**
+ * Sets the Map of variables to use
+ */
+ public void setVariables(Map variables) {
+ // I have seen this fail when the passed Map contains a key, value
+ // pair where the value is null
+ for (Iterator iter = variables.entrySet().iterator(); iter.hasNext();) {
+ Map.Entry element = (Map.Entry) iter.next();
+ if (element.getValue() != null) {
+ this.variables.put(element.getKey(), element.getValue());
+ }
+ }
+ //this.variables.putAll( variables );
+ }
+
}
diff --git a/core/src/main/java/org/apache/commons/jelly/JellyException.java b/core/src/main/java/org/apache/commons/jelly/JellyException.java
index c5df5179..76272d76 100644
--- a/core/src/main/java/org/apache/commons/jelly/JellyException.java
+++ b/core/src/main/java/org/apache/commons/jelly/JellyException.java
@@ -48,20 +48,19 @@ public class JellyException extends Exception implements LocationAware {
super(message);
}
- public JellyException(String message, Throwable cause) {
- super(message);
- this.cause = cause;
+ public JellyException(String reason, String fileName, String elementName, int columnNumber, int lineNumber) {
+ super(reason);
+ this.fileName = fileName;
+ this.elementName = elementName;
+ this.columnNumber = columnNumber;
+ this.lineNumber = lineNumber;
}
- public JellyException(Throwable cause) {
- super(cause.getLocalizedMessage());
+ public JellyException(String message, Throwable cause) {
+ super(message);
this.cause = cause;
}
- public JellyException(Throwable cause, String fileName, String elementName, int columnNumber, int lineNumber) {
- this(cause.getLocalizedMessage(), cause, fileName, elementName, columnNumber, lineNumber);
- }
-
public JellyException(String reason, Throwable cause, String fileName, String elementName, int columnNumber, int lineNumber) {
super( (reason == null ? cause.getClass().getName() : reason) );
this.cause = cause;
@@ -71,12 +70,13 @@ public class JellyException extends Exception implements LocationAware {
this.lineNumber = lineNumber;
}
- public JellyException(String reason, String fileName, String elementName, int columnNumber, int lineNumber) {
- super(reason);
- this.fileName = fileName;
- this.elementName = elementName;
- this.columnNumber = columnNumber;
- this.lineNumber = lineNumber;
+ public JellyException(Throwable cause) {
+ super(cause.getLocalizedMessage());
+ this.cause = cause;
+ }
+
+ public JellyException(Throwable cause, String fileName, String elementName, int columnNumber, int lineNumber) {
+ this(cause.getLocalizedMessage(), cause, fileName, elementName, columnNumber, lineNumber);
}
@Override
@@ -85,22 +85,6 @@ public class JellyException extends Exception implements LocationAware {
}
- /**
- * @return the line number of the tag
- */
- @Override
- public int getLineNumber() {
- return lineNumber;
- }
-
- /**
- * Sets the line number of the tag
- */
- @Override
- public void setLineNumber(int lineNumber) {
- this.lineNumber = lineNumber;
- }
-
/**
* @return the column number of the tag
*/
@@ -110,11 +94,11 @@ public class JellyException extends Exception implements LocationAware {
}
/**
- * Sets the column number of the tag
+ * @return the element name which caused the problem
*/
@Override
- public void setColumnNumber(int columnNumber) {
- this.columnNumber = columnNumber;
+ public String getElementName() {
+ return elementName;
}
/**
@@ -124,33 +108,15 @@ public class JellyException extends Exception implements LocationAware {
public String getFileName() {
return fileName;
}
-
- /**
- * Sets the Jelly file which caused the problem
- */
- @Override
- public void setFileName(String fileName) {
- this.fileName = fileName;
- }
-
/**
- * @return the element name which caused the problem
+ * @return the line number of the tag
*/
@Override
- public String getElementName() {
- return elementName;
+ public int getLineNumber() {
+ return lineNumber;
}
- /**
- * Sets the element name which caused the problem
- */
- @Override
- public void setElementName(String elementName) {
- this.elementName = elementName;
- }
-
-
@Override
public String getMessage() {
if (fileName == null && lineNumber == -1 && columnNumber == -1 && elementName == null) {
@@ -163,10 +129,19 @@ public class JellyException extends Exception implements LocationAware {
public String getReason() {
return super.getMessage();
}
+
- // #### overload the printStackTrace methods...
@Override
- public void printStackTrace(PrintWriter s) {
+ public void printStackTrace() {
+ super.printStackTrace();
+ if (cause != null) {
+ System.out.println("Root cause");
+ cause.printStackTrace();
+ }
+ }
+
+ @Override
+ public void printStackTrace(PrintStream s) {
synchronized (s) {
super.printStackTrace(s);
if (cause != null) {
@@ -175,9 +150,11 @@ public class JellyException extends Exception implements LocationAware {
}
}
}
-
+
+
+ // #### overload the printStackTrace methods...
@Override
- public void printStackTrace(PrintStream s) {
+ public void printStackTrace(PrintWriter s) {
synchronized (s) {
super.printStackTrace(s);
if (cause != null) {
@@ -187,13 +164,36 @@ public class JellyException extends Exception implements LocationAware {
}
}
+ /**
+ * Sets the column number of the tag
+ */
@Override
- public void printStackTrace() {
- super.printStackTrace();
- if (cause != null) {
- System.out.println("Root cause");
- cause.printStackTrace();
- }
+ public void setColumnNumber(int columnNumber) {
+ this.columnNumber = columnNumber;
+ }
+
+ /**
+ * Sets the element name which caused the problem
+ */
+ @Override
+ public void setElementName(String elementName) {
+ this.elementName = elementName;
+ }
+
+ /**
+ * Sets the Jelly file which caused the problem
+ */
+ @Override
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ /**
+ * Sets the line number of the tag
+ */
+ @Override
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/JellyTagException.java b/core/src/main/java/org/apache/commons/jelly/JellyTagException.java
index 5b4ac1f9..0ada4c0d 100644
--- a/core/src/main/java/org/apache/commons/jelly/JellyTagException.java
+++ b/core/src/main/java/org/apache/commons/jelly/JellyTagException.java
@@ -32,10 +32,18 @@ public class JellyTagException extends JellyException {
super(message);
}
+ public JellyTagException(String reason, String fileName, String elementName, int columnNumber, int lineNumber) {
+ super(reason, fileName, elementName, columnNumber, lineNumber);
+ }
+
public JellyTagException(String message, Throwable cause) {
super(message, cause);
}
+ public JellyTagException(String reason, Throwable cause, String fileName, String elementName, int columnNumber, int lineNumber) {
+ super(reason, cause, fileName, elementName, columnNumber, lineNumber);
+ }
+
public JellyTagException(Throwable cause) {
super(cause);
}
@@ -44,12 +52,4 @@ public class JellyTagException extends JellyException {
super(cause, fileName, elementName, columnNumber, lineNumber);
}
- public JellyTagException(String reason, Throwable cause, String fileName, String elementName, int columnNumber, int lineNumber) {
- super(reason, cause, fileName, elementName, columnNumber, lineNumber);
- }
-
- public JellyTagException(String reason, String fileName, String elementName, int columnNumber, int lineNumber) {
- super(reason, fileName, elementName, columnNumber, lineNumber);
- }
-
}
diff --git a/core/src/main/java/org/apache/commons/jelly/LocationAware.java b/core/src/main/java/org/apache/commons/jelly/LocationAware.java
index 5fff2065..079213d6 100644
--- a/core/src/main/java/org/apache/commons/jelly/LocationAware.java
+++ b/core/src/main/java/org/apache/commons/jelly/LocationAware.java
@@ -26,42 +26,42 @@ package org.apache.commons.jelly;
public interface LocationAware {
/**
- * @return the line number of the tag
+ * @return the column number of the tag
*/
- int getLineNumber();
+ int getColumnNumber();
/**
- * Sets the line number of the tag
+ * @return the element name which caused the problem
*/
- void setLineNumber(int lineNumber);
+ String getElementName();
/**
- * @return the column number of the tag
+ * @return the Jelly file which caused the problem
*/
- int getColumnNumber();
+ String getFileName();
/**
- * Sets the column number of the tag
+ * @return the line number of the tag
*/
- void setColumnNumber(int columnNumber);
+ int getLineNumber();
/**
- * @return the Jelly file which caused the problem
+ * Sets the column number of the tag
*/
- String getFileName();
+ void setColumnNumber(int columnNumber);
/**
- * Sets the Jelly file which caused the problem
+ * Sets the element name which caused the problem
*/
- void setFileName(String fileName);
+ void setElementName(String elementName);
/**
- * @return the element name which caused the problem
+ * Sets the Jelly file which caused the problem
*/
- String getElementName();
+ void setFileName(String fileName);
/**
- * Sets the element name which caused the problem
+ * Sets the line number of the tag
*/
- void setElementName(String elementName);
+ void setLineNumber(int lineNumber);
}
diff --git a/core/src/main/java/org/apache/commons/jelly/MapTagSupport.java b/core/src/main/java/org/apache/commons/jelly/MapTagSupport.java
index 6bf4940e..bcea3f8c 100644
--- a/core/src/main/java/org/apache/commons/jelly/MapTagSupport.java
+++ b/core/src/main/java/org/apache/commons/jelly/MapTagSupport.java
@@ -32,11 +32,13 @@ public abstract class MapTagSupport extends DynaTagSupport {
private Map map;
- /** Sets an attribute value of this tag before the tag is invoked
+ /**
+ * A Factory Method which allows derived tags to overload the Map
+ * implementation used by this tag
*/
- @Override
- public void setAttribute(String name, Object value) {
- getAttributes().put(name, value);
+
+ protected Map createAttributes() {
+ return new HashMap();
}
/**
@@ -49,13 +51,11 @@ public abstract class MapTagSupport extends DynaTagSupport {
}
return map;
}
- /**
- * A Factory Method which allows derived tags to overload the Map
- * implementation used by this tag
+ /** Sets an attribute value of this tag before the tag is invoked
*/
-
- protected Map createAttributes() {
- return new HashMap();
+ @Override
+ public void setAttribute(String name, Object value) {
+ getAttributes().put(name, value);
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/Tag.java b/core/src/main/java/org/apache/commons/jelly/Tag.java
index 84dc3d2d..0fadda7d 100644
--- a/core/src/main/java/org/apache/commons/jelly/Tag.java
+++ b/core/src/main/java/org/apache/commons/jelly/Tag.java
@@ -25,51 +25,54 @@ package org.apache.commons.jelly;
public interface Tag {
/**
- * Gets the parent of this tag.
+ * Evaluates this tag after all the tags properties have been initialized.
*
- * @return the parent of this tag.
+ * @param output output stream.
+ * @throws MissingAttributeException Thrown on error.
+ * @throws JellyTagException Thrown on error.
*/
- public Tag getParent();
+ public void doTag(XMLOutput output) throws MissingAttributeException, JellyTagException;
/**
- * Sets the parent of this tag.
- *
- * @param parent the parent of this tag.
+ * @return the body of the tag.
*/
- public void setParent(Tag parent);
+ public Script getBody();
/**
- * Gets the TagLibrary, null if this is an unrecognized tag (ie a StaticTag)
+ * Gets the context in which the tag will be run.
*
- * @return the TagLibrary.
+ * @return the context in which the tag will be run
*/
- public TagLibrary getTagLib();
+ public JellyContext getContext();
/**
- * Sets the tag library.
+ * Gets the parent of this tag.
*
- * @param tagLibrary the tag library.
+ * @return the parent of this tag.
*/
- public void setTagLib(TagLibrary tagLibrary);
+ public Tag getParent();
/**
- * @return the body of the tag.
+ * Gets the TagLibrary, null if this is an unrecognized tag (ie a StaticTag)
+ *
+ * @return the TagLibrary.
*/
- public Script getBody();
+ public TagLibrary getTagLib();
/**
- * Sets the body of the tag.
+ * A helper method to invoke this tags body
*
- * @param body the body of the tag.
+ * @param output XML output stream.
+ * @throws JellyTagException Thrown on error.
*/
- public void setBody(Script body);
+ public void invokeBody(XMLOutput output) throws JellyTagException;
/**
- * Gets the context in which the tag will be run.
+ * Sets the body of the tag.
*
- * @return the context in which the tag will be run
+ * @param body the body of the tag.
*/
- public JellyContext getContext();
+ public void setBody(Script body);
/**
* Sets the context in which the tag will be run.
@@ -80,19 +83,16 @@ public interface Tag {
public void setContext(JellyContext context) throws JellyTagException;
/**
- * Evaluates this tag after all the tags properties have been initialized.
+ * Sets the parent of this tag.
*
- * @param output output stream.
- * @throws MissingAttributeException Thrown on error.
- * @throws JellyTagException Thrown on error.
+ * @param parent the parent of this tag.
*/
- public void doTag(XMLOutput output) throws MissingAttributeException, JellyTagException;
+ public void setParent(Tag parent);
/**
- * A helper method to invoke this tags body
+ * Sets the tag library.
*
- * @param output XML output stream.
- * @throws JellyTagException Thrown on error.
+ * @param tagLibrary the tag library.
*/
- public void invokeBody(XMLOutput output) throws JellyTagException;
+ public void setTagLib(TagLibrary tagLibrary);
}
diff --git a/core/src/main/java/org/apache/commons/jelly/TagLibrary.java b/core/src/main/java/org/apache/commons/jelly/TagLibrary.java
index 3ee4dbfd..7be2171c 100644
--- a/core/src/main/java/org/apache/commons/jelly/TagLibrary.java
+++ b/core/src/main/java/org/apache/commons/jelly/TagLibrary.java
@@ -36,10 +36,7 @@ import org.xml.sax.Attributes;
public abstract class TagLibrary {
- private boolean allowUnknownTags = true;
- private Map tags = new HashMap();
-
- static {
+ static {
// register standard converters
@@ -60,6 +57,9 @@ public abstract class TagLibrary {
File.class
);
}
+ private boolean allowUnknownTags = true;
+
+ private Map tags = new HashMap();
/**
* Default Constructor
@@ -76,19 +76,24 @@ public abstract class TagLibrary {
this.allowUnknownTags = allowUnknownTags;
}
- /** Creates a new script to execute the given tag name and attributes */
- public TagScript createTagScript(String name, Attributes attributes)
+ /** Allows taglibs to use their own expression evaluation mechanism */
+ public Expression createExpression(
+ ExpressionFactory factory,
+ TagScript tagScript,
+ String attributeName,
+ String attributeValue)
throws JellyException {
- Object value = tags.get(name);
- if (value instanceof Class) {
- Class type = (Class) value;
- return TagScript.newInstance(type);
+ ExpressionFactory myFactory = getExpressionFactory();
+ if (myFactory == null) {
+ myFactory = factory;
}
- else if (value instanceof TagFactory) {
- return new TagScript( (TagFactory) value );
+ if (myFactory != null) {
+ return CompositeExpression.parse(attributeValue, myFactory);
}
- return null;
+
+ // will use a constant expression instead
+ return new ConstantExpression(attributeValue);
}
/** Creates a new Tag for the given tag name and attributes */
@@ -111,43 +116,24 @@ public abstract class TagLibrary {
return null;
}
- /** Allows taglibs to use their own expression evaluation mechanism */
- public Expression createExpression(
- ExpressionFactory factory,
- TagScript tagScript,
- String attributeName,
- String attributeValue)
+ /** Creates a new script to execute the given tag name and attributes */
+ public TagScript createTagScript(String name, Attributes attributes)
throws JellyException {
- ExpressionFactory myFactory = getExpressionFactory();
- if (myFactory == null) {
- myFactory = factory;
+ Object value = tags.get(name);
+ if (value instanceof Class) {
+ Class type = (Class) value;
+ return TagScript.newInstance(type);
}
- if (myFactory != null) {
- return CompositeExpression.parse(attributeValue, myFactory);
+ else if (value instanceof TagFactory) {
+ return new TagScript( (TagFactory) value );
}
-
- // will use a constant expression instead
- return new ConstantExpression(attributeValue);
+ return null;
}
// Implementation methods
//-------------------------------------------------------------------------
- /**
- * Registers a tag implementation Class for a given tag name
- */
- protected void registerTag(String name, Class type) {
- tags.put(name, type);
- }
-
- /**
- * Registers a tag factory for a given tag name
- */
- protected void registerTagFactory(String name, TagFactory tagFactory) {
- tags.put(name, tagFactory);
- }
-
/** Allows derived tag libraries to use their own factory */
public ExpressionFactory getExpressionFactory() {
return null;
@@ -157,13 +143,27 @@ public abstract class TagLibrary {
return tags;
}
- /**
+ /**
* @return the allowUnknownTags
*/
public boolean isAllowUnknownTags() {
return allowUnknownTags;
}
+ /**
+ * Registers a tag implementation Class for a given tag name
+ */
+ protected void registerTag(String name, Class type) {
+ tags.put(name, type);
+ }
+
+ /**
+ * Registers a tag factory for a given tag name
+ */
+ protected void registerTagFactory(String name, TagFactory tagFactory) {
+ tags.put(name, tagFactory);
+ }
+
/**
* @param allowUnknownTags the allowUnknownTags to set
*/
diff --git a/core/src/main/java/org/apache/commons/jelly/TagSupport.java b/core/src/main/java/org/apache/commons/jelly/TagSupport.java
index c98847d6..22036b7c 100644
--- a/core/src/main/java/org/apache/commons/jelly/TagSupport.java
+++ b/core/src/main/java/org/apache/commons/jelly/TagSupport.java
@@ -29,24 +29,6 @@ import org.apache.commons.jelly.util.TagUtils;
public abstract class TagSupport implements Tag {
- /** The parent of this tag */
- protected Tag parent;
-
- /** The TagLibrary which defines this tag */
- protected TagLibrary tagLibrary;
-
- /** The body of the tag */
- protected Script body;
- /** The current context */
-
- protected Boolean shouldTrim;
- protected boolean hasTrimmed;
-
- protected JellyContext context;
-
- /** Whether XML text should be escaped */
- private boolean escapeText = true;
-
/**
* Searches up the parent hierarchy from the given tag
* for a Tag of the given type
@@ -67,6 +49,19 @@ public abstract class TagSupport implements Tag {
}
return null;
}
+
+ /**
+ * Searches up the parent hierarchy from the given tag
+ * for a Tag matching one or more of given types.
+ *
+ * @param from the tag to start searching from
+ * @param tagClasses an array of types that might match
+ * @return the tag of the given type or null if it could not be found
+ * @see #findAncestorWithClass(Tag,Collection)
+ */
+ public static Tag findAncestorWithClass(Tag from, Class[] tagClasses) {
+ return findAncestorWithClass(from,Arrays.asList(tagClasses));
+ }
/**
* Searches up the parent hierarchy from the given tag
@@ -88,128 +83,30 @@ public abstract class TagSupport implements Tag {
}
return null;
}
+ /** The parent of this tag */
+ protected Tag parent;
+ /** The TagLibrary which defines this tag */
+ protected TagLibrary tagLibrary;
- /**
- * Searches up the parent hierarchy from the given tag
- * for a Tag matching one or more of given types.
- *
- * @param from the tag to start searching from
- * @param tagClasses an array of types that might match
- * @return the tag of the given type or null if it could not be found
- * @see #findAncestorWithClass(Tag,Collection)
- */
- public static Tag findAncestorWithClass(Tag from, Class[] tagClasses) {
- return findAncestorWithClass(from,Arrays.asList(tagClasses));
- }
-
- public TagSupport() {
- }
-
- public TagSupport(boolean shouldTrim) {
- setTrim( shouldTrim );
- }
-
- /**
- * Sets whether whitespace inside this tag should be trimmed or not.
- * Defaults to true so whitespace is trimmed
- */
- public void setTrim(boolean shouldTrim) {
- if ( shouldTrim ) {
- this.shouldTrim = Boolean.TRUE;
- }
- else {
- this.shouldTrim = Boolean.FALSE;
- }
- }
-
- public boolean isTrim() {
- if ( this.shouldTrim == null ) {
- Tag parent = getParent();
- if ( parent == null ) {
- return true;
- }
- else {
- if ( parent instanceof TagSupport ) {
- TagSupport parentSupport = (TagSupport) parent;
-
- this.shouldTrim = ( parentSupport.isTrim() ? Boolean.TRUE : Boolean.FALSE );
- }
- else {
- this.shouldTrim = Boolean.TRUE;
- }
- }
- }
-
- return this.shouldTrim.booleanValue();
- }
-
- /** @return the parent of this tag */
- @Override
- public Tag getParent() {
- return parent;
- }
-
- /** Sets the parent of this tag */
- @Override
- public void setParent(Tag parent) {
- this.parent = parent;
- }
+ /** The body of the tag */
+ protected Script body;
- /* (non-Javadoc)
- * @see org.apache.commons.jelly.Tag#getTagLib()
- */
- @Override
- public TagLibrary getTagLib() {
- return tagLibrary;
- }
+ /** The current context */
- /* (non-Javadoc)
- * @see org.apache.commons.jelly.Tag#setTagLib(org.apache.commons.jelly.TagLibrary)
- */
- @Override
- public void setTagLib(TagLibrary tagLibrary) {
- if (this.tagLibrary != null && tagLibrary != this.tagLibrary)
- throw new IllegalArgumentException("Cannot setTagLib once set");
- this.tagLibrary = tagLibrary;
- }
+ protected Boolean shouldTrim;
- /** @return the body of the tag */
- @Override
- public Script getBody() {
- if (! hasTrimmed) {
- hasTrimmed = true;
- if (isTrim()) {
- trimBody();
- }
- }
- return body;
- }
+ protected boolean hasTrimmed;
- /** Sets the body of the tag */
- @Override
- public void setBody(Script body) {
- this.body = body;
- this.hasTrimmed = false;
- }
+ protected JellyContext context;
- /** @return the context in which the tag will be run */
- @Override
- public JellyContext getContext() {
- return context;
- }
+ /** Whether XML text should be escaped */
+ private boolean escapeText = true;
- /** Sets the context in which the tag will be run */
- @Override
- public void setContext(JellyContext context) throws JellyTagException {
- this.context = context;
+ public TagSupport() {
}
- /**
- * Invokes the body of this tag using the given output
- */
- @Override
- public void invokeBody(XMLOutput output) throws JellyTagException {
- getBody().run(context, output);
+ public TagSupport(boolean shouldTrim) {
+ setTrim( shouldTrim );
}
// Implementation methods
@@ -239,6 +136,18 @@ public abstract class TagSupport implements Tag {
return findAncestorWithClass(getParent(), parentClasses);
}
+ /** @return the body of the tag */
+ @Override
+ public Script getBody() {
+ if (! hasTrimmed) {
+ hasTrimmed = true;
+ if (isTrim()) {
+ trimBody();
+ }
+ }
+ return body;
+ }
+
/**
* Executes the body of the tag and returns the result as a String.
*
@@ -248,7 +157,7 @@ public abstract class TagSupport implements Tag {
return getBodyText(escapeText);
}
- /**
+ /**
* Executes the body of the tag and returns the result as a String.
*
* @param shouldEscape Signal if the text should be escaped.
@@ -260,12 +169,32 @@ public abstract class TagSupport implements Tag {
return writer.toString();
}
+ /** @return the context in which the tag will be run */
+ @Override
+ public JellyContext getContext() {
+ return context;
+ }
+
+ /** @return the parent of this tag */
+ @Override
+ public Tag getParent() {
+ return parent;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.commons.jelly.Tag#getTagLib()
+ */
+ @Override
+ public TagLibrary getTagLib() {
+ return tagLibrary;
+ }
+
/**
- * Find all text nodes inside the top level of this body and
- * if they are just whitespace then remove them
+ * Invokes the body of this tag using the given output
*/
- protected void trimBody() {
- TagUtils.trimScript(body);
+ @Override
+ public void invokeBody(XMLOutput output) throws JellyTagException {
+ getBody().run(context, output);
}
/**
@@ -275,6 +204,40 @@ public abstract class TagSupport implements Tag {
return escapeText;
}
+ public boolean isTrim() {
+ if ( this.shouldTrim == null ) {
+ Tag parent = getParent();
+ if ( parent == null ) {
+ return true;
+ }
+ else {
+ if ( parent instanceof TagSupport ) {
+ TagSupport parentSupport = (TagSupport) parent;
+
+ this.shouldTrim = ( parentSupport.isTrim() ? Boolean.TRUE : Boolean.FALSE );
+ }
+ else {
+ this.shouldTrim = Boolean.TRUE;
+ }
+ }
+ }
+
+ return this.shouldTrim.booleanValue();
+ }
+
+ /** Sets the body of the tag */
+ @Override
+ public void setBody(Script body) {
+ this.body = body;
+ this.hasTrimmed = false;
+ }
+
+ /** Sets the context in which the tag will be run */
+ @Override
+ public void setContext(JellyContext context) throws JellyTagException {
+ this.context = context;
+ }
+
/**
* Sets whether the body of the tag should be escaped as text (so that < and > are
* escaped as &lt; and &gt;), which is the default or leave the text as XML.
@@ -282,4 +245,41 @@ public abstract class TagSupport implements Tag {
public void setEscapeText(boolean escapeText) {
this.escapeText = escapeText;
}
+
+ /** Sets the parent of this tag */
+ @Override
+ public void setParent(Tag parent) {
+ this.parent = parent;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.commons.jelly.Tag#setTagLib(org.apache.commons.jelly.TagLibrary)
+ */
+ @Override
+ public void setTagLib(TagLibrary tagLibrary) {
+ if (this.tagLibrary != null && tagLibrary != this.tagLibrary)
+ throw new IllegalArgumentException("Cannot setTagLib once set");
+ this.tagLibrary = tagLibrary;
+ }
+
+ /**
+ * Sets whether whitespace inside this tag should be trimmed or not.
+ * Defaults to true so whitespace is trimmed
+ */
+ public void setTrim(boolean shouldTrim) {
+ if ( shouldTrim ) {
+ this.shouldTrim = Boolean.TRUE;
+ }
+ else {
+ this.shouldTrim = Boolean.FALSE;
+ }
+ }
+
+ /**
+ * Find all text nodes inside the top level of this body and
+ * if they are just whitespace then remove them
+ */
+ protected void trimBody() {
+ TagUtils.trimScript(body);
+ }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/XMLOutput.java b/core/src/main/java/org/apache/commons/jelly/XMLOutput.java
index 97512697..9a05fe41 100644
--- a/core/src/main/java/org/apache/commons/jelly/XMLOutput.java
+++ b/core/src/main/java/org/apache/commons/jelly/XMLOutput.java
@@ -47,136 +47,152 @@ import org.xml.sax.helpers.DefaultHandler;
public class XMLOutput implements ContentHandler, LexicalHandler {
- protected static final String[] LEXICAL_HANDLER_NAMES =
- {
- "http://xml.org/sax/properties/lexical-handler",
- "http://xml.org/sax/handlers/LexicalHandler" };
+ private final class NamespaceStack {
+ /** A list of maps: Each map contains prefix to uri mapping */
+ private List nsStack;
- /** Empty attributes. */
- private static final Attributes EMPTY_ATTRIBUTES = new AttributesImpl();
+ private NamespaceStack() {
+ this.nsStack = new ArrayList();
+ this.nsStack.add(new HashMap());
+ }
- /** The Log to which logging calls will be made. */
- private static final Log log = LogFactory.getLog(XMLOutput.class);
+ public void decreaseLevel() {
+ nsStack.remove(0);
+ }
- /** The default for escaping of text. */
- private static final boolean DEFAULT_ESCAPE_TEXT = false;
+ public void increaseLevel() {
+ nsStack.add(0, new HashMap());
+ }
- /** The SAX ContentHandler that output goes to. */
- private ContentHandler contentHandler;
+ private boolean isRootNodeDefaultNs(String prefix, String uri) {
+ return ("".equals(prefix) && "".equals(uri) && nsStack.size() == 1);
+ }
- /** The SAX LexicalHandler that output goes to. */
- private LexicalHandler lexicalHandler;
+ public void popNamespace(String prefix) throws SAXException {
+ Map prefixUriMap = (Map)nsStack.get(0);
- /** Stack of known namespaces. */
- private NamespaceStack namespaceStack = new NamespaceStack();
+ if (prefix == null) {
+ prefix = "";
+ }
- public XMLOutput() {
- }
+ if ("xml".equals(prefix)) {
+ // We should ignore setting 'xml' prefix
+ // As declared in java of ContentHandler#startPrefixMapping
+ return;
+ }
- /** The XML-output will relay the SAX events to the indicated
- * contentHandler.
- * @param contentHandler
- */
- public XMLOutput(ContentHandler contentHandler) {
- this.contentHandler = contentHandler;
- // often classes will implement LexicalHandler as well
- if (contentHandler instanceof LexicalHandler) {
- this.lexicalHandler = (LexicalHandler) contentHandler;
+ if (prefixUriMap.containsKey(prefix)) {
+ String uri = (String) prefixUriMap.get(prefix);
+ prefixUriMap.remove(prefix);
+ // If we havent called startPrefixMapping for root node if we wanted to avoid xmlns=""
+ // We aren't going to call endPrefixMapping neither
+ if (!isRootNodeDefaultNs(prefix, uri)) {
+// System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.popNamespace() prefix="+prefix);
+ contentHandler.endPrefixMapping(prefix);
+ }
+ }/* else {
+ improper nesting ? or already removed in popNamespaces
+ }
+ */
}
- }
-
- /** The XML-output will relay the SAX events to the indicated
- * content-handler lexical-handler.
- * @param contentHandler
- * @param lexicalHandler
- */
- public XMLOutput(
- ContentHandler contentHandler,
- LexicalHandler lexicalHandler) {
- this.contentHandler = contentHandler;
- this.lexicalHandler = lexicalHandler;
- }
-
- @Override
- public String toString() {
- return super.toString()
- + "[contentHandler="
- + contentHandler
- + ";lexicalHandler="
- + lexicalHandler
- + "]";
- }
- /**
- * Provides a useful hook that implementations
- * can use to close the
- * underlying OutputStream or Writer.
- *
- * @throws IOException
- */
- public void close() throws IOException {
- }
+ public void popNamespaces() throws SAXException {
+ Map prefixUriMap = (Map)nsStack.get(0);
+ for (Iterator iter = prefixUriMap.keySet().iterator();iter.hasNext();) {
+ String prefix = (String)iter.next();
+ String uri = (String) prefixUriMap.get(prefix);
+ iter.remove();
- /** Flushes the underlying stream if {@link XMLWriter}
- * or {@link XMLOutput}.
- *
- * @throws IOException
- */
- public void flush() throws IOException {
- if (contentHandler instanceof XMLWriter) {
- ((XMLWriter)contentHandler).flush();
- } else if (contentHandler instanceof XMLOutput) {
- ((XMLOutput)contentHandler).flush();
+ // If we havent called startPrefixMapping for root node if we wanted to avoid xmlns=""
+ // We aren't going to call endPrefixMapping neither
+ if (!isRootNodeDefaultNs(prefix, uri)) {
+// System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.popNamespaces() prefix="+prefix);
+ contentHandler.endPrefixMapping(prefix);
+ }
+ }
}
- }
- // Static helper methods
- //-------------------------------------------------------------------------
+ public void pushNamespace(String prefix, String uri) throws SAXException {
+ Map prefixUriMap;
- /**
- * Creates an XMLOutput from an existing SAX XMLReader.
- */
- public static XMLOutput createXMLOutput(XMLReader xmlReader) {
- XMLOutput output = new XMLOutput(xmlReader.getContentHandler());
+ if (prefix == null) {
+ prefix = "";
+ }
+ if (uri == null) {
+ uri = "";
+ }
- // isn't it lovely what we've got to do to find the LexicalHandler... ;-)
- for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
- try {
- Object value = xmlReader.getProperty(LEXICAL_HANDLER_NAMES[i]);
- if (value instanceof LexicalHandler) {
- output.setLexicalHandler((LexicalHandler) value);
+ if ("xml".equals(prefix)) {
+ // We should ignore setting 'xml' prefix
+ // As declared in java of ContentHandler#startPrefixMapping
+ return;
+ }
+
+ // Lets find out if we already declared this same prefix,
+ // if not declare in current depth map (the first of list)
+ // and call contentHandler.startPrefixMapping(prefix, uri);
+ boolean isNew = true;
+ for (Iterator iter = nsStack.iterator(); iter.hasNext();) {
+ prefixUriMap = (Map) iter.next();
+ if (prefixUriMap.containsKey(prefix)) {
+ if (uri.equals(prefixUriMap.get(prefix))) {
+ // Its an active namespace already
+ // System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.pushNamespace() IS NOT NEW prefix="+prefix+",uri="+uri);
+ isNew = false;
+ }
+ // We found it in stack
+ // If it was exactly the same, we won't bother
break;
}
- } catch (Exception e) {
- // ignore any unsupported-operation exceptions
- if (log.isDebugEnabled()) {
- log.debug("error setting lexical handler properties", e);
+ }
+
+ if (isNew) {
+ // not declared sometime before
+ prefixUriMap = (Map) nsStack.get(0); // Current depth map
+ // Sanity check: Don't let two prefixes for different uris in
+ // same depth
+ if (prefixUriMap.containsKey(prefix)) {
+ if (!uri.equals(prefixUriMap.get(prefix))) {
+ throw new SAXException("Cannot set same prefix to different URI in same node: trying to add prefix \""
+ + prefix + "\" for uri \""+uri+"\" whereas the declared ones are " + prefixUriMap);
+ }
+ } else {
+ prefixUriMap.put(prefix, uri);
+
+ // To avoid setting xmlns="" for top node (not very nice :D)
+ // We need to specifically check this condition
+ if (!isRootNodeDefaultNs(prefix, uri)) {
+// System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.pushNamespace() prefix="+prefix+",uri="+uri);
+ contentHandler.startPrefixMapping(prefix, uri);
+ }
}
}
}
- return output;
}
- /**
- * Creates a text based XMLOutput which converts all XML events into
- * text and writes to the underlying Writer.
- */
- public static XMLOutput createXMLOutput(Writer writer) {
- return createXMLOutput(writer, DEFAULT_ESCAPE_TEXT);
- }
+ protected static final String[] LEXICAL_HANDLER_NAMES =
+ {
+ "http://xml.org/sax/properties/lexical-handler",
+ "http://xml.org/sax/handlers/LexicalHandler" };
+
+ /** Empty attributes. */
+ private static final Attributes EMPTY_ATTRIBUTES = new AttributesImpl();
+
+ /** The Log to which logging calls will be made. */
+ private static final Log log = LogFactory.getLog(XMLOutput.class);
+
+ /** The default for escaping of text. */
+ private static final boolean DEFAULT_ESCAPE_TEXT = false;
/**
- * Creates a text based XMLOutput which converts all XML events into
- * text and writes to the underlying Writer.
+ * returns an XMLOutput object that will discard all
+ * tag-generated XML events. Useful when tag output is not expected
+ * or not significant.
*
- * @param writer is the writer to output to
- * @param escapeText is whether or not text output will be escaped. This must be true
- * if the underlying output is XML or could be false if the underlying output is textual.
+ * @return a no-op XMLOutput
*/
- public static XMLOutput createXMLOutput(Writer writer, boolean escapeText) {
- XMLWriter xmlWriter = new XMLWriter(writer);
- xmlWriter.setEscapeText(escapeText);
- return createXMLOutput(xmlWriter);
+ public static XMLOutput createDummyXMLOutput() {
+ return new XMLOutput(new DefaultHandler());
}
/**
@@ -205,119 +221,200 @@ public class XMLOutput implements ContentHandler, LexicalHandler {
}
/**
- * returns an XMLOutput object that will discard all
- * tag-generated XML events. Useful when tag output is not expected
- * or not significant.
- *
- * @return a no-op XMLOutput
+ * Creates a text based XMLOutput which converts all XML events into
+ * text and writes to the underlying Writer.
*/
- public static XMLOutput createDummyXMLOutput() {
- return new XMLOutput(new DefaultHandler());
+ public static XMLOutput createXMLOutput(Writer writer) {
+ return createXMLOutput(writer, DEFAULT_ESCAPE_TEXT);
}
- // Extra helper methods provided for tag authors
- //-------------------------------------------------------------------------
-
/**
- * Outputs the given String as a piece of valid text in the
- * XML event stream.
- * Any special XML characters should come out properly escaped.
+ * Creates a text based XMLOutput which converts all XML events into
+ * text and writes to the underlying Writer.
+ *
+ * @param writer is the writer to output to
+ * @param escapeText is whether or not text output will be escaped. This must be true
+ * if the underlying output is XML or could be false if the underlying output is textual.
*/
- public void write(String text) throws SAXException {
- char[] ch = text.toCharArray();
- characters(ch, 0, ch.length);
+ public static XMLOutput createXMLOutput(Writer writer, boolean escapeText) {
+ XMLWriter xmlWriter = new XMLWriter(writer);
+ xmlWriter.setEscapeText(escapeText);
+ return createXMLOutput(xmlWriter);
}
/**
- * Outputs the given String as a piece of CDATA in the
- * XML event stream.
+ * Creates an XMLOutput from an existing SAX XMLReader.
*/
- public void writeCDATA(String text) throws SAXException {
- startCDATA();
- char[] ch = text.toCharArray();
- characters(ch, 0, ch.length);
- endCDATA();
+ public static XMLOutput createXMLOutput(XMLReader xmlReader) {
+ XMLOutput output = new XMLOutput(xmlReader.getContentHandler());
+
+ // isn't it lovely what we've got to do to find the LexicalHandler... ;-)
+ for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
+ try {
+ Object value = xmlReader.getProperty(LEXICAL_HANDLER_NAMES[i]);
+ if (value instanceof LexicalHandler) {
+ output.setLexicalHandler((LexicalHandler) value);
+ break;
+ }
+ } catch (Exception e) {
+ // ignore any unsupported-operation exceptions
+ if (log.isDebugEnabled()) {
+ log.debug("error setting lexical handler properties", e);
+ }
+ }
+ }
+ return output;
}
+ // Implementation methods
+ //-------------------------------------------------------------------------
/**
- * Outputs a comment to the XML stream.
+ * Factory method to create a new XMLOutput from an XMLWriter
*/
- public void writeComment(String text) throws SAXException {
- char[] ch = text.toCharArray();
- comment(ch, 0, ch.length);
+ protected static XMLOutput createXMLOutput(final XMLWriter xmlWriter) {
+ XMLOutput answer = new XMLOutput() {
+ @Override
+ public void close() throws IOException {
+ xmlWriter.close();
+ }
+ };
+ answer.setContentHandler(xmlWriter);
+ answer.setLexicalHandler(xmlWriter);
+ return answer;
}
- /**
- * Helper method for outputting a start element event
- * for an element in no namespace.
+ /** The SAX ContentHandler that output goes to. */
+ private ContentHandler contentHandler;
+
+ // Static helper methods
+ //-------------------------------------------------------------------------
+
+ /** The SAX LexicalHandler that output goes to. */
+ private LexicalHandler lexicalHandler;
+
+ /** Stack of known namespaces. */
+ private NamespaceStack namespaceStack = new NamespaceStack();
+
+ public XMLOutput() {
+ }
+
+ /** The XML-output will relay the SAX events to the indicated
+ * contentHandler.
+ * @param contentHandler
*/
- public void startElement(String localName) throws SAXException {
- startElement("", localName, localName, EMPTY_ATTRIBUTES);
+ public XMLOutput(ContentHandler contentHandler) {
+ this.contentHandler = contentHandler;
+ // often classes will implement LexicalHandler as well
+ if (contentHandler instanceof LexicalHandler) {
+ this.lexicalHandler = (LexicalHandler) contentHandler;
+ }
}
- /**
- * Helper method for outputting a start element event
- * for an element in no namespace.
+ /** The XML-output will relay the SAX events to the indicated
+ * content-handler lexical-handler.
+ * @param contentHandler
+ * @param lexicalHandler
*/
- public void startElement(String localName, Attributes attributes) throws SAXException {
- startElement("", localName, localName, attributes);
+ public XMLOutput(
+ ContentHandler contentHandler,
+ LexicalHandler lexicalHandler) {
+ this.contentHandler = contentHandler;
+ this.lexicalHandler = lexicalHandler;
}
/**
- * Helper method for outputting an end element event
- * for an element in no namespace.
+ * Receive notification of character data.
+ *
+ * <p>The Parser will call this method to report each chunk of
+ * character data. SAX parsers may return all contiguous character
+ * data in a single chunk, or they may split it into several
+ * chunks; however, all of the characters in any single event
+ * must come from the same external entity so that the Locator
+ * provides useful information.</p>
+ *
+ * <p>The application must not attempt to read from the array
+ * outside of the specified range.</p>
+ *
+ * <p>Individual characters may consist of more than one Java
+ * <code>char</code> value. There are two important cases where this
+ * happens, because characters can't be represented in just sixteen bits.
+ * In one case, characters are represented in a <em>Surrogate Pair</em>,
+ * using two special Unicode values. Such characters are in the so-called
+ * "Astral Planes", with a code point above U+FFFF. A second case involves
+ * composite characters, such as a base character combining with one or
+ * more accent characters.</p>
+ *
+ * <p> Your code should not assume that algorithms using
+ * <code>char</code>-at-a-time idioms will be working in character
+ * units; in some cases they will split characters. This is relevant
+ * wherever XML permits arbitrary characters, such as attribute values,
+ * processing instruction data, and comments as well as in data reported
+ * from this method. It's also generally relevant whenever Java code
+ * manipulates internationalized text; the issue isn't unique to XML.</p>
+ *
+ * <p>Note that some parsers will report whitespace in element
+ * content using the {@link #ignorableWhitespace ignorableWhitespace}
+ * method rather than this one (validating parsers <em>must</em>
+ * do so).</p>
+ *
+ * @param ch The characters from the XML document.
+ * @param start The start position in the array.
+ * @param length The number of characters to read from the array.
+ * @throws org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #ignorableWhitespace
+ * @see org.xml.sax.Locator
*/
- public void endElement(String localName) throws SAXException {
- endElement("", localName, localName);
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ contentHandler.characters(ch, start, length);
}
- // ContentHandler interface
+ // Extra helper methods provided for tag authors
//-------------------------------------------------------------------------
/**
- * Receive an object for locating the origin of SAX document events.
- *
- * <p>SAX parsers are strongly encouraged (though not absolutely
- * required) to supply a locator: if it does so, it must supply
- * the locator to the application by invoking this method before
- * invoking any of the other methods in the ContentHandler
- * interface.</p>
+ * Provides a useful hook that implementations
+ * can use to close the
+ * underlying OutputStream or Writer.
*
- * <p>The locator allows the application to determine the end
- * position of any document-related event, even if the parser is
- * not reporting an error. Typically, the application will
- * use this information for reporting its own errors (such as
- * character content that does not match an application's
- * business rules). The information returned by the locator
- * is probably not sufficient for use with a search engine.</p>
+ * @throws IOException
+ */
+ public void close() throws IOException {
+ }
+
+ /**
+ * Report an XML comment anywhere in the document.
*
- * <p>Note that the locator will return correct information only
- * during the invocation of the events in this interface. The
- * application should not attempt to use it at any other time.</p>
+ * <p>This callback will be used for comments inside or outside the
+ * document element, including comments in the external DTD
+ * subset (if read). Comments in the DTD must be properly
+ * nested inside start/endDTD and start/endEntity events (if
+ * used).</p>
*
- * @param locator An object that can return the location of
- * any SAX document event.
- * @see org.xml.sax.Locator
+ * @param ch An array holding the characters in the comment.
+ * @param start The starting position in the array.
+ * @param length The number of characters to use from the array.
+ * @throws SAXException The application may raise an exception.
*/
@Override
- public void setDocumentLocator(Locator locator) {
- contentHandler.setDocumentLocator(locator);
+ public void comment(char ch[], int start, int length) throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.comment(ch, start, length);
+ }
}
/**
- * Receive notification of the beginning of a document.
- *
- * <p>The SAX parser will invoke this method only once, before any
- * other event callbacks (except for {@link #setDocumentLocator
- * setDocumentLocator}).</p>
+ * Report the end of a CDATA section.
*
- * @throws org.xml.sax.SAXException Any SAX exception, possibly
- * wrapping another exception.
- * @see #endDocument
+ * @throws SAXException The application may raise an exception.
+ * @see #startCDATA
*/
@Override
- public void startDocument() throws SAXException {
- contentHandler.startDocument();
+ public void endCDATA() throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.endCDATA();
+ }
}
/**
@@ -339,164 +436,32 @@ public class XMLOutput implements ContentHandler, LexicalHandler {
}
/**
- * Begin the scope of a prefix-URI Namespace mapping.
- *
- * <p>The information from this event is not necessary for
- * normal Namespace processing: the SAX XML reader will
- * automatically replace prefixes for element and attribute
- * names when the <code>http://xml.org/sax/features/namespaces</code>
- * feature is <var>true</var> (the default).</p>
- *
- * <p>There are cases, however, when applications need to
- * use prefixes in character data or in attribute values,
- * where they cannot safely be expanded automatically; the
- * start/endPrefixMapping event supplies the information
- * to the application to expand prefixes in those contexts
- * itself, if necessary.</p>
- *
- * <p>Note that start/endPrefixMapping events are not
- * guaranteed to be properly nested relative to each other:
- * all startPrefixMapping events will occur immediately before the
- * corresponding {@link #startElement startElement} event,
- * and all {@link #endPrefixMapping endPrefixMapping}
- * events will occur immediately after the corresponding
- * {@link #endElement endElement} event,
- * but their order is not otherwise
- * guaranteed.</p>
+ * Report the end of DTD declarations.
*
- * <p>There should never be start/endPrefixMapping events for the
- * "xml" prefix, since it is predeclared and immutable.</p>
+ * <p>This method is intended to report the end of the
+ * DOCTYPE declaration; if the document has no DOCTYPE declaration,
+ * this method will not be invoked.</p>
*
- * @param prefix The Namespace prefix being declared.
- * An empty string is used for the default element namespace,
- * which has no prefix.
- * @param uri The Namespace URI the prefix is mapped to.
- * @throws org.xml.sax.SAXException The client may throw
- * an exception during processing.
- * @see #endPrefixMapping
- * @see #startElement
+ * @throws SAXException The application may raise an exception.
+ * @see #startDTD
*/
@Override
- public void startPrefixMapping(String prefix, String uri) throws SAXException {
- namespaceStack.pushNamespace(prefix, uri);
- // contentHandler.startPrefixMapping(prefix, uri) will be called if needed
- // in pushNamespace
+ public void endDTD() throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.endDTD();
+ }
}
/**
- * End the scope of a prefix-URI mapping.
- *
- * <p>See {@link #startPrefixMapping startPrefixMapping} for
- * details. These events will always occur immediately after the
- * corresponding {@link #endElement endElement} event, but the order of
- * {@link #endPrefixMapping endPrefixMapping} events is not otherwise
- * guaranteed.</p>
- *
- * @param prefix The prefix that was being mapped.
- * This is the empty string when a default mapping scope ends.
- * @throws org.xml.sax.SAXException The client may throw
- * an exception during processing.
- * @see #startPrefixMapping
- * @see #endElement
+ * Helper method for outputting an end element event
+ * for an element in no namespace.
*/
- @Override
- public void endPrefixMapping(String prefix) throws SAXException {
- // End prefix mapping was already called after endElement
- // contentHandler.endPrefixMapping(prefix);
+ public void endElement(String localName) throws SAXException {
+ endElement("", localName, localName);
}
- /**
- * Receive notification of the beginning of an element.
- *
- * <p>The Parser will invoke this method at the beginning of every
- * element in the XML document; there will be a corresponding
- * {@link #endElement endElement} event for every startElement event
- * (even when the element is empty). All of the element's content will be
- * reported, in order, before the corresponding endElement
- * event.</p>
- *
- * <p>This event allows up to three name components for each
- * element:</p>
- *
- * <ol>
- * <li>the Namespace URI;</li>
- * <li>the local name; and</li>
- * <li>the qualified (prefixed) name.</li>
- * </ol>
- *
- * <p>Any or all of these may be provided, depending on the
- * values of the <var>http://xml.org/sax/features/namespaces</var>
- * and the <var>http://xml.org/sax/features/namespace-prefixes</var>
- * properties:</p>
- *
- * <ul>
- * <li>the Namespace URI and local name are required when
- * the namespaces property is <var>true</var> (the default), and are
- * optional when the namespaces property is <var>false</var> (if one is
- * specified, both must be);</li>
- * <li>the qualified name is required when the namespace-prefixes property
- * is <var>true</var>, and is optional when the namespace-prefixes property
- * is <var>false</var> (the default).</li>
- * </ul>
- *
- * <p>Note that the attribute list provided will contain only
- * attributes with explicit values (specified or defaulted):
- * #IMPLIED attributes will be omitted. The attribute list
- * will contain attributes used for Namespace declarations
- * (xmlns* attributes) only if the
- * <code>http://xml.org/sax/features/namespace-prefixes</code>
- * property is true (it is false by default, and support for a
- * true value is optional).</p>
- *
- * <p>Like {@link #characters characters()}, attribute values may have
- * characters that need more than one <code>char</code> value. </p>
- *
- * @param uri The Namespace URI, or the empty string if the
- * element has no Namespace URI or if Namespace
- * processing is not being performed.
- * @param localName The local name (without prefix), or the
- * empty string if Namespace processing is not being
- * performed.
- * @param qName The qualified name (with prefix), or the
- * empty string if qualified names are not available.
- * @param atts The attributes attached to the element. If
- * there are no attributes, it shall be an empty
- * Attributes object.
- * @throws org.xml.sax.SAXException Any SAX exception, possibly
- * wrapping another exception.
- * @see #endElement
- * @see org.xml.sax.Attributes
- */
- @Override
- public void startElement(
- String uri,
- String localName,
- String qName,
- Attributes atts)
- throws SAXException {
-
- int idx = qName.indexOf(':');
- String attNsPrefix = "";
- if (idx >= 0) {
- attNsPrefix = qName.substring(0, idx);
- }
- namespaceStack.pushNamespace(attNsPrefix, uri);
- for (int i = 0; i < atts.getLength(); i++) {
- String attQName = atts.getQName(i);
- // An attribute only has an namespace if has a prefix
- // If not, stays in namespace of containing node
- idx = attQName.indexOf(':');
- if (idx >= 0) {
- attNsPrefix = attQName.substring(0, idx);
- String attUri = atts.getURI(i);
- namespaceStack.pushNamespace(attNsPrefix, attUri);
- }
- }
-
- contentHandler.startElement(uri, localName, qName, atts);
- // Inform namespaceStack of a new depth
- namespaceStack.increaseLevel();
- }
+ // ContentHandler interface
+ //-------------------------------------------------------------------------
/**
* Receive notification of the end of an element.
@@ -529,51 +494,68 @@ public class XMLOutput implements ContentHandler, LexicalHandler {
}
/**
- * Receive notification of character data.
- *
- * <p>The Parser will call this method to report each chunk of
- * character data. SAX parsers may return all contiguous character
- * data in a single chunk, or they may split it into several
- * chunks; however, all of the characters in any single event
- * must come from the same external entity so that the Locator
- * provides useful information.</p>
- *
- * <p>The application must not attempt to read from the array
- * outside of the specified range.</p>
- *
- * <p>Individual characters may consist of more than one Java
- * <code>char</code> value. There are two important cases where this
- * happens, because characters can't be represented in just sixteen bits.
- * In one case, characters are represented in a <em>Surrogate Pair</em>,
- * using two special Unicode values. Such characters are in the so-called
- * "Astral Planes", with a code point above U+FFFF. A second case involves
- * composite characters, such as a base character combining with one or
- * more accent characters.</p>
+ * Report the end of an entity.
*
- * <p> Your code should not assume that algorithms using
- * <code>char</code>-at-a-time idioms will be working in character
- * units; in some cases they will split characters. This is relevant
- * wherever XML permits arbitrary characters, such as attribute values,
- * processing instruction data, and comments as well as in data reported
- * from this method. It's also generally relevant whenever Java code
- * manipulates internationalized text; the issue isn't unique to XML.</p>
+ * @param name The name of the entity that is ending.
+ * @throws SAXException The application may raise an exception.
+ * @see #startEntity
+ */
+ @Override
+ public void endEntity(String name) throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.endEntity(name);
+ }
+ }
+
+ /**
+ * End the scope of a prefix-URI mapping.
*
- * <p>Note that some parsers will report whitespace in element
- * content using the {@link #ignorableWhitespace ignorableWhitespace}
- * method rather than this one (validating parsers <em>must</em>
- * do so).</p>
+ * <p>See {@link #startPrefixMapping startPrefixMapping} for
+ * details. These events will always occur immediately after the
+ * corresponding {@link #endElement endElement} event, but the order of
+ * {@link #endPrefixMapping endPrefixMapping} events is not otherwise
+ * guaranteed.</p>
*
- * @param ch The characters from the XML document.
- * @param start The start position in the array.
- * @param length The number of characters to read from the array.
- * @throws org.xml.sax.SAXException Any SAX exception, possibly
- * wrapping another exception.
- * @see #ignorableWhitespace
- * @see org.xml.sax.Locator
+ * @param prefix The prefix that was being mapped.
+ * This is the empty string when a default mapping scope ends.
+ * @throws org.xml.sax.SAXException The client may throw
+ * an exception during processing.
+ * @see #startPrefixMapping
+ * @see #endElement
*/
@Override
- public void characters(char[] ch, int start, int length) throws SAXException {
- contentHandler.characters(ch, start, length);
+ public void endPrefixMapping(String prefix) throws SAXException {
+ // End prefix mapping was already called after endElement
+ // contentHandler.endPrefixMapping(prefix);
+ }
+
+ /** Flushes the underlying stream if {@link XMLWriter}
+ * or {@link XMLOutput}.
+ *
+ * @throws IOException
+ */
+ public void flush() throws IOException {
+ if (contentHandler instanceof XMLWriter) {
+ ((XMLWriter)contentHandler).flush();
+ } else if (contentHandler instanceof XMLOutput) {
+ ((XMLOutput)contentHandler).flush();
+ }
+ }
+
+ // Properties
+ //-------------------------------------------------------------------------
+ /**
+ * @return the SAX ContentHandler to use to pipe SAX events into
+ */
+ public ContentHandler getContentHandler() {
+ return contentHandler;
+ }
+
+ /**
+ * @return the SAX LexicalHandler to use to pipe SAX events into
+ */
+ public LexicalHandler getLexicalHandler() {
+ return lexicalHandler;
}
/**
@@ -606,6 +588,28 @@ public class XMLOutput implements ContentHandler, LexicalHandler {
contentHandler.ignorableWhitespace(ch, start, length);
}
+ /** Pass data through the pipeline.
+ * By default, this call is ignored.
+ * Subclasses are invited to use this as a way for children tags to
+ * pass data to their parent.
+ *
+ * @param object the data to pass
+ * @throws SAXException The application may raise an exception.
+ */
+ public void objectData(Object object) throws SAXException {
+ if (contentHandler instanceof XMLOutput)
+ ((XMLOutput) contentHandler).objectData(object);
+ else {
+ if (object!=null) {
+ String output=object.toString();
+ write(output);
+ } else {
+ // we could have a "configurable null-toString"...
+ write("null");
+ }
+ }
+ }
+
/**
* Receive notification of a processing instruction.
*
@@ -635,37 +639,121 @@ public class XMLOutput implements ContentHandler, LexicalHandler {
}
/**
- * Receive notification of a skipped entity.
- * This is not called for entity references within markup constructs
- * such as element start tags or markup declarations. (The XML
- * recommendation requires reporting skipped external entities.
- * SAX also reports internal entity expansion/non-expansion, except
- * within markup constructs.)
- *
- * <p>The Parser will invoke this method each time the entity is
- * skipped. Non-validating processors may skip entities if they
- * have not seen the declarations (because, for example, the
- * entity was declared in an external DTD subset). All processors
- * may skip external entities, depending on the values of the
- * <code>http://xml.org/sax/features/external-general-entities</code>
- * and the
- * <code>http://xml.org/sax/features/external-parameter-entities</code>
- * properties.</p>
+ * Sets the SAX ContentHandler to pipe SAX events into
*
- * @param name The name of the skipped entity. If it is a
- * parameter entity, the name will begin with '%', and if
- * it is the external DTD subset, it will be the string
- * "[dtd]".
- * @throws org.xml.sax.SAXException Any SAX exception, possibly
- * wrapping another exception.
+ * @param contentHandler is the new ContentHandler to use.
+ * This value cannot be null.
*/
- @Override
- public void skippedEntity(String name) throws SAXException {
- contentHandler.skippedEntity(name);
+ public void setContentHandler(ContentHandler contentHandler) {
+ this.contentHandler = Objects.requireNonNull(contentHandler, "contentHandler");
}
- // Lexical Handler interface
- //-------------------------------------------------------------------------
+ /**
+ * Receive an object for locating the origin of SAX document events.
+ *
+ * <p>SAX parsers are strongly encouraged (though not absolutely
+ * required) to supply a locator: if it does so, it must supply
+ * the locator to the application by invoking this method before
+ * invoking any of the other methods in the ContentHandler
+ * interface.</p>
+ *
+ * <p>The locator allows the application to determine the end
+ * position of any document-related event, even if the parser is
+ * not reporting an error. Typically, the application will
+ * use this information for reporting its own errors (such as
+ * character content that does not match an application's
+ * business rules). The information returned by the locator
+ * is probably not sufficient for use with a search engine.</p>
+ *
+ * <p>Note that the locator will return correct information only
+ * during the invocation of the events in this interface. The
+ * application should not attempt to use it at any other time.</p>
+ *
+ * @param locator An object that can return the location of
+ * any SAX document event.
+ * @see org.xml.sax.Locator
+ */
+ @Override
+ public void setDocumentLocator(Locator locator) {
+ contentHandler.setDocumentLocator(locator);
+ }
+
+ // Lexical Handler interface
+ //-------------------------------------------------------------------------
+
+ /**
+ * Sets the SAX LexicalHandler to pipe SAX events into
+ *
+ * @param lexicalHandler is the new LexicalHandler to use.
+ * This value can be null.
+ */
+ public void setLexicalHandler(LexicalHandler lexicalHandler) {
+ this.lexicalHandler = lexicalHandler;
+ }
+
+ /**
+ * Receive notification of a skipped entity.
+ * This is not called for entity references within markup constructs
+ * such as element start tags or markup declarations. (The XML
+ * recommendation requires reporting skipped external entities.
+ * SAX also reports internal entity expansion/non-expansion, except
+ * within markup constructs.)
+ *
+ * <p>The Parser will invoke this method each time the entity is
+ * skipped. Non-validating processors may skip entities if they
+ * have not seen the declarations (because, for example, the
+ * entity was declared in an external DTD subset). All processors
+ * may skip external entities, depending on the values of the
+ * <code>http://xml.org/sax/features/external-general-entities</code>
+ * and the
+ * <code>http://xml.org/sax/features/external-parameter-entities</code>
+ * properties.</p>
+ *
+ * @param name The name of the skipped entity. If it is a
+ * parameter entity, the name will begin with '%', and if
+ * it is the external DTD subset, it will be the string
+ * "[dtd]".
+ * @throws org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ */
+ @Override
+ public void skippedEntity(String name) throws SAXException {
+ contentHandler.skippedEntity(name);
+ }
+
+ /**
+ * Report the start of a CDATA section.
+ *
+ * <p>The contents of the CDATA section will be reported through
+ * the regular {@link org.xml.sax.ContentHandler#characters
+ * characters} event; this event is intended only to report
+ * the boundary.</p>
+ *
+ * @throws SAXException The application may raise an exception.
+ * @see #endCDATA
+ */
+ @Override
+ public void startCDATA() throws SAXException {
+ if (lexicalHandler != null) {
+ lexicalHandler.startCDATA();
+ }
+ }
+
+ /**
+ * Receive notification of the beginning of a document.
+ *
+ * <p>The SAX parser will invoke this method only once, before any
+ * other event callbacks (except for {@link #setDocumentLocator
+ * setDocumentLocator}).</p>
+ *
+ * @throws org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endDocument
+ */
+ @Override
+ public void startDocument() throws SAXException {
+ contentHandler.startDocument();
+ }
/**
* Report the start of DTD declarations, if any.
@@ -714,20 +802,112 @@ public class XMLOutput implements ContentHandler, LexicalHandler {
}
/**
- * Report the end of DTD declarations.
+ * Helper method for outputting a start element event
+ * for an element in no namespace.
+ */
+ public void startElement(String localName) throws SAXException {
+ startElement("", localName, localName, EMPTY_ATTRIBUTES);
+ }
+
+ /**
+ * Helper method for outputting a start element event
+ * for an element in no namespace.
+ */
+ public void startElement(String localName, Attributes attributes) throws SAXException {
+ startElement("", localName, localName, attributes);
+ }
+
+ /**
+ * Receive notification of the beginning of an element.
*
- * <p>This method is intended to report the end of the
- * DOCTYPE declaration; if the document has no DOCTYPE declaration,
- * this method will not be invoked.</p>
+ * <p>The Parser will invoke this method at the beginning of every
+ * element in the XML document; there will be a corresponding
+ * {@link #endElement endElement} event for every startElement event
+ * (even when the element is empty). All of the element's content will be
+ * reported, in order, before the corresponding endElement
+ * event.</p>
*
- * @throws SAXException The application may raise an exception.
- * @see #startDTD
+ * <p>This event allows up to three name components for each
+ * element:</p>
+ *
+ * <ol>
+ * <li>the Namespace URI;</li>
+ * <li>the local name; and</li>
+ * <li>the qualified (prefixed) name.</li>
+ * </ol>
+ *
+ * <p>Any or all of these may be provided, depending on the
+ * values of the <var>http://xml.org/sax/features/namespaces</var>
+ * and the <var>http://xml.org/sax/features/namespace-prefixes</var>
+ * properties:</p>
+ *
+ * <ul>
+ * <li>the Namespace URI and local name are required when
+ * the namespaces property is <var>true</var> (the default), and are
+ * optional when the namespaces property is <var>false</var> (if one is
+ * specified, both must be);</li>
+ * <li>the qualified name is required when the namespace-prefixes property
+ * is <var>true</var>, and is optional when the namespace-prefixes property
+ * is <var>false</var> (the default).</li>
+ * </ul>
+ *
+ * <p>Note that the attribute list provided will contain only
+ * attributes with explicit values (specified or defaulted):
+ * #IMPLIED attributes will be omitted. The attribute list
+ * will contain attributes used for Namespace declarations
+ * (xmlns* attributes) only if the
+ * <code>http://xml.org/sax/features/namespace-prefixes</code>
+ * property is true (it is false by default, and support for a
+ * true value is optional).</p>
+ *
+ * <p>Like {@link #characters characters()}, attribute values may have
+ * characters that need more than one <code>char</code> value. </p>
+ *
+ * @param uri The Namespace URI, or the empty string if the
+ * element has no Namespace URI or if Namespace
+ * processing is not being performed.
+ * @param localName The local name (without prefix), or the
+ * empty string if Namespace processing is not being
+ * performed.
+ * @param qName The qualified name (with prefix), or the
+ * empty string if qualified names are not available.
+ * @param atts The attributes attached to the element. If
+ * there are no attributes, it shall be an empty
+ * Attributes object.
+ * @throws org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @see #endElement
+ * @see org.xml.sax.Attributes
*/
@Override
- public void endDTD() throws SAXException {
- if (lexicalHandler != null) {
- lexicalHandler.endDTD();
+ public void startElement(
+ String uri,
+ String localName,
+ String qName,
+ Attributes atts)
+ throws SAXException {
+
+ int idx = qName.indexOf(':');
+ String attNsPrefix = "";
+ if (idx >= 0) {
+ attNsPrefix = qName.substring(0, idx);
+ }
+ namespaceStack.pushNamespace(attNsPrefix, uri);
+ for (int i = 0; i < atts.getLength(); i++) {
+ String attQName = atts.getQName(i);
+ // An attribute only has an namespace if has a prefix
+ // If not, stays in namespace of containing node
+ idx = attQName.indexOf(':');
+ if (idx >= 0) {
+ attNsPrefix = attQName.substring(0, idx);
+ String attUri = atts.getURI(i);
+ namespaceStack.pushNamespace(attNsPrefix, attUri);
+ }
}
+
+ contentHandler.startElement(uri, localName, qName, atts);
+ // Inform namespaceStack of a new depth
+ namespaceStack.increaseLevel();
}
/**
@@ -787,266 +967,86 @@ public class XMLOutput implements ContentHandler, LexicalHandler {
}
/**
- * Report the end of an entity.
+ * Begin the scope of a prefix-URI Namespace mapping.
*
- * @param name The name of the entity that is ending.
- * @throws SAXException The application may raise an exception.
- * @see #startEntity
- */
- @Override
- public void endEntity(String name) throws SAXException {
- if (lexicalHandler != null) {
- lexicalHandler.endEntity(name);
- }
- }
-
- /**
- * Report the start of a CDATA section.
+ * <p>The information from this event is not necessary for
+ * normal Namespace processing: the SAX XML reader will
+ * automatically replace prefixes for element and attribute
+ * names when the <code>http://xml.org/sax/features/namespaces</code>
+ * feature is <var>true</var> (the default).</p>
*
- * <p>The contents of the CDATA section will be reported through
- * the regular {@link org.xml.sax.ContentHandler#characters
- * characters} event; this event is intended only to report
- * the boundary.</p>
+ * <p>There are cases, however, when applications need to
+ * use prefixes in character data or in attribute values,
+ * where they cannot safely be expanded automatically; the
+ * start/endPrefixMapping event supplies the information
+ * to the application to expand prefixes in those contexts
+ * itself, if necessary.</p>
*
- * @throws SAXException The application may raise an exception.
- * @see #endCDATA
- */
- @Override
- public void startCDATA() throws SAXException {
- if (lexicalHandler != null) {
- lexicalHandler.startCDATA();
- }
- }
-
- /**
- * Report the end of a CDATA section.
- *
- * @throws SAXException The application may raise an exception.
- * @see #startCDATA
- */
- @Override
- public void endCDATA() throws SAXException {
- if (lexicalHandler != null) {
- lexicalHandler.endCDATA();
- }
- }
-
- /**
- * Report an XML comment anywhere in the document.
+ * <p>Note that start/endPrefixMapping events are not
+ * guaranteed to be properly nested relative to each other:
+ * all startPrefixMapping events will occur immediately before the
+ * corresponding {@link #startElement startElement} event,
+ * and all {@link #endPrefixMapping endPrefixMapping}
+ * events will occur immediately after the corresponding
+ * {@link #endElement endElement} event,
+ * but their order is not otherwise
+ * guaranteed.</p>
*
- * <p>This callback will be used for comments inside or outside the
- * document element, including comments in the external DTD
- * subset (if read). Comments in the DTD must be properly
- * nested inside start/endDTD and start/endEntity events (if
- * used).</p>
+ * <p>There should never be start/endPrefixMapping events for the
+ * "xml" prefix, since it is predeclared and immutable.</p>
*
- * @param ch An array holding the characters in the comment.
- * @param start The starting position in the array.
- * @param length The number of characters to use from the array.
- * @throws SAXException The application may raise an exception.
+ * @param prefix The Namespace prefix being declared.
+ * An empty string is used for the default element namespace,
+ * which has no prefix.
+ * @param uri The Namespace URI the prefix is mapped to.
+ * @throws org.xml.sax.SAXException The client may throw
+ * an exception during processing.
+ * @see #endPrefixMapping
+ * @see #startElement
*/
@Override
- public void comment(char ch[], int start, int length) throws SAXException {
- if (lexicalHandler != null) {
- lexicalHandler.comment(ch, start, length);
- }
- }
-
- /** Pass data through the pipeline.
- * By default, this call is ignored.
- * Subclasses are invited to use this as a way for children tags to
- * pass data to their parent.
- *
- * @param object the data to pass
- * @throws SAXException The application may raise an exception.
- */
- public void objectData(Object object) throws SAXException {
- if (contentHandler instanceof XMLOutput)
- ((XMLOutput) contentHandler).objectData(object);
- else {
- if (object!=null) {
- String output=object.toString();
- write(output);
- } else {
- // we could have a "configurable null-toString"...
- write("null");
- }
- }
- }
-
- // Properties
- //-------------------------------------------------------------------------
- /**
- * @return the SAX ContentHandler to use to pipe SAX events into
- */
- public ContentHandler getContentHandler() {
- return contentHandler;
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ namespaceStack.pushNamespace(prefix, uri);
+ // contentHandler.startPrefixMapping(prefix, uri) will be called if needed
+ // in pushNamespace
}
- /**
- * Sets the SAX ContentHandler to pipe SAX events into
- *
- * @param contentHandler is the new ContentHandler to use.
- * This value cannot be null.
- */
- public void setContentHandler(ContentHandler contentHandler) {
- this.contentHandler = Objects.requireNonNull(contentHandler, "contentHandler");
+ @Override
+ public String toString() {
+ return super.toString()
+ + "[contentHandler="
+ + contentHandler
+ + ";lexicalHandler="
+ + lexicalHandler
+ + "]";
}
/**
- * @return the SAX LexicalHandler to use to pipe SAX events into
+ * Outputs the given String as a piece of valid text in the
+ * XML event stream.
+ * Any special XML characters should come out properly escaped.
*/
- public LexicalHandler getLexicalHandler() {
- return lexicalHandler;
+ public void write(String text) throws SAXException {
+ char[] ch = text.toCharArray();
+ characters(ch, 0, ch.length);
}
/**
- * Sets the SAX LexicalHandler to pipe SAX events into
- *
- * @param lexicalHandler is the new LexicalHandler to use.
- * This value can be null.
+ * Outputs the given String as a piece of CDATA in the
+ * XML event stream.
*/
- public void setLexicalHandler(LexicalHandler lexicalHandler) {
- this.lexicalHandler = lexicalHandler;
+ public void writeCDATA(String text) throws SAXException {
+ startCDATA();
+ char[] ch = text.toCharArray();
+ characters(ch, 0, ch.length);
+ endCDATA();
}
- // Implementation methods
- //-------------------------------------------------------------------------
/**
- * Factory method to create a new XMLOutput from an XMLWriter
+ * Outputs a comment to the XML stream.
*/
- protected static XMLOutput createXMLOutput(final XMLWriter xmlWriter) {
- XMLOutput answer = new XMLOutput() {
- @Override
- public void close() throws IOException {
- xmlWriter.close();
- }
- };
- answer.setContentHandler(xmlWriter);
- answer.setLexicalHandler(xmlWriter);
- return answer;
- }
-
- private final class NamespaceStack {
- /** A list of maps: Each map contains prefix to uri mapping */
- private List nsStack;
-
- private NamespaceStack() {
- this.nsStack = new ArrayList();
- this.nsStack.add(new HashMap());
- }
-
- private boolean isRootNodeDefaultNs(String prefix, String uri) {
- return ("".equals(prefix) && "".equals(uri) && nsStack.size() == 1);
- }
-
- public void pushNamespace(String prefix, String uri) throws SAXException {
- Map prefixUriMap;
-
- if (prefix == null) {
- prefix = "";
- }
- if (uri == null) {
- uri = "";
- }
-
- if ("xml".equals(prefix)) {
- // We should ignore setting 'xml' prefix
- // As declared in java of ContentHandler#startPrefixMapping
- return;
- }
-
- // Lets find out if we already declared this same prefix,
- // if not declare in current depth map (the first of list)
- // and call contentHandler.startPrefixMapping(prefix, uri);
- boolean isNew = true;
- for (Iterator iter = nsStack.iterator(); iter.hasNext();) {
- prefixUriMap = (Map) iter.next();
- if (prefixUriMap.containsKey(prefix)) {
- if (uri.equals(prefixUriMap.get(prefix))) {
- // Its an active namespace already
- // System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.pushNamespace() IS NOT NEW prefix="+prefix+",uri="+uri);
- isNew = false;
- }
- // We found it in stack
- // If it was exactly the same, we won't bother
- break;
- }
- }
-
- if (isNew) {
- // not declared sometime before
- prefixUriMap = (Map) nsStack.get(0); // Current depth map
- // Sanity check: Don't let two prefixes for different uris in
- // same depth
- if (prefixUriMap.containsKey(prefix)) {
- if (!uri.equals(prefixUriMap.get(prefix))) {
- throw new SAXException("Cannot set same prefix to different URI in same node: trying to add prefix \""
- + prefix + "\" for uri \""+uri+"\" whereas the declared ones are " + prefixUriMap);
- }
- } else {
- prefixUriMap.put(prefix, uri);
-
- // To avoid setting xmlns="" for top node (not very nice :D)
- // We need to specifically check this condition
- if (!isRootNodeDefaultNs(prefix, uri)) {
-// System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.pushNamespace() prefix="+prefix+",uri="+uri);
- contentHandler.startPrefixMapping(prefix, uri);
- }
- }
- }
- }
-
- public void popNamespaces() throws SAXException {
- Map prefixUriMap = (Map)nsStack.get(0);
- for (Iterator iter = prefixUriMap.keySet().iterator();iter.hasNext();) {
- String prefix = (String)iter.next();
- String uri = (String) prefixUriMap.get(prefix);
- iter.remove();
-
- // If we havent called startPrefixMapping for root node if we wanted to avoid xmlns=""
- // We aren't going to call endPrefixMapping neither
- if (!isRootNodeDefaultNs(prefix, uri)) {
-// System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.popNamespaces() prefix="+prefix);
- contentHandler.endPrefixMapping(prefix);
- }
- }
- }
-
- public void popNamespace(String prefix) throws SAXException {
- Map prefixUriMap = (Map)nsStack.get(0);
-
- if (prefix == null) {
- prefix = "";
- }
-
- if ("xml".equals(prefix)) {
- // We should ignore setting 'xml' prefix
- // As declared in java of ContentHandler#startPrefixMapping
- return;
- }
-
- if (prefixUriMap.containsKey(prefix)) {
- String uri = (String) prefixUriMap.get(prefix);
- prefixUriMap.remove(prefix);
- // If we havent called startPrefixMapping for root node if we wanted to avoid xmlns=""
- // We aren't going to call endPrefixMapping neither
- if (!isRootNodeDefaultNs(prefix, uri)) {
-// System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.popNamespace() prefix="+prefix);
- contentHandler.endPrefixMapping(prefix);
- }
- }/* else {
- improper nesting ? or already removed in popNamespaces
- }
- */
- }
-
- public void decreaseLevel() {
- nsStack.remove(0);
- }
-
- public void increaseLevel() {
- nsStack.add(0, new HashMap());
- }
+ public void writeComment(String text) throws SAXException {
+ char[] ch = text.toCharArray();
+ comment(ch, 0, ch.length);
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/expression/CompositeExpression.java b/core/src/main/java/org/apache/commons/jelly/expression/CompositeExpression.java
index c2ce6f82..903dcd45 100644
--- a/core/src/main/java/org/apache/commons/jelly/expression/CompositeExpression.java
+++ b/core/src/main/java/org/apache/commons/jelly/expression/CompositeExpression.java
@@ -31,22 +31,6 @@ import org.apache.commons.jelly.JellyException;
*/
public class CompositeExpression extends ExpressionSupport {
- /** The expressions */
- private List expressions;
-
- public CompositeExpression() {
- this.expressions = new ArrayList();
- }
-
- public CompositeExpression(List expressions) {
- this.expressions = expressions;
- }
-
- @Override
- public String toString() {
- return super.toString() + "[expressions=" + expressions +"]";
- }
-
/**
* Parses the given String to be either a ConstantExpression, an Expression denoted as
* "${foo}" or some String with embedded expressions such as "abc${something}def${else}xyz"
@@ -203,22 +187,14 @@ public class CompositeExpression extends ExpressionSupport {
return answer;
}
- // Properties
- //-------------------------------------------------------------------------
+ /** The expressions */
+ private List expressions;
- /**
- * @return the Expression objects that make up this
- * composite expression
- */
- public List getExpressions() {
- return expressions;
+ public CompositeExpression() {
+ this.expressions = new ArrayList();
}
- /**
- * Sets the Expression objects that make up this
- * composite expression
- */
- public void setExpressions(List expressions) {
+ public CompositeExpression(List expressions) {
this.expressions = expressions;
}
@@ -229,6 +205,9 @@ public class CompositeExpression extends ExpressionSupport {
expressions.add(expression);
}
+ // Properties
+ //-------------------------------------------------------------------------
+
/**
* A helper method to add a new constant text expression
*/
@@ -236,23 +215,22 @@ public class CompositeExpression extends ExpressionSupport {
addExpression(new ConstantExpression(text));
}
- // Expression interface
- //-------------------------------------------------------------------------
-
+ // inherit javadoc from interface
@Override
- public String getExpressionText() {
- StringBuilder buffer = new StringBuilder();
- for (Iterator iter = expressions.iterator(); iter.hasNext(); ) {
- Expression expression = (Expression) iter.next();
- buffer.append( expression.getExpressionText() );
- }
- return buffer.toString();
+ public Object evaluate(JellyContext context) {
+ return evaluateAsString(context);
}
// inherit javadoc from interface
@Override
- public Object evaluate(JellyContext context) {
- return evaluateAsString(context);
+ public Iterator evaluateAsIterator(JellyContext context) {
+ String value = evaluateAsString(context);
+ if ( value == null ) {
+ return Collections.EMPTY_LIST.iterator();
+ }
+ else {
+ return new SingletonIterator( value );
+ }
}
// inherit javadoc from interface
@@ -270,15 +248,37 @@ public class CompositeExpression extends ExpressionSupport {
}
- // inherit javadoc from interface
+ // Expression interface
+ //-------------------------------------------------------------------------
+
+ /**
+ * @return the Expression objects that make up this
+ * composite expression
+ */
+ public List getExpressions() {
+ return expressions;
+ }
+
@Override
- public Iterator evaluateAsIterator(JellyContext context) {
- String value = evaluateAsString(context);
- if ( value == null ) {
- return Collections.EMPTY_LIST.iterator();
- }
- else {
- return new SingletonIterator( value );
+ public String getExpressionText() {
+ StringBuilder buffer = new StringBuilder();
+ for (Iterator iter = expressions.iterator(); iter.hasNext(); ) {
+ Expression expression = (Expression) iter.next();
+ buffer.append( expression.getExpressionText() );
}
+ return buffer.toString();
+ }
+
+ /**
+ * Sets the Expression objects that make up this
+ * composite expression
+ */
+ public void setExpressions(List expressions) {
+ this.expressions = expressions;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[expressions=" + expressions +"]";
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/expression/ConstantExpression.java b/core/src/main/java/org/apache/commons/jelly/expression/ConstantExpression.java
index 016beb29..d74cc689 100644
--- a/core/src/main/java/org/apache/commons/jelly/expression/ConstantExpression.java
+++ b/core/src/main/java/org/apache/commons/jelly/expression/ConstantExpression.java
@@ -40,16 +40,6 @@ public class ConstantExpression extends ExpressionSupport {
this.value = value;
}
- @Override
- public String toString() {
- return super.toString() + "[value=" + value +"]";
- }
-
- @Override
- public String getExpressionText() {
- return String.valueOf(value);
- }
-
/**
* Evaluate expression against given context.
*
@@ -61,6 +51,11 @@ public class ConstantExpression extends ExpressionSupport {
return value;
}
+ @Override
+ public String getExpressionText() {
+ return String.valueOf(value);
+ }
+
/** Gets the constant value of this expression */
public Object getValue() {
return value;
@@ -70,4 +65,9 @@ public class ConstantExpression extends ExpressionSupport {
public void setValue(Object value) {
this.value = value;
}
+
+ @Override
+ public String toString() {
+ return super.toString() + "[value=" + value +"]";
+ }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/expression/Expression.java b/core/src/main/java/org/apache/commons/jelly/expression/Expression.java
index c3d08088..8146a2bd 100644
--- a/core/src/main/java/org/apache/commons/jelly/expression/Expression.java
+++ b/core/src/main/java/org/apache/commons/jelly/expression/Expression.java
@@ -25,23 +25,12 @@ import org.apache.commons.jelly.JellyContext;
*/
public interface Expression {
- /**
- * @return the textual representation of this expression
- */
- public String getExpressionText();
-
/**
* Evaluates the expression with the given context
* and returns the result
*/
public Object evaluate(JellyContext context);
- /**
- * Evaluates the expression with the given context
- * coercing the result to be a String.
- */
- public String evaluateAsString(JellyContext context);
-
/**
* Evaluates the expression with the given context
* coercing the result to be a boolean.
@@ -54,6 +43,12 @@ public interface Expression {
*/
public Iterator evaluateAsIterator(JellyContext context);
+ /**
+ * Evaluates the expression with the given context
+ * coercing the result to be a String.
+ */
+ public String evaluateAsString(JellyContext context);
+
/**
* This method evaluates the expression until a value (a non-Expression) object
* is returned.
@@ -71,4 +66,9 @@ public interface Expression {
* actual underlying value object.
*/
public Object evaluateRecurse(JellyContext context);
+
+ /**
+ * @return the textual representation of this expression
+ */
+ public String getExpressionText();
}
diff --git a/core/src/main/java/org/apache/commons/jelly/expression/ExpressionSupport.java b/core/src/main/java/org/apache/commons/jelly/expression/ExpressionSupport.java
index 6145569d..39031099 100644
--- a/core/src/main/java/org/apache/commons/jelly/expression/ExpressionSupport.java
+++ b/core/src/main/java/org/apache/commons/jelly/expression/ExpressionSupport.java
@@ -38,32 +38,6 @@ public abstract class ExpressionSupport implements Expression {
protected static final Iterator EMPTY_ITERATOR = Collections.EMPTY_LIST.iterator();
- // inherit javadoc from interface
- @Override
- public String evaluateAsString(JellyContext context) {
- Object value = evaluateRecurse(context);
- // sometimes when Jelly is used inside Maven the value
- // of an expression can actually be an expression.
- // e.g. ${foo.bar} can lookup "foo.bar" in a Maven context
- // which could actually be an expression
-
- if ( value != null ) {
- return value.toString();
- }
- return null;
- }
-
- // inherit javadoc from interface
- @Override
- public Object evaluateRecurse(JellyContext context) {
- Object value = evaluate(context);
- if (value instanceof Expression) {
- Expression expression = (Expression) value;
- return expression.evaluateRecurse(context);
- }
- return value;
- }
-
// inherit javadoc from interface
@Override
public boolean evaluateAsBoolean(JellyContext context) {
@@ -118,4 +92,30 @@ public abstract class ExpressionSupport implements Expression {
return new SingletonIterator( value );
}
}
+
+ // inherit javadoc from interface
+ @Override
+ public String evaluateAsString(JellyContext context) {
+ Object value = evaluateRecurse(context);
+ // sometimes when Jelly is used inside Maven the value
+ // of an expression can actually be an expression.
+ // e.g. ${foo.bar} can lookup "foo.bar" in a Maven context
+ // which could actually be an expression
+
+ if ( value != null ) {
+ return value.toString();
+ }
+ return null;
+ }
+
+ // inherit javadoc from interface
+ @Override
+ public Object evaluateRecurse(JellyContext context) {
+ Object value = evaluate(context);
+ if (value instanceof Expression) {
+ Expression expression = (Expression) value;
+ return expression.evaluateRecurse(context);
+ }
+ return value;
+ }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/expression/jexl/JexlExpression.java b/core/src/main/java/org/apache/commons/jelly/expression/jexl/JexlExpression.java
index 10e0bca0..31c48ea9 100644
--- a/core/src/main/java/org/apache/commons/jelly/expression/jexl/JexlExpression.java
+++ b/core/src/main/java/org/apache/commons/jelly/expression/jexl/JexlExpression.java
@@ -28,66 +28,6 @@ import org.apache.commons.jexl.JexlContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-/**
- * Represents a <a href="https://commons.apache.org/jexl/">Jexl</a>
- * expression which fully supports the Expression Language in JSTL and JSP
- * along with some extra features like object method invocation.
- */
-
-public class JexlExpression extends ExpressionSupport {
-
- /** The Log to which logging calls will be made. */
- private static final Log log = LogFactory.getLog(JexlExpression.class);
-
- /** The Jexl expression object */
- private Expression expression;
-
- public JexlExpression(Expression expression) {
- this.expression = expression;
- }
-
- @Override
- public String toString() {
- return super.toString() + "[" + expression.getExpression() + "]";
- }
-
- // Expression interface
- //-------------------------------------------------------------------------
- @Override
- public String getExpressionText() {
- return "${" + expression.getExpression() + "}";
- }
-
- @Override
- public Object evaluate(JellyContext context) {
- try {
- JexlContext jexlContext = new JellyJexlContext( context );
- if (log.isDebugEnabled()) {
- log.debug("Evaluating EL: " + expression.getExpression());
- }
- Object value = expression.evaluate(jexlContext);
-
- if (log.isDebugEnabled()) {
- log.debug("value of expression: " + value);
- }
-
- return value;
- }
- catch (Exception e) {
- if (context.isSuppressExpressionExceptions()) {
- log.warn("Caught exception evaluating: " + expression + ". Reason: " + e, e);
- return null;
- } else {
- if (e instanceof RuntimeException)
- throw (RuntimeException)e;
- if (e instanceof IllegalStateException)
- throw (IllegalStateException )e;
- throw (IllegalStateException)new IllegalStateException (e.getMessage(), e);
- }
- }
- }
-}
-
final class JellyJexlContext implements JexlContext {
private Map vars;
@@ -97,14 +37,14 @@ final class JellyJexlContext implements JexlContext {
}
@Override
- public void setVars(Map vars) {
- this.vars.clear();
- this.vars.putAll( vars );
+ public Map getVars() {
+ return this.vars;
}
@Override
- public Map getVars() {
- return this.vars;
+ public void setVars(Map vars) {
+ this.vars.clear();
+ this.vars.putAll( vars );
}
}
@@ -116,11 +56,6 @@ final class JellyMap implements Map {
this.context = context;
}
- @Override
- public Object get(Object key) {
- return context.getVariable( (String) key );
- }
-
@Override
public void clear() {
// not implemented
@@ -141,6 +76,11 @@ final class JellyMap implements Map {
return null;
}
+ @Override
+ public Object get(Object key) {
+ return context.getVariable( (String) key );
+ }
+
@Override
public boolean isEmpty() {
return false;
@@ -176,3 +116,63 @@ final class JellyMap implements Map {
return null;
}
}
+
+/**
+ * Represents a <a href="https://commons.apache.org/jexl/">Jexl</a>
+ * expression which fully supports the Expression Language in JSTL and JSP
+ * along with some extra features like object method invocation.
+ */
+
+public class JexlExpression extends ExpressionSupport {
+
+ /** The Log to which logging calls will be made. */
+ private static final Log log = LogFactory.getLog(JexlExpression.class);
+
+ /** The Jexl expression object */
+ private Expression expression;
+
+ public JexlExpression(Expression expression) {
+ this.expression = expression;
+ }
+
+ @Override
+ public Object evaluate(JellyContext context) {
+ try {
+ JexlContext jexlContext = new JellyJexlContext( context );
+ if (log.isDebugEnabled()) {
+ log.debug("Evaluating EL: " + expression.getExpression());
+ }
+ Object value = expression.evaluate(jexlContext);
+
+ if (log.isDebugEnabled()) {
+ log.debug("value of expression: " + value);
+ }
+
+ return value;
+ }
+ catch (Exception e) {
+ if (context.isSuppressExpressionExceptions()) {
+ log.warn("Caught exception evaluating: " + expression + ". Reason: " + e, e);
+ return null;
+ } else {
+ if (e instanceof RuntimeException)
+ throw (RuntimeException)e;
+ if (e instanceof IllegalStateException)
+ throw (IllegalStateException )e;
+ throw (IllegalStateException)new IllegalStateException (e.getMessage(), e);
+ }
+ }
+ }
+
+ // Expression interface
+ //-------------------------------------------------------------------------
+ @Override
+ public String getExpressionText() {
+ return "${" + expression.getExpression() + "}";
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[" + expression.getExpression() + "]";
+ }
+}
diff --git a/core/src/main/java/org/apache/commons/jelly/expression/jexl/JexlExpressionFactory.java b/core/src/main/java/org/apache/commons/jelly/expression/jexl/JexlExpressionFactory.java
index 3a6c7042..5dd2c17a 100644
--- a/core/src/main/java/org/apache/commons/jelly/expression/jexl/JexlExpressionFactory.java
+++ b/core/src/main/java/org/apache/commons/jelly/expression/jexl/JexlExpressionFactory.java
@@ -34,9 +34,44 @@ import org.apache.commons.jelly.expression.ExpressionSupport;
public class JexlExpressionFactory implements ExpressionFactory {
+ private final class ExpressionSupportLocal extends ExpressionSupport {
+
+ protected Expression jexlExpression = null;
+ protected String text = null;
+
+ public ExpressionSupportLocal(Expression jexlExpression, String text) {
+ this.jexlExpression = jexlExpression;
+ this.text = text;
+ }
+
+ @Override
+ public Object evaluate(JellyContext context) {
+ Object answer = jexlExpression.evaluate(context);
+
+ if ( answer == null ) {
+ answer = context.getVariable(text);
+ }
+
+ return answer;
+ }
+
+ @Override
+ public String getExpressionText() {
+ return "${" + text + "}";
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[expression:" + text + "]";
+ }
+ }
+
/** Whether we should allow Ant-style expressions, using dots as part of variable name */
private boolean supportAntVariables = true;
+ // Properties
+ //-------------------------------------------------------------------------
+
// ExpressionFactory interface
//-------------------------------------------------------------------------
@Override
@@ -69,9 +104,6 @@ public class JexlExpressionFactory implements ExpressionFactory {
return jexlExpression;
}
- // Properties
- //-------------------------------------------------------------------------
-
/**
* @return whether we should allow Ant-style expressions, using dots as
* part of variable name
@@ -80,14 +112,6 @@ public class JexlExpressionFactory implements ExpressionFactory {
return supportAntVariables;
}
- /**
- * Sets whether we should allow Ant-style expressions, using dots as
- * part of variable name
- */
- public void setSupportAntVariables(boolean supportAntVariables) {
- this.supportAntVariables = supportAntVariables;
- }
-
// Implementation methods
//-------------------------------------------------------------------------
@@ -107,36 +131,12 @@ public class JexlExpressionFactory implements ExpressionFactory {
return true;
}
- private final class ExpressionSupportLocal extends ExpressionSupport {
-
- protected Expression jexlExpression = null;
- protected String text = null;
-
- public ExpressionSupportLocal(Expression jexlExpression, String text) {
- this.jexlExpression = jexlExpression;
- this.text = text;
- }
-
- @Override
- public Object evaluate(JellyContext context) {
- Object answer = jexlExpression.evaluate(context);
-
- if ( answer == null ) {
- answer = context.getVariable(text);
- }
-
- return answer;
- }
-
- @Override
- public String getExpressionText() {
- return "${" + text + "}";
- }
-
- @Override
- public String toString() {
- return super.toString() + "[expression:" + text + "]";
- }
+ /**
+ * Sets whether we should allow Ant-style expressions, using dots as
+ * part of variable name
+ */
+ public void setSupportAntVariables(boolean supportAntVariables) {
+ this.supportAntVariables = supportAntVariables;
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/expression/xpath/XPathExpression.java b/core/src/main/java/org/apache/commons/jelly/expression/xpath/XPathExpression.java
index ac44e112..4026d46c 100644
--- a/core/src/main/java/org/apache/commons/jelly/expression/xpath/XPathExpression.java
+++ b/core/src/main/java/org/apache/commons/jelly/expression/xpath/XPathExpression.java
@@ -58,16 +58,21 @@ public class XPathExpression extends ExpressionSupport implements VariableContex
this.uris = createUriMap(namespaceContext);
}
- @Override
- public String toString() {
- return getExpressionText();
- }
-
- // Expression interface
- //-------------------------------------------------------------------------
- @Override
- public String getExpressionText() {
- return this.text;
+ /**
+ * Factory method to create a synchronized Map of non-null and non-blank
+ * namespace prefixes to namespace URIs
+ */
+ protected Map createUriMap(Map namespaceContext) {
+ // now lets clone the Map but ignoring default or null prefixes
+ Map uris = new Hashtable(namespaceContext.size());
+ for (Iterator iter = namespaceContext.entrySet().iterator(); iter.hasNext(); ) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String prefix = (String) entry.getKey();
+ if (prefix != null && prefix.length() != 0) {
+ uris.put(prefix, entry.getValue());
+ }
+ }
+ return uris;
}
@Override
@@ -99,6 +104,13 @@ public class XPathExpression extends ExpressionSupport implements VariableContex
return null;
}
+ // Expression interface
+ //-------------------------------------------------------------------------
+ @Override
+ public String getExpressionText() {
+ return this.text;
+ }
+
// VariableContext interface
//-------------------------------------------------------------------------
@Override
@@ -117,20 +129,8 @@ public class XPathExpression extends ExpressionSupport implements VariableContex
// Implementation methods
//-------------------------------------------------------------------------
- /**
- * Factory method to create a synchronized Map of non-null and non-blank
- * namespace prefixes to namespace URIs
- */
- protected Map createUriMap(Map namespaceContext) {
- // now lets clone the Map but ignoring default or null prefixes
- Map uris = new Hashtable(namespaceContext.size());
- for (Iterator iter = namespaceContext.entrySet().iterator(); iter.hasNext(); ) {
- Map.Entry entry = (Map.Entry) iter.next();
- String prefix = (String) entry.getKey();
- if (prefix != null && prefix.length() != 0) {
- uris.put(prefix, entry.getValue());
- }
- }
- return uris;
+ @Override
+ public String toString() {
+ return getExpressionText();
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/Attribute.java b/core/src/main/java/org/apache/commons/jelly/impl/Attribute.java
index f3cadc1b..952e1cdf 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/Attribute.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/Attribute.java
@@ -40,19 +40,11 @@ public class Attribute {
//-------------------------------------------------------------------------
/**
- * Returns whether this attribute is required.
- * @return boolean
- */
- public boolean isRequired() {
- return required;
- }
-
- /**
- * Sets whether this attribute is required.
- * @param required is true if this attribute is a mandatory attribute
+ * Returns the defaultValue.
+ * @return Expression
*/
- public void setRequired(boolean required) {
- this.required = required;
+ public Expression getDefaultValue() {
+ return defaultValue;
}
/**
@@ -64,19 +56,11 @@ public class Attribute {
}
/**
- * Sets the name.
- * @param name The name to set
- */
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * Returns the defaultValue.
- * @return Expression
+ * Returns whether this attribute is required.
+ * @return boolean
*/
- public Expression getDefaultValue() {
- return defaultValue;
+ public boolean isRequired() {
+ return required;
}
/**
@@ -87,4 +71,20 @@ public class Attribute {
this.defaultValue = defaultValue;
}
+ /**
+ * Sets the name.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Sets whether this attribute is required.
+ * @param required is true if this attribute is a mandatory attribute
+ */
+ public void setRequired(boolean required) {
+ this.required = required;
+ }
+
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/DefaultTagLibraryResolver.java b/core/src/main/java/org/apache/commons/jelly/impl/DefaultTagLibraryResolver.java
index f919fc8a..d2e559b1 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/DefaultTagLibraryResolver.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/DefaultTagLibraryResolver.java
@@ -59,6 +59,85 @@ public class DefaultTagLibraryResolver implements TagLibraryResolver {
// TagLibraryResolver interface
//-------------------------------------------------------------------------
+ /**
+ * Gets the class loader to be used for instantiating application objects
+ * when required. This is determined based upon the following rules:
+ * <ul>
+ * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
+ * <li>The thread context class loader, if it exists and the
+ * <code>useContextClassLoader</code> property is set to true</li>
+ * <li>The class loader used to load the XMLParser class itself.
+ * </ul>
+ */
+ public ClassLoader getClassLoader() {
+ return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass());
+ }
+
+ // Properties
+ //-------------------------------------------------------------------------
+
+ /**
+ * @return the DiscoverClasses instance to use to locate services.
+ * This object is lazily created if it has not been configured.
+ */
+ public DiscoverClasses getDiscoverClasses() {
+ if ( discovery == null ) {
+ ClassLoaders loaders = ClassLoaders.getAppLoaders(TagLibrary.class, getClass(), false);
+ discovery = new DiscoverClasses(loaders);
+ }
+ return discovery;
+ }
+
+ /**
+ * Gets the boolean as to whether the context classloader should be used.
+ */
+ public boolean getUseContextClassLoader() {
+ return useContextClassLoader;
+ }
+
+ /**
+ * Instantiates the given class name. Otherwise an exception is logged
+ * and null is returned
+ */
+ protected TagLibrary loadClass(String uri, String className) {
+ try {
+ Class theClass = getClassLoader().loadClass(className);
+ if ( theClass != null ) {
+ return newInstance(uri, theClass);
+ }
+ }
+ catch (ClassNotFoundException e) {
+ log.error("Could not find the class: " + className + " when trying to resolve URI: " + uri, e);
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new instance of the given TagLibrary class or
+ * return null if it could not be instantiated.
+ */
+ protected TagLibrary newInstance(String uri, Class theClass) {
+ try {
+ Object object = theClass.newInstance();
+ if (object instanceof TagLibrary) {
+ return (TagLibrary) object;
+ }
+ else {
+ log.error(
+ "The tag library object mapped to: "
+ + uri
+ + " is not a TagLibrary. Object = "
+ + object);
+ }
+ }
+ catch (Exception e) {
+ log.error(
+ "Could not instantiate instance of class: " + theClass.getName() + ". Reason: " + e,
+ e);
+ }
+ return null;
+ }
+
/**
* Attempts to resolve the given URI to be associated with a TagLibrary
* otherwise null is returned to indicate no tag library could be found
@@ -106,23 +185,6 @@ public class DefaultTagLibraryResolver implements TagLibraryResolver {
return null;
}
- // Properties
- //-------------------------------------------------------------------------
-
- /**
- * Gets the class loader to be used for instantiating application objects
- * when required. This is determined based upon the following rules:
- * <ul>
- * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
- * <li>The thread context class loader, if it exists and the
- * <code>useContextClassLoader</code> property is set to true</li>
- * <li>The class loader used to load the XMLParser class itself.
- * </ul>
- */
- public ClassLoader getClassLoader() {
- return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass());
- }
-
/**
* Sets the class loader to be used for instantiating application objects
* when required.
@@ -134,11 +196,15 @@ public class DefaultTagLibraryResolver implements TagLibraryResolver {
this.classLoader = classLoader;
}
+ // Implementation methods
+ //-------------------------------------------------------------------------
+
/**
- * Gets the boolean as to whether the context classloader should be used.
+ * Sets the fully configured DiscoverClasses instance to be used to
+ * lookup services
*/
- public boolean getUseContextClassLoader() {
- return useContextClassLoader;
+ public void setDiscoverClasses(DiscoverClasses discovery) {
+ this.discovery = discovery;
}
/**
@@ -154,70 +220,4 @@ public class DefaultTagLibraryResolver implements TagLibraryResolver {
useContextClassLoader = use;
}
- /**
- * @return the DiscoverClasses instance to use to locate services.
- * This object is lazily created if it has not been configured.
- */
- public DiscoverClasses getDiscoverClasses() {
- if ( discovery == null ) {
- ClassLoaders loaders = ClassLoaders.getAppLoaders(TagLibrary.class, getClass(), false);
- discovery = new DiscoverClasses(loaders);
- }
- return discovery;
- }
-
- /**
- * Sets the fully configured DiscoverClasses instance to be used to
- * lookup services
- */
- public void setDiscoverClasses(DiscoverClasses discovery) {
- this.discovery = discovery;
- }
-
- // Implementation methods
- //-------------------------------------------------------------------------
-
- /**
- * Instantiates the given class name. Otherwise an exception is logged
- * and null is returned
- */
- protected TagLibrary loadClass(String uri, String className) {
- try {
- Class theClass = getClassLoader().loadClass(className);
- if ( theClass != null ) {
- return newInstance(uri, theClass);
- }
- }
- catch (ClassNotFoundException e) {
- log.error("Could not find the class: " + className + " when trying to resolve URI: " + uri, e);
- }
- return null;
- }
-
- /**
- * Creates a new instance of the given TagLibrary class or
- * return null if it could not be instantiated.
- */
- protected TagLibrary newInstance(String uri, Class theClass) {
- try {
- Object object = theClass.newInstance();
- if (object instanceof TagLibrary) {
- return (TagLibrary) object;
- }
- else {
- log.error(
- "The tag library object mapped to: "
- + uri
- + " is not a TagLibrary. Object = "
- + object);
- }
- }
- catch (Exception e) {
- log.error(
- "Could not instantiate instance of class: " + theClass.getName() + ". Reason: " + e,
- e);
- }
- return null;
- }
-
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/DynamicBeanTag.java b/core/src/main/java/org/apache/commons/jelly/impl/DynamicBeanTag.java
index 5fb1557e..70a217dd 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/DynamicBeanTag.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/DynamicBeanTag.java
@@ -101,33 +101,6 @@ public class DynamicBeanTag extends DynaBeanTagSupport implements BeanSource {
setAttributesSet.clear();
}
- @Override
- public void setAttribute(String name, Object value) throws JellyTagException {
- boolean isVariableName = false;
- if (variableNameAttribute != null ) {
- if ( variableNameAttribute.equals( name ) ) {
- if (value == null) {
- var = null;
- }
- else {
- var = value.toString();
- }
- isVariableName = true;
- }
- }
- if (! isVariableName) {
-
- // #### strictly speaking we could
- // know what attributes are specified at compile time
- // so this dynamic set is unnecessary
- setAttributesSet.add(name);
-
- // we could maybe implement attribute specific validation here
-
- super.setAttribute(name, value);
- }
- }
-
// Tag interface
//-------------------------------------------------------------------------
@Override
@@ -196,6 +169,16 @@ public class DynamicBeanTag extends DynaBeanTagSupport implements BeanSource {
}
}
+ // Properties
+ //-------------------------------------------------------------------------
+ /**
+ * @return the bean that has just been created
+ */
+ @Override
+ public Object getBean() {
+ return bean;
+ }
+
/**
* Report the state of the bean when method invocation fails
* so that the user can determine any problems that might
@@ -220,13 +203,30 @@ public class DynamicBeanTag extends DynaBeanTagSupport implements BeanSource {
throw new JellyTagException(e);
}
- // Properties
- //-------------------------------------------------------------------------
- /**
- * @return the bean that has just been created
- */
@Override
- public Object getBean() {
- return bean;
+ public void setAttribute(String name, Object value) throws JellyTagException {
+ boolean isVariableName = false;
+ if (variableNameAttribute != null ) {
+ if ( variableNameAttribute.equals( name ) ) {
+ if (value == null) {
+ var = null;
+ }
+ else {
+ var = value.toString();
+ }
+ isVariableName = true;
+ }
+ }
+ if (! isVariableName) {
+
+ // #### strictly speaking we could
+ // know what attributes are specified at compile time
+ // so this dynamic set is unnecessary
+ setAttributesSet.add(name);
+
+ // we could maybe implement attribute specific validation here
+
+ super.setAttribute(name, value);
+ }
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/DynamicDynaBeanTag.java b/core/src/main/java/org/apache/commons/jelly/impl/DynamicDynaBeanTag.java
index b4d71191..b8f748dd 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/DynamicDynaBeanTag.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/DynamicDynaBeanTag.java
@@ -74,33 +74,6 @@ public class DynamicDynaBeanTag extends DynaBeanTagSupport implements BeanSource
setAttributesSet.clear();
}
- @Override
- public void setAttribute(String name, Object value) throws JellyTagException {
- boolean isVariableName = false;
- if (variableNameAttribute != null ) {
- if ( variableNameAttribute.equals( name ) ) {
- if (value == null) {
- var = null;
- }
- else {
- var = value.toString();
- }
- isVariableName = true;
- }
- }
- if (! isVariableName) {
-
- // #### strictly speaking we could
- // know what attributes are specified at compile time
- // so this dynamic set is unnecessary
- setAttributesSet.add(name);
-
- // we could maybe implement attribute specific validation here
-
- super.setAttribute(name, value);
- }
- }
-
// Tag interface
//-------------------------------------------------------------------------
@Override
@@ -145,4 +118,31 @@ public class DynamicDynaBeanTag extends DynaBeanTagSupport implements BeanSource
public Object getBean() {
return getDynaBean();
}
+
+ @Override
+ public void setAttribute(String name, Object value) throws JellyTagException {
+ boolean isVariableName = false;
+ if (variableNameAttribute != null ) {
+ if ( variableNameAttribute.equals( name ) ) {
+ if (value == null) {
+ var = null;
+ }
+ else {
+ var = value.toString();
+ }
+ isVariableName = true;
+ }
+ }
+ if (! isVariableName) {
+
+ // #### strictly speaking we could
+ // know what attributes are specified at compile time
+ // so this dynamic set is unnecessary
+ setAttributesSet.add(name);
+
+ // we could maybe implement attribute specific validation here
+
+ super.setAttribute(name, value);
+ }
+ }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/DynamicTag.java b/core/src/main/java/org/apache/commons/jelly/impl/DynamicTag.java
index 595e381e..b5c7987b 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/DynamicTag.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/DynamicTag.java
@@ -77,14 +77,6 @@ public class DynamicTag extends DynaTagSupport {
getTemplate().run(newJellyContext, output);
}
- // DynaTag interface
- //-------------------------------------------------------------------------
- @Override
- public void setAttribute(String name, Object value) {
- attributes.put(name, value);
- attributes.put(name + "Attr", value);
- }
-
// Properties
//-------------------------------------------------------------------------
/** The template to be executed by this tag which may well
@@ -94,6 +86,14 @@ public class DynamicTag extends DynaTagSupport {
return template;
}
+ // DynaTag interface
+ //-------------------------------------------------------------------------
+ @Override
+ public void setAttribute(String name, Object value) {
+ attributes.put(name, value);
+ attributes.put(name + "Attr", value);
+ }
+
public void setTemplate(Script template) {
this.template = template;
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/DynamicTagLibrary.java b/core/src/main/java/org/apache/commons/jelly/impl/DynamicTagLibrary.java
index 7770104d..f8dacae8 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/DynamicTagLibrary.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/DynamicTagLibrary.java
@@ -42,21 +42,6 @@ public class DynamicTagLibrary extends TagLibrary {
this.uri = uri;
}
- /** Creates a new script to execute the given tag name and attributes */
- @Override
- public TagScript createTagScript(final String name, final Attributes attributes)
- throws JellyException {
-
- return new TagScript(
- new TagFactory() {
- @Override
- public Tag createTag(String name, Attributes attributes) throws JellyException {
- return DynamicTagLibrary.this.createTag(name, attributes);
- }
- }
- );
- }
-
/** Creates a new Tag for the given tag name if it exists */
@Override
public Tag createTag(String name, Attributes attributes)
@@ -79,30 +64,19 @@ public class DynamicTagLibrary extends TagLibrary {
return null;
}
- /**
- * Creates a new tag with the given name and template
- */
- public void registerDynamicTag(String name, Script template) {
- templates.put(name, template);
- }
-
- /**
- * Creates a new Jelly Bean Tag with the given name
- */
- public void registerBeanTag(String name, TagFactory factory) {
- templates.put(name, factory);
- }
+ /** Creates a new script to execute the given tag name and attributes */
+ @Override
+ public TagScript createTagScript(final String name, final Attributes attributes)
+ throws JellyException {
- /**
- * Returns the script associated with the given tag name
- *
- * @param name The tag name
- * @return The script associated with <code>name</code>, or
- * {@code null} if the tag doesn't exist or isn't a script
- */
- public Script getDynamicTag(String name) {
- Object result = templates.get(name);
- return (result instanceof Script) ? (Script) result : null;
+ return new TagScript(
+ new TagFactory() {
+ @Override
+ public Tag createTag(String name, Attributes attributes) throws JellyException {
+ return DynamicTagLibrary.this.createTag(name, attributes);
+ }
+ }
+ );
}
/**
@@ -126,22 +100,44 @@ public class DynamicTagLibrary extends TagLibrary {
return result;
}
+ /**
+ * Returns the script associated with the given tag name
+ *
+ * @param name The tag name
+ * @return The script associated with <code>name</code>, or
+ * {@code null} if the tag doesn't exist or isn't a script
+ */
+ public Script getDynamicTag(String name) {
+ Object result = templates.get(name);
+ return (result instanceof Script) ? (Script) result : null;
+ }
+
+ /**
+ * Returns the parent library which will be used to resolve unknown tags.
+ * @return TagLibrary
+ */
+ public TagLibrary getParent() {
+ return parent;
+ }
+
// Properties
//-------------------------------------------------------------------------
public String getUri() {
return uri;
}
- public void setUri(String uri) {
- this.uri = uri;
+ /**
+ * Creates a new Jelly Bean Tag with the given name
+ */
+ public void registerBeanTag(String name, TagFactory factory) {
+ templates.put(name, factory);
}
/**
- * Returns the parent library which will be used to resolve unknown tags.
- * @return TagLibrary
+ * Creates a new tag with the given name and template
*/
- public TagLibrary getParent() {
- return parent;
+ public void registerDynamicTag(String name, Script template) {
+ templates.put(name, template);
}
/**
@@ -152,4 +148,8 @@ public class DynamicTagLibrary extends TagLibrary {
this.parent = parent;
}
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/Embedded.java b/core/src/main/java/org/apache/commons/jelly/impl/Embedded.java
index c4188b58..87d85ea6 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/Embedded.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/Embedded.java
@@ -56,6 +56,8 @@ import org.xml.sax.SAXException;
* </pre>
*/
public class Embedded {
+ /** The Log to which logging calls will be made. */
+ private static final Log log = LogFactory.getLog(Embedded.class);
/** Jelly Engine */
Jelly jellyEngine = new Jelly();
/** JellyContext*/
@@ -75,8 +77,6 @@ public class Embedded {
boolean scriptCompiled = false;
/** ErrorMsg*/
private String errorMsg;
- /** The Log to which logging calls will be made. */
- private static final Log log = LogFactory.getLog(Embedded.class);
/**
* Default Constructor
@@ -86,74 +86,6 @@ public class Embedded {
//m_context.setClassLoader(new TagLibraryClassLoader(m_context));
}
- /**
- * Method setContext.
- * @param context
- */
- public void setContext(JellyContext context) {
- this.context = context;
- }
-
- /**
- * Method getContext.
- * @return JellyContext
- */
- public JellyContext getContext() {
- return context;
- }
-
- /**
- * Sets a new variable within the context for the script to use.
- * @param name
- * @param value
- */
- public void setVariable(String name, Object value) {
- context.setVariable(name, value);
- }
-
- /**
- * Sets the input script
- * @param scriptAsString
- */
- public void setScript(String scriptAsString) {
-
- try {
- URL url = resolveURL(scriptAsString);
- inputStream = url.openStream();
- }
- catch (MalformedURLException e) {
- //Encapsulate the string within
- inputStream = new ByteArrayInputStream(scriptAsString.getBytes());
- }
- catch (IOException e) {
- //Error reading from the URL
- inputStream = null;
- }
-
- compileScriptAndKeep();
-
- }
-
- /**
- * @return the URL for the relative file name or absolute URL
- */
- private URL resolveURL(String name) throws MalformedURLException {
- File file = new File(name);
- if (file.exists()) {
- return file.toURL();
- }
- return new URL(name);
- }
-
- /**
- * Sets the input stream
- * @param scriptAsInputStream
- */
- public void setScript(InputStream scriptAsInputStream) {
- inputStream = scriptAsInputStream;
- compileScriptAndKeep();
- }
-
/**
* Compile the script
*/
@@ -178,12 +110,54 @@ public class Embedded {
}
/**
- * Method setOutputStream.
- * @param outputStream
+ * Execute the jelly script and capture the errors (ifany) within.
*/
- public void setOutputStream(OutputStream outputStream) {
- this.outputStream = outputStream;
- this.output = XMLOutput.createXMLOutput(new OutputStreamWriter(this.outputStream));
+ public boolean execute() {
+ if (log.isDebugEnabled())
+ log.debug("Starting Execution");
+ //If script has not been compiled then return the errorMsg that occurredd during compilation
+ if (!scriptCompiled) {
+ if (log.isErrorEnabled())
+ log.error(scriptCompilationException.getMessage());
+ setErrorMsg(scriptCompilationException.getMessage());
+ return false;
+ }
+ if (inputStream == null) {
+ if (log.isErrorEnabled())
+ log.error("[Error] Input script-resource NOT accessible");
+ setErrorMsg("[Error] Input script-resource NOT accessible");
+ return false;
+ }
+ try {
+ script.run(context, output);
+ outputStream.close();
+ output.flush();
+ }
+ catch (Exception e) {
+ if (log.isErrorEnabled())
+ log.error(e.getMessage());
+ setErrorMsg(e.getMessage());
+ return false;
+ }
+ if (log.isDebugEnabled())
+ log.debug("Done Executing");
+ return true;
+ }
+
+ /**
+ * Method getContext.
+ * @return JellyContext
+ */
+ public JellyContext getContext() {
+ return context;
+ }
+
+ /**
+ * Returns the errorMsg.
+ * @return String
+ */
+ public String getErrorMsg() {
+ return errorMsg;
}
/**
@@ -206,11 +180,22 @@ public class Embedded {
}
/**
- * Returns the errorMsg.
- * @return String
+ * @return the URL for the relative file name or absolute URL
*/
- public String getErrorMsg() {
- return errorMsg;
+ private URL resolveURL(String name) throws MalformedURLException {
+ File file = new File(name);
+ if (file.exists()) {
+ return file.toURL();
+ }
+ return new URL(name);
+ }
+
+ /**
+ * Method setContext.
+ * @param context
+ */
+ public void setContext(JellyContext context) {
+ this.context = context;
}
/**
@@ -222,38 +207,53 @@ public class Embedded {
}
/**
- * Execute the jelly script and capture the errors (ifany) within.
+ * Method setOutputStream.
+ * @param outputStream
*/
- public boolean execute() {
- if (log.isDebugEnabled())
- log.debug("Starting Execution");
- //If script has not been compiled then return the errorMsg that occurredd during compilation
- if (!scriptCompiled) {
- if (log.isErrorEnabled())
- log.error(scriptCompilationException.getMessage());
- setErrorMsg(scriptCompilationException.getMessage());
- return false;
- }
- if (inputStream == null) {
- if (log.isErrorEnabled())
- log.error("[Error] Input script-resource NOT accessible");
- setErrorMsg("[Error] Input script-resource NOT accessible");
- return false;
- }
+ public void setOutputStream(OutputStream outputStream) {
+ this.outputStream = outputStream;
+ this.output = XMLOutput.createXMLOutput(new OutputStreamWriter(this.outputStream));
+ }
+
+ /**
+ * Sets the input stream
+ * @param scriptAsInputStream
+ */
+ public void setScript(InputStream scriptAsInputStream) {
+ inputStream = scriptAsInputStream;
+ compileScriptAndKeep();
+ }
+
+ /**
+ * Sets the input script
+ * @param scriptAsString
+ */
+ public void setScript(String scriptAsString) {
+
try {
- script.run(context, output);
- outputStream.close();
- output.flush();
+ URL url = resolveURL(scriptAsString);
+ inputStream = url.openStream();
}
- catch (Exception e) {
- if (log.isErrorEnabled())
- log.error(e.getMessage());
- setErrorMsg(e.getMessage());
- return false;
+ catch (MalformedURLException e) {
+ //Encapsulate the string within
+ inputStream = new ByteArrayInputStream(scriptAsString.getBytes());
}
- if (log.isDebugEnabled())
- log.debug("Done Executing");
- return true;
+ catch (IOException e) {
+ //Error reading from the URL
+ inputStream = null;
+ }
+
+ compileScriptAndKeep();
+
+ }
+
+ /**
+ * Sets a new variable within the context for the script to use.
+ * @param name
+ * @param value
+ */
+ public void setVariable(String name, Object value) {
+ context.setVariable(name, value);
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/ExpressionScript.java b/core/src/main/java/org/apache/commons/jelly/impl/ExpressionScript.java
index a2e61687..3562a945 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/ExpressionScript.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/ExpressionScript.java
@@ -38,9 +38,11 @@ public class ExpressionScript implements Script {
this.expression = expression;
}
+ // Script interface
+ //-------------------------------------------------------------------------
@Override
- public String toString() {
- return super.toString() + "[expression=" + expression + "]";
+ public Script compile() {
+ return this;
}
/** @return the expression evaluated as a String and output by this script */
@@ -48,18 +50,6 @@ public class ExpressionScript implements Script {
return expression;
}
- /** Sets the expression evaluated as a String and output by this script */
- public void setExpression(Expression expression) {
- this.expression = expression;
- }
-
- // Script interface
- //-------------------------------------------------------------------------
- @Override
- public Script compile() {
- return this;
- }
-
/** Evaluates the body of a tag */
@Override
public void run(JellyContext context, XMLOutput output) throws JellyTagException {
@@ -74,4 +64,14 @@ public class ExpressionScript implements Script {
}
}
+
+ /** Sets the expression evaluated as a String and output by this script */
+ public void setExpression(Expression expression) {
+ this.expression = expression;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[expression=" + expression + "]";
+ }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/ScriptBlock.java b/core/src/main/java/org/apache/commons/jelly/impl/ScriptBlock.java
index 6441ecea..713f92cf 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/ScriptBlock.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/ScriptBlock.java
@@ -39,32 +39,11 @@ public class ScriptBlock implements Script {
public ScriptBlock() {
}
- /**
- * @see Object#toString()
- */
- @Override
- public String toString() {
- return super.toString() + "[scripts=" + list + "]";
- }
-
/** Add a new script to the end of this block */
public void addScript(Script script) {
list.add(script);
}
- /** Removes a script from this block */
- public void removeScript(Script script) {
- list.remove(script);
- }
-
- /**
- * Gets the child scripts that make up this block. This list is live
- * so that it can be modified if required
- */
- public List getScriptList() {
- return list;
- }
-
// Script interface
//-------------------------------------------------------------------------
@Override
@@ -82,6 +61,19 @@ public class ScriptBlock implements Script {
return this;
}
+ /**
+ * Gets the child scripts that make up this block. This list is live
+ * so that it can be modified if required
+ */
+ public List getScriptList() {
+ return list;
+ }
+
+ /** Removes a script from this block */
+ public void removeScript(Script script) {
+ list.remove(script);
+ }
+
/** Evaluates the body of a tag */
@Override
public void run(JellyContext context, XMLOutput output) throws JellyTagException {
@@ -96,6 +88,14 @@ public class ScriptBlock implements Script {
script.run(context, output);
}
}
+
+ /**
+ * @see Object#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + "[scripts=" + list + "]";
+ }
/**
* Trim the body of the script.
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/StaticTag.java b/core/src/main/java/org/apache/commons/jelly/impl/StaticTag.java
index b30f19be..0cb0bf4e 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/StaticTag.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/StaticTag.java
@@ -50,11 +50,6 @@ public class StaticTag extends DynaTagSupport {
this.qualifiedName = qualifiedName;
}
- @Override
- public String toString() {
- return super.toString() + "[qname=" + qualifiedName + ";attributes=" + attributes + "]";
- }
-
// Tag interface
//-------------------------------------------------------------------------
@Override
@@ -70,13 +65,18 @@ public class StaticTag extends DynaTagSupport {
}
}
- public void setAttribute(String name, String prefix, String nsURI, Object value) {
- if (value == null)
- return;
- if (prefix != null && prefix.length() > 0)
- attributes.addAttribute(nsURI, name, prefix + ":" + name, "CDATA", value.toString());
- else
- attributes.addAttribute("", name, name, "CDATA", value.toString());
+ public String getLocalName() {
+ return localName;
+ }
+
+ public String getQName() {
+ return qualifiedName;
+ }
+
+ // Properties
+ //-------------------------------------------------------------------------
+ public String getUri() {
+ return uri;
}
// DynaTag interface
@@ -96,18 +96,21 @@ public class StaticTag extends DynaTagSupport {
}
}
- // Properties
- //-------------------------------------------------------------------------
- public String getUri() {
- return uri;
- }
-
- public void setUri(String uri) {
- this.uri = uri;
+ public void setAttribute(String name, String prefix, String nsURI, Object value) {
+ if (value == null)
+ return;
+ if (prefix != null && prefix.length() > 0)
+ attributes.addAttribute(nsURI, name, prefix + ":" + name, "CDATA", value.toString());
+ else
+ attributes.addAttribute("", name, name, "CDATA", value.toString());
}
- public String getQName() {
- return qualifiedName;
+ public void setLocalName(String localName) {
+ this.localName = localName;
+ // FIXME This just doesn't seem right or work...
+ if (qualifiedName == null || !qualifiedName.endsWith(localName)) {
+ localName = qualifiedName;
+ }
}
public void setQName(String qualifiedName) {
@@ -121,15 +124,12 @@ public class StaticTag extends DynaTagSupport {
}
}
- public String getLocalName() {
- return localName;
+ public void setUri(String uri) {
+ this.uri = uri;
}
- public void setLocalName(String localName) {
- this.localName = localName;
- // FIXME This just doesn't seem right or work...
- if (qualifiedName == null || !qualifiedName.endsWith(localName)) {
- localName = qualifiedName;
- }
+ @Override
+ public String toString() {
+ return super.toString() + "[qname=" + qualifiedName + ";attributes=" + attributes + "]";
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/StaticTagScript.java b/core/src/main/java/org/apache/commons/jelly/impl/StaticTagScript.java
index 6d838a69..49aca4b3 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/StaticTagScript.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/StaticTagScript.java
@@ -45,6 +45,24 @@ public class StaticTagScript extends TagScript {
super(tagFactory);
}
+ /**
+ * Attempts to find a dynamically created tag that has been created since this
+ * script was compiled
+ */
+ protected Tag findDynamicTag(JellyContext context, StaticTag tag) throws JellyException {
+ // lets see if there's a tag library for this URI...
+ TagLibrary taglib = context.getTagLibrary( tag.getUri() );
+ if ( taglib != null ) {
+ Tag newTag = taglib.createTag( tag.getLocalName(), getSaxAttributes() );
+ if ( newTag != null ) {
+ newTag.setParent( tag.getParent() );
+ newTag.setBody( tag.getBody() );
+ return newTag;
+ }
+ }
+ return tag;
+ }
+
// Script interface
//-------------------------------------------------------------------------
@Override
@@ -120,22 +138,4 @@ public class StaticTagScript extends TagScript {
throw new JellyTagException("could not end namespace prefixes", e);
}
}
-
- /**
- * Attempts to find a dynamically created tag that has been created since this
- * script was compiled
- */
- protected Tag findDynamicTag(JellyContext context, StaticTag tag) throws JellyException {
- // lets see if there's a tag library for this URI...
- TagLibrary taglib = context.getTagLibrary( tag.getUri() );
- if ( taglib != null ) {
- Tag newTag = taglib.createTag( tag.getLocalName(), getSaxAttributes() );
- if ( newTag != null ) {
- newTag.setParent( tag.getParent() );
- newTag.setBody( tag.getBody() );
- return newTag;
- }
- }
- return tag;
- }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/TagScript.java b/core/src/main/java/org/apache/commons/jelly/impl/TagScript.java
index ce54fd4e..34f32574 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/TagScript.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/TagScript.java
@@ -48,6 +48,23 @@ import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
+final class ExpressionAttribute {
+ String name;
+ String prefix;
+
+ String nsURI;
+ Expression exp;
+ public ExpressionAttribute(String name, Expression exp) {
+ this(name, "", "", exp);
+ }
+ public ExpressionAttribute(String name, String prefix, String nsURI, Expression exp) {
+ this.name = name;
+ this.prefix = prefix;
+ this.nsURI = nsURI;
+ this.exp = exp;
+ }
+}
+
/**
* <p><code>TagScript</code> is a Script that evaluates a custom tag.</p>
*
@@ -59,6 +76,15 @@ public class TagScript implements Script {
/** The Log to which logging calls will be made. */
private static final Log log = LogFactory.getLog(TagScript.class);
+ /**
+ * @return a new TagScript based on whether
+ * the given Tag class is a bean tag or DynaTag
+ */
+ public static TagScript newInstance(Class tagClass) {
+ TagFactory factory = new DefaultTagFactory(tagClass);
+ return new TagScript(factory);
+ }
+
/** The attribute expressions that are created */
protected Map attributes = new Hashtable();
@@ -83,10 +109,10 @@ public class TagScript implements Script {
/** The line number of the tag */
private int lineNumber = -1;
-
+
/** The column number of the tag */
private int columnNumber = -1;
-
+
/** The TagLibrary that we belong to */
private TagLibrary tagLibrary;
@@ -98,26 +124,17 @@ public class TagScript implements Script {
/** The parent TagScript */
private TagScript parent;
-
+
/** The SAX attributes */
private Attributes saxAttributes;
/** The url of the script when parsed */
private URL scriptURL = null;
-
+
/** A synchronized WeakHashMap from the current Thread (key) to a Tag object (value).
*/
private Map threadLocalTagCache = Collections.synchronizedMap(new WeakHashMap());
- /**
- * @return a new TagScript based on whether
- * the given Tag class is a bean tag or DynaTag
- */
- public static TagScript newInstance(Class tagClass) {
- TagFactory factory = new DefaultTagFactory(tagClass);
- return new TagScript(factory);
- }
-
public TagScript() {
}
@@ -125,43 +142,6 @@ public class TagScript implements Script {
this.tagFactory = tagFactory;
}
- @Override
- public String toString() {
- return super.toString() + "[tag=" + elementName + ";at=" + lineNumber + ":" + columnNumber + "]";
- }
-
- /**
- * Compiles the tags body
- */
- @Override
- public Script compile() throws JellyException {
- if (tagBody != null) {
- tagBody = tagBody.compile();
- }
- return this;
- }
-
- /**
- * Sets the optional namespaces prefix to URI map of
- * the namespaces attached to this Tag
- */
- public void setTagNamespacesMap(Map tagNamespacesMap) {
- // lets check that this is a thread-safe map
- if ( ! (tagNamespacesMap instanceof Hashtable) ) {
- tagNamespacesMap = new Hashtable( tagNamespacesMap );
- }
- this.tagNamespacesMap = tagNamespacesMap;
- }
-
- /**
- * Configures this TagScript from the SAX Locator, setting the column
- * and line numbers
- */
- public void setLocator(Locator locator) {
- setLineNumber( locator.getLineNumber() );
- setColumnNumber( locator.getColumnNumber() );
- }
-
/** Add an initialization attribute for the tag.
* This method must be called after the setTag() method
*/
@@ -184,236 +164,171 @@ public class TagScript implements Script {
attributes.put(name, new ExpressionAttribute(name, prefix, nsURI, expression));
}
+ protected void applyLocation(LocationAware locationAware) {
+ if (locationAware.getLineNumber() == -1) {
+ locationAware.setColumnNumber(columnNumber);
+ locationAware.setLineNumber(lineNumber);
+ }
+ if ( locationAware.getFileName() == null ) {
+ locationAware.setFileName( fileName );
+ }
+ if ( locationAware.getElementName() == null ) {
+ locationAware.setElementName( elementName );
+ }
+ }
+
/**
- * Strips off the name of a script to create a new context URL
- * FIXME: Copied from JellyContext
+ * Flushes the current cached tag so that it will be created, lazily, next invocation
*/
- private URL getJellyContextURL(URL url) throws MalformedURLException {
- String text = url.toString();
- int idx = text.lastIndexOf('/');
- text = text.substring(0, idx + 1);
- return new URL(text);
+ protected void clearTag() {
+ Thread t = Thread.currentThread();
+ threadLocalTagCache.put(t, null);
}
- // Script interface
- //-------------------------------------------------------------------------
-
- /** Evaluates the body of a tag */
+ /**
+ * Compiles the tags body
+ */
@Override
- public void run(JellyContext context, XMLOutput output) throws JellyTagException {
- URL rootURL = context.getRootURL();
- URL currentURL = context.getCurrentURL();
- if ( ! context.isCacheTags() ) {
- clearTag();
+ public Script compile() throws JellyException {
+ if (tagBody != null) {
+ tagBody = tagBody.compile();
}
- try {
- Tag tag = getTag(context);
- if ( tag == null ) {
- return;
- }
- tag.setContext(context);
- setContextURLs(context);
-
- if ( tag instanceof DynaTag ) {
- DynaTag dynaTag = (DynaTag) tag;
-
- // ### probably compiling this to 2 arrays might be quicker and smaller
- for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
- Map.Entry entry = (Map.Entry) iter.next();
- String name = (String) entry.getKey();
- Expression expression = ((ExpressionAttribute) entry.getValue()).exp;
-
- Class type = dynaTag.getAttributeType(name);
- Object value = null;
- if (type != null && type.isAssignableFrom(Expression.class) && !type.isAssignableFrom(Object.class)) {
- value = expression;
- }
- else {
- value = expression.evaluateRecurse(context);
- }
- dynaTag.setAttribute(name, value);
- }
- }
- else {
- // treat the tag as a bean
- DynaBean dynaBean = new ConvertingWrapDynaBean( tag );
- for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
- Map.Entry entry = (Map.Entry) iter.next();
- String name = (String) entry.getKey();
- Expression expression = ((ExpressionAttribute) entry.getValue()).exp;
-
- DynaProperty property = dynaBean.getDynaClass().getDynaProperty(name);
- if (property == null) {
- throw new JellyException("This tag does not understand the '" + name + "' attribute" );
- }
- Class type = property.getType();
-
- Object value = null;
- if (type.isAssignableFrom(Expression.class) && !type.isAssignableFrom(Object.class)) {
- value = expression;
- }
- else {
- value = expression.evaluateRecurse(context);
- }
- dynaBean.set(name, value);
- }
- }
+ return this;
+ }
- tag.doTag(output);
- if (output != null) {
- output.flush();
- }
- }
- catch (JellyTagException e) {
- handleException(e);
- } catch (JellyException e) {
- handleException(e);
- } catch (IOException e) {
- handleException(e);
- } catch (RuntimeException e) {
- handleException(e);
+ /**
+ * Compiles a newly created tag if required, sets its parent and body.
+ */
+ protected void configureTag(Tag tag, JellyContext context) throws JellyException {
+ tag.setTagLib(tagLibrary);
+ if (tag instanceof CompilableTag) {
+ ((CompilableTag) tag).compile();
}
- catch (Error e) {
- /*
- * Not sure if we should be converting errors to exceptions,
- * but not trivial to remove because JUnit tags throw
- * Errors in the normal course of operation. Hmm...
- */
- handleException(e);
- } finally {
- context.setRootURL(rootURL);
- context.setCurrentURL(currentURL);
+ Tag parentTag = null;
+ if ( parent != null ) {
+ parentTag = parent.getTag(context);
}
+ tag.setParent( parentTag );
+ tag.setBody( tagBody );
+ if (tag instanceof NamespaceAwareTag) {
+ NamespaceAwareTag naTag = (NamespaceAwareTag) tag;
+ naTag.setNamespaceContext(getNamespaceContext());
+ }
+ if (tag instanceof LocationAware) {
+ applyLocation((LocationAware) tag);
+ }
}
/**
- * Sets the context's root and current URL if not present
- * @param context
- * @throws JellyTagException
+ * Converts the given value to the required type.
+ *
+ * @param value is the value to be converted. This will not be null
+ * @param requiredType the type that the value should be converted to
*/
- protected void setContextURLs(JellyContext context) throws JellyTagException {
- if ((context.getCurrentURL() == null || context.getRootURL() == null) && scriptURL != null)
- {
- if (context.getRootURL() == null) context.setRootURL(scriptURL);
- if (context.getCurrentURL() == null) context.setCurrentURL(scriptURL);
+ protected Object convertType(Object value, Class requiredType)
+ throws JellyException {
+ if (requiredType.isInstance(value)) {
+ return value;
+ }
+ if (value instanceof String) {
+ return ConvertUtils.convert((String) value, requiredType);
}
+ return value;
}
- // Properties
+ // Script interface
//-------------------------------------------------------------------------
/**
- * @return the tag to be evaluated, creating it lazily if required.
+ * Creates a new Jelly exception, adorning it with location information
*/
- public Tag getTag(JellyContext context) throws JellyException {
- Thread t = Thread.currentThread();
- Tag tag = (Tag) threadLocalTagCache.get(t);
- if (tag == null) {
- tag = createTag();
- if (tag != null) {
- threadLocalTagCache.put(t, tag);
- configureTag(tag, context);
- }
- }
- return tag;
+ protected JellyException createJellyException(String reason) {
+ return new JellyException(
+ reason, fileName, elementName, columnNumber, lineNumber
+ );
}
/**
- * Returns the Factory of Tag instances.
- * @return the factory
+ * Creates a new Jelly exception, adorning it with location information
*/
- public TagFactory getTagFactory() {
- return tagFactory;
- }
+ protected JellyException createJellyException(String reason, Exception cause) {
+ if (cause instanceof JellyException) {
+ return (JellyException) cause;
+ }
- /**
- * Sets the Factory of Tag instances.
- * @param tagFactory The factory to set
- */
- public void setTagFactory(TagFactory tagFactory) {
- this.tagFactory = tagFactory;
+ if (cause instanceof InvocationTargetException) {
+ return new JellyException(
+ reason,
+ ((InvocationTargetException) cause).getTargetException(),
+ fileName,
+ elementName,
+ columnNumber,
+ lineNumber);
+ }
+ return new JellyException(
+ reason, cause, fileName, elementName, columnNumber, lineNumber
+ );
}
- /**
- * @return the tagLibrary
- */
- public TagLibrary getTagLibrary() {
- return tagLibrary;
- }
-
- /**
- * @param tagLibrary the tagLibrary to set
- */
- public void setTagLibrary(TagLibrary tagLibrary) {
- this.tagLibrary = tagLibrary;
- }
+ // Properties
+ //-------------------------------------------------------------------------
- /**
- * Returns the parent.
- * @return TagScript
+ /**
+ * Factory method to create a new Tag instance.
+ * The default implementation is to delegate to the TagFactory
*/
- public TagScript getParent() {
- return parent;
+ protected Tag createTag() throws JellyException {
+ if ( tagFactory != null) {
+ return tagFactory.createTag(localName, getSaxAttributes());
+ }
+ return null;
}
/**
- * Returns the tagBody.
- * @return Script
+ * End the new namespace prefixes mapped for the current element
*/
- public Script getTagBody() {
- return tagBody;
+ protected void endNamespacePrefixes(XMLOutput output) throws SAXException {
+ if ( tagNamespacesMap != null ) {
+ for ( Iterator iter = tagNamespacesMap.keySet().iterator(); iter.hasNext(); ) {
+ String prefix = (String) iter.next();
+ output.endPrefixMapping(prefix);
+ }
+ }
}
/**
- * Sets the parent.
- * @param parent The parent to set
+ * @return the column number of the tag
*/
- public void setParent(TagScript parent) {
- this.parent = parent;
+ public int getColumnNumber() {
+ return columnNumber;
}
/**
- * Sets the tagBody.
- * @param tagBody The tagBody to set
+ * @return the element name which caused the problem
*/
- public void setTagBody(Script tagBody) {
- this.tagBody = tagBody;
+ public String getElementName() {
+ return elementName;
}
- /**
+ /**
* @return the Jelly file which caused the problem
*/
public String getFileName() {
return fileName;
}
- /**
- * Sets the Jelly file which caused the problem
- */
- public void setFileName(String fileName) {
- this.fileName = fileName;
- try
- {
- this.scriptURL = getJellyContextURL(new URL(fileName));
- } catch (MalformedURLException e) {
- log.debug("error setting script url", e);
- }
- }
-
- /**
- * @return the element name which caused the problem
+ /**
+ * Strips off the name of a script to create a new context URL
+ * FIXME: Copied from JellyContext
*/
- public String getElementName() {
- return elementName;
+ private URL getJellyContextURL(URL url) throws MalformedURLException {
+ String text = url.toString();
+ int idx = text.lastIndexOf('/');
+ text = text.substring(0, idx + 1);
+ return new URL(text);
}
- /**
- * Sets the element name which caused the problem
- */
- public void setElementName(String elementName) {
- this.elementName = elementName;
- }
/**
* @return the line number of the tag
*/
@@ -421,43 +336,6 @@ public class TagScript implements Script {
return lineNumber;
}
- /**
- * Sets the line number of the tag
- */
- public void setLineNumber(int lineNumber) {
- this.lineNumber = lineNumber;
- }
-
- /**
- * @return the column number of the tag
- */
- public int getColumnNumber() {
- return columnNumber;
- }
-
- /**
- * Sets the column number of the tag
- */
- public void setColumnNumber(int columnNumber) {
- this.columnNumber = columnNumber;
- }
-
- /**
- * Returns the SAX attributes of this tag
- * @return Attributes
- */
- public Attributes getSaxAttributes() {
- return saxAttributes;
- }
-
- /**
- * Sets the SAX attributes of this tag
- * @param saxAttributes The saxAttributes to set
- */
- public void setSaxAttributes(Attributes saxAttributes) {
- this.saxAttributes = saxAttributes;
- }
-
/**
* Returns the local, non namespaced XML name of this tag
* @return String
@@ -466,14 +344,6 @@ public class TagScript implements Script {
return localName;
}
- /**
- * Sets the local, non namespaced name of this tag.
- * @param localName The localName to set
- */
- public void setLocalName(String localName) {
- this.localName = localName;
- }
-
/**
* Returns the namespace context of this tag. This is all the prefixes
* in scope in the document where this tag is used which are mapped to
@@ -504,134 +374,106 @@ public class TagScript implements Script {
return namespaceContext;
}
- // Implementation methods
- //-------------------------------------------------------------------------
-
/**
- * Factory method to create a new Tag instance.
- * The default implementation is to delegate to the TagFactory
+ * Returns the parent.
+ * @return TagScript
*/
- protected Tag createTag() throws JellyException {
- if ( tagFactory != null) {
- return tagFactory.createTag(localName, getSaxAttributes());
- }
- return null;
+ public TagScript getParent() {
+ return parent;
}
-
/**
- * Compiles a newly created tag if required, sets its parent and body.
+ * Returns the SAX attributes of this tag
+ * @return Attributes
*/
- protected void configureTag(Tag tag, JellyContext context) throws JellyException {
- tag.setTagLib(tagLibrary);
- if (tag instanceof CompilableTag) {
- ((CompilableTag) tag).compile();
- }
- Tag parentTag = null;
- if ( parent != null ) {
- parentTag = parent.getTag(context);
- }
- tag.setParent( parentTag );
- tag.setBody( tagBody );
-
- if (tag instanceof NamespaceAwareTag) {
- NamespaceAwareTag naTag = (NamespaceAwareTag) tag;
- naTag.setNamespaceContext(getNamespaceContext());
- }
- if (tag instanceof LocationAware) {
- applyLocation((LocationAware) tag);
- }
+ public Attributes getSaxAttributes() {
+ return saxAttributes;
}
/**
- * Flushes the current cached tag so that it will be created, lazily, next invocation
+ * @return the tag to be evaluated, creating it lazily if required.
*/
- protected void clearTag() {
+ public Tag getTag(JellyContext context) throws JellyException {
Thread t = Thread.currentThread();
- threadLocalTagCache.put(t, null);
+ Tag tag = (Tag) threadLocalTagCache.get(t);
+ if (tag == null) {
+ tag = createTag();
+ if (tag != null) {
+ threadLocalTagCache.put(t, tag);
+ configureTag(tag, context);
+ }
+ }
+ return tag;
}
/**
- * Allows the script to set the tag instance to be used, such as in a StaticTagScript
- * when a StaticTag is switched with a DynamicTag
+ * Returns the tagBody.
+ * @return Script
*/
- protected void setTag(Tag tag, JellyContext context) {
- Thread t = Thread.currentThread();
- threadLocalTagCache.put(t, tag);
+ public Script getTagBody() {
+ return tagBody;
}
-
/**
- * Output the new namespace prefixes used for this element
+ * Returns the Factory of Tag instances.
+ * @return the factory
*/
- protected void startNamespacePrefixes(XMLOutput output) throws SAXException {
- if ( tagNamespacesMap != null ) {
- for ( Iterator iter = tagNamespacesMap.entrySet().iterator(); iter.hasNext(); ) {
- Map.Entry entry = (Map.Entry) iter.next();
- String prefix = (String) entry.getKey();
- String uri = (String) entry.getValue();
- output.startPrefixMapping(prefix, uri);
- }
- }
+ public TagFactory getTagFactory() {
+ return tagFactory;
}
/**
- * End the new namespace prefixes mapped for the current element
- */
- protected void endNamespacePrefixes(XMLOutput output) throws SAXException {
- if ( tagNamespacesMap != null ) {
- for ( Iterator iter = tagNamespacesMap.keySet().iterator(); iter.hasNext(); ) {
- String prefix = (String) iter.next();
- output.endPrefixMapping(prefix);
- }
- }
- }
+ * @return the tagLibrary
+ */
+ public TagLibrary getTagLibrary() {
+ return tagLibrary;
+ }
/**
- * Converts the given value to the required type.
+ * A helper method to handle this non-Jelly exception.
+ * This method will rethrow the exception, wrapped in a JellyException
+ * while adding line number information etc.
*
- * @param value is the value to be converted. This will not be null
- * @param requiredType the type that the value should be converted to
+ * Is this method wise?
*/
- protected Object convertType(Object value, Class requiredType)
- throws JellyException {
- if (requiredType.isInstance(value)) {
- return value;
+ protected void handleException(Error e) throws Error, JellyTagException {
+ if (log.isTraceEnabled()) {
+ log.trace( "Caught exception: " + e, e );
}
- if (value instanceof String) {
- return ConvertUtils.convert((String) value, requiredType);
+
+ if (e instanceof LocationAware) {
+ applyLocation((LocationAware) e);
}
- return value;
- }
- /**
- * Creates a new Jelly exception, adorning it with location information
- */
- protected JellyException createJellyException(String reason) {
- return new JellyException(
- reason, fileName, elementName, columnNumber, lineNumber
- );
+ throw new JellyTagException(e, fileName, elementName, columnNumber, lineNumber);
}
/**
- * Creates a new Jelly exception, adorning it with location information
+ * A helper method to handle this non-Jelly exception.
+ * This method will rethrow the exception, wrapped in a JellyException
+ * while adding line number information etc.
*/
- protected JellyException createJellyException(String reason, Exception cause) {
- if (cause instanceof JellyException) {
- return (JellyException) cause;
+ protected void handleException(Exception e) throws JellyTagException {
+ if (log.isTraceEnabled()) {
+ log.trace( "Caught exception: " + e, e );
}
- if (cause instanceof InvocationTargetException) {
- return new JellyException(
- reason,
- ((InvocationTargetException) cause).getTargetException(),
- fileName,
- elementName,
- columnNumber,
- lineNumber);
+ if (e instanceof LocationAware) {
+ applyLocation((LocationAware) e);
}
- return new JellyException(
- reason, cause, fileName, elementName, columnNumber, lineNumber
- );
+
+ if ( e instanceof JellyException ) {
+ e.fillInStackTrace();
+ }
+
+ if ( e instanceof InvocationTargetException) {
+ throw new JellyTagException( ((InvocationTargetException)e).getTargetException(),
+ fileName,
+ elementName,
+ columnNumber,
+ lineNumber );
+ }
+
+ throw new JellyTagException(e, fileName, elementName, columnNumber, lineNumber);
}
/**
@@ -639,14 +481,14 @@ public class TagScript implements Script {
* This method adorns the JellyException with location information
* such as adding line number information etc.
*/
- protected void handleException(JellyTagException e) throws JellyTagException {
+ protected void handleException(JellyException e) throws JellyTagException {
if (log.isTraceEnabled()) {
log.trace( "Caught exception: " + e, e );
}
applyLocation(e);
- throw e;
+ throw new JellyTagException(e);
}
/**
@@ -654,91 +496,249 @@ public class TagScript implements Script {
* This method adorns the JellyException with location information
* such as adding line number information etc.
*/
- protected void handleException(JellyException e) throws JellyTagException {
+ protected void handleException(JellyTagException e) throws JellyTagException {
if (log.isTraceEnabled()) {
log.trace( "Caught exception: " + e, e );
}
applyLocation(e);
- throw new JellyTagException(e);
+ throw e;
}
- protected void applyLocation(LocationAware locationAware) {
- if (locationAware.getLineNumber() == -1) {
- locationAware.setColumnNumber(columnNumber);
- locationAware.setLineNumber(lineNumber);
+ /** Evaluates the body of a tag */
+ @Override
+ public void run(JellyContext context, XMLOutput output) throws JellyTagException {
+ URL rootURL = context.getRootURL();
+ URL currentURL = context.getCurrentURL();
+ if ( ! context.isCacheTags() ) {
+ clearTag();
}
- if ( locationAware.getFileName() == null ) {
- locationAware.setFileName( fileName );
+ try {
+ Tag tag = getTag(context);
+ if ( tag == null ) {
+ return;
+ }
+ tag.setContext(context);
+ setContextURLs(context);
+
+ if ( tag instanceof DynaTag ) {
+ DynaTag dynaTag = (DynaTag) tag;
+
+ // ### probably compiling this to 2 arrays might be quicker and smaller
+ for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String name = (String) entry.getKey();
+ Expression expression = ((ExpressionAttribute) entry.getValue()).exp;
+
+ Class type = dynaTag.getAttributeType(name);
+ Object value = null;
+ if (type != null && type.isAssignableFrom(Expression.class) && !type.isAssignableFrom(Object.class)) {
+ value = expression;
+ }
+ else {
+ value = expression.evaluateRecurse(context);
+ }
+ dynaTag.setAttribute(name, value);
+ }
+ }
+ else {
+ // treat the tag as a bean
+ DynaBean dynaBean = new ConvertingWrapDynaBean( tag );
+ for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String name = (String) entry.getKey();
+ Expression expression = ((ExpressionAttribute) entry.getValue()).exp;
+
+ DynaProperty property = dynaBean.getDynaClass().getDynaProperty(name);
+ if (property == null) {
+ throw new JellyException("This tag does not understand the '" + name + "' attribute" );
+ }
+ Class type = property.getType();
+
+ Object value = null;
+ if (type.isAssignableFrom(Expression.class) && !type.isAssignableFrom(Object.class)) {
+ value = expression;
+ }
+ else {
+ value = expression.evaluateRecurse(context);
+ }
+ dynaBean.set(name, value);
+ }
+ }
+
+ tag.doTag(output);
+ if (output != null) {
+ output.flush();
+ }
}
- if ( locationAware.getElementName() == null ) {
- locationAware.setElementName( elementName );
+ catch (JellyTagException e) {
+ handleException(e);
+ } catch (JellyException e) {
+ handleException(e);
+ } catch (IOException e) {
+ handleException(e);
+ } catch (RuntimeException e) {
+ handleException(e);
+ }
+ catch (Error e) {
+ /*
+ * Not sure if we should be converting errors to exceptions,
+ * but not trivial to remove because JUnit tags throw
+ * Errors in the normal course of operation. Hmm...
+ */
+ handleException(e);
+ } finally {
+ context.setRootURL(rootURL);
+ context.setCurrentURL(currentURL);
}
+
}
/**
- * A helper method to handle this non-Jelly exception.
- * This method will rethrow the exception, wrapped in a JellyException
- * while adding line number information etc.
+ * Sets the column number of the tag
*/
- protected void handleException(Exception e) throws JellyTagException {
- if (log.isTraceEnabled()) {
- log.trace( "Caught exception: " + e, e );
- }
+ public void setColumnNumber(int columnNumber) {
+ this.columnNumber = columnNumber;
+ }
- if (e instanceof LocationAware) {
- applyLocation((LocationAware) e);
+ /**
+ * Sets the context's root and current URL if not present
+ * @param context
+ * @throws JellyTagException
+ */
+ protected void setContextURLs(JellyContext context) throws JellyTagException {
+ if ((context.getCurrentURL() == null || context.getRootURL() == null) && scriptURL != null)
+ {
+ if (context.getRootURL() == null) context.setRootURL(scriptURL);
+ if (context.getCurrentURL() == null) context.setCurrentURL(scriptURL);
}
+ }
- if ( e instanceof JellyException ) {
- e.fillInStackTrace();
- }
+ // Implementation methods
+ //-------------------------------------------------------------------------
- if ( e instanceof InvocationTargetException) {
- throw new JellyTagException( ((InvocationTargetException)e).getTargetException(),
- fileName,
- elementName,
- columnNumber,
- lineNumber );
+ /**
+ * Sets the element name which caused the problem
+ */
+ public void setElementName(String elementName) {
+ this.elementName = elementName;
+ }
+
+
+ /**
+ * Sets the Jelly file which caused the problem
+ */
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ try
+ {
+ this.scriptURL = getJellyContextURL(new URL(fileName));
+ } catch (MalformedURLException e) {
+ log.debug("error setting script url", e);
}
+ }
- throw new JellyTagException(e, fileName, elementName, columnNumber, lineNumber);
+ /**
+ * Sets the line number of the tag
+ */
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
}
/**
- * A helper method to handle this non-Jelly exception.
- * This method will rethrow the exception, wrapped in a JellyException
- * while adding line number information etc.
- *
- * Is this method wise?
+ * Sets the local, non namespaced name of this tag.
+ * @param localName The localName to set
*/
- protected void handleException(Error e) throws Error, JellyTagException {
- if (log.isTraceEnabled()) {
- log.trace( "Caught exception: " + e, e );
- }
+ public void setLocalName(String localName) {
+ this.localName = localName;
+ }
- if (e instanceof LocationAware) {
- applyLocation((LocationAware) e);
- }
+ /**
+ * Configures this TagScript from the SAX Locator, setting the column
+ * and line numbers
+ */
+ public void setLocator(Locator locator) {
+ setLineNumber( locator.getLineNumber() );
+ setColumnNumber( locator.getColumnNumber() );
+ }
- throw new JellyTagException(e, fileName, elementName, columnNumber, lineNumber);
+ /**
+ * Sets the parent.
+ * @param parent The parent to set
+ */
+ public void setParent(TagScript parent) {
+ this.parent = parent;
}
-}
-final class ExpressionAttribute {
- public ExpressionAttribute(String name, Expression exp) {
- this(name, "", "", exp);
+ /**
+ * Sets the SAX attributes of this tag
+ * @param saxAttributes The saxAttributes to set
+ */
+ public void setSaxAttributes(Attributes saxAttributes) {
+ this.saxAttributes = saxAttributes;
}
- public ExpressionAttribute(String name, String prefix, String nsURI, Expression exp) {
- this.name = name;
- this.prefix = prefix;
- this.nsURI = nsURI;
- this.exp = exp;
+
+ /**
+ * Allows the script to set the tag instance to be used, such as in a StaticTagScript
+ * when a StaticTag is switched with a DynamicTag
+ */
+ protected void setTag(Tag tag, JellyContext context) {
+ Thread t = Thread.currentThread();
+ threadLocalTagCache.put(t, tag);
}
- String name;
- String prefix;
- String nsURI;
- Expression exp;
+ /**
+ * Sets the tagBody.
+ * @param tagBody The tagBody to set
+ */
+ public void setTagBody(Script tagBody) {
+ this.tagBody = tagBody;
+ }
+
+ /**
+ * Sets the Factory of Tag instances.
+ * @param tagFactory The factory to set
+ */
+ public void setTagFactory(TagFactory tagFactory) {
+ this.tagFactory = tagFactory;
+ }
+
+ /**
+ * @param tagLibrary the tagLibrary to set
+ */
+ public void setTagLibrary(TagLibrary tagLibrary) {
+ this.tagLibrary = tagLibrary;
+ }
+
+ /**
+ * Sets the optional namespaces prefix to URI map of
+ * the namespaces attached to this Tag
+ */
+ public void setTagNamespacesMap(Map tagNamespacesMap) {
+ // lets check that this is a thread-safe map
+ if ( ! (tagNamespacesMap instanceof Hashtable) ) {
+ tagNamespacesMap = new Hashtable( tagNamespacesMap );
+ }
+ this.tagNamespacesMap = tagNamespacesMap;
+ }
+
+ /**
+ * Output the new namespace prefixes used for this element
+ */
+ protected void startNamespacePrefixes(XMLOutput output) throws SAXException {
+ if ( tagNamespacesMap != null ) {
+ for ( Iterator iter = tagNamespacesMap.entrySet().iterator(); iter.hasNext(); ) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String prefix = (String) entry.getKey();
+ String uri = (String) entry.getValue();
+ output.startPrefixMapping(prefix, uri);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[tag=" + elementName + ";at=" + lineNumber + ":" + columnNumber + "]";
+ }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/impl/TextScript.java b/core/src/main/java/org/apache/commons/jelly/impl/TextScript.java
index c04ab518..e4d3516f 100644
--- a/core/src/main/java/org/apache/commons/jelly/impl/TextScript.java
+++ b/core/src/main/java/org/apache/commons/jelly/impl/TextScript.java
@@ -36,32 +36,38 @@ public class TextScript implements Script {
this.text = text;
}
+ // Script interface
+ //-------------------------------------------------------------------------
@Override
- public String toString() {
- return super.toString() + "[text=" + text + "]";
+ public Script compile() {
+ return this;
}
- /**
- * Trims whitespace from the start and end of the text in this script
- */
- public void trimWhitespace() {
- this.text = text.trim();
+ /** @return the text output by this script */
+ public String getText() {
+ return text;
}
- /**
- * Trims whitespace from the start of the text
- */
- public void trimStartWhitespace() {
- int index = 0;
- for ( int length = text.length(); index < length; index++ ) {
- char ch = text.charAt(index);
- if (!Character.isWhitespace(ch)) {
- break;
+ /** Evaluates the body of a tag */
+ @Override
+ public void run(JellyContext context, XMLOutput output) throws JellyTagException {
+ if (text != null) {
+ try {
+ output.write(text);
+ } catch (SAXException e) {
+ throw new JellyTagException("could not write to XMLOutput", e);
}
}
- if ( index > 0 ) {
- this.text = text.substring(index);
- }
+ }
+
+ /** Sets the text output by this script */
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "[text=" + text + "]";
}
/**
@@ -81,32 +87,26 @@ public class TextScript implements Script {
}
}
- /** @return the text output by this script */
- public String getText() {
- return text;
- }
-
- /** Sets the text output by this script */
- public void setText(String text) {
- this.text = text;
- }
-
- // Script interface
- //-------------------------------------------------------------------------
- @Override
- public Script compile() {
- return this;
- }
-
- /** Evaluates the body of a tag */
- @Override
- public void run(JellyContext context, XMLOutput output) throws JellyTagException {
- if (text != null) {
- try {
- output.write(text);
- } catch (SAXException e) {
- throw new JellyTagException("could not write to XMLOutput", e);
+ /**
+ * Trims whitespace from the start of the text
+ */
+ public void trimStartWhitespace() {
+ int index = 0;
+ for ( int length = text.length(); index < length; index++ ) {
+ char ch = text.charAt(index);
+ if (!Character.isWhitespace(ch)) {
+ break;
}
}
+ if ( index > 0 ) {
+ this.text = text.substring(index);
+ }
+ }
+
+ /**
+ * Trims whitespace from the start and end of the text in this script
+ */
+ public void trimWhitespace() {
+ this.text = text.trim();
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/parser/DefaultNamespaceFilter.java b/core/src/main/java/org/apache/commons/jelly/parser/DefaultNamespaceFilter.java
index d85b21f0..e0b0894d 100644
--- a/core/src/main/java/org/apache/commons/jelly/parser/DefaultNamespaceFilter.java
+++ b/core/src/main/java/org/apache/commons/jelly/parser/DefaultNamespaceFilter.java
@@ -45,19 +45,19 @@ public class DefaultNamespaceFilter extends XMLFilterImpl {
/**
* All incoming empty URIs will be remapped to the default.
*
- * @param prefix incoming prefix
- * @param uri URI to check and potentially replace
+ * @param namespaceURI
+ * URI to check and potentially replace
+ * @param localName
+ * @param qName
* @throws SAXException
*/
@Override
- public void startPrefixMapping(java.lang.String prefix,
- java.lang.String uri)
- throws SAXException {
-
- if (uri.isEmpty()) {
- super.startPrefixMapping(prefix, this.uriDefault);
+ public void endElement(String namespaceURI, String localName, String qName)
+ throws SAXException {
+ if (namespaceURI.isEmpty()) {
+ super.endElement(this.uriDefault, localName, qName);
} else {
- super.startPrefixMapping(prefix, uri);
+ super.endElement(namespaceURI, localName, qName);
}
}
@@ -88,19 +88,19 @@ public class DefaultNamespaceFilter extends XMLFilterImpl {
/**
* All incoming empty URIs will be remapped to the default.
*
- * @param namespaceURI
- * URI to check and potentially replace
- * @param localName
- * @param qName
+ * @param prefix incoming prefix
+ * @param uri URI to check and potentially replace
* @throws SAXException
*/
@Override
- public void endElement(String namespaceURI, String localName, String qName)
- throws SAXException {
- if (namespaceURI.isEmpty()) {
- super.endElement(this.uriDefault, localName, qName);
+ public void startPrefixMapping(java.lang.String prefix,
+ java.lang.String uri)
+ throws SAXException {
+
+ if (uri.isEmpty()) {
+ super.startPrefixMapping(prefix, this.uriDefault);
} else {
- super.endElement(namespaceURI, localName, qName);
+ super.startPrefixMapping(prefix, uri);
}
}
}
\ No newline at end of file
diff --git a/core/src/main/java/org/apache/commons/jelly/parser/XMLParser.java b/core/src/main/java/org/apache/commons/jelly/parser/XMLParser.java
index 2a40b2b6..068a8990 100644
--- a/core/src/main/java/org/apache/commons/jelly/parser/XMLParser.java
+++ b/core/src/main/java/org/apache/commons/jelly/parser/XMLParser.java
@@ -73,6 +73,11 @@ public class XMLParser extends DefaultHandler {
*/
private static Properties jellyProperties;
+ /**
+ * The SAXParserFactory that is created the first time we need it.
+ */
+ protected static SAXParserFactory factory = null;
+
/** JellyContext which is used to locate tag libraries*/
private JellyContext context = new JellyContext();
@@ -117,11 +122,6 @@ public class XMLParser extends DefaultHandler {
*/
protected ErrorHandler errorHandler = null;
- /**
- * The SAXParserFactory that is created the first time we need it.
- */
- protected static SAXParserFactory factory = null;
-
/**
* The SAXParser we will use to parse the input stream.
*/
@@ -220,535 +220,332 @@ public class XMLParser extends DefaultHandler {
}
/**
- * Parse the content of the specified file using this XMLParser. Returns
- * the root element from the object stack (if any).
- *
- * @param file File containing the XML data to be parsed
- * @return The script.
- * @throws IOException if an input/output error occurs
- * @throws SAXException if a parsing exception occurs
- */
- public Script parse(File file) throws IOException, SAXException {
- return parse(file.toURL());
- }
-
- /**
- * Parse the content of the specified file using this XMLParser. Returns
- * the root element from the object stack (if any).
- *
- * @param url URL containing the XML data to be parsed
- * @return The script.
- * @throws IOException if an input/output error occurs
- * @throws SAXException if a parsing exception occurs
+ * Adds the given Expression object to the current Script.
*/
- public Script parse(URL url) throws IOException, SAXException {
- ensureConfigured();
- this.fileName = url.toString();
-
- InputSource source = new InputSource(url.toString());
+ protected void addExpressionScript(ScriptBlock script, Expression expression) {
+ if ( expression instanceof ConstantExpression ) {
+ ConstantExpression constantExpression
+ = (ConstantExpression) expression;
+ Object value = constantExpression.getValue();
+ if ( value != null ) {
+ script.addScript(new TextScript( value.toString() ));
+ }
+ }
+ else
+ if ( expression instanceof CompositeExpression ) {
+ CompositeTextScriptBlock newBlock = new CompositeTextScriptBlock();
+ script.addScript(newBlock);
- getXMLReader().parse(source);
- return script;
+ CompositeExpression compositeExpression
+ = (CompositeExpression) expression;
+ Iterator iter = compositeExpression.getExpressions().iterator();
+ while (iter.hasNext()) {
+ addExpressionScript( newBlock, (Expression) iter.next() );
+ }
+ }
+ else {
+ script.addScript(new ExpressionScript(expression));
+ }
}
/**
- * Parse the content of the specified input source using this XMLParser.
- * Returns the root element from the object stack (if any).
- *
- * @param input Input source containing the XML data to be parsed
- * @return The script.
- * @throws IOException if an input/output error occurs
- * @throws SAXException if a parsing exception occurs
+ * Adds the text to the current script block parsing any embedded
+ * expressions into ExpressionScript objects.
*/
- public Script parse(InputSource input) throws IOException, SAXException {
- ensureConfigured();
- this.fileName = input.getSystemId();
- getXMLReader().parse(input);
- return script;
+ protected void addTextScript(String text) throws JellyException {
+ Expression expression = createExpression(tagScript, "", text);
+ addExpressionScript(script, expression);
}
/**
- * Parse the content of the specified input stream using this XMLParser.
- * Returns the root element from the object stack (if any).
- * (Note: if reading a File or URL, use one of the URL-based
- * parse methods instead. This method will not be able
- * to resolve any relative paths inside a DTD.)
+ * Process notification of character data received from the body of
+ * an XML element.
*
- * @param input Input stream containing the XML data to be parsed
- * @return The script.
- * @throws IOException
- * if an input/output error occurs
- * @throws SAXException
- * if a parsing exception occurs
+ * @param buffer The characters from the XML document
+ * @param start Starting offset into the buffer
+ * @param length Number of characters from the buffer
+ * @throws SAXException if a parsing error is to be reported
*/
- public Script parse(InputStream input) throws IOException, SAXException {
- ensureConfigured();
- this.fileName = getCurrentURI();
- getXMLReader().parse(new InputSource(input));
- return script;
+ @Override
+ public void characters(char buffer[], int start, int length)
+ throws SAXException {
+ textBuffer.append(buffer, start, length);
}
/**
- * Parse the content of the specified reader using this XMLParser.
- * Returns the root element from the object stack (if any).
- * (Note: if reading a File or URL, use one of the URL-based
- * parse methods instead. This method will not be able
- * to resolve any relative paths inside a DTD.)
- *
- * @param reader Reader containing the XML data to be parsed
- * @return The script.
- * @throws IOException
- * if an input/output error occurs
- * @throws SAXException
- * if a parsing exception occurs
+ * This method is called only once before parsing occurs
+ * which allows tag libraries to be registered and so forth
*/
- public Script parse(Reader reader) throws IOException, SAXException {
- ensureConfigured();
- this.fileName = getCurrentURI();
- getXMLReader().parse(new InputSource(reader));
- return script;
+ protected void configure() {
+ // load the properties file of libraries available
+ Properties properties = getJellyProperties();
+ for (Iterator iter = properties.entrySet().iterator(); iter.hasNext();) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String uri = (String) entry.getKey();
+ String className = (String) entry.getValue();
+ String libraryURI = "jelly:" + uri;
+
+ // don't overload any Mock Tags already
+ if ( ! context.isTagLibraryRegistered(libraryURI) ) {
+ context.registerTagLibrary(libraryURI, className);
+ }
+ }
}
/**
- * Parse the content of the specified URI using this XMLParser.
- * Returns the root element from the object stack (if any).
- *
- * @param uri URI containing the XML data to be parsed
- * @return The script.
- * @throws IOException if an input/output error occurs
- * @throws SAXException if a parsing exception occurs
+ * Configure a newly created TagScript instance before any Expressions are created;
+ * see configureTagScript(TagScript) for StaticTags
+ * @param tagLibrary the TagLibrary that created the TagScript
+ * @param aTagScript the TagScript that's just been created
*/
- public Script parse(String uri) throws IOException, SAXException {
- ensureConfigured();
- this.fileName = uri;
- getXMLReader().parse(uri);
- return script;
+ protected void configureTagScript(TagLibrary tagLibrary, TagScript aTagScript) {
+ // Set the TagLibrary that created the script
+ aTagScript.setTagLibrary(tagLibrary);
+
+ configureTagScript(aTagScript);
}
/**
- * Return the currently mapped namespace URI for the specified prefix,
- * if any; otherwise return {@code null}. These mappings come and
- * go dynamically as the document is parsed.
- *
- * @param prefix Prefix to look up
- * @return The matching prefix.
+ * Configure a newly created TagScript instance before any Expressions are created
+ * @param aTagScript
*/
- public String findNamespaceURI(String prefix) {
- ArrayDeque stack = (ArrayDeque) namespaces.get(prefix);
- if (stack == null) {
- return (null);
- }
- try {
- return ((String) stack.peek());
- }
- catch (EmptyStackException e) {
- return (null);
- }
- }
+ protected void configureTagScript(TagScript aTagScript) {
+ // set parent relationship...
+ aTagScript.setParent(this.tagScript);
- public JellyContext getContext() {
- return context;
+ // set the namespace Map
+ if ( elementNamespaces != null ) {
+ aTagScript.setTagNamespacesMap( elementNamespaces );
+ elementNamespaces = null;
+ }
}
- public void setContext(JellyContext context) {
- this.context = context;
+ protected Expression createConstantExpression(
+ String tagName,
+ String attributeName,
+ String attributeValue) {
+ return new ConstantExpression(attributeValue);
}
/**
- * Sets the jelly namespace to use for unprefixed elements.
- * Will be overridden by an explicit namespace in the
- * XML document.
- *
- * @param namespace jelly namespace to use (e.g. 'jelly:core')
+ * Creates an expression, using the ExpressionFactory returned by getExpressionFactory();
+ * the default implementation defers to the TagLibrary to create the Expression
+ * @param attributeName
+ * @param value
+ * @return The parsed exception.
+ * @throws JellyException
*/
- public void setDefaultNamespaceURI(String namespace) {
- this.defaultNamespaceURI = namespace;
+ public Expression createExpression(TagScript script, String attributeName, String value) throws JellyException {
+ ExpressionFactory factory = getExpressionFactory(script);
+ TagLibrary tagLibrary = script.getTagLibrary();
+ if (tagLibrary != null)
+ return tagLibrary.createExpression(factory, script, attributeName, value);
+ return CompositeExpression.parse(value, factory);
}
/**
- * Return the class loader to be used for instantiating application objects
- * when required. This is determined based upon the following rules:
- * <ul>
- * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
- * <li>The thread context class loader, if it exists and the
- * <code>useContextClassLoader</code> property is set to true</li>
- * <li>The class loader used to load the XMLParser class itself.
- * </ul>
+ * Called to create an instance of the ExpressionFactory (@see getExpressionFactory)
+ *
+ * @return A new JexlExpressionFactory.
*/
- public ClassLoader getClassLoader() {
- return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass());
+ protected ExpressionFactory createExpressionFactory() {
+ return new JexlExpressionFactory();
}
/**
- * Sets the class loader to be used for instantiating application objects
- * when required.
+ * Create a SAX exception which also understands about the location in
+ * the digester file where the exception occurs
*
- * @param classLoader The new class loader to use, or {@code null}
- * to revert to the standard rules
+ * @return the new exception
*/
- public void setClassLoader(ClassLoader classLoader) {
- this.classLoader = classLoader;
+ protected SAXException createSAXException(Exception e) {
+ return createSAXException(e.getMessage(), e);
}
/**
- * Return the boolean as to whether the context classloader should be used.
+ * Create a SAX exception which also understands about the location in
+ * the digester file where the exception occurs
+ *
+ * @return the new exception
*/
- public boolean getUseContextClassLoader() {
- return useContextClassLoader;
+ protected SAXException createSAXException(String message) {
+ return createSAXException(message, null);
}
/**
- * Determine whether to use the Context ClassLoader (the one found by
- * calling <code>Thread.currentThread().getContextClassLoader()</code>)
- * to resolve/load classes. If not
- * using Context ClassLoader, then the class-loading defaults to
- * using the calling-class' ClassLoader.
+ * Create a SAX exception which also understands about the location in
+ * the file where the exception occurs
*
- * @param use determines whether to use JellyContext ClassLoader.
- */
- public void setUseContextClassLoader(boolean use) {
- useContextClassLoader = use;
- }
-
- /**
- * Return the error handler for this XMLParser.
- */
- public ErrorHandler getErrorHandler() {
- return (this.errorHandler);
- }
-
- /**
- * Sets the error handler for this XMLParser.
- *
- * @param errorHandler The new error handler
- */
- public void setErrorHandler(ErrorHandler errorHandler) {
- this.errorHandler = errorHandler;
- }
-
- /**
- * Return the current Logger associated with this instance of the XMLParser
- */
- public Log getLogger() {
- return log;
- }
-
- /**
- * Sets the current logger for this XMLParser.
- */
- public void setLogger(Log log) {
- this.log = log;
- }
-
- /**
- * Locates an expression factory by looking at the TagLibrary for the specified
- * tag and falling back by creating one. If the current tag is a StaticTag (IE
- * has no TagLibrary) then continues up the stack looking for a valid one.
- * @param tagScript
- * @return the expression factory used to evaluate tag attributes
- */
- public ExpressionFactory getExpressionFactory(TagScript tagScript) {
- // Check the tag library
- TagLibrary tagLibrary = null;
- if (tagScript != null)
- tagLibrary = tagScript.getTagLibrary();
-
- // If the tagScript is a StaticTag, then go up the stack looking for a
- // tagScript that belongs to a real TagLibrary
- if (tagLibrary == null && tagScript instanceof StaticTagScript)
- for (int i = tagScriptStack.size() - 1; i > -1; i--) {
- TagScript script = (TagScript)tagScriptStack.get(i);
- tagLibrary = script.getTagLibrary();
- if (tagLibrary != null)
- break;
- if (!(script instanceof StaticTagScript))
- break;
- }
- if (tagLibrary != null) {
- ExpressionFactory factory = tagLibrary.getExpressionFactory();
- if (factory != null)
- return factory;
- }
- if (expressionFactory == null) {
- expressionFactory = createExpressionFactory();
- }
- return expressionFactory;
- }
-
- /** Sets the expression factory used to evaluate tag attributes */
- public void setExpressionFactory(ExpressionFactory expressionFactory) {
- this.expressionFactory = expressionFactory;
- }
-
- /**
- * Called to create an instance of the ExpressionFactory (@see getExpressionFactory)
- *
- * @return A new JexlExpressionFactory.
- */
- protected ExpressionFactory createExpressionFactory() {
- return new JexlExpressionFactory();
- }
-
- /**
- * Creates an expression, using the ExpressionFactory returned by getExpressionFactory();
- * the default implementation defers to the TagLibrary to create the Expression
- * @param attributeName
- * @param value
- * @return The parsed exception.
- * @throws JellyException
- */
- public Expression createExpression(TagScript script, String attributeName, String value) throws JellyException {
- ExpressionFactory factory = getExpressionFactory(script);
- TagLibrary tagLibrary = script.getTagLibrary();
- if (tagLibrary != null)
- return tagLibrary.createExpression(factory, script, attributeName, value);
- return CompositeExpression.parse(value, factory);
- }
-
- /**
- * Return the SAXParser we will use to parse the input stream. If there
- * is a problem creating the parser, return {@code null}.
+ * @return the new exception
*/
- public SAXParser getParser() {
- // Return the parser we already created (if any)
- if (parser != null) {
- return (parser);
+ protected SAXException createSAXException(String message, Exception e) {
+ log.warn("Underlying exception: " + e);
+ if (e != null) {
+ e.printStackTrace();
}
- // Create and return a new parser
- synchronized (this) {
- try {
- if (factory == null) {
- factory = SAXParserFactory.newInstance();
- }
- factory.setNamespaceAware(true);
- factory.setValidating(validating);
- parser = factory.newSAXParser();
- return (parser);
+ if (locator != null) {
+ String error =
+ "Error at ("
+ + locator.getLineNumber()
+ + ", "
+ + locator.getColumnNumber()
+ + "): "
+ + message;
+ if (e != null) {
+ return new SAXParseException(error, locator, e);
}
- catch (Exception e) {
- log.error("XMLParser.getParser: ", e);
- return (null);
+ else {
+ return new SAXParseException(error, locator);
}
}
- }
-
- /**
- * By setting the reader in the constructor, you can bypass JAXP and
- * be able to use digester in Weblogic 6.0.
- *
- * @deprecated Use getXMLReader() instead, which can throw a
- * SAXException if the reader cannot be instantiated
- */
- public XMLReader getReader() {
- try {
- return (getXMLReader());
+ log.error("No Locator!");
+ if (e != null) {
+ return new SAXException(message, e);
}
- catch (SAXException e) {
- log.error("Cannot get XMLReader", e);
- return (null);
+ else {
+ return new SAXException(message);
}
}
/**
- * Return the XMLReader to be used for parsing the input document.
- *
- * @throws SAXException if no XMLReader can be instantiated
+ * Factory method to create a static Tag that represents some static content.
*/
- public synchronized XMLReader getXMLReader() throws SAXException {
- if (reader == null) {
- reader = getParser().getXMLReader();
- if (!allowDtdToCallExternalEntities) {
- reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
- reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
- }
- if (this.defaultNamespaceURI != null) {
- reader = new DefaultNamespaceFilter(this.defaultNamespaceURI, reader);
+ protected TagScript createStaticTag(
+ final String namespaceURI,
+ final String localName,
+ final String qName,
+ Attributes list)
+ throws SAXException {
+ try {
+ //StaticTag tag = new StaticTag( namespaceURI, localName, qName );
+ StaticTagScript script = new StaticTagScript(
+ new TagFactory() {
+ @Override
+ public Tag createTag(String name, Attributes attributes) {
+ return new StaticTag( namespaceURI, localName, qName );
+ }
+ }
+ );
+ configureTagScript(script);
+
+ // now iterate through through the expressions
+ int size = list.getLength();
+ for (int i = 0; i < size; i++) {
+ String attributeValue = list.getValue(i);
+ Expression expression = createExpression(script, null, attributeValue);
+// Expression expression = CompositeExpression.parse(
+// attributeValue, getExpressionFactory()
+// );
+ String attrQName = list.getQName(i);
+ int p = attrQName.indexOf(':');
+ String prefix = p>=0 ?
+ attrQName.substring(0, p):
+ "";
+ script.addAttribute(list.getLocalName(i),
+ prefix, list.getURI(i), expression);
}
+ return script;
+ }
+ catch (Exception e) {
+ log.warn(
+ "Could not create static tag for URI: "
+ + namespaceURI
+ + " tag name: "
+ + localName,
+ e);
+ throw createSAXException(e);
}
- //set up the parse
- reader.setContentHandler(this);
- reader.setDTDHandler(this);
- //reader.setEntityResolver(this);
- reader.setErrorHandler(this);
-
- return reader;
- }
-
- /**
- * Return the validating parser flag.
- */
- public boolean getValidating() {
- return (this.validating);
- }
-
- /**
- * Sets the validating parser flag. This must be called before
- * <code>parse()</code> is called the first time.
- *
- * @param validating The new validating parser flag.
- */
- public void setValidating(boolean validating) {
- this.validating = validating;
- }
-
- /**
- * Returns the script that has just been created if this class is used
- * as a SAX ContentHandler and passed into some XML processor or parser.
- *
- * @return the ScriptBlock created if SAX events are piped into this class,
- * which must include a startDocument() and endDocument()
- */
- public ScriptBlock getScript() {
- return script;
- }
-
- // ContentHandler interface
- //-------------------------------------------------------------------------
- /**
- * Process notification of the beginning of the document being reached.
- *
- * @throws SAXException if a parsing error is to be reported
- */
- @Override
- public void startDocument() throws SAXException {
- script = new ScriptBlock();
- textBuffer = new StringBuffer();
- tagScript = null;
- scriptStack.clear();
- tagScriptStack.clear();
- }
-
- /**
- * Process notification of the end of the document being reached.
- *
- * @throws SAXException if a parsing error is to be reported
- */
- @Override
- public void endDocument() throws SAXException {
- textBuffer = null;
- }
-
- /**
- * Gets the internal state for allowance for DTDs to call external resources.
- *
- * @return true if we are allowed to make external calls to entities during XML
- * parsing by custom declared resources in the DTD.
- */
- public boolean isAllowDtdToCallExternalEntities() {
- return allowDtdToCallExternalEntities;
- }
-
- /**
- * Sets the boolean
- * allowDtdToCallExternalEntities. If this is set to false,
- * the XMLParser will be created with:
- * XMLReader spf = XMLReaderFactory.createXMLReader();
- * spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
- * spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
- * spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
- * as given by
- * https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#XMLReader
- */
- public void setAllowDtdToCallExternalEntities(boolean allowDtdToCallExternalEntities) {
- this.allowDtdToCallExternalEntities = allowDtdToCallExternalEntities;
}
/**
- * Process notification of the start of an XML element being reached.
- *
- * @param namespaceURI The Namespace URI, or the empty string if the
- * element has no Namespace URI or if Namespace processing is not
- * being performed.
- * @param localName The local name (without prefix), or the empty
- * string if Namespace processing is not being performed.
- * @param qName The qualified name (with prefix), or the empty
- * string if qualified names are not available.\
- * @param list The attributes attached to the element. If there are
- * no attributes, it shall be an empty Attributes object.
- * @throws SAXException if a parsing error is to be reported
+ * Factory method to create new Tag script for the given namespaceURI and name or
+ * return null if this is not a custom Tag.
*/
- @Override
- public void startElement(
+ protected TagScript createTag(
String namespaceURI,
String localName,
- String qName,
Attributes list)
throws SAXException {
-
try {
- // add check to ensure namespace URI is "" for no namespace
- if ( namespaceURI == null ) {
- namespaceURI = "";
+ // use the URI to load a taglib
+ TagLibrary taglib = context.getTagLibrary(namespaceURI);
+ if (taglib == null) {
+ if (namespaceURI != null && namespaceURI.startsWith("jelly:")) {
+ String uri = namespaceURI.substring(6);
+ // try to find the class on the classpath
+ try {
+ Class taglibClass = getClassLoader().loadClass(uri);
+ taglib = (TagLibrary) taglibClass.getConstructor().newInstance();
+ context.registerTagLibrary(namespaceURI, taglib);
+ }
+ catch (ClassNotFoundException e) {
+ throw createSAXException("Could not load class: " + uri + " so taglib instantiation failed", e);
+ }
+ catch (IllegalAccessException e) {
+ throw createSAXException("Constructor for class is not accessible: " + uri + " so taglib instantiation failed", e);
+ }
+ catch (InstantiationException e) {
+ throw createSAXException("Class could not be instantiated: " + uri + " so taglib instantiation failed", e);
+ }
+ catch (ClassCastException e) {
+ throw createSAXException("Class is not a TagLibrary: " + uri + " so taglib instantiation failed", e);
+ }
+ }
}
+ if (taglib != null) {
+ TagScript script = taglib.createTagScript(localName, list);
+ if ( script != null ) {
+ configureTagScript(taglib, script);
- // if this is a tag then create a script to run it
- // otherwise pass the text to the current body
- TagScript newTagScript = createTag(namespaceURI, localName, list);
- if (newTagScript == null) {
- newTagScript = createStaticTag(namespaceURI, localName, qName, list);
- }
- tagScript = newTagScript;
- tagScriptStack.add(tagScript);
- if (tagScript != null) {
- // set the line number details
- if ( locator != null ) {
- tagScript.setLocator(locator);
- }
- // sets the file name element names
- if (fileName != null)
- tagScript.setFileName(fileName);
- tagScript.setElementName(qName);
- tagScript.setLocalName(localName);
+ // clone the attributes to keep them around after this parse
+ script.setSaxAttributes(new AttributesImpl(list));
- if (textBuffer.length() > 0) {
- addTextScript(textBuffer.toString());
- textBuffer.setLength(0);
- }
- script.addScript(tagScript);
- // start a new body
- scriptStack.push(script);
- script = new ScriptBlock();
- tagScript.setTagBody(script);
- }
- else {
- // XXXX: might wanna handle empty elements later...
- textBuffer.append("<");
- textBuffer.append(qName);
- int size = list.getLength();
- for (int i = 0; i < size; i++) {
- textBuffer.append(" ");
- textBuffer.append(list.getQName(i));
- textBuffer.append("=");
- textBuffer.append("\"");
- textBuffer.append(list.getValue(i));
- textBuffer.append("\"");
- }
- textBuffer.append(">");
+ // now iterate through through the expressions
+ int size = list.getLength();
+ for (int i = 0; i < size; i++) {
+ String attributeName = list.getLocalName(i);
+ // Fix for JELLY-184 where the xmlns attributes have a blank name and cause
+ // an exception later on
+ if (attributeName.length() == 0)
+ continue;
+ String attributeValue = list.getValue(i);
+ Expression expression =
+ createExpression(script,
+ attributeName,
+ attributeValue);
+// Expression expression =
+// taglib.createExpression(
+// getExpressionFactory(),
+// script,
+// attributeName,
+// attributeValue);
+ if (expression == null) {
+ expression = createConstantExpression(localName, attributeName, attributeValue);
+ }
+ script.addAttribute(attributeName, expression);
+ }
+ } else if (!taglib.isAllowUnknownTags())
+ throw new JellyException("Unrecognized tag called " + localName + " in TagLibrary " + namespaceURI);
+ return script;
}
- }
- catch (SAXException e) {
- throw e;
+ return null;
}
catch (Exception e) {
- log.error( "Caught exception: " + e, e );
- throw new SAXException( "Runtime Exception: " + e, e );
+ log.warn(
+ "Could not create taglib or URI: " + namespaceURI + " tag name: " + localName,
+ e);
+ throw createSAXException(e);
}
}
/**
- * Process notification of character data received from the body of
- * an XML element.
+ * Process notification of the end of the document being reached.
*
- * @param buffer The characters from the XML document
- * @param start Starting offset into the buffer
- * @param length Number of characters from the buffer
* @throws SAXException if a parsing error is to be reported
*/
@Override
- public void characters(char buffer[], int start, int length)
- throws SAXException {
- textBuffer.append(buffer, start, length);
+ public void endDocument() throws SAXException {
+ textBuffer = null;
}
/**
@@ -785,61 +582,322 @@ public class XMLParser extends DefaultHandler {
if ( tagScriptStack.isEmpty() ) {
tagScript = null;
}
- else {
- tagScript = (TagScript) tagScriptStack.get(tagScriptStack.size() - 1);
+ else {
+ tagScript = (TagScript) tagScriptStack.get(tagScriptStack.size() - 1);
+ }
+ } catch (Exception e) {
+ log.error( "Caught exception: " + e, e );
+ throw new SAXException( "Runtime Exception: " + e, e );
+ }
+ }
+
+ /**
+ * Process notification that a namespace prefix is going out of scope.
+ *
+ * @param prefix Prefix that is going out of scope
+ * @throws SAXException if a parsing error is to be reported
+ */
+ @Override
+ public void endPrefixMapping(String prefix) throws SAXException {
+ // Deregister this prefix mapping
+ ArrayDeque stack = (ArrayDeque) namespaces.get(prefix);
+ if (stack == null) {
+ return;
+ }
+ try {
+ stack.pop();
+ if (stack.isEmpty()) {
+ namespaces.remove(prefix);
+ }
+ }
+ catch (EmptyStackException e) {
+ throw createSAXException("endPrefixMapping popped too many times");
+ }
+ }
+
+ // Implementation methods
+ //-------------------------------------------------------------------------
+ /**
+ * If this object has not been configured then register the default
+ * namespaces
+ */
+ private void ensureConfigured() {
+ if (!configured) {
+ configure();
+ configured = true;
+ }
+ }
+
+ /**
+ * Forward notification of a parsing error to the application supplied
+ * error handler, if any, otherwise throw a SAXException with the error.
+ *
+ * @param exception The error information
+ * @throws SAXException if a parsing exception occurs
+ */
+ @Override
+ public void error(SAXParseException exception) throws SAXException {
+ log.error(
+ "Parse Error at line "
+ + exception.getLineNumber()
+ + " column "
+ + exception.getColumnNumber()
+ + ": "
+ + exception.getMessage(),
+ exception);
+ if (errorHandler != null) {
+ errorHandler.error(exception);
+ } else {
+ throw exception;
+ }
+ }
+
+ /**
+ * Forward notification of a fatal parsing error to the application
+ * supplied error handler, if any, otherwise throw a SAXException with the error.
+ *
+ * @param exception The fatal error information
+ * @throws SAXException if a parsing exception occurs
+ */
+ @Override
+ public void fatalError(SAXParseException exception) throws SAXException {
+ log.error(
+ "Parse Fatal Error at line "
+ + exception.getLineNumber()
+ + " column "
+ + exception.getColumnNumber()
+ + ": "
+ + exception.getMessage(),
+ exception);
+ if (errorHandler != null) {
+ errorHandler.fatalError(exception);
+ } else {
+ throw exception;
+ }
+ }
+
+ /**
+ * Return the currently mapped namespace URI for the specified prefix,
+ * if any; otherwise return {@code null}. These mappings come and
+ * go dynamically as the document is parsed.
+ *
+ * @param prefix Prefix to look up
+ * @return The matching prefix.
+ */
+ public String findNamespaceURI(String prefix) {
+ ArrayDeque stack = (ArrayDeque) namespaces.get(prefix);
+ if (stack == null) {
+ return (null);
+ }
+ try {
+ return ((String) stack.peek());
+ }
+ catch (EmptyStackException e) {
+ return (null);
+ }
+ }
+
+ /**
+ * Return the class loader to be used for instantiating application objects
+ * when required. This is determined based upon the following rules:
+ * <ul>
+ * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
+ * <li>The thread context class loader, if it exists and the
+ * <code>useContextClassLoader</code> property is set to true</li>
+ * <li>The class loader used to load the XMLParser class itself.
+ * </ul>
+ */
+ public ClassLoader getClassLoader() {
+ return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass());
+ }
+
+ public JellyContext getContext() {
+ return context;
+ }
+
+ /**
+ * @return the current context URI as a String or null if there is no
+ * current context defined on the JellyContext
+ */
+ protected String getCurrentURI() {
+ URL url = this.getContext().getCurrentURL();
+ return (url != null) ? url.toString() : null;
+ }
+
+ /**
+ * Return the error handler for this XMLParser.
+ */
+ public ErrorHandler getErrorHandler() {
+ return (this.errorHandler);
+ }
+
+ /**
+ * Locates an expression factory by looking at the TagLibrary for the specified
+ * tag and falling back by creating one. If the current tag is a StaticTag (IE
+ * has no TagLibrary) then continues up the stack looking for a valid one.
+ * @param tagScript
+ * @return the expression factory used to evaluate tag attributes
+ */
+ public ExpressionFactory getExpressionFactory(TagScript tagScript) {
+ // Check the tag library
+ TagLibrary tagLibrary = null;
+ if (tagScript != null)
+ tagLibrary = tagScript.getTagLibrary();
+
+ // If the tagScript is a StaticTag, then go up the stack looking for a
+ // tagScript that belongs to a real TagLibrary
+ if (tagLibrary == null && tagScript instanceof StaticTagScript)
+ for (int i = tagScriptStack.size() - 1; i > -1; i--) {
+ TagScript script = (TagScript)tagScriptStack.get(i);
+ tagLibrary = script.getTagLibrary();
+ if (tagLibrary != null)
+ break;
+ if (!(script instanceof StaticTagScript))
+ break;
+ }
+ if (tagLibrary != null) {
+ ExpressionFactory factory = tagLibrary.getExpressionFactory();
+ if (factory != null)
+ return factory;
+ }
+ if (expressionFactory == null) {
+ expressionFactory = createExpressionFactory();
+ }
+ return expressionFactory;
+ }
+
+ /**
+ * A helper method which loads the static Jelly properties once on startup
+ */
+ protected synchronized Properties getJellyProperties() {
+ if (jellyProperties == null) {
+ jellyProperties = new Properties();
+
+ InputStream in = null;
+ URL url =
+ getClassLoader().getResource("org/apache/commons/jelly/jelly.properties");
+ if (url != null) {
+ log.debug("Loading Jelly default tag libraries from: " + url);
+ try {
+ in = url.openStream();
+ jellyProperties .load(in);
+ }
+ catch (IOException e) {
+ log.error("Could not load jelly properties from: " + url + ". Reason: " + e, e);
+ }
+ finally {
+ try {
+ in.close();
+ }
+ catch (Exception e) {
+ if (log.isDebugEnabled()) log.debug("error closing jelly.properties", e);
+ }
+ }
+ }
+ }
+ return jellyProperties;
+ }
+
+ /**
+ * Return the current Logger associated with this instance of the XMLParser
+ */
+ public Log getLogger() {
+ return log;
+ }
+
+ /**
+ * Return the SAXParser we will use to parse the input stream. If there
+ * is a problem creating the parser, return {@code null}.
+ */
+ public SAXParser getParser() {
+ // Return the parser we already created (if any)
+ if (parser != null) {
+ return (parser);
+ }
+ // Create and return a new parser
+ synchronized (this) {
+ try {
+ if (factory == null) {
+ factory = SAXParserFactory.newInstance();
+ }
+ factory.setNamespaceAware(true);
+ factory.setValidating(validating);
+ parser = factory.newSAXParser();
+ return (parser);
+ }
+ catch (Exception e) {
+ log.error("XMLParser.getParser: ", e);
+ return (null);
}
- } catch (Exception e) {
- log.error( "Caught exception: " + e, e );
- throw new SAXException( "Runtime Exception: " + e, e );
}
}
/**
- * Process notification that a namespace prefix is coming in to scope.
+ * By setting the reader in the constructor, you can bypass JAXP and
+ * be able to use digester in Weblogic 6.0.
*
- * @param prefix Prefix that is being declared
- * @param namespaceURI Corresponding namespace URI being mapped to
- * @throws SAXException if a parsing error is to be reported
+ * @deprecated Use getXMLReader() instead, which can throw a
+ * SAXException if the reader cannot be instantiated
*/
- @Override
- public void startPrefixMapping(String prefix, String namespaceURI)
- throws SAXException {
- // Register this prefix mapping
- ArrayDeque stack = (ArrayDeque) namespaces.get(prefix);
- if (stack == null) {
- stack = new ArrayDeque();
- namespaces.put(prefix, stack);
+ public XMLReader getReader() {
+ try {
+ return (getXMLReader());
}
- stack.push(namespaceURI);
-
- if ( elementNamespaces == null ) {
- elementNamespaces = new HashMap();
+ catch (SAXException e) {
+ log.error("Cannot get XMLReader", e);
+ return (null);
}
- elementNamespaces.put(prefix, namespaceURI);
}
/**
- * Process notification that a namespace prefix is going out of scope.
+ * Returns the script that has just been created if this class is used
+ * as a SAX ContentHandler and passed into some XML processor or parser.
*
- * @param prefix Prefix that is going out of scope
- * @throws SAXException if a parsing error is to be reported
+ * @return the ScriptBlock created if SAX events are piped into this class,
+ * which must include a startDocument() and endDocument()
*/
- @Override
- public void endPrefixMapping(String prefix) throws SAXException {
- // Deregister this prefix mapping
- ArrayDeque stack = (ArrayDeque) namespaces.get(prefix);
- if (stack == null) {
- return;
- }
- try {
- stack.pop();
- if (stack.isEmpty()) {
- namespaces.remove(prefix);
+ public ScriptBlock getScript() {
+ return script;
+ }
+
+ /**
+ * Return the boolean as to whether the context classloader should be used.
+ */
+ public boolean getUseContextClassLoader() {
+ return useContextClassLoader;
+ }
+
+ /**
+ * Return the validating parser flag.
+ */
+ public boolean getValidating() {
+ return (this.validating);
+ }
+
+ /**
+ * Return the XMLReader to be used for parsing the input document.
+ *
+ * @throws SAXException if no XMLReader can be instantiated
+ */
+ public synchronized XMLReader getXMLReader() throws SAXException {
+ if (reader == null) {
+ reader = getParser().getXMLReader();
+ if (!allowDtdToCallExternalEntities) {
+ reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ }
+ if (this.defaultNamespaceURI != null) {
+ reader = new DefaultNamespaceFilter(this.defaultNamespaceURI, reader);
}
}
- catch (EmptyStackException e) {
- throw createSAXException("endPrefixMapping popped too many times");
- }
+ //set up the parse
+ reader.setContentHandler(this);
+ reader.setDTDHandler(this);
+ //reader.setEntityResolver(this);
+ reader.setErrorHandler(this);
+
+ return reader;
}
/**
@@ -858,467 +916,409 @@ public class XMLParser extends DefaultHandler {
}
/**
- * Process notification of a processing instruction that was encountered.
+ * Gets the internal state for allowance for DTDs to call external resources.
*
- * @param target The processing instruction target
- * @param data The processing instruction data (if any)
- * @throws SAXException if a parsing error is to be reported
+ * @return true if we are allowed to make external calls to entities during XML
+ * parsing by custom declared resources in the DTD.
*/
- @Override
- public void processingInstruction(String target, String data)
- throws SAXException {
- // No processing is required
+ public boolean isAllowDtdToCallExternalEntities() {
+ return allowDtdToCallExternalEntities;
}
/**
- * Sets the document locator associated with our parser.
+ * Receive notification of a notation declaration event.
*
- * @param locator The new locator
+ * @param name The notation name
+ * @param publicId The public identifier (if any)
+ * @param systemId The system identifier (if any)
*/
@Override
- public void setDocumentLocator(Locator locator) {
- this.locator = locator;
+ public void notationDecl(String name, String publicId, String systemId) {
}
/**
- * Process notification of a skipped entity.
+ * Parse the content of the specified file using this XMLParser. Returns
+ * the root element from the object stack (if any).
*
- * @param name Name of the skipped entity
- * @throws SAXException if a parsing error is to be reported
+ * @param file File containing the XML data to be parsed
+ * @return The script.
+ * @throws IOException if an input/output error occurs
+ * @throws SAXException if a parsing exception occurs
*/
- @Override
- public void skippedEntity(String name) throws SAXException {
- // No processing required
+ public Script parse(File file) throws IOException, SAXException {
+ return parse(file.toURL());
}
/**
- * Receive notification of a notation declaration event.
+ * Parse the content of the specified input source using this XMLParser.
+ * Returns the root element from the object stack (if any).
*
- * @param name The notation name
- * @param publicId The public identifier (if any)
- * @param systemId The system identifier (if any)
+ * @param input Input source containing the XML data to be parsed
+ * @return The script.
+ * @throws IOException if an input/output error occurs
+ * @throws SAXException if a parsing exception occurs
*/
- @Override
- public void notationDecl(String name, String publicId, String systemId) {
+ public Script parse(InputSource input) throws IOException, SAXException {
+ ensureConfigured();
+ this.fileName = input.getSystemId();
+ getXMLReader().parse(input);
+ return script;
}
/**
- * Receive notification of an unparsed entity declaration event.
+ * Parse the content of the specified input stream using this XMLParser.
+ * Returns the root element from the object stack (if any).
+ * (Note: if reading a File or URL, use one of the URL-based
+ * parse methods instead. This method will not be able
+ * to resolve any relative paths inside a DTD.)
*
- * @param name The unparsed entity name
- * @param publicId The public identifier (if any)
- * @param systemId The system identifier (if any)
- * @param notation The name of the associated notation
+ * @param input Input stream containing the XML data to be parsed
+ * @return The script.
+ * @throws IOException
+ * if an input/output error occurs
+ * @throws SAXException
+ * if a parsing exception occurs
*/
- @Override
- public void unparsedEntityDecl(
- String name,
- String publicId,
- String systemId,
- String notation) {
+ public Script parse(InputStream input) throws IOException, SAXException {
+ ensureConfigured();
+ this.fileName = getCurrentURI();
+ getXMLReader().parse(new InputSource(input));
+ return script;
}
/**
- * Forward notification of a parsing error to the application supplied
- * error handler, if any, otherwise throw a SAXException with the error.
+ * Parse the content of the specified reader using this XMLParser.
+ * Returns the root element from the object stack (if any).
+ * (Note: if reading a File or URL, use one of the URL-based
+ * parse methods instead. This method will not be able
+ * to resolve any relative paths inside a DTD.)
*
- * @param exception The error information
- * @throws SAXException if a parsing exception occurs
+ * @param reader Reader containing the XML data to be parsed
+ * @return The script.
+ * @throws IOException
+ * if an input/output error occurs
+ * @throws SAXException
+ * if a parsing exception occurs
*/
- @Override
- public void error(SAXParseException exception) throws SAXException {
- log.error(
- "Parse Error at line "
- + exception.getLineNumber()
- + " column "
- + exception.getColumnNumber()
- + ": "
- + exception.getMessage(),
- exception);
- if (errorHandler != null) {
- errorHandler.error(exception);
- } else {
- throw exception;
- }
+ public Script parse(Reader reader) throws IOException, SAXException {
+ ensureConfigured();
+ this.fileName = getCurrentURI();
+ getXMLReader().parse(new InputSource(reader));
+ return script;
}
/**
- * Forward notification of a fatal parsing error to the application
- * supplied error handler, if any, otherwise throw a SAXException with the error.
+ * Parse the content of the specified URI using this XMLParser.
+ * Returns the root element from the object stack (if any).
*
- * @param exception The fatal error information
+ * @param uri URI containing the XML data to be parsed
+ * @return The script.
+ * @throws IOException if an input/output error occurs
* @throws SAXException if a parsing exception occurs
*/
- @Override
- public void fatalError(SAXParseException exception) throws SAXException {
- log.error(
- "Parse Fatal Error at line "
- + exception.getLineNumber()
- + " column "
- + exception.getColumnNumber()
- + ": "
- + exception.getMessage(),
- exception);
- if (errorHandler != null) {
- errorHandler.fatalError(exception);
- } else {
- throw exception;
- }
+ public Script parse(String uri) throws IOException, SAXException {
+ ensureConfigured();
+ this.fileName = uri;
+ getXMLReader().parse(uri);
+ return script;
}
- /**
- * Forward notification of a parse warning to the application supplied
- * error handler (if any). Unlike XMLParser.error(SAXParseException) and
- * XMLParser.fatalError(SAXParseException), this implementation will
- * NOT throw a SAXException by default if no error handler is supplied.
+ /**
+ * Parse the content of the specified file using this XMLParser. Returns
+ * the root element from the object stack (if any).
*
- * @param exception The warning information
+ * @param url URL containing the XML data to be parsed
+ * @return The script.
+ * @throws IOException if an input/output error occurs
* @throws SAXException if a parsing exception occurs
*/
- @Override
- public void warning(SAXParseException exception) throws SAXException {
- log.error(
- "Parse Warning at line "
- + exception.getLineNumber()
- + " column "
- + exception.getColumnNumber()
- + ": "
- + exception.getMessage(),
- exception);
- if (errorHandler != null) {
- errorHandler.warning(exception);
- }
+ public Script parse(URL url) throws IOException, SAXException {
+ ensureConfigured();
+ this.fileName = url.toString();
+
+ InputSource source = new InputSource(url.toString());
+
+ getXMLReader().parse(source);
+ return script;
}
- // Implementation methods
- //-------------------------------------------------------------------------
/**
- * If this object has not been configured then register the default
- * namespaces
+ * Process notification of a processing instruction that was encountered.
+ *
+ * @param target The processing instruction target
+ * @param data The processing instruction data (if any)
+ * @throws SAXException if a parsing error is to be reported
*/
- private void ensureConfigured() {
- if (!configured) {
- configure();
- configured = true;
- }
+ @Override
+ public void processingInstruction(String target, String data)
+ throws SAXException {
+ // No processing is required
}
/**
- * This method is called only once before parsing occurs
- * which allows tag libraries to be registered and so forth
+ * Sets the boolean
+ * allowDtdToCallExternalEntities. If this is set to false,
+ * the XMLParser will be created with:
+ * XMLReader spf = XMLReaderFactory.createXMLReader();
+ * spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ * spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ * spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
+ * as given by
+ * https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#XMLReader
*/
- protected void configure() {
- // load the properties file of libraries available
- Properties properties = getJellyProperties();
- for (Iterator iter = properties.entrySet().iterator(); iter.hasNext();) {
- Map.Entry entry = (Map.Entry) iter.next();
- String uri = (String) entry.getKey();
- String className = (String) entry.getValue();
- String libraryURI = "jelly:" + uri;
-
- // don't overload any Mock Tags already
- if ( ! context.isTagLibraryRegistered(libraryURI) ) {
- context.registerTagLibrary(libraryURI, className);
- }
- }
+ public void setAllowDtdToCallExternalEntities(boolean allowDtdToCallExternalEntities) {
+ this.allowDtdToCallExternalEntities = allowDtdToCallExternalEntities;
}
/**
- * A helper method which loads the static Jelly properties once on startup
+ * Sets the class loader to be used for instantiating application objects
+ * when required.
+ *
+ * @param classLoader The new class loader to use, or {@code null}
+ * to revert to the standard rules
*/
- protected synchronized Properties getJellyProperties() {
- if (jellyProperties == null) {
- jellyProperties = new Properties();
+ public void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
- InputStream in = null;
- URL url =
- getClassLoader().getResource("org/apache/commons/jelly/jelly.properties");
- if (url != null) {
- log.debug("Loading Jelly default tag libraries from: " + url);
- try {
- in = url.openStream();
- jellyProperties .load(in);
- }
- catch (IOException e) {
- log.error("Could not load jelly properties from: " + url + ". Reason: " + e, e);
- }
- finally {
- try {
- in.close();
- }
- catch (Exception e) {
- if (log.isDebugEnabled()) log.debug("error closing jelly.properties", e);
- }
- }
- }
- }
- return jellyProperties;
+ public void setContext(JellyContext context) {
+ this.context = context;
}
/**
- * Factory method to create new Tag script for the given namespaceURI and name or
- * return null if this is not a custom Tag.
+ * Sets the jelly namespace to use for unprefixed elements.
+ * Will be overridden by an explicit namespace in the
+ * XML document.
+ *
+ * @param namespace jelly namespace to use (e.g. 'jelly:core')
*/
- protected TagScript createTag(
- String namespaceURI,
- String localName,
- Attributes list)
- throws SAXException {
- try {
- // use the URI to load a taglib
- TagLibrary taglib = context.getTagLibrary(namespaceURI);
- if (taglib == null) {
- if (namespaceURI != null && namespaceURI.startsWith("jelly:")) {
- String uri = namespaceURI.substring(6);
- // try to find the class on the classpath
- try {
- Class taglibClass = getClassLoader().loadClass(uri);
- taglib = (TagLibrary) taglibClass.getConstructor().newInstance();
- context.registerTagLibrary(namespaceURI, taglib);
- }
- catch (ClassNotFoundException e) {
- throw createSAXException("Could not load class: " + uri + " so taglib instantiation failed", e);
- }
- catch (IllegalAccessException e) {
- throw createSAXException("Constructor for class is not accessible: " + uri + " so taglib instantiation failed", e);
- }
- catch (InstantiationException e) {
- throw createSAXException("Class could not be instantiated: " + uri + " so taglib instantiation failed", e);
- }
- catch (ClassCastException e) {
- throw createSAXException("Class is not a TagLibrary: " + uri + " so taglib instantiation failed", e);
- }
- }
- }
- if (taglib != null) {
- TagScript script = taglib.createTagScript(localName, list);
- if ( script != null ) {
- configureTagScript(taglib, script);
-
- // clone the attributes to keep them around after this parse
- script.setSaxAttributes(new AttributesImpl(list));
-
- // now iterate through through the expressions
- int size = list.getLength();
- for (int i = 0; i < size; i++) {
- String attributeName = list.getLocalName(i);
- // Fix for JELLY-184 where the xmlns attributes have a blank name and cause
- // an exception later on
- if (attributeName.length() == 0)
- continue;
- String attributeValue = list.getValue(i);
- Expression expression =
- createExpression(script,
- attributeName,
- attributeValue);
-// Expression expression =
-// taglib.createExpression(
-// getExpressionFactory(),
-// script,
-// attributeName,
-// attributeValue);
- if (expression == null) {
- expression = createConstantExpression(localName, attributeName, attributeValue);
- }
- script.addAttribute(attributeName, expression);
- }
- } else if (!taglib.isAllowUnknownTags())
- throw new JellyException("Unrecognized tag called " + localName + " in TagLibrary " + namespaceURI);
- return script;
- }
- return null;
- }
- catch (Exception e) {
- log.warn(
- "Could not create taglib or URI: " + namespaceURI + " tag name: " + localName,
- e);
- throw createSAXException(e);
- }
+ public void setDefaultNamespaceURI(String namespace) {
+ this.defaultNamespaceURI = namespace;
}
/**
- * Factory method to create a static Tag that represents some static content.
+ * Sets the document locator associated with our parser.
+ *
+ * @param locator The new locator
*/
- protected TagScript createStaticTag(
- final String namespaceURI,
- final String localName,
- final String qName,
- Attributes list)
- throws SAXException {
- try {
- //StaticTag tag = new StaticTag( namespaceURI, localName, qName );
- StaticTagScript script = new StaticTagScript(
- new TagFactory() {
- @Override
- public Tag createTag(String name, Attributes attributes) {
- return new StaticTag( namespaceURI, localName, qName );
- }
- }
- );
- configureTagScript(script);
-
- // now iterate through through the expressions
- int size = list.getLength();
- for (int i = 0; i < size; i++) {
- String attributeValue = list.getValue(i);
- Expression expression = createExpression(script, null, attributeValue);
-// Expression expression = CompositeExpression.parse(
-// attributeValue, getExpressionFactory()
-// );
- String attrQName = list.getQName(i);
- int p = attrQName.indexOf(':');
- String prefix = p>=0 ?
- attrQName.substring(0, p):
- "";
- script.addAttribute(list.getLocalName(i),
- prefix, list.getURI(i), expression);
- }
- return script;
- }
- catch (Exception e) {
- log.warn(
- "Could not create static tag for URI: "
- + namespaceURI
- + " tag name: "
- + localName,
- e);
- throw createSAXException(e);
- }
+ @Override
+ public void setDocumentLocator(Locator locator) {
+ this.locator = locator;
}
/**
- * Configure a newly created TagScript instance before any Expressions are created;
- * see configureTagScript(TagScript) for StaticTags
- * @param tagLibrary the TagLibrary that created the TagScript
- * @param aTagScript the TagScript that's just been created
+ * Sets the error handler for this XMLParser.
+ *
+ * @param errorHandler The new error handler
*/
- protected void configureTagScript(TagLibrary tagLibrary, TagScript aTagScript) {
- // Set the TagLibrary that created the script
- aTagScript.setTagLibrary(tagLibrary);
+ public void setErrorHandler(ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
- configureTagScript(aTagScript);
+ /** Sets the expression factory used to evaluate tag attributes */
+ public void setExpressionFactory(ExpressionFactory expressionFactory) {
+ this.expressionFactory = expressionFactory;
}
/**
- * Configure a newly created TagScript instance before any Expressions are created
- * @param aTagScript
+ * Sets the current logger for this XMLParser.
*/
- protected void configureTagScript(TagScript aTagScript) {
- // set parent relationship...
- aTagScript.setParent(this.tagScript);
-
- // set the namespace Map
- if ( elementNamespaces != null ) {
- aTagScript.setTagNamespacesMap( elementNamespaces );
- elementNamespaces = null;
- }
+ public void setLogger(Log log) {
+ this.log = log;
}
/**
- * Adds the text to the current script block parsing any embedded
- * expressions into ExpressionScript objects.
+ * Determine whether to use the Context ClassLoader (the one found by
+ * calling <code>Thread.currentThread().getContextClassLoader()</code>)
+ * to resolve/load classes. If not
+ * using Context ClassLoader, then the class-loading defaults to
+ * using the calling-class' ClassLoader.
+ *
+ * @param use determines whether to use JellyContext ClassLoader.
*/
- protected void addTextScript(String text) throws JellyException {
- Expression expression = createExpression(tagScript, "", text);
- addExpressionScript(script, expression);
+ public void setUseContextClassLoader(boolean use) {
+ useContextClassLoader = use;
}
/**
- * Adds the given Expression object to the current Script.
+ * Sets the validating parser flag. This must be called before
+ * <code>parse()</code> is called the first time.
+ *
+ * @param validating The new validating parser flag.
*/
- protected void addExpressionScript(ScriptBlock script, Expression expression) {
- if ( expression instanceof ConstantExpression ) {
- ConstantExpression constantExpression
- = (ConstantExpression) expression;
- Object value = constantExpression.getValue();
- if ( value != null ) {
- script.addScript(new TextScript( value.toString() ));
- }
- }
- else
- if ( expression instanceof CompositeExpression ) {
- CompositeTextScriptBlock newBlock = new CompositeTextScriptBlock();
- script.addScript(newBlock);
-
- CompositeExpression compositeExpression
- = (CompositeExpression) expression;
- Iterator iter = compositeExpression.getExpressions().iterator();
- while (iter.hasNext()) {
- addExpressionScript( newBlock, (Expression) iter.next() );
- }
- }
- else {
- script.addScript(new ExpressionScript(expression));
- }
+ public void setValidating(boolean validating) {
+ this.validating = validating;
}
- protected Expression createConstantExpression(
- String tagName,
- String attributeName,
- String attributeValue) {
- return new ConstantExpression(attributeValue);
+ /**
+ * Process notification of a skipped entity.
+ *
+ * @param name Name of the skipped entity
+ * @throws SAXException if a parsing error is to be reported
+ */
+ @Override
+ public void skippedEntity(String name) throws SAXException {
+ // No processing required
}
+ // ContentHandler interface
+ //-------------------------------------------------------------------------
/**
- * @return the current context URI as a String or null if there is no
- * current context defined on the JellyContext
+ * Process notification of the beginning of the document being reached.
+ *
+ * @throws SAXException if a parsing error is to be reported
*/
- protected String getCurrentURI() {
- URL url = this.getContext().getCurrentURL();
- return (url != null) ? url.toString() : null;
+ @Override
+ public void startDocument() throws SAXException {
+ script = new ScriptBlock();
+ textBuffer = new StringBuffer();
+ tagScript = null;
+ scriptStack.clear();
+ tagScriptStack.clear();
}
/**
- * Create a SAX exception which also understands about the location in
- * the file where the exception occurs
+ * Process notification of the start of an XML element being reached.
*
- * @return the new exception
+ * @param namespaceURI The Namespace URI, or the empty string if the
+ * element has no Namespace URI or if Namespace processing is not
+ * being performed.
+ * @param localName The local name (without prefix), or the empty
+ * string if Namespace processing is not being performed.
+ * @param qName The qualified name (with prefix), or the empty
+ * string if qualified names are not available.\
+ * @param list The attributes attached to the element. If there are
+ * no attributes, it shall be an empty Attributes object.
+ * @throws SAXException if a parsing error is to be reported
*/
- protected SAXException createSAXException(String message, Exception e) {
- log.warn("Underlying exception: " + e);
- if (e != null) {
- e.printStackTrace();
- }
- if (locator != null) {
- String error =
- "Error at ("
- + locator.getLineNumber()
- + ", "
- + locator.getColumnNumber()
- + "): "
- + message;
- if (e != null) {
- return new SAXParseException(error, locator, e);
+ @Override
+ public void startElement(
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes list)
+ throws SAXException {
+
+ try {
+ // add check to ensure namespace URI is "" for no namespace
+ if ( namespaceURI == null ) {
+ namespaceURI = "";
+ }
+
+ // if this is a tag then create a script to run it
+ // otherwise pass the text to the current body
+ TagScript newTagScript = createTag(namespaceURI, localName, list);
+ if (newTagScript == null) {
+ newTagScript = createStaticTag(namespaceURI, localName, qName, list);
+ }
+ tagScript = newTagScript;
+ tagScriptStack.add(tagScript);
+ if (tagScript != null) {
+ // set the line number details
+ if ( locator != null ) {
+ tagScript.setLocator(locator);
+ }
+ // sets the file name element names
+ if (fileName != null)
+ tagScript.setFileName(fileName);
+ tagScript.setElementName(qName);
+ tagScript.setLocalName(localName);
+
+ if (textBuffer.length() > 0) {
+ addTextScript(textBuffer.toString());
+ textBuffer.setLength(0);
+ }
+ script.addScript(tagScript);
+ // start a new body
+ scriptStack.push(script);
+ script = new ScriptBlock();
+ tagScript.setTagBody(script);
}
else {
- return new SAXParseException(error, locator);
+ // XXXX: might wanna handle empty elements later...
+ textBuffer.append("<");
+ textBuffer.append(qName);
+ int size = list.getLength();
+ for (int i = 0; i < size; i++) {
+ textBuffer.append(" ");
+ textBuffer.append(list.getQName(i));
+ textBuffer.append("=");
+ textBuffer.append("\"");
+ textBuffer.append(list.getValue(i));
+ textBuffer.append("\"");
+ }
+ textBuffer.append(">");
}
}
- log.error("No Locator!");
- if (e != null) {
- return new SAXException(message, e);
+ catch (SAXException e) {
+ throw e;
}
- else {
- return new SAXException(message);
+ catch (Exception e) {
+ log.error( "Caught exception: " + e, e );
+ throw new SAXException( "Runtime Exception: " + e, e );
}
}
/**
- * Create a SAX exception which also understands about the location in
- * the digester file where the exception occurs
+ * Process notification that a namespace prefix is coming in to scope.
*
- * @return the new exception
+ * @param prefix Prefix that is being declared
+ * @param namespaceURI Corresponding namespace URI being mapped to
+ * @throws SAXException if a parsing error is to be reported
*/
- protected SAXException createSAXException(Exception e) {
- return createSAXException(e.getMessage(), e);
+ @Override
+ public void startPrefixMapping(String prefix, String namespaceURI)
+ throws SAXException {
+ // Register this prefix mapping
+ ArrayDeque stack = (ArrayDeque) namespaces.get(prefix);
+ if (stack == null) {
+ stack = new ArrayDeque();
+ namespaces.put(prefix, stack);
+ }
+ stack.push(namespaceURI);
+
+ if ( elementNamespaces == null ) {
+ elementNamespaces = new HashMap();
+ }
+ elementNamespaces.put(prefix, namespaceURI);
}
+
/**
- * Create a SAX exception which also understands about the location in
- * the digester file where the exception occurs
+ * Receive notification of an unparsed entity declaration event.
*
- * @return the new exception
+ * @param name The unparsed entity name
+ * @param publicId The public identifier (if any)
+ * @param systemId The system identifier (if any)
+ * @param notation The name of the associated notation
*/
- protected SAXException createSAXException(String message) {
- return createSAXException(message, null);
+ @Override
+ public void unparsedEntityDecl(
+ String name,
+ String publicId,
+ String systemId,
+ String notation) {
+ }
+ /**
+ * Forward notification of a parse warning to the application supplied
+ * error handler (if any). Unlike XMLParser.error(SAXParseException) and
+ * XMLParser.fatalError(SAXParseException), this implementation will
+ * NOT throw a SAXException by default if no error handler is supplied.
+ *
+ * @param exception The warning information
+ * @throws SAXException if a parsing exception occurs
+ */
+ @Override
+ public void warning(SAXParseException exception) throws SAXException {
+ log.error(
+ "Parse Warning at line "
+ + exception.getLineNumber()
+ + " column "
+ + exception.getColumnNumber()
+ + ": "
+ + exception.getMessage(),
+ exception);
+ if (errorHandler != null) {
+ errorHandler.warning(exception);
+ }
}
}
diff --git a/core/src/main/java/org/apache/commons/jelly/servlet/JellyServlet.java b/core/src/main/java/org/apache/commons/jelly/servlet/JellyServlet.java
index 443cdbd0..8b551e01 100644
--- a/core/src/main/java/org/apache/commons/jelly/servlet/JellyServlet.java
+++ b/core/src/main/java/org/apache/commons/jelly/servlet/JellyServlet.java
@@ -48,6 +48,22 @@ public class JellyServlet extends HttpServlet {
*/
public static final String RESPONSE = "response";
+ /**
+ * See org.apache.velocity.servlet.VelocityServlet#createContext
+ * @param req
+ * @param res
+ * @return a new context.
+ */
+ protected JellyContext createContext(
+ HttpServletRequest req,
+ HttpServletResponse res) {
+
+ JellyContext ctx = new JellyServletContext(getServletContext());
+ ctx.setVariable(REQUEST, req);
+ ctx.setVariable(RESPONSE, res);
+ return ctx;
+ }
+
@Override
protected void doGet(
HttpServletRequest request,
@@ -87,19 +103,42 @@ public class JellyServlet extends HttpServlet {
}
/**
- * See org.apache.velocity.servlet.VelocityServlet#createContext
- * @param req
- * @param res
- * @return a new context.
+ * Invoked when there is an error thrown in any part of doRequest() processing.
+ * <br><br>
+ * Default will send a simple HTML response indicating there was a problem.
+ *<br><br>
+ * Ripped from VelocityServlet.
+ *
+ * @param request original HttpServletRequest from servlet container.
+ * @param response HttpServletResponse object from servlet container.
+ * @param cause Exception that was thrown by some other part of process.
*/
- protected JellyContext createContext(
- HttpServletRequest req,
- HttpServletResponse res) {
+ protected void error(
+ HttpServletRequest request,
+ HttpServletResponse response,
+ Exception cause)
+ throws ServletException, IOException {
- JellyContext ctx = new JellyServletContext(getServletContext());
- ctx.setVariable(REQUEST, req);
- ctx.setVariable(RESPONSE, res);
- return ctx;
+ StringBuilder html = new StringBuilder();
+ html.append("<html>");
+ html.append("<title>Error</title>");
+ html.append("<body bgcolor=\"#ffffff\">");
+ html.append("<h2>JellyServlet : Error processing the script</h2>");
+ html.append("<pre>");
+ String why = cause.getMessage();
+ if (why != null && why.trim().length() > 0) {
+ html.append(why);
+ html.append("<br>");
+ }
+
+ StringWriter sw = new StringWriter();
+ cause.printStackTrace(new PrintWriter(sw));
+
+ html.append(sw.toString());
+ html.append("</pre>");
+ html.append("</body>");
+ html.append("</html>");
+ response.getOutputStream().print(html.toString());
}
/**
@@ -154,43 +193,4 @@ public class JellyServlet extends HttpServlet {
xmlOutput.close();
output.flush();
}
-
- /**
- * Invoked when there is an error thrown in any part of doRequest() processing.
- * <br><br>
- * Default will send a simple HTML response indicating there was a problem.
- *<br><br>
- * Ripped from VelocityServlet.
- *
- * @param request original HttpServletRequest from servlet container.
- * @param response HttpServletResponse object from servlet container.
- * @param cause Exception that was thrown by some other part of process.
- */
- protected void error(
- HttpServletRequest request,
- HttpServletResponse response,
- Exception cause)
- throws ServletException, IOException {
-
- StringBuilder html = new StringBuilder();
- html.append("<html>");
- html.append("<title>Error</title>");
- html.append("<body bgcolor=\"#ffffff\">");
- html.append("<h2>JellyServlet : Error processing the script</h2>");
- html.append("<pre>");
- String why = cause.getMessage();
- if (why != null && why.trim().length() > 0) {
- html.append(why);
- html.append("<br>");
- }
-
- StringWriter sw = new StringWriter();
- cause.printStackTrace(new PrintWriter(sw));
-
- html.append(sw.toString());
- html.append("</pre>");
- html.append("</body>");
- html.append("</html>");
- response.getOutputStream().print(html.toString());
- }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/servlet/JellyServletContext.java b/core/src/main/java/org/apache/commons/jelly/servlet/JellyServletContext.java
index 3b987982..ea5e635f 100644
--- a/core/src/main/java/org/apache/commons/jelly/servlet/JellyServletContext.java
+++ b/core/src/main/java/org/apache/commons/jelly/servlet/JellyServletContext.java
@@ -35,15 +35,20 @@ public class JellyServletContext extends JellyContext {
public JellyServletContext() {
}
- public JellyServletContext(final ServletContext ctx) {
+ public JellyServletContext(final JellyContext parent, final ServletContext ctx) {
+ super(parent);
this.ctx = ctx;
}
- public JellyServletContext(final JellyContext parent, final ServletContext ctx) {
- super(parent);
+ public JellyServletContext(final ServletContext ctx) {
this.ctx = ctx;
}
+ @Override
+ protected JellyContext createChildContext() {
+ return new JellyServletContext(this, ctx);
+ }
+
/**
* Allow access of relative URIs when performing <j:include>.
*
@@ -66,9 +71,4 @@ public class JellyServletContext extends JellyContext {
public InputStream getResourceAsStream(final String s) {
return ctx.getResourceAsStream(s);
}
-
- @Override
- protected JellyContext createChildContext() {
- return new JellyServletContext(this, ctx);
- }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/tags/Resources.java b/core/src/main/java/org/apache/commons/jelly/tags/Resources.java
index f0015bb1..cd877510 100644
--- a/core/src/main/java/org/apache/commons/jelly/tags/Resources.java
+++ b/core/src/main/java/org/apache/commons/jelly/tags/Resources.java
@@ -60,13 +60,6 @@ public class Resources {
return rb.getString(name);
}
- /** Gets a message with arbitrarily many arguments. */
- public static String getMessage(String name, Object[] a)
- throws MissingResourceException {
- String res = rb.getString(name);
- return MessageFormat.format(res, a);
- }
-
/** Gets a message with one argument. */
public static String getMessage(String name, Object a1)
throws MissingResourceException {
@@ -121,4 +114,11 @@ public class Resources {
return getMessage(name, new Object[] { a1, a2, a3, a4, a5, a6 });
}
+ /** Gets a message with arbitrarily many arguments. */
+ public static String getMessage(String name, Object[] a)
+ throws MissingResourceException {
+ String res = rb.getString(name);
+ return MessageFormat.format(res, a);
+ }
+
}
diff --git a/core/src/main/java/org/apache/commons/jelly/tags/core/ArgTag.java b/core/src/main/java/org/apache/commons/jelly/tags/core/ArgTag.java
index 470cecde..9104e212 100644
--- a/core/src/main/java/org/apache/commons/jelly/tags/core/ArgTag.java
+++ b/core/src/main/java/org/apache/commons/jelly/tags/core/ArgTag.java
@@ -42,141 +42,12 @@ public class ArgTag extends BaseClassLoaderTag {
// constructors
//-------------------------------------------------------------------------
- public ArgTag() {
- }
+ /** My bag of converters, by target Class */
+ private static Map converterMap = new HashMap();
// attribute setters
//-------------------------------------------------------------------------
- /**
- * The name of the argument class or type, if any.
- * This may be a fully specified class name or
- * a primitive type name
- * (<code>boolean</code>, <code>int</code>, <code>double</code>, etc.).
- */
- public void setType(String type) {
- this.typeString = type;
- }
-
- /** The (possibly null) value of this argument. */
- public void setValue(Object value) {
- this.value= value;
- }
-
- // tag methods
- //-------------------------------------------------------------------------
-
- @Override
- public void doTag(XMLOutput output) throws JellyTagException {
- invokeBody(output);
-
- Class klass = null;
- if ("boolean".equals(typeString)) {
- klass = Boolean.TYPE;
- assertNotNull(value);
- } else if ("byte".equals(typeString)) {
- klass = Byte.TYPE;
- assertNotNull(value);
- } else if ("short".equals(typeString)) {
- klass = Short.TYPE;
- assertNotNull(value);
- } else if ("int".equals(typeString)) {
- klass = Integer.TYPE;
- assertNotNull(value);
- } else if ("char".equals(typeString)) {
- klass = Character.TYPE;
- assertNotNull(value);
- } else if ("float".equals(typeString)) {
- klass = Float.TYPE;
- assertNotNull(value);
- } else if ("long".equals(typeString)) {
- klass = Long.TYPE;
- assertNotNull(value);
- } else if ("double".equals(typeString)) {
- klass = Double.TYPE;
- assertNotNull(value);
- } else if (null != typeString) {
- try {
- // klass = getClassLoader().loadClass(typeString);
- // JELLY-274: rather use the three args static class-load-method
- klass = Class.forName(typeString, false, getClassLoader());
- } catch (ClassNotFoundException e) {
- throw new JellyTagException(e);
- }
- } else if (null == value) { // and (by construction) null == typeString
- klass = Object.class;
- } else {
- klass = value.getClass();
- }
-
- if (!isInstanceOf(klass, value)) {
- if (klass.equals(Class.class)) {
- try {
- // value = getClassLoader().loadClass((String) value);
- // JELLY-274: rather use three-args class.forName
- value = Class.forName((String) value, false, getClassLoader());
- } catch (ClassNotFoundException e) {
- throw new JellyTagException(e);
- }
- } else {
- value = convert(klass, value);
- }
- }
-
- ArgTagParent parent = (ArgTagParent)findAncestorWithClass(ArgTagParent.class);
- if (null == parent) {
- throw new JellyTagException("This tag must be enclosed inside an ArgTagParent implementation (for example, <new> or <invoke>)");
- } else {
- parent.addArgument(klass, value);
- }
- }
-
- // private methods
- //-------------------------------------------------------------------------
-
- private void assertNotNull(Object value) throws JellyTagException {
- if (null == value) {
- throw new JellyTagException("A " + typeString + " instance cannot be null.");
- }
- }
-
- private boolean isInstanceOf(Class klass, Object value) {
- return (null == value || (klass.isInstance(value)));
- }
-
- // attributes
- //-------------------------------------------------------------------------
-
- /** The name of the parameter type, if any. */
- private String typeString;
-
- /** The value of the parameter, if any */
- private Object value;
-
- // static stuff
- //-------------------------------------------------------------------------
-
- private static Object convert(Class klass, Object value) throws JellyTagException {
- if (null == value) {
- return null;
- } else if (!klass.isInstance(value)) {
- Converter converter = (Converter) (converterMap.get(klass));
- if (null == converter) {
- throw new JellyTagException("Can't convert " + value + " to " + klass);
- }
- try {
- return converter.convert(klass, value);
- } catch (ConversionException e) {
- throw new JellyTagException("Can't convert " + value + " to " + klass + " (" + e.toString() + ")", e);
- }
- } else {
- return value;
- }
-
- }
-
- /** My bag of converters, by target Class */
- private static Map converterMap = new HashMap();
// these inner classes should probably move to beanutils
static {
{
@@ -191,6 +62,8 @@ public class ArgTag extends BaseClassLoaderTag {
}
{
Converter c = new Converter() {
+ private Converter inner = new ByteConverter();
+
@Override
public Object convert(Class klass, Object value) {
if (value instanceof Number) {
@@ -199,14 +72,14 @@ public class ArgTag extends BaseClassLoaderTag {
return inner.convert(klass, value);
}
}
-
- private Converter inner = new ByteConverter();
};
converterMap.put(Byte.TYPE, c);
converterMap.put(Byte.class, c);
}
{
Converter c = new Converter() {
+ private Converter inner = new ShortConverter();
+
@Override
public Object convert(Class klass, Object value) {
if (value instanceof Number) {
@@ -215,14 +88,14 @@ public class ArgTag extends BaseClassLoaderTag {
return inner.convert(klass, value);
}
}
-
- private Converter inner = new ShortConverter();
};
converterMap.put(Short.TYPE, c);
converterMap.put(Short.class, c);
}
{
Converter c = new Converter() {
+ private Converter inner = new IntegerConverter();
+
@Override
public Object convert(Class klass, Object value) {
if (value instanceof Number) {
@@ -231,14 +104,14 @@ public class ArgTag extends BaseClassLoaderTag {
return inner.convert(klass, value);
}
}
-
- private Converter inner = new IntegerConverter();
};
converterMap.put(Integer.TYPE, c);
converterMap.put(Integer.class, c);
}
{
Converter c = new Converter() {
+ private Converter inner = new LongConverter();
+
@Override
public Object convert(Class klass, Object value) {
if (value instanceof Number) {
@@ -247,14 +120,14 @@ public class ArgTag extends BaseClassLoaderTag {
return inner.convert(klass, value);
}
}
-
- private Converter inner = new LongConverter();
};
converterMap.put(Long.TYPE, c);
converterMap.put(Long.class, c);
}
{
Converter c = new Converter() {
+ private Converter inner = new FloatConverter();
+
@Override
public Object convert(Class klass, Object value) {
if (value instanceof Number) {
@@ -263,14 +136,14 @@ public class ArgTag extends BaseClassLoaderTag {
return inner.convert(klass, value);
}
}
-
- private Converter inner = new FloatConverter();
};
converterMap.put(Float.TYPE, c);
converterMap.put(Float.class, c);
}
{
Converter c = new Converter() {
+ private Converter inner = new DoubleConverter();
+
@Override
public Object convert(Class klass, Object value) {
if (value instanceof Number) {
@@ -279,11 +152,138 @@ public class ArgTag extends BaseClassLoaderTag {
return inner.convert(klass, value);
}
}
-
- private Converter inner = new DoubleConverter();
};
converterMap.put(Double.TYPE, c);
converterMap.put(Double.class, c);
}
}
+
+ private static Object convert(Class klass, Object value) throws JellyTagException {
+ if (null == value) {
+ return null;
+ } else if (!klass.isInstance(value)) {
+ Converter converter = (Converter) (converterMap.get(klass));
+ if (null == converter) {
+ throw new JellyTagException("Can't convert " + value + " to " + klass);
+ }
+ try {
+ return converter.convert(klass, value);
+ } catch (ConversionException e) {
+ throw new JellyTagException("Can't convert " + value + " to " + klass + " (" + e.toString() + ")", e);
+ }
+ } else {
+ return value;
+ }
+
+ }
+
+ // tag methods
+ //-------------------------------------------------------------------------
+
+ /** The name of the parameter type, if any. */
+ private String typeString;
+
+ // private methods
+ //-------------------------------------------------------------------------
+
+ /** The value of the parameter, if any */
+ private Object value;
+
+ public ArgTag() {
+ }
+
+ // attributes
+ //-------------------------------------------------------------------------
+
+ private void assertNotNull(Object value) throws JellyTagException {
+ if (null == value) {
+ throw new JellyTagException("A " + typeString + " instance cannot be null.");
+ }
+ }
+
+ @Override
+ public void doTag(XMLOutput output) throws JellyTagException {
+ invokeBody(output);
+
+ Class klass = null;
+ if ("boolean".equals(typeString)) {
+ klass = Boolean.TYPE;
+ assertNotNull(value);
+ } else if ("byte".equals(typeString)) {
+ klass = Byte.TYPE;
+ assertNotNull(value);
+ } else if ("short".equals(typeString)) {
+ klass = Short.TYPE;
+ assertNotNull(value);
+ } else if ("int".equals(typeString)) {
+ klass = Integer.TYPE;
+ assertNotNull(value);
+ } else if ("char".equals(typeString)) {
+ klass = Character.TYPE;
+ assertNotNull(value);
+ } else if ("float".equals(typeString)) {
+ klass = Float.TYPE;
+ assertNotNull(value);
+ } else if ("long".equals(typeString)) {
+ klass = Long.TYPE;
+ assertNotNull(value);
+ } else if ("double".equals(typeString)) {
+ klass = Double.TYPE;
+ assertNotNull(value);
+ } else if (null != typeString) {
+ try {
+ // klass = getClassLoader().loadClass(typeString);
+ // JELLY-274: rather use the three args static class-load-method
+ klass = Class.forName(typeString, false, getClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw new JellyTagException(e);
+ }
+ } else if (null == value) { // and (by construction) null == typeString
+ klass = Object.class;
+ } else {
+ klass = value.getClass();
+ }
+
+ if (!isInstanceOf(klass, value)) {
+ if (klass.equals(Class.class)) {
+ try {
+ // value = getClassLoader().loadClass((String) value);
+ // JELLY-274: rather use three-args class.forName
+ value = Class.forName((String) value, false, getClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw new JellyTagException(e);
+ }
+ } else {
+ value = convert(klass, value);
+ }
+ }
+
+ ArgTagParent parent = (ArgTagParent)findAncestorWithClass(ArgTagParent.class);
+ if (null == parent) {
+ throw new JellyTagException("This tag must be enclosed inside an ArgTagParent implementation (for example, <new> or <invoke>)");
+ } else {
+ parent.addArgument(klass, value);
+ }
+ }
+
+ // static stuff
+ //-------------------------------------------------------------------------
+
+ private boolean isInstanceOf(Class klass, Object value) {
+ return (null == value || (klass.isInstance(value)));
+ }
+
+ /**
+ * The name of the argument class or type, if any.
+ * This may be a fully specified class name or
+ * a primitive type name
+ * (<code>boolean</code>, <code>int</code>, <code>double</code>, etc.).
+ */
+ public void setType(String type) {
+ this.typeString = type;
+ }
+ /** The (possibly null) value of this argument. */
+ public void setValue(Object value) {
+ this.value= value;
+ }
}
diff --git a/core/src/main/java/org/apache/commons/jelly/tags/core/BaseClassLoaderTag.java b/core/src/main/java/org/apache/commons/jelly/tags/core/BaseClassLoaderTag.java
index 5a1fc358..befd7ed7 100644
--- a/core/src/main/java/org/apache/commons/jelly/tags/core/BaseClassLoaderTag.java
+++ b/core/src/main/java/org/apache/commons/jelly/tags/core/BaseClassLoaderTag.java
@@ -50,6 +50,13 @@ public abstract class BaseClassLoaderTag extends TagSupport {
return ClassLoaderUtils.getClassLoader(classLoader, useContextClassLoader, getClass());
}
+ /**
+ * Gets the boolean as to whether the context classloader should be used.
+ */
+ public boolean getUseContextClassLoader() {
+ return useContextClassLoader;
+ }
+
/**
... 27588 lines suppressed ...