org.raml.jaxrs.codegen.core.AbstractGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.raml.jaxrs.codegen.core.AbstractGenerator.java

Source

/*
 * Copyright 2013-2015 (c) MuleSoft, Inc.
 *
 * Licensed 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.raml.jaxrs.codegen.core;

import static org.apache.commons.lang.StringUtils.defaultString;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.apache.commons.lang.StringUtils.join;
import static org.apache.commons.lang.StringUtils.strip;
import static org.apache.commons.lang.builder.ToStringStyle.SHORT_PREFIX_STYLE;
import static org.raml.jaxrs.codegen.core.Names.EXAMPLE_PREFIX;
import static org.raml.jaxrs.codegen.core.Names.GENERIC_PAYLOAD_ARGUMENT_NAME;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.mail.internet.MimeMultipart;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.raml.jaxrs.codegen.core.Configuration.JaxrsVersion;
import org.raml.jaxrs.codegen.core.ext.GeneratorExtension;
import org.raml.model.Action;
import org.raml.model.MimeType;
import org.raml.model.Raml;
import org.raml.model.Resource;
import org.raml.model.Response;
import org.raml.model.parameter.AbstractParam;
import org.raml.model.parameter.FormParameter;
import org.raml.model.parameter.Header;
import org.raml.model.parameter.QueryParameter;
import org.raml.model.parameter.UriParameter;
import org.raml.parser.loader.ClassPathResourceLoader;
import org.raml.parser.loader.CompositeResourceLoader;
import org.raml.parser.loader.FileResourceLoader;
import org.raml.parser.loader.ResourceLoader;
import org.raml.parser.loader.UrlResourceLoader;
import org.raml.parser.rule.ValidationResult;
import org.raml.parser.visitor.RamlDocumentBuilder;
import org.raml.parser.visitor.RamlValidationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.sun.codemodel.JAnnotationArrayMember;
import com.sun.codemodel.JAnnotationUse;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JDocComment;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;

/**
 * <p>Abstract AbstractGenerator class.</p>
 *
 * @author kor
 * @version $Id: $Id
 */
public abstract class AbstractGenerator {
    /** Constant <code>DEFAULT_ANNOTATION_PARAMETER="value"</code> */
    protected static final String DEFAULT_ANNOTATION_PARAMETER = "value";

    /** Constant <code>LOGGER</code> */
    protected static final Logger LOGGER = LoggerFactory.getLogger(Generator.class);

    protected Context context;
    protected Types types;
    protected List<GeneratorExtension> extensions;

    private ResourceLoader[] prepareResourceLoaders(final Configuration configuration, final String location) {
        File sourceDirectory = configuration.getSourceDirectory();
        ArrayList<ResourceLoader> loaderList = new ArrayList<ResourceLoader>(
                Arrays.asList(new UrlResourceLoader(), new ClassPathResourceLoader()));
        if (sourceDirectory != null) {
            String sourceDirAbsPath = sourceDirectory.getAbsolutePath();
            loaderList.add(new FileResourceLoader(sourceDirAbsPath));
        }
        //Supporting all  options that occured in real life at the moment
        //TODO make loading more consistent (we should drop some options)
        if (location != null && location.length() > 0) {
            String sourceDirAbsPath = sourceDirectory.getAbsolutePath();
            String fl = new File(location).getParent();
            if (sourceDirAbsPath.endsWith(fl)) {
                sourceDirAbsPath = sourceDirAbsPath.substring(0, sourceDirAbsPath.length() - fl.length());
                loaderList.add(new FileResourceLoader(sourceDirAbsPath));
                loaderList.add(new FileResourceLoader(sourceDirectory) {

                    @Override
                    public InputStream fetchResource(String resourceName) {
                        File includedFile = new File(resourceName);
                        FileInputStream inputStream = null;
                        if (logger.isDebugEnabled()) {
                            logger.debug(
                                    String.format("Looking for resource: %s on directory: %s...", resourceName));
                        }
                        try {
                            return new FileInputStream(includedFile);
                        } catch (FileNotFoundException e) {
                            //ignore
                        }
                        return inputStream;
                    }
                });
            } else {
                loaderList.add(new FileResourceLoader(location));
                loaderList.add(new FileResourceLoader(""));
            }
        }
        ResourceLoader[] loaderArray = loaderList.toArray(new ResourceLoader[loaderList.size()]);
        return loaderArray;
    }

