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.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.WebApplicationException;
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.xml.bind.JAXBContext;
import javax.xml.bind.annotation.XmlRootElement;
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.ReflectionInvokationHandler;
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.utils.AnnotationUtils;
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 = MediaType.valueOf("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 List externalSchemasCache;
    private List externalSchemaLinks;
    private Map> externalQnamesMap;
    
    private ConcurrentHashMap schemaLocationMap = 
        new ConcurrentHashMap();
        
    private ElementQNameResolver resolver;
    private List privateAddresses;
    private String applicationTitle;
    private String nsPrefix = DEFAULT_NS_PREFIX;
    private MediaType defaultMediaType = DEFAULT_MEDIA_TYPE;
    
    public WadlGenerator() {

    }

    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.privateAddresses = other.privateAddresses;
        this.resolver = other.resolver;
        this.addResourceAndMethodIds = other.addResourceAndMethodIds;
        this.singleResourceMultipleMethods = other.singleResourceMultipleMethods;
        this.useJaxbContextForQnames = other.useJaxbContextForQnames;
        this.useSingleSlashResource = other.useSingleSlashResource;
    }

    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 (!schemaLocationMap.isEmpty()) {
                String path = ui.getPath(false);
                if (path.startsWith("/") && path.length() > 0) {
                    path = path.substring(1);
                }
                if (schemaLocationMap.containsKey(path)) {
                    return getExistingSchema(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 = new StringBuilder();
        sbMain.append("");

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

        List cris = getResourcesList(m, resource);

        ResourceTypes resourceTypes =
            ResourceUtils.getAllRequestResponseTypes(cris, useJaxbContextForQnames);
        Set> allTypes = resourceTypes.getAllTypes().keySet();
        
        JAXBContext context = useJaxbContextForQnames 
            ? ResourceUtils.createJaxbContext(new HashSet>(allTypes), null, null) : null;
        
        SchemaWriter schemaWriter = createSchemaWriter(resourceTypes, context, ui);
        ElementQNameResolver qnameResolver =
            schemaWriter == null ? null : createElementQNameResolver(context);

        Map, QName> clsMap = new IdentityHashMap, QName>();
        Set visitedResources = new LinkedHashSet();
        for (ClassResourceInfo cri : cris) {
            startResourceTag(sbResources, cri.getServiceClass(), cri.getURITemplate().getValue());
            Annotation[] anns = cri.getServiceClass().getAnnotations();
            if (anns.length == 0) {
                Annotation ann = AnnotationUtils.getClassAnnotation(cri.getServiceClass(), 
                                                                    Description.class);
                if (ann != null) {
                    anns = new Annotation[] {ann};
                }
            }
            handleDocs(anns, 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("");

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

    private Object createResponseEntity(String entity, boolean isJson) {
        if (!isJson) {
            return entity;
        }
        try {
            return DOMUtils.readXml(new StringReader(entity));
        }  catch (Exception ex) {
            throw new WebApplicationException(ex, 500);
        }
    }
    
    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;
        }
    }
    
    private 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);
    }


    private 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(">");
    }

    private String getPath(String path) {
        String thePath = null;
        if (ignoreForwardSlash && path.startsWith("/") && path.length() > 0) {
            thePath = path.substring(1);
        } else {
            thePath = path;
        }
        if (thePath.contains("&")) {
            thePath = thePath.replace("&", "&");
        }
        return thePath;
    }
    
    private void startMethodTag(StringBuilder sb, OperationResourceInfo ori) {
        sb.append("");
    }

    //CHECKSTYLE:OFF
    private 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("");
    }

    private void handlePathAndMatrixClassParams(StringBuilder sb, Map params,
            boolean isJson) {
        doHandleClassParams(sb, params, isJson, ParameterType.PATH);
        doHandleClassParams(sb, params, isJson, ParameterType.MATRIX);
    }
    
    private 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);        
            }
        }
    }
    
    private void handlePathAndMatrixParams(StringBuilder sb, OperationResourceInfo ori, boolean isJson) {
        handleParams(sb, ori, ParameterType.PATH, isJson);
        handleParams(sb, ori, ParameterType.MATRIX, isJson);
    }


    private 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);
        }

    }

    private 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