All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.cxf.jaxrs.model.wadl.WadlGenerator Maven / Gradle / Ivy

There is a newer version: 3.0.0-milestone2
Show newest version
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.cxf.jaxrs.model.wadl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.common.WSDLConstants;
import org.apache.cxf.common.jaxb.JAXBBeanInfo;
import org.apache.cxf.common.jaxb.JAXBContextProxy;
import org.apache.cxf.common.jaxb.JAXBUtils;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.PackageUtils;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.common.util.XmlSchemaPrimitiveUtils;
import org.apache.cxf.common.xmlschema.SchemaCollection;
import org.apache.cxf.common.xmlschema.XmlSchemaConstants;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.helpers.XMLUtils;
import org.apache.cxf.jaxrs.JAXRSServiceImpl;
import org.apache.cxf.jaxrs.ext.Oneway;
import org.apache.cxf.jaxrs.ext.RequestHandler;
import org.apache.cxf.jaxrs.ext.multipart.Multipart;
import org.apache.cxf.jaxrs.ext.xml.XMLSource;
import org.apache.cxf.jaxrs.impl.HttpHeadersImpl;
import org.apache.cxf.jaxrs.impl.UriInfoImpl;
import org.apache.cxf.jaxrs.model.ClassResourceInfo;
import org.apache.cxf.jaxrs.model.OperationResourceInfo;
import org.apache.cxf.jaxrs.model.Parameter;
import org.apache.cxf.jaxrs.model.ParameterType;
import org.apache.cxf.jaxrs.model.ResourceTypes;
import org.apache.cxf.jaxrs.model.URITemplate;
import org.apache.cxf.jaxrs.provider.ProviderFactory;
import org.apache.cxf.jaxrs.utils.AnnotationUtils;
import org.apache.cxf.jaxrs.utils.ExceptionUtils;
import org.apache.cxf.jaxrs.utils.InjectionUtils;
import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import org.apache.cxf.jaxrs.utils.ResourceUtils;
import org.apache.cxf.jaxrs.utils.schemas.SchemaHandler;
import org.apache.cxf.message.Message;
import org.apache.cxf.service.Service;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.staxutils.DelegatingXMLStreamWriter;
import org.apache.cxf.staxutils.StaxUtils;
import org.apache.ws.commons.schema.XmlSchema;

public class WadlGenerator implements RequestHandler {

    public static final String WADL_QUERY = "_wadl";
    public static final MediaType WADL_TYPE = JAXRSUtils.toMediaType("application/vnd.sun.wadl+xml");
    public static final String WADL_NS = "http://wadl.dev.java.net/2009/02";

    private static final MediaType DEFAULT_MEDIA_TYPE = MediaType.APPLICATION_XML_TYPE;
    private static final Logger LOG = LogUtils.getL7dLogger(WadlGenerator.class);
    private static final String JAXB_DEFAULT_NAMESPACE = "##default";
    private static final String JAXB_DEFAULT_NAME = "##default";
    private static final String CLASSPATH_PREFIX = "classpath:";
    private static final String DEFAULT_NS_PREFIX = "prefix";
    
    private String wadlNamespace;
    private boolean ignoreMessageWriters = true;
    private boolean singleResourceMultipleMethods = true;
    private boolean useSingleSlashResource;
    private boolean ignoreForwardSlash;
    private boolean addResourceAndMethodIds;
    private boolean ignoreRequests;
    private boolean linkJsonToXmlSchema;
    private boolean useJaxbContextForQnames = true;
    private boolean supportCollections = true;
    private boolean supportJaxbXmlType = true;
    private boolean supportJaxbSubstitutions = true;

    private List externalSchemasCache;
    private List externalSchemaLinks;
    private Map> externalQnamesMap;

    private ConcurrentHashMap docLocationMap = new ConcurrentHashMap();

    private ElementQNameResolver resolver;
    private List privateAddresses;
    private String applicationTitle;
    private String nsPrefix = DEFAULT_NS_PREFIX;
    private MediaType defaultMediaType = DEFAULT_MEDIA_TYPE;

    private Bus bus; 

    public WadlGenerator() {

    }

    public WadlGenerator(Bus bus) {
        this.bus = bus;
    }

