001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.model.dataformat;
018
019import java.util.ArrayList;
020import java.util.List;
021import javax.xml.bind.annotation.XmlAccessType;
022import javax.xml.bind.annotation.XmlAccessorType;
023import javax.xml.bind.annotation.XmlAttribute;
024import javax.xml.bind.annotation.XmlElement;
025import javax.xml.bind.annotation.XmlRootElement;
026import javax.xml.bind.annotation.XmlTransient;
027
028import org.apache.camel.CamelContext;
029import org.apache.camel.model.DataFormatDefinition;
030import org.apache.camel.spi.DataFormat;
031import org.apache.camel.spi.Metadata;
032import org.apache.camel.spi.RouteContext;
033import org.apache.camel.util.ObjectHelper;
034
035/**
036 * YAML is a data format to marshal and unmarshal Java objects to and from YAML.
037 *
038 * @version 
039 */
040@Metadata(firstVersion = "2.17.0", label = "dataformat,transformation,yaml", title = "YAML")
041@XmlRootElement(name = "yaml")
042@XmlAccessorType(XmlAccessType.FIELD)
043public class YAMLDataFormat extends DataFormatDefinition {
044    @XmlAttribute @Metadata(defaultValue = "SnakeYAML")
045    private YAMLLibrary library = YAMLLibrary.SnakeYAML;
046    @XmlTransient
047    private ClassLoader classLoader;
048    @XmlTransient
049    private Class<?> unmarshalType;
050    @XmlAttribute
051    private String unmarshalTypeName;
052    @XmlAttribute
053    private String constructor;
054    @XmlAttribute
055    private String representer;
056    @XmlAttribute
057    private String dumperOptions;
058    @XmlAttribute
059    private String resolver;
060    @XmlAttribute @Metadata(defaultValue = "true")
061    private Boolean useApplicationContextClassLoader = true;
062    @XmlAttribute @Metadata(defaultValue = "false")
063    private Boolean prettyFlow = false;
064    @XmlAttribute @Metadata(defaultValue = "false")
065    private Boolean allowAnyType = false;
066    @XmlElement(name = "typeFilter")
067    private List<YAMLTypeFilterDefinition> typeFilters;
068
069    public YAMLDataFormat() {
070        this(YAMLLibrary.SnakeYAML);
071    }
072
073    public YAMLDataFormat(YAMLLibrary library) {
074        super("yaml-" + library.name().toLowerCase());
075        this.library = library;
076    }
077
078    public YAMLDataFormat(YAMLLibrary library, Class<?> unmarshalType) {
079        super("yaml-" + library.name().toLowerCase());
080        this.library = library;
081        this.unmarshalType = unmarshalType;
082    }
083
084    public YAMLLibrary getLibrary() {
085        return library;
086    }
087
088    /**
089     * Which yaml library to use.
090     * <p/>
091     * By default it is SnakeYAML
092     */
093    public void setLibrary(YAMLLibrary library) {
094        this.library = library;
095        setDataFormatName("yaml-" + library.name().toLowerCase());
096    }
097
098    public Class<?> getUnmarshalType() {
099        return unmarshalType;
100    }
101
102    /**
103     * Class of the object to be created
104     */
105    public void setUnmarshalType(Class<?> type) {
106        this.unmarshalType = type;
107    }
108
109    public String getUnmarshalTypeName() {
110        return unmarshalTypeName;
111    }
112
113    /**
114     * Class name of the java type to use when unarmshalling
115     */
116    public void setUnmarshalTypeName(String unmarshalTypeName) {
117        this.unmarshalTypeName = unmarshalTypeName;
118    }
119
120    public ClassLoader getClassLoader() {
121        return classLoader;
122    }
123
124    /**
125     * Set a custom classloader
126     */
127    public void setClassLoader(ClassLoader classLoader) {
128        this.classLoader = classLoader;
129    }
130
131    public String getConstructor() {
132        return constructor;
133    }
134
135    /**
136     * BaseConstructor to construct incoming documents.
137     */
138    public void setConstructor(String constructor) {
139        this.constructor = constructor;
140    }
141
142    public String getRepresenter() {
143        return representer;
144    }
145
146    /**
147     * Representer to emit outgoing objects.
148     */
149    public void setRepresenter(String representer) {
150        this.representer = representer;
151    }
152
153    public String getDumperOptions() {
154        return dumperOptions;
155    }
156
157    /**
158     * DumperOptions to configure outgoing objects.
159     */
160    public void setDumperOptions(String dumperOptions) {
161        this.dumperOptions = dumperOptions;
162    }
163
164    public String getResolver() {
165        return resolver;
166    }
167
168    /**
169     * Resolver to detect implicit type
170     */
171    public void setResolver(String resolver) {
172        this.resolver = resolver;
173    }
174
175    public boolean isUseApplicationContextClassLoader() {
176        return useApplicationContextClassLoader;
177    }
178
179    /**
180     * Use ApplicationContextClassLoader as custom ClassLoader
181     */
182    public void setUseApplicationContextClassLoader(boolean useApplicationContextClassLoader) {
183        this.useApplicationContextClassLoader = useApplicationContextClassLoader;
184    }
185
186    public boolean isPrettyFlow() {
187        return prettyFlow;
188    }
189
190    /**
191     * Force the emitter to produce a pretty YAML document when using the flow
192     * style.
193     */
194    public void setPrettyFlow(boolean prettyFlow) {
195        this.prettyFlow = prettyFlow;
196    }
197
198    public boolean isAllowAnyType() {
199        return allowAnyType;
200    }
201
202    /**
203     * Allow any class to be un-marshaled
204     */
205    public void setAllowAnyType(boolean allowAnyType) {
206        this.allowAnyType = allowAnyType;
207    }
208
209    public List<YAMLTypeFilterDefinition> getTypeFilters() {
210        return typeFilters;
211    }
212
213    /**
214     * Set the types SnakeYAML is allowed to un-marshall
215     */
216    public void setTypeFilters(List<YAMLTypeFilterDefinition> typeFilters) {
217        this.typeFilters = typeFilters;
218    }
219
220    @Override
221    protected DataFormat createDataFormat(RouteContext routeContext) {
222        if (library == YAMLLibrary.SnakeYAML) {
223            setProperty(routeContext.getCamelContext(), this, "dataFormatName", "yaml-snakeyaml");
224        }
225
226        return super.createDataFormat(routeContext);
227    }
228
229    @Override
230    protected void configureDataFormat(DataFormat dataFormat, CamelContext camelContext) {
231        if (library == YAMLLibrary.SnakeYAML) {
232            configureSnakeDataFormat(dataFormat, camelContext);
233        }
234    }
235
236    protected void configureSnakeDataFormat(DataFormat dataFormat, CamelContext camelContext) {
237        Class<?> yamlUnmarshalType =  unmarshalType;
238        if (yamlUnmarshalType == null && unmarshalTypeName != null) {
239            try {
240                yamlUnmarshalType = camelContext.getClassResolver().resolveMandatoryClass(unmarshalTypeName);
241            } catch (ClassNotFoundException e) {
242                throw ObjectHelper.wrapRuntimeCamelException(e);
243            }
244        }
245
246        setProperty(dataFormat, camelContext, "unmarshalType", yamlUnmarshalType);
247        setProperty(dataFormat, camelContext, "classLoader", classLoader);
248        setProperty(dataFormat, camelContext, "useApplicationContextClassLoader", useApplicationContextClassLoader);
249        setProperty(dataFormat, camelContext, "prettyFlow", prettyFlow);
250        setProperty(dataFormat, camelContext, "allowAnyType", allowAnyType);
251
252        if (typeFilters != null && !typeFilters.isEmpty()) {
253            List<String> typeFilterDefinitions = new ArrayList<>(typeFilters.size());
254            for (YAMLTypeFilterDefinition definition : typeFilters) {
255                String value = definition.getValue();
256
257                if (!value.startsWith("type") && !value.startsWith("regexp")) {
258                    YAMLTypeFilterType type = definition.getType();
259                    if (type == null) {
260                        type = YAMLTypeFilterType.type;
261                    }
262
263                    value = type.name() + ":" + value;
264                }
265
266                typeFilterDefinitions.add(value);
267            }
268
269            setProperty(dataFormat, camelContext, "typeFilterDefinitions", typeFilterDefinitions);
270        }
271
272        setPropertyRef(dataFormat, camelContext, "constructor", constructor);
273        setPropertyRef(dataFormat, camelContext, "representer", representer);
274        setPropertyRef(dataFormat, camelContext, "dumperOptions", dumperOptions);
275        setPropertyRef(dataFormat, camelContext, "resolver", resolver);
276    }
277
278    protected void setProperty(DataFormat dataFormat, CamelContext camelContext, String propertyName, Object propertyValue) {
279        if (ObjectHelper.isNotEmpty(propertyValue)) {
280            setProperty(camelContext, dataFormat, propertyName, propertyValue);
281        }
282    }
283
284    protected void setPropertyRef(DataFormat dataFormat, CamelContext camelContext, String propertyName, String propertyValue) {
285        if (ObjectHelper.isNotEmpty(propertyValue)) {
286            // must be a reference value
287            String ref = propertyValue.startsWith("#") ? propertyValue : "#" + propertyValue;
288            setProperty(camelContext, dataFormat, propertyName, ref);
289        }
290    }
291
292}