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