    public WadlGenerator(WadlGenerator other) {
        this.wadlNamespace = other.wadlNamespace;
        this.externalQnamesMap = other.externalQnamesMap;
        this.externalSchemaLinks = other.externalSchemaLinks;
        this.externalSchemasCache = other.externalSchemasCache;
        this.ignoreMessageWriters = other.ignoreMessageWriters;
        this.ignoreForwardSlash = other.ignoreForwardSlash; 
        this.ignoreRequests = other.ignoreRequests;
        this.linkJsonToXmlSchema = other.linkJsonToXmlSchema;  
        this.privateAddresses = other.privateAddresses;
        this.resolver = other.resolver;
        this.addResourceAndMethodIds = other.addResourceAndMethodIds;
        this.singleResourceMultipleMethods = other.singleResourceMultipleMethods;
        this.useJaxbContextForQnames = other.useJaxbContextForQnames;
        this.useSingleSlashResource = other.useSingleSlashResource;
        this.supportCollections = other.supportCollections;
        this.supportJaxbXmlType = other.supportJaxbXmlType;
        this.nsPrefix = other.nsPrefix;
        this.applicationTitle = other.applicationTitle;
        this.defaultMediaType = other.defaultMediaType;
        this.bus = other.bus;     
    }

    public Response handleRequest(Message m, ClassResourceInfo resource) {

        if (!"GET".equals(m.get(Message.HTTP_REQUEST_METHOD))) {
            return null;
        }

        UriInfo ui = new UriInfoImpl(m);
        if (!ui.getQueryParameters().containsKey(WADL_QUERY)) {
            if (!docLocationMap.isEmpty()) {
                String path = ui.getPath(false);
                if (path.startsWith("/") && path.length() > 0) {
                    path = path.substring(1);
                }
                if (docLocationMap.containsKey(path)) {
                    return getExistingResource(m, ui, path);
                }
            }
            return null;
        }

        if (ignoreRequests) {
            return Response.status(404).build();
        }

        HttpHeaders headers = new HttpHeadersImpl(m);
        List accepts = headers.getAcceptableMediaTypes();
        MediaType type = accepts.contains(WADL_TYPE) ? WADL_TYPE : accepts
            .contains(MediaType.APPLICATION_JSON_TYPE) ? MediaType.APPLICATION_JSON_TYPE : defaultMediaType;

        Response response = getExistingWadl(m, ui, type);
        if (response != null) {
            return response;
        }

        boolean isJson = type == MediaType.APPLICATION_JSON_TYPE;

        StringBuilder sbMain = generateWADL(getBaseURI(m, ui), getResourcesList(m, resource), isJson, m, ui);

        m.getExchange().put(JAXRSUtils.IGNORE_MESSAGE_WRITERS, !isJson && ignoreMessageWriters);
        return Response.ok().type(type).entity(createResponseEntity(sbMain.toString(), isJson)).build();
    }

    public StringBuilder generateWADL(String baseURI, 
                                       List cris, 
                                       boolean isJson,
                                       Message m,
                                       UriInfo ui) {
        StringBuilder sbMain = new StringBuilder();
        sbMain.append("");

        StringBuilder sbResources = new StringBuilder();
        sbResources.append("");

        MessageBodyWriter jaxbWriter = (m != null && useJaxbContextForQnames)
             ? ProviderFactory.getInstance(m).getRegisteredJaxbWriter() : null;
        ResourceTypes resourceTypes = ResourceUtils.getAllRequestResponseTypes(cris, 
                                                                               useJaxbContextForQnames,
                                                                               jaxbWriter);
        checkXmlSeeAlso(resourceTypes);
        Set> allTypes = resourceTypes.getAllTypes().keySet();

        JAXBContext jaxbContext = useJaxbContextForQnames ? ResourceUtils
            .createJaxbContext(new HashSet>(allTypes), null, null) : null;

        SchemaWriter schemaWriter = createSchemaWriter(resourceTypes, jaxbContext, ui);
        ElementQNameResolver qnameResolver = schemaWriter == null
            ? null : createElementQNameResolver(jaxbContext);

        Map, QName> clsMap = new IdentityHashMap, QName>();
        Set visitedResources = new LinkedHashSet();
        for (ClassResourceInfo cri : cris) {
            startResourceTag(sbResources, cri.getServiceClass(), cri.getURITemplate().getValue());
            Annotation description = AnnotationUtils.getClassAnnotation(cri.getServiceClass(), Description.class);
            if (description == null) {
                description = AnnotationUtils.getClassAnnotation(cri.getServiceClass(), Descriptions.class);
            }
            if (description != null) {
                handleDocs(new Annotation[] {description}, sbResources, DocTarget.RESOURCE, true, isJson);
            }
            handleResource(sbResources, allTypes, qnameResolver, clsMap, cri, visitedResources, isJson);
            sbResources.append("");
        }
        sbResources.append("");

        handleGrammars(sbMain, sbGrammars, schemaWriter, clsMap);

        sbGrammars.append("");
        sbMain.append(">");
        handleApplicationDocs(sbMain);
        sbMain.append(sbGrammars.toString());
        sbMain.append(sbResources.toString());
        sbMain.append("");
        return sbMain;
    }

