/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.core.transformer;
import org.mule.runtime.api.metadata.DataType;
import org.mule.runtime.api.meta.AbstractAnnotatedObject;
import org.mule.runtime.core.api.TransformationService;
import org.mule.runtime.core.api.MuleContext;
import org.mule.runtime.core.api.Event;
import org.mule.runtime.api.exception.MuleException;
import org.mule.runtime.core.api.context.MuleContextAware;
import org.mule.runtime.api.lifecycle.InitialisationException;
import org.mule.runtime.core.api.transformer.Converter;
import org.mule.runtime.core.api.transformer.TransformerException;
import org.mule.runtime.core.api.transformer.MessageTransformerException;
import java.nio.charset.Charset;
import java.util.LinkedList;
import java.util.List;
/**
* Composes many converters to behave as a single one.
* <p/>
* When {@link #transform(Object)} is called each converter in the same order they are included in the composition. The output of
* a given converter is the input of the next composed converter.
*/
public class CompositeConverter extends AbstractAnnotatedObject implements Converter, MuleContextAware {
private String name;
private LinkedList<Converter> chain;
private TransformationService transformationService;
private MuleContext muleContext;
/**
* Create a new conversion chain using the specified converters
*
* @param converters List of converters using to build the chain
*/
public CompositeConverter(Converter... converters) {
if (converters.length == 0) {
throw new IllegalArgumentException("There must be at least one converter");
}
chain = new LinkedList<>();
name = compositeConverterName(converters);
this.transformationService = transformationService;
}
private String compositeConverterName(Converter[] converters) {
StringBuilder builder = new StringBuilder();
for (Converter converter : converters) {
chain.addLast(converter);
builder.append(converter.getName());
}
return builder.toString();
}
@Override
public boolean isSourceDataTypeSupported(DataType dataType) {
return chain.size() > 0 && chain.peekFirst().isSourceDataTypeSupported(dataType);
}
@Override
public List<DataType> getSourceDataTypes() {
return chain.peekFirst().getSourceDataTypes();
}
@Override
public boolean isAcceptNull() {
return chain.size() > 0 && chain.peekFirst().isAcceptNull();
}
@Override
public boolean isIgnoreBadInput() {
return chain.size() > 0 && chain.peekFirst().isIgnoreBadInput();
}
@Override
public Object transform(Object src) throws TransformerException {
return transform(src, null);
}
@Override
public Object transform(Object src, Charset encoding) throws TransformerException {
Object current = src;
Charset currentEncoding = encoding;
for (Converter converter : chain) {
if (currentEncoding != null) {
current = converter.transform(current, currentEncoding);
} else {
current = converter.transform(current);
}
currentEncoding = converter.getReturnDataType().getMediaType().getCharset().orElse(encoding);
}
return current;
}
@Override
public void setReturnDataType(DataType type) {
chain.peekLast().setReturnDataType(type);
}
@Override
public DataType getReturnDataType() {
return chain.peekLast().getReturnDataType();
}
@Override
public void dispose() {
for (Converter converter : chain) {
converter.dispose();
}
}
@Override
public void initialise() throws InitialisationException {
for (Converter converter : chain) {
converter.initialise();
}
}
@Override
public Event process(Event event) throws MuleException {
if (event != null && event.getMessage() != null) {
try {
event = Event.builder(event)
.message(muleContext.getTransformationService().applyTransformers(event.getMessage(), event, this)).build();
} catch (Exception e) {
throw new MessageTransformerException(this, e);
}
}
return event;
}
@Override
public void setMuleContext(MuleContext context) {
this.muleContext = context;
for (Converter converter : chain) {
converter.setMuleContext(context);
}
}
@Override
public void setName(String name) {
throw new UnsupportedOperationException("Cannot change composite converter name");
}
@Override
public String getName() {
return name;
}
@Override
public int getPriorityWeighting() {
int priorityWeighting = 0;
for (Converter converter : chain) {
priorityWeighting += converter.getPriorityWeighting();
}
return priorityWeighting;
}
@Override
public void setPriorityWeighting(int weighting) {}
public LinkedList<Converter> getConverters() {
return new LinkedList<>(chain);
}
}