    private void validate(final Configuration configuration) {
        Validate.notNull(configuration, "configuration can't be null");

        final File outputDirectory = configuration.getOutputDirectory();
        Validate.notNull(outputDirectory, "outputDirectory can't be null");

        Validate.isTrue(outputDirectory.isDirectory(), outputDirectory + " is not a pre-existing directory");
        Validate.isTrue(outputDirectory.canWrite(), outputDirectory + " can't be written to");

        if (outputDirectory.listFiles().length > 0) {
            LOGGER.warn("Directory " + outputDirectory
                    + " is not empty, generation will work but pre-existing files may remain and produce unexpected results");
        }

        Validate.notEmpty(configuration.getBasePackageName(), "base package name can't be empty");
    }

    /**
     * <p>run.</p>
     *
     * @param raml a {@link org.raml.model.Raml} object.
     * @param configuration a {@link org.raml.jaxrs.codegen.core.Configuration} object.
     * @return a {@link java.util.Set} object.
     * @throws java.lang.Exception if any.
     */
    public Set<String> run(final Reader raml, final Configuration configuration) throws Exception {
        System.out.println("relative includes are not supported in this mode!");
        return run(raml, configuration, "");
    }

    /**
     * <p>run.</p>
     *
     * @param raml a {@link org.raml.model.Raml} object.
     * @param configuration a {@link org.raml.jaxrs.codegen.core.Configuration} object.
     * @return a {@link java.util.Set} object.
     * @throws java.lang.Exception if any.
     */
    protected Set<String> run(final Raml raml, final Configuration configuration) throws Exception {
        validate(configuration);
        extensions = configuration.getExtensions();
        context = new Context(configuration, raml);
        types = new Types(context);

        for (GeneratorExtension e : extensions) {
            e.setRaml(raml);
        }

        Collection<Resource> resources = raml.getResources().values();
        types.generateClassesFromXmlSchemas(resources);

        for (final Resource resource : resources) {
            createResourceInterface(resource, raml);
        }

        return context.generate();
    }

    /**
     * <p>createResourceInterface.</p>
     *
     * @param resource a {@link org.raml.model.Resource} object.
     * @param raml a {@link org.raml.model.Raml} object.
     * @throws java.lang.Exception if any.
     */
    protected void createResourceInterface(final Resource resource, final Raml raml) throws Exception {
        final String resourceInterfaceName = Names.buildResourceInterfaceName(resource);
        final JDefinedClass resourceInterface = context.createResourceInterface(resourceInterfaceName);
        context.setCurrentResourceInterface(resourceInterface);

        final String path = strip(resource.getRelativeUri(), "/");
        resourceInterface.annotate(Path.class).param(DEFAULT_ANNOTATION_PARAMETER,
                StringUtils.defaultIfBlank(path, "/"));

        if (isNotBlank(resource.getDescription())) {
            resourceInterface.javadoc().add(resource.getDescription());
        }

        addResourceMethods(resource, resourceInterface, path);

        /* call registered extensions */
        for (GeneratorExtension e : extensions) {
            e.onCreateResourceInterface(resourceInterface, resource);
        }
    }

    /**
     * <p>addResourceMethods.</p>
     *
     * @param resource a {@link org.raml.model.Resource} object.
     * @param resourceInterface a {@link com.sun.codemodel.JDefinedClass} object.
     * @param resourceInterfacePath a {@link java.lang.String} object.
     * @throws java.lang.Exception if any.
     */
    protected void addResourceMethods(final Resource resource, final JDefinedClass resourceInterface,
            final String resourceInterfacePath) throws Exception {
        for (final Action action : resource.getActions().values()) {
            if (!action.hasBody()) {
                addResourceMethods(resourceInterface, resourceInterfacePath, action, null, false);
            } else if (action.getBody().size() == 1) {
                final MimeType bodyMimeType = action.getBody().values().iterator().next();
                addResourceMethods(resourceInterface, resourceInterfacePath, action, bodyMimeType, false);
            } else {
                for (final MimeType bodyMimeType : action.getBody().values()) {
                    addResourceMethods(resourceInterface, resourceInterfacePath, action, bodyMimeType, true);
                }
            }
        }

        for (final Resource childResource : resource.getResources().values()) {
            addResourceMethods(childResource, resourceInterface, resourceInterfacePath);
        }
    }