    private Object createResponseEntity(String entity, boolean isJson) {
        if (!isJson) {
            return entity;
        }
        try {
            return StaxUtils.read(new StringReader(entity));
        } catch (Exception ex) {
            throw ExceptionUtils.toInternalServerErrorException(ex, null);
        }
    }

    private String getBaseURI(Message m, UriInfo ui) {
        EndpointInfo ei = m.getExchange().get(Endpoint.class).getEndpointInfo();
        String publishedEndpointUrl = (String)ei.getProperty("publishedEndpointUrl");
        if (publishedEndpointUrl == null) {
            return ui.getBaseUri().toString();
        } else {
            return publishedEndpointUrl;
        }
    }

    protected void handleGrammars(StringBuilder sbApp, StringBuilder sbGrammars, SchemaWriter writer,
                                  Map, QName> clsMap) {
        if (writer == null) {
            return;
        }

        Map map = new HashMap();
        for (QName qname : clsMap.values()) {
            map.put(qname.getPrefix(), qname.getNamespaceURI());
        }
        for (Map.Entry entry : map.entrySet()) {
            sbApp.append(" xmlns:").append(entry.getKey()).append("=\"").append(entry.getValue())
                .append("\"");
        }

        writer.write(sbGrammars);
    }

    protected void handleResource(StringBuilder sb, Set> jaxbTypes,
                                  ElementQNameResolver qnameResolver, Map, QName> clsMap,
                                  ClassResourceInfo cri, Set visitedResources,
                                  boolean isJson) {
        visitedResources.add(cri);
        Map classParams = getClassParameters(cri);

        List sortedOps = sortOperationsByPath(cri.getMethodDispatcher()
            .getOperationResourceInfos());

        boolean resourceTagOpened = false;
        for (int i = 0; i < sortedOps.size(); i++) {
            OperationResourceInfo ori = sortedOps.get(i);

            if (ori.getHttpMethod() == null) {
                Class cls = getMethod(ori).getReturnType();
                ClassResourceInfo subcri = cri.findResource(cls, cls);
                if (subcri != null && !visitedResources.contains(subcri)) {
                    startResourceTag(sb, subcri.getServiceClass(), ori.getURITemplate().getValue());
                    handleDocs(subcri.getServiceClass().getAnnotations(), sb, DocTarget.RESOURCE, true,
                               isJson);
                    handlePathAndMatrixParams(sb, ori, isJson);
                    handleResource(sb, jaxbTypes, qnameResolver, clsMap, subcri, visitedResources, isJson);
                    sb.append("");
                } else {
                    handleDynamicSubresource(sb, jaxbTypes, qnameResolver, clsMap, ori, subcri, isJson);
                }
                continue;
            }
            OperationResourceInfo nextOp = i + 1 < sortedOps.size() ? sortedOps.get(i + 1) : null;
            resourceTagOpened = handleOperation(sb, jaxbTypes, qnameResolver, clsMap, ori, classParams,
                                                nextOp, resourceTagOpened, isJson, i);
        }
    }

    private Map getClassParameters(ClassResourceInfo cri) {
        Map classParams = new LinkedHashMap();
        List paramMethods = cri.getParameterMethods();
        for (Method m : paramMethods) {
            classParams.put(ResourceUtils.getParameter(0, m.getAnnotations(), m.getParameterTypes()[0]), m);
        }
        List fieldParams = cri.getParameterFields();
        for (Field f : fieldParams) {
            classParams.put(ResourceUtils.getParameter(0, f.getAnnotations(), f.getType()), f);
        }
        return classParams;
    }

