/*
* Copyright 2013-2017 (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.emitter;
import com.google.common.base.Optional;
import com.google.common.collect.Ordering;
import org.raml.api.*;
import org.raml.emitter.plugins.DefaultTypeHandler;
import org.raml.emitter.plugins.ResponseHandler;
import org.raml.emitter.plugins.DefaultResponseHandler;
import org.raml.jaxrs.emitters.AnnotationInstanceEmitter;
import org.raml.jaxrs.emitters.AnnotationTypeEmitter;
import org.raml.jaxrs.emitters.ParameterEmitter;
import org.raml.jaxrs.plugins.TypeHandler;
import org.raml.jaxrs.plugins.TypeSelector;
import org.raml.jaxrs.types.TypeRegistry;
import org.raml.jaxrs.common.RamlGenerator;
import org.raml.utilities.IndentedAppendable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
public class IndentedAppendableEmitter implements Emitter {
private static final Logger logger = LoggerFactory.getLogger(IndentedAppendableEmitter.class);
private TypeRegistry typeRegistry = new TypeRegistry();
private List<ResponseHandler> responseHandlerAlternatives = Arrays.<ResponseHandler>asList(new DefaultResponseHandler());
private final IndentedAppendable writer;
private AnnotationTypeEmitter annotationTypeEmitter;
private AnnotationInstanceEmitter annotationInstanceEmitter;
private ParameterEmitter parameterEmitter;
private IndentedAppendableEmitter(IndentedAppendable writer) {
this.writer = writer;
}
public static IndentedAppendableEmitter create(IndentedAppendable appendable) {
checkNotNull(appendable);
return new IndentedAppendableEmitter(appendable);
}
@Override
public void emit(RamlApi api) throws RamlEmissionException {
try {
this.annotationTypeEmitter = new AnnotationTypeEmitter(writer, api.getSupportedAnnotation());
this.annotationInstanceEmitter = new AnnotationInstanceEmitter(writer, api.getSupportedAnnotation());
this.parameterEmitter = new ParameterEmitter(writer);
writeApi(api);
} catch (IOException e) {
throw new RamlEmissionException(format("unable to emit api: %s", api.getBaseUri()), e);
}
}
private void writeApi(RamlApi api) throws IOException {
writeHeader();
writeTitle(api.getTitle());
writeVersion(api.getVersion());
writeBaseUri(api.getBaseUri());
writeDefaultMediaType(api.getDefaultMediaType());
writeSupportedAnnotations(api.getSupportedAnnotation());
for (RamlResource resource : api.getResources()) {
writeResource(resource);
}
writeTypes();
}
private void writeSupportedAnnotations(List<RamlSupportedAnnotation> supportedAnnotation) throws IOException {
if (supportedAnnotation.size() == 0) {
return;
}
annotationTypeEmitter.emitAnnotations();
}
private void writeTypes() throws IOException {
writer.appendLine("types:");
writer.indent();
typeRegistry.writeAll(annotationInstanceEmitter, writer);
writer.outdent();
}
private void writeDefaultMediaType(RamlMediaType defaultMediaType) throws IOException {
writer.appendEscapedLine("mediaType", defaultMediaType.toStringRepresentation());
}
private void writeResource(RamlResource resource)
throws IOException {
writer.appendLine(format("%s:", resource.getPath()));
writer.indent();
for (RamlResourceMethod method : resource.getMethods()) {
writeMethod(method);
}
for (RamlResource child : resource.getChildren()) {
writeResource(child);
}
writer.outdent();
}
private void writeMethod(RamlResourceMethod method) throws IOException {
writer.appendLine(format("%s:", method.getHttpMethod()));
writer.indent();
annotationInstanceEmitter.emit(method);
Optional<String> description = method.getDescription();
if (description.isPresent() && !description.get().isEmpty()) {
writeDescription(description.get());
}
if (!method.getConsumedMediaTypes().isEmpty()
&& (method.getConsumedType().isPresent() || !method.getMultiFormDataParameter().isEmpty() || !method.getFormParameters()
.isEmpty())) {
writer.appendLine("body:");
writer.indent();
for (RamlMediaType ramlMediaType : method.getConsumedMediaTypes()) {
if (ramlMediaType.toStringRepresentation().equals("multipart/form-data")) {
writer.appendLine(ramlMediaType.toStringRepresentation() + ":");
writer.indent();
writeMultiPartFormData(method);
writer.outdent();
} else {
if (ramlMediaType.toStringRepresentation().equals("application/x-www-form-urlencoded")) {
writer.appendLine(ramlMediaType.toStringRepresentation() + ":");
writer.indent();
writeFormParam(method);
writer.outdent();
} else {
Type type = method.getConsumedType().get().getType();
TypeHandler typeHandler = pickTypeHandler(type);
typeHandler.writeType(typeRegistry, writer, ramlMediaType, method, method.getConsumedType().get());
}
}
}
writer.outdent();
}
ResponseHandler handler = pickResponseHandler(method);
TypeSelector selector = new TypeSelector() {
@Override
public TypeHandler pickTypeWriter(RamlResourceMethod method, RamlMediaType producedMediaType) throws IOException {
return pickTypeHandler(method.getProducedType().get().getType());
}
};
if (!method.getProducedMediaTypes().isEmpty()) {
writer.appendLine("responses:");
writer.indent();
handler.writeResponses(typeRegistry, writer, method, selector);
writer.outdent();
}
if (!method.getHeaderParameters().isEmpty()) {
writeHeaderParameters(method.getHeaderParameters());
}
if (!method.getQueryParameters().isEmpty()) {
writeQueryParameters(method.getQueryParameters());
}
writer.outdent();
}
private void writeMultiPartFormData(RamlResourceMethod method) throws IOException {
writer.appendLine("type:");
writer.indent();
writer.appendLine("properties:");
writer.indent();
List<RamlMultiFormDataParameter> formData = method.getMultiFormDataParameter();
for (RamlMultiFormDataParameter formDatum : formData) {
Type type = formDatum.getPartEntity().getType();
writer.appendLine(formDatum.getName() + ":");
writer.indent();
TypeHandler typeHandler = pickTypeHandler(type);
typeHandler.writeType(typeRegistry, writer, RamlMediaType.UNKNOWN_TYPE, method, formDatum.getPartEntity());
writer.outdent();
}
writer.outdent();
writer.outdent();
}
private void writeFormParam(RamlResourceMethod method) throws IOException {
writer.appendLine("type:");
writer.indent();
writer.appendLine("properties:");
writer.indent();
List<RamlFormParameter> formData = method.getFormParameters();
for (RamlFormParameter formDatum : formData) {
writer.appendLine(formDatum.getName(), RamlTypes.fromType(formDatum.getType())
.getRamlSyntax());
}
writer.outdent();
writer.outdent();
}
private TypeHandler pickTypeHandler(final Type type) throws IOException {
Class<?> c = (Class) type;
RamlGenerator generatorAnnotation = c.getAnnotation(RamlGenerator.class);
if (generatorAnnotation != null) {
try {
return generatorAnnotation.value().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
logger.error("unable to create generator", e);
throw new IOException("enable to create generator", e);
}
} else {
return new DefaultTypeHandler();
}
}
private ResponseHandler pickResponseHandler(final RamlResourceMethod method) {
Ordering<ResponseHandler> bodies = new Ordering<ResponseHandler>() {
@Override
public int compare(ResponseHandler left, ResponseHandler right) {
return left.handlesResponses(method) - right.handlesResponses(method);
}
};
return bodies.max(this.responseHandlerAlternatives);
}
private void writeDescription(String description) throws IOException {
writer.appendEscapedLine("description", description);
}
private void writeHeaderParameters(Iterable<RamlHeaderParameter> headerParameters)
throws IOException {
writer.appendLine("headers:");
writer.indent();
for (RamlHeaderParameter parameter : headerParameters) {
parameterEmitter.emit(parameter);
}
writer.outdent();
}
private void writeQueryParameters(Iterable<RamlQueryParameter> queryParameters)
throws IOException {
writer.appendLine("queryParameters:");
writer.indent();
for (RamlQueryParameter parameter : queryParameters) {
parameterEmitter.emit(parameter);
}
writer.outdent();
}
private void writeHeader() throws IOException {
writer.appendLine("#%RAML 1.0");
}
private void writeTitle(String title) throws IOException {
writer.appendEscapedLine("title", title);
}
private void writeVersion(String version) throws IOException {
writer.appendEscapedLine("version", version);
}
private void writeBaseUri(String baseUri) throws IOException {
writer.appendEscapedLine("baseUri", baseUri);
}
}