    /**
     * <p>getUniqueResponseMimeTypes.</p>
     *
     * @param action a {@link org.raml.model.Action} object.
     * @return a {@link java.util.Collection} object.
     */
    protected Collection<MimeType> getUniqueResponseMimeTypes(final Action action) {
        final Map<String, MimeType> responseMimeTypes = new HashMap<String, MimeType>();
        for (final Response response : action.getResponses().values()) {
            if (response.hasBody()) {
                for (final MimeType responseMimeType : response.getBody().values()) {
                    if (responseMimeType != null) {
                        responseMimeTypes.put(responseMimeType.getType(), responseMimeType);
                    }
                }
            }
        }
        return responseMimeTypes.values();
    }

    /**
     * <p>addResourceMethod.</p>
     *
     * @param resourceInterface a {@link com.sun.codemodel.JDefinedClass} object.
     * @param resourceInterfacePath a {@link java.lang.String} object.
     * @param action a {@link org.raml.model.Action} object.
     * @param bodyMimeType a {@link org.raml.model.MimeType} object.
     * @param addBodyMimeTypeInMethodName a boolean.
     * @param uniqueResponseMimeTypes a {@link java.util.Collection} object.
     * @throws java.lang.Exception if any.
     */
    protected abstract void addResourceMethod(final JDefinedClass resourceInterface,
            final String resourceInterfacePath, final Action action, final MimeType bodyMimeType,
            final boolean addBodyMimeTypeInMethodName, final Collection<MimeType> uniqueResponseMimeTypes)
            throws Exception;

    /**
     * <p>addParamAnnotation.</p>
     *
     * @param resourceInterfacePath a {@link java.lang.String} object.
     * @param action a {@link org.raml.model.Action} object.
     * @param method a {@link com.sun.codemodel.JMethod} object.
     */
    protected void addParamAnnotation(final String resourceInterfacePath, final Action action,
            final JMethod method) {
        final String path = StringUtils.substringAfter(action.getResource().getUri(), resourceInterfacePath + "/");
        if (isNotBlank(path)) {
            method.annotate(Path.class).param(DEFAULT_ANNOTATION_PARAMETER, path);
        }
    }

    private void addCatchAllFormParametersArgument(final MimeType bodyMimeType, final JMethod method,
            final JDocComment javadoc, final JType argumentType) {
        method.param(argumentType, GENERIC_PAYLOAD_ARGUMENT_NAME);

        // build a javadoc text out of all the params
        for (final Entry<String, List<FormParameter>> namedFormParameters : bodyMimeType.getFormParameters()
                .entrySet()) {
            final StringBuilder sb = new StringBuilder();
            sb.append(namedFormParameters.getKey()).append(": ");

            for (final FormParameter formParameter : namedFormParameters.getValue()) {
                appendParameterJavadocDescription(formParameter, sb);
            }

            javadoc.addParam(GENERIC_PAYLOAD_ARGUMENT_NAME).add(sb.toString());
        }
    }

    /**
     * <p>addParameterJavaDoc.</p>
     *
     * @param parameter a {@link org.raml.model.parameter.AbstractParam} object.
     * @param parameterName a {@link java.lang.String} object.
     * @param javadoc a {@link com.sun.codemodel.JDocComment} object.
     */
    protected void addParameterJavaDoc(final AbstractParam parameter, final String parameterName,
            final JDocComment javadoc) {
        javadoc.addParam(parameterName)
                .add(defaultString(parameter.getDescription()) + getPrefixedExampleOrBlank(parameter.getExample()));
    }

    /**
     * <p>getPrefixedExampleOrBlank.</p>
     *
     * @param example a {@link java.lang.String} object.
     * @return a {@link java.lang.String} object.
     */
    protected String getPrefixedExampleOrBlank(final String example) {
        return isNotBlank(example) ? EXAMPLE_PREFIX + example : "";
    }

    /**
     * <p>appendParameterJavadocDescription.</p>
     *
     * @param param a {@link org.raml.model.parameter.AbstractParam} object.
     * @param sb a {@link java.lang.StringBuilder} object.
     */
    protected void appendParameterJavadocDescription(final AbstractParam param, final StringBuilder sb) {
        if (isNotBlank(param.getDisplayName())) {
            sb.append(param.getDisplayName());
        }

        if (isNotBlank(param.getDescription())) {
            if (sb.length() > 0) {
                sb.append(" - ");
            }
            sb.append(param.getDescription());
        }

        if (isNotBlank(param.getExample())) {
            sb.append(EXAMPLE_PREFIX).append(param.getExample());
        }

        sb.append("<br/>\n");
    }

