001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.common.tools.wsdl;
018    
019    import java.net.URI;
020    import java.util.Collection;
021    import java.util.ArrayList;
022    import java.util.Collections;
023    import java.util.HashMap;
024    import java.util.Iterator;
025    import java.util.List;
026    import java.util.Map;
027    
028    import javax.xml.parsers.DocumentBuilder;
029    import javax.xml.parsers.DocumentBuilderFactory;
030    
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    import org.w3c.dom.Document;
034    import org.w3c.dom.Element;
035    import org.w3c.dom.Node;
036    import org.w3c.dom.NodeList;
037    import org.xml.sax.InputSource;
038    
039    /**
040     * Collection of schemas.
041     *  
042     * @author gnodet
043     */
044    public class SchemaCollection {
045    
046        private static Log log = LogFactory.getLog(SchemaCollection.class);
047        
048        private Map<String, Schema> schemas;
049        private URI baseUri;
050        
051        public SchemaCollection() {
052            this(null);
053        }
054        
055        public SchemaCollection(URI baseUri) {
056            if (log.isDebugEnabled()) {
057                log.debug("Initializing schema collection with baseUri: " + baseUri);
058            }
059            this.baseUri = baseUri;
060            this.schemas = new HashMap<String, Schema>();
061        }
062        
063        public Schema getSchema(String namespaceURI) {
064            return schemas.get(namespaceURI);
065        }
066        
067        public void read(Element elem, URI sourceUri) throws Exception {
068            String namespace = elem.getAttribute("targetNamespace");
069            Schema schema = schemas.get(namespace);
070            if (schema == null) {
071                    schema = new Schema();
072                    schema.addSourceUri(sourceUri);
073                    schema.setRoot(elem);
074                    schema.setNamespace(elem.getAttribute("targetNamespace"));
075                    schemas.put(schema.getNamespace(), schema);
076            } else if (!schema.getSourceUris().contains(sourceUri)) {
077                    NodeList nodes = elem.getChildNodes();
078                    for (int i = 0; i < nodes.getLength(); i++) {
079                            schema.getRoot().appendChild(schema.getRoot().getOwnerDocument().importNode(nodes.item(i), true));
080                    }
081                    schema.addSourceUri(sourceUri);
082            }
083            handleImports(schema, sourceUri);
084        }
085        
086        public void read(String location, URI baseUri) throws Exception {
087            if (log.isDebugEnabled()) {
088                log.debug("Reading schema at '" + location + "' with baseUri '" + baseUri + "'");
089            }
090            if (baseUri == null) {
091                baseUri = this.baseUri;
092            }
093            URI loc;
094            if (baseUri != null) {
095                loc = resolve(baseUri, location);
096                if (!loc.isAbsolute()) {
097                    throw new IllegalArgumentException("Unable to resolve '" + loc.toString() + "' relative to '" + baseUri + "'");
098                }
099            } else {
100                loc = new URI(location);
101                if (!loc.isAbsolute()) {
102                    throw new IllegalArgumentException("Location '" + loc.toString() + "' is not absolute and no baseUri specified");
103                }
104            }
105            InputSource inputSource = new InputSource();
106            inputSource.setByteStream(loc.toURL().openStream());
107            inputSource.setSystemId(loc.toString());
108            read(inputSource);
109        }
110        
111        public void read(InputSource inputSource) throws Exception {
112            DocumentBuilderFactory docFac = DocumentBuilderFactory.newInstance();
113            docFac.setNamespaceAware(true);
114            DocumentBuilder builder = docFac.newDocumentBuilder();
115            Document doc = builder.parse(inputSource);
116            read(doc.getDocumentElement(), 
117                 inputSource.getSystemId() != null ? new URI(inputSource.getSystemId()) : null);
118        }
119        
120        protected void handleImports(Schema schema, URI baseUri) throws Exception {
121            NodeList children = schema.getRoot().getChildNodes();
122            List<Element> imports = new ArrayList<Element>();
123            List<Element> includes = new ArrayList<Element>();
124            for (int i = 0; i < children.getLength(); i++) {
125                Node child = children.item(i);
126                if (child instanceof Element) {
127                    Element ce = (Element) child;
128                    if ("http://www.w3.org/2001/XMLSchema".equals(ce.getNamespaceURI()) &&
129                        "import".equals(ce.getLocalName())) {
130                        imports.add(ce);
131                    } else if ("http://www.w3.org/2001/XMLSchema".equals(ce.getNamespaceURI()) &&
132                               "include".equals(ce.getLocalName())) {
133                            includes.add(ce);
134                    }
135                }
136            }
137            for (Iterator<Element> iter = imports.iterator(); iter.hasNext();) {
138                Element ce = iter.next();
139                String namespace = ce.getAttribute("namespace");
140                String location = ce.getAttribute("schemaLocation");
141                schema.addImport(namespace);
142                schema.getRoot().removeChild(ce);
143                if (location != null && !"".equals(location)) {
144                    read(location, baseUri);
145                }
146            }
147            for (Iterator<Element> iter = includes.iterator(); iter.hasNext();) {
148                Element ce = iter.next();
149                    String location = ce.getAttribute("schemaLocation");
150                Node parentNode = ce.getParentNode();
151                Element root = schema.getRoot();
152                if (root == parentNode) { 
153                    log.debug("Removing child include node: " + ce);
154                    schema.getRoot().removeChild(ce);
155                } else {
156                    log.warn("Skipping child include node removal: " + ce);
157                }
158                    if (location != null && !"".equals(location)) {
159                        read(location, baseUri);
160                    }
161            }
162        }
163        
164        protected static URI resolve(URI base, String location) {
165            if ("jar".equals(base.getScheme())) {
166                String str = base.toString();
167                String[] parts = str.split("!");
168                parts[1] = URI.create(parts[1]).resolve(location).toString();
169                return URI.create(parts[0] + "!" + parts[1]);
170            }
171            return base.resolve(location);
172        }
173    
174        public int getSize() {
175            if (schemas != null) {
176               return schemas.size();
177            } else {
178               return 0;
179            }
180         }
181         
182         public Collection<Schema> getSchemas() {
183            if (schemas != null) {
184               return schemas.values();
185            } else {
186               return Collections.emptySet();
187            }
188         }
189    }