001package 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 022import static java.util.Collections.unmodifiableSet; 023import static org.apache.commons.digester3.binder.DigesterLoader.newLoader; 024 025import java.io.File; 026import java.io.InputStream; 027import java.io.Reader; 028import java.io.StringReader; 029import java.net.MalformedURLException; 030import java.net.URL; 031import java.net.URLConnection; 032import java.util.HashSet; 033import java.util.Set; 034 035import org.apache.commons.digester3.Digester; 036import org.apache.commons.digester3.binder.AbstractRulesModule; 037import org.xml.sax.InputSource; 038 039/** 040 * {@link org.apache.commons.digester3.binder.RulesModule} implementation that allows loading rules from 041 * XML files. 042 * 043 * @since 3.0 044 */ 045public abstract class FromXmlRulesModule 046 extends AbstractRulesModule 047{ 048 049 private static final String DIGESTER_PUBLIC_ID = "-//Apache Commons //DTD digester-rules XML V1.0//EN"; 050 051 private static final String DIGESTER_DTD_PATH = "digester-rules.dtd"; 052 053 private final URL xmlRulesDtdUrl = FromXmlRulesModule.class.getResource( DIGESTER_DTD_PATH ); 054 055 private final Set<String> systemIds = new HashSet<String>(); 056 057 private String rootPath; 058 059 /** 060 * {@inheritDoc} 061 */ 062 @Override 063 protected void configure() 064 { 065 if ( !systemIds.isEmpty() ) 066 { 067 throw new IllegalStateException( "Re-entry is not allowed." ); 068 } 069 070 try 071 { 072 loadRules(); 073 } 074 finally 075 { 076 systemIds.clear(); 077 } 078 } 079 080 /** 081 * 082 */ 083 protected abstract void loadRules(); 084 085 /** 086 * Reads the XML rules from the given {@code org.xml.sax.InputSource}. 087 * 088 * @param inputSource The {@code org.xml.sax.InputSource} where reading the XML rules from. 089 */ 090 protected final void loadXMLRules( InputSource inputSource ) 091 { 092 if ( inputSource == null ) 093 { 094 throw new IllegalArgumentException( "Argument 'inputSource' must be not null" ); 095 } 096 097 String systemId = inputSource.getSystemId(); 098 if ( systemId != null && !systemIds.add( systemId ) ) 099 { 100 addError( "XML rules file '%s' already bound", systemId ); 101 } 102 103 XmlRulesModule xmlRulesModule = new XmlRulesModule( new NameSpaceURIRulesBinder( rulesBinder() ), 104 getSystemIds(), rootPath ); 105 Digester digester = newLoader( xmlRulesModule ) 106 .register( DIGESTER_PUBLIC_ID, xmlRulesDtdUrl.toString() ) 107 .setXIncludeAware( true ) 108 .setValidating( true ) 109 .newDigester(); 110 111 try 112 { 113 digester.parse( inputSource ); 114 } 115 catch ( Exception e ) 116 { 117 addError( "Impossible to load XML defined in the InputSource '%s': %s", inputSource.getSystemId(), 118 e.getMessage() ); 119 } 120 } 121 122 /** 123 * Opens a new {@code org.xml.sax.InputSource} given a {@code java.io.InputStream}. 124 * 125 * @param input The {@code java.io.InputStream} where reading the XML rules from. 126 */ 127 protected final void loadXMLRules( InputStream input ) 128 { 129 if ( input == null ) 130 { 131 throw new IllegalArgumentException( "Argument 'input' must be not null" ); 132 } 133 134 loadXMLRules( new InputSource( input ) ); 135 } 136 137 /** 138 * Opens a new {@code org.xml.sax.InputSource} given a {@code java.io.Reader}. 139 * 140 * @param reader The {@code java.io.Reader} where reading the XML rules from. 141 */ 142 protected final void loadXMLRules( Reader reader ) 143 { 144 if ( reader == null ) 145 { 146 throw new IllegalArgumentException( "Argument 'input' must be not null" ); 147 } 148 149 loadXMLRules( new InputSource( reader ) ); 150 } 151 152 /** 153 * Opens a new {@code org.xml.sax.InputSource} given a {@code java.io.File}. 154 * 155 * @param file The {@code java.io.File} where reading the XML rules from. 156 */ 157 protected final void loadXMLRules( File file ) 158 { 159 if ( file == null ) 160 { 161 throw new IllegalArgumentException( "Argument 'input' must be not null" ); 162 } 163 164 try 165 { 166 loadXMLRules( file.toURI().toURL() ); 167 } 168 catch ( MalformedURLException e ) 169 { 170 rulesBinder().addError( e ); 171 } 172 } 173 174 /** 175 * Opens a new {@code org.xml.sax.InputSource} given a URI in String representation. 176 * 177 * @param uri The URI in String representation where reading the XML rules from. 178 */ 179 protected final void loadXMLRules( String uri ) 180 { 181 if ( uri == null ) 182 { 183 throw new IllegalArgumentException( "Argument 'uri' must be not null" ); 184 } 185 186 try 187 { 188 loadXMLRules( new URL( uri ) ); 189 } 190 catch ( MalformedURLException e ) 191 { 192 rulesBinder().addError( e ); 193 } 194 } 195 196 /** 197 * Opens a new {@code org.xml.sax.InputSource} given a {@code java.net.URL}. 198 * 199 * @param url The {@code java.net.URL} where reading the XML rules from. 200 */ 201 protected final void loadXMLRules( URL url ) 202 { 203 if ( url == null ) 204 { 205 throw new IllegalArgumentException( "Argument 'url' must be not null" ); 206 } 207 208 try 209 { 210 URLConnection connection = url.openConnection(); 211 connection.setUseCaches( false ); 212 InputStream stream = connection.getInputStream(); 213 InputSource source = new InputSource( stream ); 214 source.setSystemId( url.toExternalForm() ); 215 216 loadXMLRules( source ); 217 } 218 catch ( Exception e ) 219 { 220 rulesBinder().addError( e ); 221 } 222 } 223 224 /** 225 * Opens a new {@code org.xml.sax.InputSource} given an XML document in textual form. 226 * 227 * @param xmlText The XML document in textual form where reading the XML rules from. 228 */ 229 protected final void loadXMLRulesFromText( String xmlText ) 230 { 231 if ( xmlText == null ) 232 { 233 throw new IllegalArgumentException( "Argument 'xmlText' must be not null" ); 234 } 235 236 loadXMLRules( new StringReader( xmlText ) ); 237 } 238 239 /** 240 * Set the root path (will be used when composing modules). 241 * 242 * @param rootPath The root path 243 */ 244 protected final void useRootPath( String rootPath ) 245 { 246 this.rootPath = rootPath; 247 } 248 249 /** 250 * Returns the XML source SystemIds load by this module. 251 * 252 * @return The XML source SystemIds load by this module 253 */ 254 public final Set<String> getSystemIds() 255 { 256 return unmodifiableSet( systemIds ); 257 } 258 259}