    private void addPlainBodyArgument(final MimeType bodyMimeType, final JMethod method, final JDocComment javadoc)
            throws IOException {

        method.param(types.getRequestEntityClass(bodyMimeType), GENERIC_PAYLOAD_ARGUMENT_NAME);

        javadoc.addParam(GENERIC_PAYLOAD_ARGUMENT_NAME).add(getPrefixedExampleOrBlank(bodyMimeType.getExample()));
    }

    private boolean hasAMultiTypeFormParameter(final MimeType bodyMimeType) {
        for (final List<FormParameter> formParameters : bodyMimeType.getFormParameters().values()) {
            if (formParameters.size() > 1) {
                return true;
            }
        }
        return false;
    }

    /**
     * <p>addFormParameters.</p>
     *
     * @param bodyMimeType a {@link org.raml.model.MimeType} object.
     * @param method a {@link com.sun.codemodel.JMethod} object.
     * @param javadoc a {@link com.sun.codemodel.JDocComment} object.
     * @throws java.lang.Exception if any.
     */
    protected void addFormParameters(final MimeType bodyMimeType, final JMethod method, final JDocComment javadoc)
            throws Exception {
        if (hasAMultiTypeFormParameter(bodyMimeType)) {
            // use a "catch all" MultivaluedMap<String, String> parameter
            final JClass type = types.getGeneratorClass(MultivaluedMap.class).narrow(String.class, String.class);

            addCatchAllFormParametersArgument(bodyMimeType, method, javadoc, type);
        } else {
            for (final Entry<String, List<FormParameter>> namedFormParameters : bodyMimeType.getFormParameters()
                    .entrySet()) {
                addParameter(namedFormParameters.getKey(), namedFormParameters.getValue().get(0), FormParam.class,
                        method, javadoc);
            }
        }
    }

    /**
     * <p>addConsumesAnnotation.</p>
     *
     * @param bodyMimeType a {@link org.raml.model.MimeType} object.
     * @param method a {@link com.sun.codemodel.JMethod} object.
     */
    protected void addConsumesAnnotation(final MimeType bodyMimeType, final JMethod method) {
        if (bodyMimeType != null) {
            method.annotate(Consumes.class).param(DEFAULT_ANNOTATION_PARAMETER, bodyMimeType.getType());
        }
    }

    /**
     * <p>addProducesAnnotation.</p>
     *
     * @param uniqueResponseMimeTypes a {@link java.util.Collection} object.
     * @param method a {@link com.sun.codemodel.JMethod} object.
     */
    protected void addProducesAnnotation(final Collection<MimeType> uniqueResponseMimeTypes, final JMethod method) {
        if (uniqueResponseMimeTypes.isEmpty()) {
            return;
        }

        final JAnnotationArrayMember paramArray = method.annotate(Produces.class)
                .paramArray(DEFAULT_ANNOTATION_PARAMETER);

        for (final MimeType responseMimeType : uniqueResponseMimeTypes) {
            paramArray.param(responseMimeType.getType());
        }
    }

    /**
     * <p>addBodyParameters.</p>
     *
     * @param bodyMimeType a {@link org.raml.model.MimeType} object.
     * @param method a {@link com.sun.codemodel.JMethod} object.
     * @param javadoc a {@link com.sun.codemodel.JDocComment} object.
     * @throws java.lang.Exception if any.
     */
    protected void addBodyParameters(final MimeType bodyMimeType, final JMethod method, final JDocComment javadoc)
            throws Exception {
        if (bodyMimeType == null) {
            return;
        } else if (MediaType.APPLICATION_FORM_URLENCODED.equals(bodyMimeType.getType())) {
            addFormParameters(bodyMimeType, method, javadoc);
        } else if (MediaType.MULTIPART_FORM_DATA.equals(bodyMimeType.getType())) {
            // use a "catch all" javax.mail.internet.MimeMultipart parameter
            addCatchAllFormParametersArgument(bodyMimeType, method, javadoc,
                    types.getGeneratorType(MimeMultipart.class));
        } else {
            addPlainBodyArgument(bodyMimeType, method, javadoc);
        }
    }

