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 }