    private void startResourceTag(StringBuilder sb, Class serviceClass, String path) {
        sb.append(", QName>(0));
            }
            String pName = jaxbQname == null ? PackageUtils.getPackageName(serviceClass) : jaxbQname
                .getNamespaceURI();
            String localName = jaxbQname == null ? serviceClass.getSimpleName() : jaxbQname.getLocalPart();
            String finalName = jaxbQname == null ? pName + "." : "{" + pName + "}";
            sb.append(" id=\"").append(finalName + localName).append("\"");
        }
        sb.append(">");
    }

    protected String getPath(String path) {
        String thePath = null;
        if (ignoreForwardSlash && path.startsWith("/") && path.length() > 0) {
            thePath = path.substring(1);
        } else {
            thePath = path;
        }
        
        return xmlEncodeIfNeeded(thePath);
    }

    private void checkXmlSeeAlso(ResourceTypes resourceTypes) {
        if (!this.useJaxbContextForQnames) {
            return;
        }
        List> extraClasses = new LinkedList>();
        for (Class cls : resourceTypes.getAllTypes().keySet()) {
            if (!isXmlRoot(cls) || Modifier.isAbstract(cls.getModifiers())) {
                XmlSeeAlso seeAlsoAnn = cls.getAnnotation(XmlSeeAlso.class);
                if (seeAlsoAnn != null) {
                    List> seeAlsoList = CastUtils.cast(Arrays.asList(seeAlsoAnn.value()));
                    if (this.supportJaxbSubstitutions) {
                        for (Class seeAlsoCls : seeAlsoList) {
                            resourceTypes.getSubstitutions().put(seeAlsoCls, cls);
                        }
                    } 
                    extraClasses.addAll(seeAlsoList);
                }
            }
        }
        for (Class cls : extraClasses) {
            resourceTypes.getAllTypes().put(cls, cls);
        }
    }
    
    private String xmlEncodeIfNeeded(String value) {
        return XMLUtils.xmlEncode(value);
    }
    
    private void startMethodTag(StringBuilder sb, OperationResourceInfo ori) {
        sb.append("");
    }

    // CHECKSTYLE:OFF
    protected boolean handleOperation(StringBuilder sb, Set> jaxbTypes,
                                      ElementQNameResolver qnameResolver, Map, QName> clsMap,
                                      OperationResourceInfo ori, Map classParams,
                                      OperationResourceInfo nextOp, boolean resourceTagOpened,
                                      boolean isJson, int index) {
        Annotation[] anns = getMethod(ori).getAnnotations();
        // CHECKSTYLE:ON
        boolean samePathOperationFollows = singleResourceMultipleMethods && compareOperations(ori, nextOp);

        String path = ori.getURITemplate().getValue();
        if (!resourceTagOpened && openResource(path)) {
            resourceTagOpened = true;
            URITemplate template = ori.getClassResourceInfo().getURITemplate();
            if (template != null) {
                String parentPath = template.getValue();
                if (parentPath.endsWith("/") && path.startsWith("/") && path.length() > 1) {
                    path = path.substring(1);
                }
            }
            sb.append("");
            handleDocs(anns, sb, DocTarget.RESOURCE, false, isJson);
            handlePathAndMatrixClassParams(sb, classParams, isJson);
            handlePathAndMatrixParams(sb, ori, isJson);
        } else if (index == 0) {
            handlePathAndMatrixClassParams(sb, classParams, isJson);
            handlePathAndMatrixParams(sb, ori, isJson);
        }

        startMethodTag(sb, ori);
        handleDocs(anns, sb, DocTarget.METHOD, true, isJson);
        if (getMethod(ori).getParameterTypes().length != 0 || classParams.size() != 0) {
            sb.append("");
            handleDocs(anns, sb, DocTarget.REQUEST, false, isJson);

            boolean isForm = isFormRequest(ori);

            doHandleClassParams(sb, classParams, isJson, ParameterType.QUERY, ParameterType.HEADER);
            for (Parameter p : ori.getParameters()) {
                if (isForm && p.getType() == ParameterType.REQUEST_BODY) {
                    continue;
                }
                handleParameter(sb, jaxbTypes, qnameResolver, clsMap, ori, p, isJson);
            }
            if (isForm) {
                handleFormRepresentation(sb, jaxbTypes, qnameResolver, clsMap, ori, getFormClass(ori), isJson);
            }
            sb.append("");
        }
        sb.append(" returnType = getMethod(ori).getReturnType();
        boolean isVoid = void.class == returnType;
        if (isVoid) {
            boolean oneway = getMethod(ori).getAnnotation(Oneway.class) != null;
            sb.append(" status=\"" + (oneway ? 202 : 204) + "\"");
        }
        sb.append(">");
        handleDocs(anns, sb, DocTarget.RESPONSE, false, isJson);
        if (!isVoid) {
            handleRepresentation(sb, jaxbTypes, qnameResolver, clsMap, ori, returnType, isJson, false);
        }
        sb.append("");

        sb.append("");

        if (resourceTagOpened && !samePathOperationFollows) {
            sb.append("");
            resourceTagOpened = false;
        }
        return resourceTagOpened;
    }

    protected boolean compareOperations(OperationResourceInfo ori1, OperationResourceInfo ori2) {
        if (ori1 == null || ori2 == null
            || !ori1.getURITemplate().getValue().equals(ori2.getURITemplate().getValue())
            || ori1.getHttpMethod() != null && ori2.getHttpMethod() == null || ori2.getHttpMethod() != null
            && ori1.getHttpMethod() == null) {
            return false;
        }
        int ori1PathParams = 0;
        int ori1MatrixParams = 0;
        for (Parameter p : ori1.getParameters()) {
            if (p.getType() == ParameterType.PATH) {
                ori1PathParams++;
            } else if (p.getType() == ParameterType.MATRIX) {
                ori1MatrixParams++;
            }
        }

        int ori2PathParams = 0;
        int ori2MatrixParams = 0;
        for (Parameter p : ori2.getParameters()) {
            if (p.getType() == ParameterType.PATH) {
                ori2PathParams++;
            } else if (p.getType() == ParameterType.MATRIX) {
                ori2MatrixParams++;
            }
        }

        return ori1PathParams == ori2PathParams && ori1MatrixParams == ori2MatrixParams;
    }

    private boolean openResource(String path) {
        if ("/".equals(path)) {
            return useSingleSlashResource;
        }
        return true;
    }

    protected void handleDynamicSubresource(StringBuilder sb, Set> jaxbTypes,
                                            ElementQNameResolver qnameResolver, Map, QName> clsMap,
                                            OperationResourceInfo ori, ClassResourceInfo subcri,
                                            boolean isJson) {
        if (!isJson) {
            if (subcri != null) {
                sb.append("");
            } else {
                sb.append("");
            }
        }
        startResourceTag(sb, subcri != null ? subcri.getServiceClass() : Object.class, ori.getURITemplate()
            .getValue());
        handlePathAndMatrixParams(sb, ori, isJson);
        sb.append("");
    }

    protected void handlePathAndMatrixClassParams(StringBuilder sb, Map params,
                                                  boolean isJson) {
        doHandleClassParams(sb, params, isJson, ParameterType.PATH);
        doHandleClassParams(sb, params, isJson, ParameterType.MATRIX);
    }

    protected void doHandleClassParams(StringBuilder sb, Map params, boolean isJson,
                                       ParameterType... pType) {
        Set pTypes = new LinkedHashSet(Arrays.asList(pType));
        for (Map.Entry entry : params.entrySet()) {
            Parameter pm = entry.getKey();
            Object obj = entry.getValue();
            if (pTypes.contains(pm.getType())) {
                Class cls = obj instanceof Method ? ((Method)obj).getParameterTypes()[0] : ((Field)obj)
                    .getType();
                Type type = obj instanceof Method
                    ? ((Method)obj).getGenericParameterTypes()[0] : ((Field)obj).getGenericType();
                Annotation[] ann = obj instanceof Method
                    ? ((Method)obj).getParameterAnnotations()[0] : ((Field)obj).getAnnotations();
                doWriteParam(sb, pm, cls, type, pm.getName(), ann, isJson);
            }
        }
    }

    protected void handlePathAndMatrixParams(StringBuilder sb, OperationResourceInfo ori, boolean isJson) {
        handleParams(sb, ori, ParameterType.PATH, isJson);
        handleParams(sb, ori, ParameterType.MATRIX, isJson);
    }

    protected void handleParameter(StringBuilder sb, Set> jaxbTypes,
                                   ElementQNameResolver qnameResolver, Map, QName> clsMap,
                                   OperationResourceInfo ori, Parameter pm, boolean isJson) {
        Class cls = getMethod(ori).getParameterTypes()[pm.getIndex()];
        if (pm.getType() == ParameterType.REQUEST_BODY) {
            handleRepresentation(sb, jaxbTypes, qnameResolver, clsMap, ori, cls, isJson, true);
            return;
        }
        if (pm.getType() == ParameterType.PATH || pm.getType() == ParameterType.MATRIX) {
            return;
        }
        if (pm.getType() == ParameterType.HEADER || pm.getType() == ParameterType.QUERY) {
            writeParam(sb, pm, ori, isJson);
        }

    }

    protected void handleParams(StringBuilder sb, OperationResourceInfo ori, ParameterType type,
                                boolean isJson) {
        for (Parameter pm : ori.getParameters()) {
            if (pm.getType() == type) {
                writeParam(sb, pm, ori, isJson);
            }
        }
    }

    private Annotation[] getBodyAnnotations(OperationResourceInfo ori, boolean inbound) {
        Method opMethod = getMethod(ori);
        if (inbound) {
            for (Parameter pm : ori.getParameters()) {
                if (pm.getType() == ParameterType.REQUEST_BODY) {
                    return opMethod.getParameterAnnotations()[pm.getIndex()];
                }
            }
            return new Annotation[] {};
        } else {
            return opMethod.getDeclaredAnnotations();
        }
    }

    private void writeParam(StringBuilder sb, Parameter pm, OperationResourceInfo ori, boolean isJson) {
        Method method = getMethod(ori);
        Class type = method.getParameterTypes()[pm.getIndex()];
        if (!"".equals(pm.getName())) {
            doWriteParam(sb, pm, type, method.getGenericParameterTypes()[pm.getIndex()], pm.getName(),
                         method.getParameterAnnotations()[pm.getIndex()], isJson);
        } else {
            List> parentBeanClasses = new LinkedList>();
            parentBeanClasses.add(type);
            doWriteBeanParam(sb, type, pm, null, parentBeanClasses, isJson);
        }
    }

    private void doWriteBeanParam(StringBuilder sb, Class type, Parameter pm, String parentName,
                                  List> parentBeanClasses, boolean isJson) {
        Map> pms = InjectionUtils.getParametersFromBeanClass(type, pm.getType(), true);
        for (Map.Entry> entry : pms.entrySet()) {
            String name = entry.getKey().getName();
            if (parentName != null) {
                name = parentName + "." + name;
            }
            Class paramCls = entry.getValue();
            boolean isPrimitive = InjectionUtils.isPrimitive(paramCls) || paramCls.isEnum();
            if (isPrimitive || InjectionUtils.isSupportedCollectionOrArray(paramCls)) {
                doWriteParam(sb, entry.getKey(), paramCls, paramCls, name, new Annotation[] {}, isJson);
            } else if (!parentBeanClasses.contains(paramCls)) {
                parentBeanClasses.add(paramCls);
                doWriteBeanParam(sb, paramCls, entry.getKey(), name, parentBeanClasses, isJson);
            }
        }
    }

    protected void doWriteParam(StringBuilder sb, Parameter pm, Class type, Type genericType,
                                String paramName, Annotation[] anns, boolean isJson) {
        ParameterType pType = pm.getType();
        boolean isForm = isFormParameter(pm, type, anns);
        if (paramName == null && isForm) {
            Multipart m = AnnotationUtils.getAnnotation(anns, Multipart.class);
            if (m != null) {
                paramName = m.value();
            }
        }
        sb.append("");
            handleDocs(anns, sb, DocTarget.PARAM, true, isJson);
            setEnumOptions(sb, type);
            sb.append("");
        } else {
            addDocsAndCloseElement(sb, anns, "param", DocTarget.PARAM, true, isJson);
        }
    }

    private void setEnumOptions(StringBuilder sb, Class enumClass) {
        try {
            Method m = enumClass.getMethod("values", new Class[] {});
            Object[] values = (Object[])m.invoke(null, new Object[] {});
            m = enumClass.getMethod("toString", new Class[] {});
            for (Object o : values) {
                String str = (String)m.invoke(o, new Object[] {});
                sb.append("




© 2015 - 2024 Weber Informatics LLC | Privacy Policy