    /**
     * <p>addPathParameters.</p>
     *
     * @param action a {@link org.raml.model.Action} object.
     * @param method a {@link com.sun.codemodel.JMethod} object.
     * @param javadoc a {@link com.sun.codemodel.JDocComment} object.
     * @throws java.lang.Exception if any.
     */
    protected void addPathParameters(final Action action, final JMethod method, final JDocComment javadoc)
            throws Exception {
        addAllResourcePathParameters(action.getResource(), method, javadoc);
    }

    private void addAllResourcePathParameters(Resource resource, final JMethod method, final JDocComment javadoc)
            throws Exception {

        for (final Entry<String, UriParameter> namedUriParameter : resource.getUriParameters().entrySet()) {
            addParameter(namedUriParameter.getKey(), namedUriParameter.getValue(), PathParam.class, method,
                    javadoc);
        }

        Resource parentResource = resource.getParentResource();

        if (parentResource != null) {
            addAllResourcePathParameters(parentResource, method, javadoc);
        }

    }

    /**
     * <p>addHeaderParameters.</p>
     *
     * @param action a {@link org.raml.model.Action} object.
     * @param method a {@link com.sun.codemodel.JMethod} object.
     * @param javadoc a {@link com.sun.codemodel.JDocComment} object.
     * @throws java.lang.Exception if any.
     */
    protected void addHeaderParameters(final Action action, final JMethod method, final JDocComment javadoc)
            throws Exception {
        for (final Entry<String, Header> namedHeaderParameter : action.getHeaders().entrySet()) {
            addParameter(namedHeaderParameter.getKey(), namedHeaderParameter.getValue(), HeaderParam.class, method,
                    javadoc);
        }
    }

    /**
     * <p>addBaseJavaDoc.</p>
     *
     * @param action a {@link org.raml.model.Action} object.
     * @param method a {@link com.sun.codemodel.JMethod} object.
     * @return a {@link com.sun.codemodel.JDocComment} object.
     */
    protected JDocComment addBaseJavaDoc(final Action action, final JMethod method) {
        final JDocComment javadoc = method.javadoc();
        if (isNotBlank(action.getDescription())) {
            javadoc.add(action.getDescription());
        }
        return javadoc;
    }

    /**
     * <p>addQueryParameters.</p>
     *
     * @param action a {@link org.raml.model.Action} object.
     * @param method a {@link com.sun.codemodel.JMethod} object.
     * @param javadoc a {@link com.sun.codemodel.JDocComment} object.
     * @throws java.lang.Exception if any.
     */
    protected void addQueryParameters(final Action action, final JMethod method, final JDocComment javadoc)
            throws Exception {
        for (final Entry<String, QueryParameter> namedQueryParameter : action.getQueryParameters().entrySet()) {
            addParameter(namedQueryParameter.getKey(), namedQueryParameter.getValue(), QueryParam.class, method,
                    javadoc);
        }
    }

    private void addParameter(final String name, final AbstractParam parameter,
            final Class<? extends Annotation> annotationClass, final JMethod method, final JDocComment javadoc)
            throws Exception {
        if (this.context.getConfiguration().getIgnoredParameterNames().contains(name)) {
            return;
        }
        for (GeneratorExtension e : extensions) {
            if (!e.AddParameterFilter(name, parameter, annotationClass, method)) {
                return;
            }
        }

        final String argumentName = Names.buildVariableName(name);

        final JVar argumentVariable = method.param(types.buildParameterType(parameter, argumentName), argumentName);

        argumentVariable.annotate(annotationClass).param(DEFAULT_ANNOTATION_PARAMETER, name);

        if (parameter.getDefaultValue() != null) {
            argumentVariable.annotate(DefaultValue.class).param(DEFAULT_ANNOTATION_PARAMETER,
                    parameter.getDefaultValue());
        }

        if (context.getConfiguration().isUseJsr303Annotations()) {
            addJsr303Annotations(parameter, argumentVariable);
        }

        addParameterJavaDoc(parameter, argumentVariable.name(), javadoc);
    }

