View Javadoc
1   /**
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Intalio, Inc.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Intalio, Inc. Exolab is a registered
23   *    trademark of Intalio, Inc.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 1999-2004 (C) Intalio, Inc. All Rights Reserved.
42   *
43   * $Id$
44   */
45  
46  package org.exolab.castor.xml.schema.reader;
47  
48  import org.exolab.castor.net.URIException;
49  import org.exolab.castor.net.URILocation;
50  import org.exolab.castor.net.URIResolver;
51  import org.exolab.castor.xml.AttributeSet;
52  import org.exolab.castor.xml.XMLException;
53  import org.exolab.castor.xml.schema.SchemaContext;
54  import org.exolab.castor.xml.schema.Schema;
55  import org.exolab.castor.xml.schema.SchemaException;
56  import org.exolab.castor.xml.schema.SchemaNames;
57  import org.xml.sax.InputSource;
58  import org.xml.sax.Locator;
59  import org.xml.sax.Parser;
60  
61  public class ImportUnmarshaller extends ComponentReader
62  {
63  
64  
65      public ImportUnmarshaller(
66              final SchemaContext schemaContext,
67              final Schema schema,
68              final AttributeSet atts,
69              final URIResolver uriResolver,
70              final Locator locator, 
71              final SchemaUnmarshallerState state)
72      throws XMLException {
73          super(schemaContext);
74          setURIResolver(uriResolver);
75  
76          URILocation uri = null;
77          //-- Get schemaLocation
78          String schemaLocation = atts.getValue(SchemaNames.SCHEMALOCATION_ATTR);
79          //-- Get namespace
80          String namespace = atts.getValue("namespace");
81  
82          if ((schemaLocation == null) && (namespace == null)) {
83              //-- A legal <import/> element...just return
84              return;
85          }
86  
87          boolean hasLocation = (schemaLocation != null);
88          if (hasLocation) {
89  
90              if (schemaLocation.indexOf("\\") != -1) {
91                  String err = "'" + schemaLocation +
92                      "' is not a valid URI as defined by IETF RFC 2396.";
93                  err += "The URI mustn't contain '\\'.";
94                  throw new SchemaException(err);
95              }
96  
97              if (namespace == null) namespace = "";
98  
99              try {
100                 String documentBase = locator.getSystemId();
101                 if (documentBase != null) {
102                     if (!documentBase.endsWith("/"))
103                         documentBase = documentBase.substring(0, documentBase.lastIndexOf('/') +1 );
104                 }
105                 uri = getURIResolver().resolve(schemaLocation, documentBase);
106                 if (uri != null) {
107                     schemaLocation = uri.getAbsoluteURI();
108                 }
109             }
110             catch (URIException urix) {
111                 throw new XMLException(urix);
112             }
113         }
114         else {
115             schemaLocation = namespace;
116             try {
117                 uri = getURIResolver().resolveURN(namespace);
118             }
119             catch (URIException urix) {
120                 throw new XMLException(urix);
121             }
122             if (uri == null) {
123                 String err = "Unable to resolve Schema corresponding " +
124                     "to namespace '" + namespace + "'.";
125                 throw new SchemaException(err);
126 
127             }
128         }
129 
130         //-- Make sure targetNamespace is not the same as the
131         //-- importing schema, see section 4.2.3 in the
132         //-- XML Schema Recommendation
133         if (namespace.equals(schema.getTargetNamespace()) )
134             throw new SchemaException("the 'namespace' attribute in the <import> element cannot be the same of the targetNamespace of the global schema");
135 
136         //-- Schema object to hold import schema
137         boolean addSchema = false;
138         Schema importedSchema = schema.getImportedSchema(namespace, true);
139 
140         //-- Have we already imported this XML Schema file?
141         if (state.processed(schemaLocation)) {
142            if (importedSchema == null)
143                schema.addImportedSchema(state.getSchema(schemaLocation));
144            return;
145         }
146 
147         boolean alreadyLoaded = false;
148         if (importedSchema == null) {
149             if (uri instanceof SchemaLocation) {
150                 importedSchema = ((SchemaLocation)uri).getSchema();
151                 schema.addImportedSchema(importedSchema);
152                 alreadyLoaded = true;
153             }
154             else {
155                 importedSchema = new Schema();
156                 addSchema = true;
157             }
158         }
159         else {
160             // check schema location, if different, allow merge
161             if (hasLocation) { 
162                 String tmpLocation = importedSchema.getSchemaLocation(); 
163                 alreadyLoaded = schemaLocation.equals(tmpLocation) || importedSchema.includeProcessed(schemaLocation); 
164                 //-- keep track of the original schemaLocation as an include 
165                 if(! alreadyLoaded) {
166                     importedSchema.addInclude(tmpLocation);
167                 }
168             } else {
169                 //-- only namespace can be used, no way to distinguish
170                 //-- multiple imports...mark as alreadyLoaded
171                 //-- see W3C XML Schema 1.0 Recommendation (part 1)
172                 //-- section 4.2.3...
173                 //-- <quote>... Given that the schemaLocation [attribute] is only
174                 //--   a hint, it is open to applications to ignore all but the
175                 //--   first <import> for a given namespace, regardless of the
176                 //--   <em>actual value</em> of schemaLocation, but such a strategy
177                 //--   risks missing useful information when new schemaLocations
178                 //--  are offered.</quote>
179                 alreadyLoaded = true;
180             }
181         }
182 
183         state.markAsProcessed(schemaLocation, importedSchema);
184 
185         if (alreadyLoaded) return;
186 
187         //-- Parser Schema
188         Parser parser = null;
189         try {
190             parser = getSchemaContext().getParser();
191         }
192         catch(RuntimeException rte) {}
193         if (parser == null) {
194             throw new SchemaException("Error failed to create parser for import");
195         }
196     //-- Create Schema object and setup unmarshaller
197     SchemaUnmarshaller schemaUnmarshaller = new SchemaUnmarshaller(getSchemaContext(), state);
198           schemaUnmarshaller.setURIResolver(getURIResolver());
199     schemaUnmarshaller.setSchema(importedSchema);
200     Sax2ComponentReader handler = new Sax2ComponentReader(schemaUnmarshaller);
201     parser.setDocumentHandler(handler);
202     parser.setErrorHandler(handler);
203 
204         try {
205             InputSource source = new InputSource(uri.getReader());
206             source.setSystemId(uri.getAbsoluteURI());
207             parser.parse(source);
208         }
209         catch(java.io.IOException ioe) {
210             throw new SchemaException("Error reading import file '"+schemaLocation+"': "+ ioe);
211         }
212         catch(org.xml.sax.SAXException sx) {
213             throw new SchemaException(sx);
214         }
215 
216         //-- Add schema to list of imported schemas (if not already present)
217         if (addSchema)
218         {
219             importedSchema.setSchemaLocation(schemaLocation);
220             schema.addImportedSchema(importedSchema);
221         }
222     }
223 
224 
225     /**
226      * Sets the name of the element that this UnknownUnmarshaller handles
227     **/
228     public String elementName() {
229         return SchemaNames.IMPORT;
230     } //-- elementName
231 
232     /**
233      * Returns the Object created by this ComponentReader
234      * @return the Object created by this ComponentReader
235     **/
236     public Object getObject() {
237         return null;
238     } //-- getObject
239 
240 }