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     */
017    package org.apache.camel.component.file;
018    
019    import java.io.BufferedReader;
020    import java.io.File;
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.Reader;
024    import java.io.Serializable;
025    
026    import org.apache.camel.Converter;
027    import org.apache.camel.Exchange;
028    import org.apache.camel.FallbackConverter;
029    import org.apache.camel.NoTypeConversionAvailableException;
030    import org.apache.camel.TypeConverter;
031    import org.apache.camel.converter.IOConverter;
032    import org.apache.camel.spi.TypeConverterRegistry;
033    import org.slf4j.Logger;
034    import org.slf4j.LoggerFactory;
035    
036    /**
037     * A set of converter methods for working with generic file types
038     */
039    @Converter
040    public final class GenericFileConverter {
041    
042        private static final Logger LOG = LoggerFactory.getLogger(GenericFileConverter.class);
043    
044        private GenericFileConverter() {
045            // Helper Class
046        }
047    
048        @FallbackConverter
049        public static Object convertTo(Class<?> type, Exchange exchange, Object value, TypeConverterRegistry registry)
050            throws IOException, NoTypeConversionAvailableException {
051    
052            // use a fallback type converter so we can convert the embedded body if the value is GenericFile
053            if (GenericFile.class.isAssignableFrom(value.getClass())) {
054    
055                GenericFile<?> file = (GenericFile<?>) value;
056                Class<?> from = file.getBody().getClass();
057    
058                // maybe from is already the type we want
059                if (from.isAssignableFrom(type)) {
060                    return file.getBody();
061                }
062    
063                // no then try to lookup a type converter
064                TypeConverter tc = registry.lookup(type, from);
065                if (tc != null) {
066                    Object body = file.getBody();
067                    // if its a file and we have a charset then use a reader to ensure we read the content using the given charset
068                    // this is a bit complicated, but a file consumer can be configured with an explicit charset, which means
069                    // we should read the file content with that given charset, and ignore any other charset properties
070    
071                    // if the desired type is InputStream or Reader we can use the optimized methods
072                    if (Reader.class.isAssignableFrom(type)) {
073                        Reader reader = genericFileToReader(file, exchange);
074                        if (reader != null) {
075                            return reader;
076                        }
077                    }
078                    if (InputStream.class.isAssignableFrom(type)) {
079                        InputStream is = genericFileToInputStream(file, exchange);
080                        if (is != null) {
081                            return is;
082                        }
083                    }
084    
085                    // okay if the file has a charset configured then we must try to load the file using that charset
086                    // which mean we have to use the Reader first, and then convert from there
087                    if (body instanceof File && file.getCharset() != null) {
088                        Reader reader = genericFileToReader(file, exchange);
089                        // we dont want a reader back, so use the type converter registry to find a suitable converter
090                        TypeConverter readerTc = registry.lookup(type, Reader.class);
091                        if (readerTc != null) {
092                            // use the reader based type converter
093                            return readerTc.convertTo(type, exchange, reader);
094                        }
095                    }
096                    // fallback and use the type suitable type converter
097                    return tc.convertTo(type, exchange, body);
098                }
099            }
100            
101            return null;
102        }
103    
104        @Converter
105        public static InputStream genericFileToInputStream(GenericFile<?> file, Exchange exchange) throws IOException, NoTypeConversionAvailableException {
106            if (file.getFile() instanceof File) {
107                // prefer to use a file input stream if its a java.io.File
108                File f = (File) file.getFile();
109                // the file must exists
110                if (f.exists()) {
111                    // read the file using the specified charset
112                    String charset = file.getCharset();
113                    if (charset != null) {
114                        LOG.debug("Read file {} with charset {}", f, file.getCharset());
115                    } else {
116                        LOG.debug("Read file {} (no charset)", f);
117                    }
118                    return IOConverter.toInputStream(f, charset);
119                }
120            }
121            if (exchange != null) {
122                // otherwise ensure the body is loaded as we want the input stream of the body
123                file.getBinding().loadContent(exchange, file);
124                return exchange.getContext().getTypeConverter().convertTo(InputStream.class, exchange, file.getBody());
125            } else {
126                // should revert to fallback converter if we don't have an exchange
127                return null;
128            }
129        }
130    
131        @Converter
132        public static String genericFileToString(GenericFile<?> file, Exchange exchange) throws IOException, NoTypeConversionAvailableException {
133            // use reader first as it supports the file charset
134            BufferedReader reader = genericFileToReader(file, exchange);
135            if (reader != null) {
136                return IOConverter.toString(reader);
137            }
138            if (exchange != null) {
139                // otherwise ensure the body is loaded as we want the content of the body
140                file.getBinding().loadContent(exchange, file);
141                return exchange.getContext().getTypeConverter().convertTo(String.class, exchange, file.getBody());
142            } else {
143                // should revert to fallback converter if we don't have an exchange
144                return null;
145            }
146        }
147    
148        @Converter
149        public static Serializable genericFileToSerializable(GenericFile<?> file, Exchange exchange) throws IOException, NoTypeConversionAvailableException {
150            if (exchange != null) {
151                // load the file using input stream
152                InputStream is = genericFileToInputStream(file, exchange);
153                if (is != null) {
154                    return exchange.getContext().getTypeConverter().convertTo(Serializable.class, exchange, is);
155                }
156            }
157            // should revert to fallback converter if we don't have an exchange
158            return null;
159        }
160    
161        private static BufferedReader genericFileToReader(GenericFile<?> file, Exchange exchange) throws IOException, NoTypeConversionAvailableException {
162            if (file.getFile() instanceof File) {
163                // prefer to use a file input stream if its a java.io.File
164                File f = (File) file.getFile();
165                // the file must exists
166                if (!f.exists()) {
167                    return null;
168                }
169                // and use the charset if the file was explicit configured with a charset
170                String charset = file.getCharset();
171                if (charset != null) {
172                    LOG.debug("Read file {} with charset {}", f, file.getCharset());
173                    return IOConverter.toReader(f, charset);
174                } else {
175                    LOG.debug("Read file {} (no charset)", f);
176                    return IOConverter.toReader(f, exchange);
177                }
178            }
179            return null;
180        }
181    }