001 package org.apache.commons.digester3.xmlrules;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import static java.util.Collections.unmodifiableSet;
023 import static org.apache.commons.digester3.binder.DigesterLoader.newLoader;
024
025 import java.io.File;
026 import java.io.InputStream;
027 import java.io.Reader;
028 import java.io.StringReader;
029 import java.net.MalformedURLException;
030 import java.net.URL;
031 import java.net.URLConnection;
032 import java.util.ArrayList;
033 import java.util.HashSet;
034 import java.util.List;
035 import java.util.Set;
036
037 import org.apache.commons.digester3.Digester;
038 import org.apache.commons.digester3.binder.AbstractRulesModule;
039 import org.xml.sax.InputSource;
040
041 /**
042 * {@link org.apache.commons.digester3.binder.RulesModule} implementation that allows loading rules from
043 * XML files.
044 *
045 * @since 3.0
046 */
047 public abstract class FromXmlRulesModule
048 extends AbstractRulesModule
049 {
050
051 private static final String DIGESTER_PUBLIC_ID = "-//Apache Commons //DTD digester-rules XML V1.0//EN";
052
053 private static final String DIGESTER_DTD_PATH = "digester-rules.dtd";
054
055 private final URL xmlRulesDtdUrl = FromXmlRulesModule.class.getResource( DIGESTER_DTD_PATH );
056
057 private final List<InputSource> inputSource = new ArrayList<InputSource>();
058
059 private final Set<String> systemIds = new HashSet<String>();
060
061 private String rootPath;
062
063 /**
064 * {@inheritDoc}
065 */
066 @Override
067 protected void configure()
068 {
069 if ( !inputSource.isEmpty() )
070 {
071 throw new IllegalStateException( "Re-entry is not allowed." );
072 }
073
074 try
075 {
076 loadRules();
077
078 XmlRulesModule xmlRulesModule = new XmlRulesModule( new NameSpaceURIRulesBinder( rulesBinder() ),
079 getSystemIds(), rootPath );
080 Digester digester = newLoader( xmlRulesModule )
081 .register( DIGESTER_PUBLIC_ID, xmlRulesDtdUrl.toString() )
082 .setXIncludeAware( true )
083 .setValidating( true )
084 .newDigester();
085
086 for ( InputSource source : inputSource )
087 {
088 try
089 {
090 digester.parse( source );
091 }
092 catch ( Exception e )
093 {
094 addError( "Impossible to load XML defined in the InputSource '%s': %s", source.getSystemId(),
095 e.getMessage() );
096 }
097 }
098 }
099 finally
100 {
101 inputSource.clear();
102 }
103 }
104
105 /**
106 *
107 */
108 protected abstract void loadRules();
109
110 /**
111 * Reads the XML rules from the given {@code org.xml.sax.InputSource}.
112 *
113 * @param inputSource The {@code org.xml.sax.InputSource} where reading the XML rules from.
114 */
115 protected final void loadXMLRules( InputSource inputSource )
116 {
117 if ( inputSource == null )
118 {
119 throw new IllegalArgumentException( "Argument 'inputSource' must be not null" );
120 }
121
122 this.inputSource.add( inputSource );
123
124 String systemId = inputSource.getSystemId();
125 if ( systemId != null )
126 {
127 if ( !systemIds.add( systemId ) )
128 {
129 addError( "XML rules file '%s' already bound", systemId );
130 }
131 }
132 }
133
134 /**
135 * Opens a new {@code org.xml.sax.InputSource} given a {@code java.io.InputStream}.
136 *
137 * @param input The {@code java.io.InputStream} where reading the XML rules from.
138 */
139 protected final void loadXMLRules( InputStream input )
140 {
141 if ( input == null )
142 {
143 throw new IllegalArgumentException( "Argument 'input' must be not null" );
144 }
145
146 loadXMLRules( new InputSource( input ) );
147 }
148
149 /**
150 * Opens a new {@code org.xml.sax.InputSource} given a {@code java.io.Reader}.
151 *
152 * @param reader The {@code java.io.Reader} where reading the XML rules from.
153 */
154 protected final void loadXMLRules( Reader reader )
155 {
156 if ( reader == null )
157 {
158 throw new IllegalArgumentException( "Argument 'input' must be not null" );
159 }
160
161 loadXMLRules( new InputSource( reader ) );
162 }
163
164 /**
165 * Opens a new {@code org.xml.sax.InputSource} given a {@code java.io.File}.
166 *
167 * @param file The {@code java.io.File} where reading the XML rules from.
168 */
169 protected final void loadXMLRules( File file )
170 {
171 if ( file == null )
172 {
173 throw new IllegalArgumentException( "Argument 'input' must be not null" );
174 }
175
176 try
177 {
178 loadXMLRules( file.toURI().toURL() );
179 }
180 catch ( MalformedURLException e )
181 {
182 rulesBinder().addError( e );
183 }
184 }
185
186 /**
187 * Opens a new {@code org.xml.sax.InputSource} given a URI in String representation.
188 *
189 * @param uri The URI in String representation where reading the XML rules from.
190 */
191 protected final void loadXMLRules( String uri )
192 {
193 if ( uri == null )
194 {
195 throw new IllegalArgumentException( "Argument 'uri' must be not null" );
196 }
197
198 try
199 {
200 loadXMLRules( new URL( uri ) );
201 }
202 catch ( MalformedURLException e )
203 {
204 rulesBinder().addError( e );
205 }
206 }
207
208 /**
209 * Opens a new {@code org.xml.sax.InputSource} given a {@code java.net.URL}.
210 *
211 * @param url The {@code java.net.URL} where reading the XML rules from.
212 */
213 protected final void loadXMLRules( URL url )
214 {
215 if ( url == null )
216 {
217 throw new IllegalArgumentException( "Argument 'url' must be not null" );
218 }
219
220 try
221 {
222 URLConnection connection = url.openConnection();
223 connection.setUseCaches( false );
224 InputStream stream = connection.getInputStream();
225 InputSource source = new InputSource( stream );
226 source.setSystemId( url.toExternalForm() );
227
228 loadXMLRules( source );
229 }
230 catch ( Exception e )
231 {
232 rulesBinder().addError( e );
233 }
234 }
235
236 /**
237 * Opens a new {@code org.xml.sax.InputSource} given an XML document in textual form.
238 *
239 * @param xmlText The XML document in textual form where reading the XML rules from.
240 */
241 protected final void loadXMLRulesFromText( String xmlText )
242 {
243 if ( xmlText == null )
244 {
245 throw new IllegalArgumentException( "Argument 'xmlText' must be not null" );
246 }
247
248 loadXMLRules( new StringReader( xmlText ) );
249 }
250
251 /**
252 * Set the root path (will be used when composing modules).
253 *
254 * @param rootPath The root path
255 */
256 protected final void useRootPath( String rootPath )
257 {
258 this.rootPath = rootPath;
259 }
260
261 /**
262 * Returns the XML source SystemIds load by this module.
263 *
264 * @return The XML source SystemIds load by this module
265 */
266 public final Set<String> getSystemIds()
267 {
268 return unmodifiableSet( systemIds );
269 }
270
271 }