1/*
2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.xml.internal.ws.spi.db;
27
28import static com.sun.xml.internal.ws.model.RuntimeModeler.DocWrappeeNamespapceQualified;
29
30import java.io.IOException;
31import java.util.ArrayList;
32import java.util.Map.Entry;
33import java.util.HashMap;
34import java.util.HashSet;
35import java.util.List;
36import java.util.Set;
37
38import static com.sun.xml.internal.ws.wsdl.writer.WSDLGenerator.XsdNs;
39
40import javax.xml.bind.JAXBException;
41import javax.xml.bind.SchemaOutputResolver;
42import javax.xml.namespace.QName;
43import javax.xml.transform.Result;
44import javax.xml.ws.WebServiceException;
45
46import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexType;
47import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Element;
48import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ExplicitGroup;
49import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalElement;
50import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Occurs;
51import com.sun.xml.internal.txw2.TXW;
52import com.sun.xml.internal.txw2.output.ResultFactory;
53import com.sun.xml.internal.ws.api.model.SEIModel;
54import com.sun.xml.internal.ws.model.AbstractSEIModelImpl;
55import com.sun.xml.internal.ws.model.JavaMethodImpl;
56import com.sun.xml.internal.ws.model.ParameterImpl;
57import com.sun.xml.internal.ws.model.WrapperParameter;
58import com.sun.xml.internal.ws.wsdl.writer.document.xsd.Schema;
59
60/**
61 * ServiceArtifactSchemaGenerator generates XML schema for service artifacts including the wrapper types of
62 * document-literal stype operation and exceptions.
63 *
64 * @author shih-chang.chen@oracle.com
65 */
66public class ServiceArtifactSchemaGenerator {
67
68    protected AbstractSEIModelImpl model;
69    protected SchemaOutputResolver xsdResolver;
70
71    public ServiceArtifactSchemaGenerator(SEIModel model) {
72        this.model = (AbstractSEIModelImpl)model;
73    }
74
75    static final String FilePrefix = "jaxwsGen";
76    protected int fileIndex = 0;
77    protected Schema create(String tns) {
78        try {
79            Result res = xsdResolver.createOutput(tns, FilePrefix + (fileIndex++) + ".xsd");
80            return TXW.create(Schema.class, ResultFactory.createSerializer(res));
81        } catch (IOException e) {
82            // TODO Auto-generated catch block
83            throw new WebServiceException(e);
84        }
85    }
86
87    public void generate(SchemaOutputResolver resolver) {
88        xsdResolver = resolver;
89        List<WrapperParameter> wrappers = new ArrayList<WrapperParameter>();
90        for (JavaMethodImpl method : model.getJavaMethods()) {
91            if(method.getBinding().isRpcLit()) continue;
92            for (ParameterImpl p : method.getRequestParameters()) {
93                if (p instanceof WrapperParameter) {
94                    if (WrapperComposite.class.equals((((WrapperParameter)p).getTypeInfo().type))) {
95                        wrappers.add((WrapperParameter)p);
96                    }
97                }
98            }
99            for (ParameterImpl p : method.getResponseParameters()) {
100                if (p instanceof WrapperParameter) {
101                    if (WrapperComposite.class.equals((((WrapperParameter)p).getTypeInfo().type))) {
102                        wrappers.add((WrapperParameter)p);
103                    }
104                }
105            }
106        }
107        if (wrappers.isEmpty()) return;
108        HashMap<String, Schema> xsds = initWrappersSchemaWithImports(wrappers);
109        postInit(xsds);
110        for(WrapperParameter wp : wrappers) {
111            String tns = wp.getName().getNamespaceURI();
112            Schema xsd = xsds.get(tns);
113            Element e =  xsd._element(Element.class);
114            e._attribute("name", wp.getName().getLocalPart());
115            e.type(wp.getName());
116            ComplexType ct =  xsd._element(ComplexType.class);
117            ct._attribute("name", wp.getName().getLocalPart());
118            ExplicitGroup sq = ct.sequence();
119            for (ParameterImpl p : wp.getWrapperChildren() ) if (p.getBinding().isBody()) addChild(sq, p);
120        }
121        for(Schema xsd: xsds.values()) xsd.commit();
122    }
123
124    protected void postInit(HashMap<String, Schema> xsds) {
125    }
126
127    protected void addChild(ExplicitGroup sq, ParameterImpl param) {
128        TypeInfo typeInfo = param.getItemType();
129        boolean repeatedElement = false;
130        if (typeInfo == null) {
131            typeInfo = param.getTypeInfo();
132        } else {
133            if (typeInfo.getWrapperType() != null) typeInfo = param.getTypeInfo();
134            else repeatedElement = true;
135        }
136        Occurs child = addChild(sq, param.getName(), typeInfo);
137        if (repeatedElement && child != null) {
138            child.maxOccurs("unbounded");
139        }
140    }
141
142    protected Occurs addChild(ExplicitGroup sq, QName name, TypeInfo typeInfo) {
143        LocalElement le = null;;
144        QName type = model.getBindingContext().getTypeName(typeInfo);
145        if (type != null) {
146            le = sq.element();
147            le._attribute("name", name.getLocalPart());
148            le.type(type);
149        } else {
150            if (typeInfo.type instanceof Class) {
151                try {
152                    QName elemName = model.getBindingContext().getElementName((Class)typeInfo.type);
153                    if (elemName.getLocalPart().equals("any") && elemName.getNamespaceURI().equals(XsdNs)) {
154                        return sq.any();
155                    } else {
156                        le = sq.element();
157                        le.ref(elemName);
158                    }
159                } catch (JAXBException je) {
160                    throw new WebServiceException(je.getMessage(), je);
161                }
162            }
163        }
164        return le;
165    }
166
167    //All the imports have to go first ...
168    private HashMap<String, Schema> initWrappersSchemaWithImports(List<WrapperParameter> wrappers) {
169        Object o = model.databindingInfo().properties().get(DocWrappeeNamespapceQualified);
170        boolean wrappeeQualified = (o!= null && o instanceof Boolean) ? ((Boolean) o) : false;
171        HashMap<String, Schema> xsds = new HashMap<String, Schema>();
172        HashMap<String, Set<String>> imports = new HashMap<String, Set<String>>();
173        for(WrapperParameter wp : wrappers) {
174            String tns = wp.getName().getNamespaceURI();
175            Schema xsd = xsds.get(tns);
176            if (xsd == null) {
177                xsd = create(tns);
178                xsd.targetNamespace(tns);
179                if (wrappeeQualified) xsd._attribute("elementFormDefault", "qualified");
180                xsds.put(tns, xsd);
181            }
182            for (ParameterImpl p : wp.getWrapperChildren() ) {
183                String nsToImport = (p.getBinding().isBody())? bodyParamNS(p): null;
184                if (nsToImport != null && !nsToImport.equals(tns) && !nsToImport.equals("http://www.w3.org/2001/XMLSchema")) {
185                    Set<String> importSet = imports.get(tns);
186                    if (importSet == null) {
187                        importSet = new HashSet<String>();
188                        imports.put(tns, importSet);
189                    }
190                    importSet.add(nsToImport);
191                }
192            }
193        }
194        for(Entry<String, Set<String>> entry: imports.entrySet()) {
195            String tns = entry.getKey();
196            Set<String> importSet = entry.getValue();
197            Schema xsd = xsds.get(tns);
198            for(String nsToImport : importSet) xsd._namespace(nsToImport, true);
199            for(String nsToImport : importSet) {
200                com.sun.xml.internal.ws.wsdl.writer.document.xsd.Import imp = xsd._import();
201                imp.namespace(nsToImport);
202            }
203        }
204        return xsds;
205    }
206
207    protected String bodyParamNS(ParameterImpl p) {
208        String nsToImport = null;
209        TypeInfo typeInfo = p.getItemType();
210        if (typeInfo == null) typeInfo = p.getTypeInfo();
211        QName type = model.getBindingContext().getTypeName(typeInfo);
212        if (type != null) {
213            nsToImport = type.getNamespaceURI();
214        } else {
215            if (typeInfo.type instanceof Class) {
216                try {
217                    QName elemRef = model.getBindingContext().getElementName((Class)typeInfo.type);
218                    if (elemRef != null) nsToImport = elemRef.getNamespaceURI();
219                } catch (JAXBException je) {
220                    throw new WebServiceException(je.getMessage(), je);
221                }
222            }
223        }
224        return nsToImport;
225    }
226}
227