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.cxfbc;
018    
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.HashMap;
022    import java.util.IdentityHashMap;
023    import java.util.Iterator;
024    import java.util.List;
025    import java.util.Map;
026    import java.util.Map.Entry;
027    
028    import javax.wsdl.Definition;
029    import javax.wsdl.Import;
030    import javax.wsdl.Types;
031    import javax.wsdl.extensions.UnknownExtensibilityElement;
032    import javax.wsdl.extensions.schema.Schema;
033    import javax.wsdl.extensions.schema.SchemaImport;
034    import javax.wsdl.extensions.schema.SchemaReference;
035    
036    import org.w3c.dom.Attr;
037    import org.w3c.dom.Element;
038    import org.w3c.dom.NodeList;
039    
040    import org.apache.cxf.Bus;
041    import org.apache.cxf.catalog.CatalogXmlSchemaURIResolver;
042    import org.apache.cxf.common.xmlschema.SchemaCollection;
043    import org.apache.cxf.helpers.CastUtils;
044    import org.apache.cxf.service.model.SchemaInfo;
045    import org.apache.cxf.service.model.ServiceInfo;
046    import org.apache.ws.commons.schema.XmlSchema;
047    
048    import static org.apache.cxf.helpers.CastUtils.cast;
049    
050    public final class SchemaUtil {
051        private final Map<String, Element> schemaList;
052        private final Map<String, String> catalogResolved = new HashMap<String, String>();
053        private final Bus bus;
054    
055        public SchemaUtil(final Bus b, final Map<String, Element> s) {
056            this.bus = b;
057            this.schemaList = s;
058        }
059        public void getSchemas(final Definition def, final ServiceInfo serviceInfo) {
060            SchemaCollection schemaCol = serviceInfo.getXmlSchemaCollection();
061            getSchemas(def, schemaCol, serviceInfo);
062        }
063        public void getSchemas(final Definition def, 
064                               SchemaCollection schemaCol, 
065                               ServiceInfo serviceInfo) {
066            getSchemas(def, schemaCol, serviceInfo.getSchemas());
067        }
068    
069        public void getSchemas(final Definition def, 
070                               final SchemaCollection schemaCol,
071                               List<SchemaInfo> schemas) {
072            List<Definition> defList = new ArrayList<Definition>();
073            parseImports(def, defList);
074            extractSchema(def, schemaCol, schemas);
075            // added
076            getSchemaList(def);
077            
078            Map<Definition, Definition> done = new IdentityHashMap<Definition, Definition>();
079            done.put(def, def);
080            for (Definition def2 : defList) {
081                if (!done.containsKey(def2)) {
082                    extractSchema(def2, schemaCol, schemas);
083                    // added
084                    getSchemaList(def2);
085                    done.put(def2, def2);
086                }
087            }
088        }
089    
090        @SuppressWarnings("unchecked")
091        private void extractSchema(Definition def, SchemaCollection schemaCol, List<SchemaInfo> schemaInfos) {
092            Types typesElement = def.getTypes();
093            if (typesElement != null) {
094                int schemaCount = 1;
095                for (Object obj : typesElement.getExtensibilityElements()) {
096                    org.w3c.dom.Element schemaElem = null;
097                    if (obj instanceof Schema) {
098                        Schema schema = (Schema)obj;
099                        schemaElem = schema.getElement();
100                    } else if (obj instanceof UnknownExtensibilityElement) {
101                        org.w3c.dom.Element elem = ((UnknownExtensibilityElement)obj).getElement();
102                        if (elem.getLocalName().equals("schema")) {
103                            schemaElem = elem;
104                        }
105                    }
106                    if (schemaElem != null) {
107                        synchronized (schemaElem.getOwnerDocument()) {
108                            //for (Object prefix : def.getNamespaces().keySet()) {
109                            Map<String, String> nameSpaces = CastUtils.cast(def.getNamespaces());
110                            for (Entry<String, String> ent : nameSpaces.entrySet()) {
111                                String prefix = ent.getKey();
112                                String ns = nameSpaces.get(prefix);
113                                if ("".equals(prefix)) {
114                                    if (!schemaElem.hasAttribute("xmlns")) {
115                                        Attr attr = 
116                                            schemaElem.getOwnerDocument()
117                                                .createAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, 
118                                                                   "xmlns");
119                                        attr.setValue(ns);
120                                        schemaElem.setAttributeNodeNS(attr);
121                                    }
122                                } else if (!schemaElem.hasAttribute("xmlns:" + prefix)) {
123                                    String namespace = javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
124                                    Attr attr = 
125                                        schemaElem.getOwnerDocument().createAttributeNS(namespace, 
126                                                                                        "xmlns:" + prefix);
127                                    attr.setValue(ns);
128                                    schemaElem.setAttributeNodeNS(attr);
129                                }
130                            }
131                            String systemId = def.getDocumentBaseURI() + "#types" + schemaCount;
132        
133                            schemaCol.setBaseUri(def.getDocumentBaseURI());
134                            CatalogXmlSchemaURIResolver schemaResolver =
135                                new CatalogXmlSchemaURIResolver(bus);
136                            schemaCol.setSchemaResolver(schemaResolver);
137                            
138                            XmlSchema xmlSchema = schemaCol.read(schemaElem, systemId);
139                            catalogResolved.putAll(schemaResolver.getResolvedMap());
140                            SchemaInfo schemaInfo = new SchemaInfo(xmlSchema.getTargetNamespace());
141                            schemaInfo.setSchema(xmlSchema);
142                            schemaInfo.setSystemId(systemId);
143                            schemaInfo.setElement(schemaElem);
144                            schemaInfos.add(schemaInfo);
145                            schemaCount++;
146                        }
147                    }
148                }
149            }
150        }
151    
152        private void parseImports(Definition def, List<Definition> defList) {
153            List<Import> importList = new ArrayList<Import>();
154    
155            Collection<List<Import>> ilist = cast(def.getImports().values());
156            for (List<Import> list : ilist) {
157                importList.addAll(list);
158            }
159            for (Import impt : importList) {
160                if (!defList.contains(impt.getDefinition())) {
161                    defList.add(impt.getDefinition());
162                    parseImports(impt.getDefinition(), defList);
163                }
164            }
165        }
166    
167        // Workaround for getting the elements
168        private void getSchemaList(Definition def) {
169            Types typesElement = def.getTypes();
170            if (typesElement != null) {
171                Iterator ite = typesElement.getExtensibilityElements().iterator();
172                while (ite.hasNext()) {
173                    Object obj = ite.next();
174                    if (obj instanceof Schema) {
175                        Schema schema = (Schema)obj;
176                        addSchema(schema.getDocumentBaseURI(), schema);
177                    }
178                }
179            }
180        }
181    
182        private void addSchema(String docBaseURI, Schema schema) {
183            //String docBaseURI = schema.getDocumentBaseURI();
184            Element schemaEle = schema.getElement();
185            if (schemaList.get(docBaseURI) == null) {
186                schemaList.put(docBaseURI, schemaEle);
187            } else if (schemaList.get(docBaseURI) != null && schemaList.containsValue(schemaEle)) {
188                // do nothing
189            } else {
190                String tns = schema.getDocumentBaseURI() + "#"
191                             + schema.getElement().getAttribute("targetNamespace");
192                if (schemaList.get(tns) == null) {
193                    schemaList.put(tns, schema.getElement());
194                }
195            }
196    
197            //handle imports
198            Map<String, List> imports = CastUtils.cast(schema.getImports());
199            if (imports != null && imports.size() > 0) {
200                for (Entry<String, List> ent : imports.entrySet()) {
201                    String importNamespace = ent.getKey();
202                    List<SchemaImport> schemaImports = CastUtils.cast(imports.get(importNamespace));
203                    
204                    for (SchemaImport schemaImport : schemaImports) {
205                        Schema tempImport = schemaImport.getReferencedSchema();                   
206                        String key = schemaImport.getSchemaLocationURI();
207                        if (importNamespace == null && tempImport != null) {
208                            importNamespace = tempImport.getDocumentBaseURI();
209                        }
210                        
211                        if (tempImport != null && !catalogResolved.containsKey(key)) {                 
212                            key = tempImport.getDocumentBaseURI();
213                        }
214                        
215                        if (tempImport != null
216                            && !isSchemaParsed(key, importNamespace)
217                            && !schemaList.containsValue(tempImport.getElement())) {
218                            addSchema(key, tempImport);
219                        }
220                        if (tempImport != null) {
221                          //keep this imported schema inline
222                          inlineTransformer(key, tempImport.getElement(), schema.getElement(), false);
223                        }
224                    }
225    
226                }
227            }
228            //handle includes
229            List<SchemaReference> includes = CastUtils.cast(schema.getIncludes());
230            if (includes != null && includes.size() > 0) {
231                String includeNamespace = schema.getElement().getAttribute("targetNamespace");
232    
233                for (SchemaReference schemaInclude : includes) {
234                    Schema tempInclude = schemaInclude.getReferencedSchema();
235                    String key = tempInclude.getDocumentBaseURI();
236                    if (includeNamespace == null && tempInclude != null) {
237                        includeNamespace = tempInclude.getDocumentBaseURI();
238                    }
239    
240                    if (tempInclude != null && !catalogResolved.containsKey(key)) {
241                        key = tempInclude.getDocumentBaseURI();
242                    }
243    
244                    if (tempInclude != null && !isSchemaParsed(key, includeNamespace)
245                            && !schemaList.containsValue(tempInclude.getElement())) {
246                        addSchema(key, tempInclude);
247                    }
248                    if (tempInclude != null) {
249                      //keep this included schema inline
250                      inlineTransformer(key, tempInclude.getElement(), schema.getElement(), true);
251                    }
252                }
253    
254            }
255        }
256    
257        private boolean isSchemaParsed(String baseUri, String ns) {
258            if (schemaList.get(baseUri) != null) {
259                Element ele = schemaList.get(baseUri);
260                String tns = ele.getAttribute("targetNamespace");
261                if (ns.equals(tns)) {
262                    return true;
263                }
264            }
265            return false;
266        }
267        
268        private void inlineTransformer(String key, Element inlineSchema, Element outerSchema, boolean isInclude) {
269            NodeList nl = null;
270            if (isInclude) {
271                nl = outerSchema.getElementsByTagNameNS(
272                        "http://www.w3.org/2001/XMLSchema", "include");
273            } else {
274                nl = outerSchema.getElementsByTagNameNS(
275                        "http://www.w3.org/2001/XMLSchema", "import");
276            }
277            for (int j = 0; j < nl.getLength(); j++) {
278            
279                String schemaLocation = ((Element)nl.item(j)).getAttribute("schemaLocation");
280                
281                if (schemaLocation != null && getXsdFileName(schemaLocation, "/").
282                        equals(getXsdFileName(key, "/"))) {
283                                                
284                    outerSchema.removeChild(nl.item(j));
285                    for (int i = 0; i < inlineSchema.getChildNodes().getLength(); i++) {
286                        outerSchema.appendChild(
287                                outerSchema.getOwnerDocument().importNode(
288                                    inlineSchema.getChildNodes().item(i), true));
289                    }
290                    outerSchema.setPrefix("xs");
291                    outerSchema.setAttribute("xmlns:xs", "http://www.w3.org/2001/XMLSchema");
292                }
293            }
294        }
295        
296        private String getXsdFileName(String path, String sep) {
297            String name = "";
298            if (path.lastIndexOf(sep) >= 0) {
299                name = path.substring(path.lastIndexOf(sep) + 1);
300            } else {
301                name = path;
302            }
303            return name;
304        }
305    }