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