    private void addJsr303Annotations(final AbstractParam parameter, final JVar argumentVariable) {
        if (isNotBlank(parameter.getPattern())) {
            LOGGER.info("Pattern constraint ignored for parameter: "
                    + ToStringBuilder.reflectionToString(parameter, SHORT_PREFIX_STYLE));
        }

        final Integer minLength = parameter.getMinLength();
        final Integer maxLength = parameter.getMaxLength();
        if ((minLength != null) || (maxLength != null)) {
            final JAnnotationUse sizeAnnotation = argumentVariable.annotate(Size.class);

            if (minLength != null) {
                sizeAnnotation.param("min", minLength);
            }

            if (maxLength != null) {
                sizeAnnotation.param("max", maxLength);
            }
        }

        final BigDecimal minimum = parameter.getMinimum();
        if (minimum != null) {
            addMinMaxConstraint(parameter, "minimum", Min.class, minimum, argumentVariable);
        }

        final BigDecimal maximum = parameter.getMaximum();
        if (maximum != null) {
            addMinMaxConstraint(parameter, "maximum", Max.class, maximum, argumentVariable);
        }

        if (parameter.isRequired()) {
            argumentVariable.annotate(NotNull.class);
        }
    }

    private void addMinMaxConstraint(final AbstractParam parameter, final String name,
            final Class<? extends Annotation> clazz, final BigDecimal value, final JVar argumentVariable) {
        try {
            final long boundary = value.longValueExact();
            argumentVariable.annotate(clazz).param(DEFAULT_ANNOTATION_PARAMETER, boundary);
        } catch (final ArithmeticException ae) {
            LOGGER.info("Non integer " + name + " constraint ignored for parameter: "
                    + ToStringBuilder.reflectionToString(parameter, SHORT_PREFIX_STYLE));
        }
    }

    private void addResourceMethods(final JDefinedClass resourceInterface, final String resourceInterfacePath,
            final Action action, final MimeType bodyMimeType, final boolean addBodyMimeTypeInMethodName)
            throws Exception {
        final Collection<MimeType> uniqueResponseMimeTypes = getUniqueResponseMimeTypes(action);

        addResourceMethod(resourceInterface, resourceInterfacePath, action, bodyMimeType,
                addBodyMimeTypeInMethodName, uniqueResponseMimeTypes);
    }

    /**
     * <p>toDetailedString.</p>
     *
     * @param item a {@link org.raml.parser.rule.ValidationResult} object.
     * @return a {@link java.lang.String} object.
     */
    protected static String toDetailedString(ValidationResult item) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\t");
        stringBuilder.append(item.getLevel());
        stringBuilder.append(" ");
        stringBuilder.append(item.getMessage());
        if (item.getLine() != ValidationResult.UNKNOWN) {
            stringBuilder.append(" (line ");
            stringBuilder.append(item.getLine());
            if (item.getStartColumn() != ValidationResult.UNKNOWN) {
                stringBuilder.append(", col ");
                stringBuilder.append(item.getStartColumn());
                if (item.getEndColumn() != item.getStartColumn()) {
                    stringBuilder.append(" to ");
                    stringBuilder.append(item.getEndColumn());
                }
            }
            stringBuilder.append(")");
        }
        return stringBuilder.toString();
    }

    /**
     * <p>run.</p>
     *
     * @param ramlReader a {@link java.io.Reader} object.
     * @param configuration a {@link org.raml.jaxrs.codegen.core.Configuration} object.
     * @return a {@link java.util.Set} object.
     * @throws java.lang.Exception if any.
     */
    public Set<String> run(final Reader ramlReader, final Configuration configuration, String readerLocation)
            throws Exception {
        if (isNotBlank(configuration.getAsyncResourceTrait())
                && configuration.getJaxrsVersion() == JaxrsVersion.JAXRS_1_1) {
            throw new IllegalArgumentException("Asynchronous resources are not supported in JAX-RS 1.1");
        }
        final String ramlBuffer = IOUtils.toString(ramlReader);
        String folder = new File(readerLocation).getParent();
        ResourceLoader[] loaderArray = prepareResourceLoaders(configuration, folder);

        final List<ValidationResult> results = RamlValidationService
                .createDefault(new CompositeResourceLoader(loaderArray)).validate(ramlBuffer, readerLocation);
        if (ValidationResult.areValid(results)) {
            return run(new RamlDocumentBuilder(new CompositeResourceLoader(loaderArray)).build(ramlBuffer,
                    readerLocation), configuration);
        } else {
            final List<String> validationErrors = Lists.transform(results,
                    new Function<ValidationResult, String>() {

                        public String apply(final ValidationResult vr) {
                            return toDetailedString(vr);
                        }
                    });

            throw new IllegalArgumentException("Invalid RAML definition:\n" + join(validationErrors, "\n"));
        }
    }
}