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.converter;
018
019import java.io.BufferedReader;
020import java.io.BufferedWriter;
021import java.io.ByteArrayInputStream;
022import java.io.ByteArrayOutputStream;
023import java.io.File;
024import java.io.FileInputStream;
025import java.io.FileNotFoundException;
026import java.io.FileOutputStream;
027import java.io.IOException;
028import java.io.InputStream;
029import java.io.InputStreamReader;
030import java.io.ObjectInput;
031import java.io.ObjectInputStream;
032import java.io.ObjectOutput;
033import java.io.ObjectOutputStream;
034import java.io.ObjectStreamClass;
035import java.io.OutputStream;
036import java.io.OutputStreamWriter;
037import java.io.Reader;
038import java.io.StringReader;
039import java.io.UnsupportedEncodingException;
040import java.io.Writer;
041import java.net.URL;
042import java.nio.ByteBuffer;
043import java.nio.CharBuffer;
044import java.nio.charset.Charset;
045import java.nio.charset.UnsupportedCharsetException;
046import java.util.Properties;
047
048import org.apache.camel.Converter;
049import org.apache.camel.Exchange;
050import org.apache.camel.util.IOHelper;
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053
054/**
055 * Some core java.io based <a
056 * href="http://camel.apache.org/type-converter.html">Type Converters</a>
057 *
058 * @version 
059 */
060@Converter
061public final class IOConverter {
062    private static final Logger LOG = LoggerFactory.getLogger(IOConverter.class);
063
064    /**
065     * Utility classes should not have a public constructor.
066     */
067    private IOConverter() {
068    }
069
070    @Converter
071    public static InputStream toInputStream(URL url) throws IOException {
072        return IOHelper.buffered(url.openStream());
073    }
074
075    @Converter
076    public static InputStream toInputStream(File file) throws IOException {
077        return IOHelper.buffered(new FileInputStream(file));
078    }
079
080    public static InputStream toInputStream(File file, String charset) throws IOException {
081        if (charset != null) {
082            final BufferedReader reader = toReader(file, charset);
083            final Charset defaultStreamCharset = Charset.defaultCharset();
084            return new InputStream() {
085                private ByteBuffer bufferBytes;
086                private CharBuffer bufferedChars = CharBuffer.allocate(4096);
087
088                @Override
089                public int read() throws IOException {
090                    if (bufferBytes == null || bufferBytes.remaining() <= 0) {
091                        bufferedChars.clear();
092                        int len = reader.read(bufferedChars);
093                        bufferedChars.flip();
094                        if (len == -1) {
095                            return -1;
096                        }
097                        bufferBytes = defaultStreamCharset.encode(bufferedChars);
098                    }
099                    return bufferBytes.get();
100                }
101
102                @Override
103                public void close() throws IOException {
104                    reader.close();
105                }
106
107                @Override
108                public void reset() throws IOException {
109                    reader.reset();
110                }
111            };
112        } else {
113            return IOHelper.buffered(new FileInputStream(file));
114        }
115    }
116
117    /**
118     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
119     */
120    @Deprecated
121    public static BufferedReader toReader(File file) throws IOException {
122        return toReader(file, (String) null);
123    }
124
125    @Converter
126    public static BufferedReader toReader(File file, Exchange exchange) throws IOException {
127        return toReader(file, IOHelper.getCharsetName(exchange));
128    }
129
130    public static BufferedReader toReader(File file, String charset) throws IOException {
131        FileInputStream in = new FileInputStream(file);
132        return IOHelper.buffered(new EncodingFileReader(in, charset));
133    }
134
135    @Converter
136    public static File toFile(String name) {
137        return new File(name);
138    }
139
140    @Converter
141    public static OutputStream toOutputStream(File file) throws FileNotFoundException {
142        return IOHelper.buffered(new FileOutputStream(file));
143    }
144
145    /**
146     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
147     */
148    @Deprecated
149    public static BufferedWriter toWriter(File file) throws IOException {
150        FileOutputStream os = new FileOutputStream(file, false);
151        return toWriter(os, IOHelper.getCharsetName(null, true));
152    }
153    
154    @Converter
155    public static BufferedWriter toWriter(File file, Exchange exchange) throws IOException {
156        FileOutputStream os = new FileOutputStream(file, false);
157        return toWriter(os, IOHelper.getCharsetName(exchange));
158    }
159
160    public static BufferedWriter toWriter(File file, boolean append, String charset) throws IOException {
161        return toWriter(new FileOutputStream(file, append), charset);
162    }
163
164    public static BufferedWriter toWriter(FileOutputStream os, String charset) throws IOException {
165        return IOHelper.buffered(new EncodingFileWriter(os, charset));
166    }
167
168    /**
169     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
170     */
171    @Deprecated
172    public static Reader toReader(InputStream in) throws IOException {
173        return toReader(in, null);
174    }
175
176    @Converter
177    public static Reader toReader(InputStream in, Exchange exchange) throws IOException {
178        return IOHelper.buffered(new InputStreamReader(in, IOHelper.getCharsetName(exchange)));
179    }
180
181    /**
182     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
183     */
184    @Deprecated
185    public static Writer toWriter(OutputStream out) throws IOException {
186        return toWriter(out, null);
187    }
188    
189    @Converter
190    public static Writer toWriter(OutputStream out, Exchange exchange) throws IOException {
191        return IOHelper.buffered(new OutputStreamWriter(out, IOHelper.getCharsetName(exchange)));
192    }
193
194    @Converter
195    public static StringReader toReader(String text) {
196        // no buffering required as the complete string input is already passed
197        // over as a whole
198        return new StringReader(text);
199    }
200
201    /**
202     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
203     */
204    @Deprecated
205    public static InputStream toInputStream(String text) throws IOException {
206        return toInputStream(text, null);
207    }
208    
209    @Converter
210    public static InputStream toInputStream(String text, Exchange exchange) throws IOException {
211        return toInputStream(text.getBytes(IOHelper.getCharsetName(exchange)));
212    }
213    
214    @Converter
215    public static InputStream toInputStream(StringBuffer buffer, Exchange exchange) throws IOException {
216        return toInputStream(buffer.toString(), exchange);
217    }
218    
219    @Converter
220    public static InputStream toInputStream(StringBuilder builder, Exchange exchange) throws IOException {
221        return toInputStream(builder.toString(), exchange);
222    }
223    
224    /**
225     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
226     */
227    @Deprecated
228    public static InputStream toInputStream(BufferedReader buffer) throws IOException {
229        return toInputStream(buffer, null);
230    }
231    
232    @Converter
233    public static InputStream toInputStream(BufferedReader buffer, Exchange exchange) throws IOException {
234        return toInputStream(toString(buffer), exchange);
235    }
236
237    /**
238     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
239     */
240    @Deprecated
241    public static String toString(byte[] data) throws IOException {
242        return toString(data, null);
243    }
244    
245    @Converter
246    public static String toString(byte[] data, Exchange exchange) throws IOException {
247        return new String(data, IOHelper.getCharsetName(exchange));
248    }
249
250    /**
251     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
252     */
253    @Deprecated
254    public static String toString(File file) throws IOException {
255        return toString(file, null);
256    }
257    
258    @Converter
259    public static String toString(File file, Exchange exchange) throws IOException {
260        return toString(toReader(file, exchange));
261    }
262
263    @Converter
264    public static byte[] toByteArray(File file) throws IOException {
265        InputStream is = toInputStream(file);
266        try {
267            return toBytes(is);
268        } finally {
269            IOHelper.close(is, "file", LOG);
270        }
271    }
272    
273    /**
274     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
275     */
276    @Deprecated
277    public static byte[] toByteArray(Reader reader) throws IOException {
278        return toByteArray(reader, null);
279    }
280    
281    @Converter
282    public static byte[] toByteArray(Reader reader, Exchange exchange) throws IOException {
283        return toByteArray(IOHelper.buffered(reader), exchange);
284    }
285
286    /**
287     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
288     */
289    @Deprecated
290    public static String toString(URL url) throws IOException {
291        return toString(url, null);
292    }
293
294    @Converter
295    public static String toString(URL url, Exchange exchange) throws IOException {
296        InputStream is = toInputStream(url);
297        try {
298            return toString(is, exchange);
299        } finally {
300            IOHelper.close(is, "url", LOG);
301        }
302    }
303
304    @Converter
305    public static String toString(Reader reader) throws IOException {
306        return toString(IOHelper.buffered(reader));
307    }
308
309    @Converter
310    public static String toString(BufferedReader reader) throws IOException {
311        if (reader == null) {
312            return null;
313        }
314
315        StringBuilder sb = new StringBuilder(1024);
316        char[] buf = new char[1024];
317        try {
318            int len;
319            // read until we reach then end which is the -1 marker
320            while ((len = reader.read(buf)) != -1) {
321                sb.append(buf, 0, len);
322            }
323        } finally {
324            IOHelper.close(reader, "reader", LOG);
325        }
326
327        return sb.toString();
328    }
329    
330    /**
331     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
332     */
333    @Deprecated
334    public static byte[] toByteArray(BufferedReader reader) throws IOException {
335        return toByteArray(reader, null);
336    }
337    
338    @Converter
339    public static byte[] toByteArray(BufferedReader reader, Exchange exchange) throws IOException {
340        String s = toString(reader);
341        return toByteArray(s, exchange);
342    }
343
344    /**
345     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
346     */
347    @Deprecated
348    public static byte[] toByteArray(String value) throws IOException {
349        return toByteArray(value, null);
350    }
351
352    @Converter
353    public static byte[] toByteArray(String value, Exchange exchange) throws IOException {
354        return value != null ? value.getBytes(IOHelper.getCharsetName(exchange)) : null;
355    }
356
357    /**
358     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
359     */
360    @Deprecated
361    public static String toString(InputStream in) throws IOException {
362        return toString(in, null);
363    }
364
365    @Converter
366    public static String toString(InputStream in, Exchange exchange) throws IOException {
367        return toString(toReader(in, exchange));
368    }
369
370    @Converter
371    public static InputStream toInputStream(byte[] data) {
372        // no buffering required as the complete byte input is already passed
373        // over as a whole
374        return new ByteArrayInputStream(data);
375    }
376
377    @Converter
378    public static ObjectOutput toObjectOutput(OutputStream stream) throws IOException {
379        if (stream instanceof ObjectOutput) {
380            return (ObjectOutput) stream;
381        } else {
382            return new ObjectOutputStream(IOHelper.buffered(stream));
383        }
384    }
385
386    @Converter
387    public static ObjectInput toObjectInput(final InputStream stream, final Exchange exchange) throws IOException {
388        if (stream instanceof ObjectInput) {
389            return (ObjectInput) stream;
390        } else {
391            return new ObjectInputStream(IOHelper.buffered(stream)) {
392                @Override
393                protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
394                    // need to let Camel be able to resolve class using ClassResolver SPI, to let class loading
395                    // work in OSGi and other containers
396                    Class<?>  answer = null;
397                    String name = objectStreamClass.getName();
398                    if (exchange != null) {
399                        LOG.trace("Loading class {} using Camel ClassResolver", name);
400                        answer = exchange.getContext().getClassResolver().resolveClass(name);
401                    }
402                    if (answer == null) {
403                        LOG.trace("Loading class {} using JDK default implementation", name);
404                        answer = super.resolveClass(objectStreamClass);
405                    }
406                    return answer;
407                }
408            };
409        }
410    }
411
412    @Converter
413    public static byte[] toBytes(InputStream stream) throws IOException {
414        ByteArrayOutputStream bos = new ByteArrayOutputStream();
415        IOHelper.copy(IOHelper.buffered(stream), bos);
416
417        // no need to close the ByteArrayOutputStream as it's close()
418        // implementation is noop
419        return bos.toByteArray();
420    }
421
422    @Converter
423    public static byte[] toByteArray(ByteArrayOutputStream os) {
424        return os.toByteArray();
425    }
426
427    /**
428     * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
429     */
430    @Deprecated
431    public static String toString(ByteArrayOutputStream os) throws IOException {
432        return toString(os, null);
433    }
434
435    @Converter
436    public static String toString(ByteArrayOutputStream os, Exchange exchange) throws IOException {
437        return os.toString(IOHelper.getCharsetName(exchange));
438    }
439
440    @Converter
441    public static InputStream toInputStream(ByteArrayOutputStream os) {
442        // no buffering required as the complete byte array input is already
443        // passed over as a whole
444        return new ByteArrayInputStream(os.toByteArray());
445    }
446
447    @Converter
448    public static Properties toProperties(File file) throws IOException {
449        return toProperties(new FileInputStream(file));
450    }
451
452    @Converter
453    public static Properties toProperties(InputStream is) throws IOException {
454        Properties prop = new Properties();
455        try {
456            prop.load(is);
457        } finally {
458            IOHelper.close(is);
459        }
460        return prop;
461    }
462
463    @Converter
464    public static Properties toProperties(Reader reader) throws IOException {
465        Properties prop = new Properties();
466        try {
467            prop.load(reader);
468        } finally {
469            IOHelper.close(reader);
470        }
471        return prop;
472    }
473
474    /**
475     * Gets the charset name if set as header or property {@link Exchange#CHARSET_NAME}.
476     *
477     * @param exchange  the exchange
478     * @param useDefault should we fallback and use JVM default charset if no property existed?
479     * @return the charset, or <tt>null</tt> if no found
480     */
481    @Deprecated
482    public static String getCharsetName(Exchange exchange, boolean useDefault) {
483        return IOHelper.getCharsetName(exchange, useDefault);
484    }
485    
486    @Deprecated
487    public static String getCharsetName(Exchange exchange) {
488        return getCharsetName(exchange, true);
489    }
490
491    /**
492     * Encoding-aware file reader. 
493     */
494    private static class EncodingFileReader extends InputStreamReader {
495
496        private final FileInputStream in;
497
498        /**
499         * @param in file to read
500         * @param charset character set to use
501         */
502        public EncodingFileReader(FileInputStream in, String charset)
503            throws FileNotFoundException, UnsupportedEncodingException {
504            super(in, charset);
505            this.in = in;
506        }
507
508        @Override
509        public void close() throws IOException {
510            try {
511                super.close();
512            } finally {
513                in.close();
514            }
515        }
516    }
517    
518    /**
519     * Encoding-aware file writer. 
520     */
521    private static class EncodingFileWriter extends OutputStreamWriter {
522
523        private final FileOutputStream out;
524
525        /**
526         * @param out file to write
527         * @param charset character set to use
528         */
529        public EncodingFileWriter(FileOutputStream out, String charset)
530            throws FileNotFoundException, UnsupportedEncodingException {
531            super(out, charset);
532            this.out = out;
533        }
534
535        @Override
536        public void close() throws IOException {
537            try {
538                super.close();
539            } finally {
540                out.close();
541            }
542        }
543    }
544    
545    /**
546     * This method will take off the quotes and double quotes of the charset
547     */
548    @Deprecated
549    public static String normalizeCharset(String charset) {
550        return IOHelper.normalizeCharset(charset);
551    }
552    
553    @Deprecated
554    public static void validateCharset(String charset) throws UnsupportedCharsetException {
555        IOHelper.validateCharset(charset);
556    }
557
558}