    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
    018package org.apache.commons.codec.net;
    019
    020import java.io.UnsupportedEncodingException;
    021import java.nio.charset.Charset;
    022
    023import org.apache.commons.codec.Charsets;
    024import org.apache.commons.codec.DecoderException;
    025import org.apache.commons.codec.EncoderException;
    026import org.apache.commons.codec.StringDecoder;
    027import org.apache.commons.codec.StringEncoder;
    028import org.apache.commons.codec.binary.Base64;
    029
    030/**
    031 * Identical to the Base64 encoding defined by <a href="http://www.ietf.org/rfc/rfc1521.txt">RFC 1521</a>
    032 * and allows a character set to be specified.
    033 * <p>
    034 * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a> describes techniques to allow the encoding of non-ASCII
    035 * text in various portions of a RFC 822 [2] message header, in a manner which is unlikely to confuse existing message
    036 * handling software.
    037 * <p>
    038 * This class is immutable and thread-safe.
    039 *
    040 * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">MIME (Multipurpose Internet Mail Extensions) Part Two: Message
    041 *          Header Extensions for Non-ASCII Text</a>
    042 *
    043 * @since 1.3
    044 * @version $Id: BCodec.java 1429868 2013-01-07 16:08:05Z ggregory $
    045 */
    046public class BCodec extends RFC1522Codec implements StringEncoder, StringDecoder {
    047    /**
    048     * The default charset used for string decoding and encoding.
    049     */
    050    private final Charset charset;
    051
    052    /**
    053     * Default constructor.
    054     */
    055    public BCodec() {
    056        this(Charsets.UTF_8);
    057    }
    058
    059    /**
    060     * Constructor which allows for the selection of a default charset
    061     *
    062     * @param charset
    063     *            the default string charset to use.
    064     *
    065     * @see <a href="http://download.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html">Standard charsets</a>
    066     * @since 1.7
    067     */
    068    public BCodec(final Charset charset) {
    069        this.charset = charset;
    070    }
    071
    072    /**
    073     * Constructor which allows for the selection of a default charset
    074     *
    075     * @param charsetName
    076     *            the default charset to use.
    077     * @throws java.nio.charset.UnsupportedCharsetException
    078     *             If the named charset is unavailable
    079     * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable
    080     * @see <a href="http://download.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html">Standard charsets</a>
    081     */
    082    public BCodec(final String charsetName) {
    083        this(Charset.forName(charsetName));
    084    }
    085
    086    @Override
    087    protected String getEncoding() {
    088        return "B";
    089    }
    090
    091    @Override
    092    protected byte[] doEncoding(final byte[] bytes) {
    093        if (bytes == null) {
    094            return null;
    095        }
    096        return Base64.encodeBase64(bytes);
    097    }
    098
    099    @Override
    100    protected byte[] doDecoding(final byte[] bytes) {
    101        if (bytes == null) {
    102            return null;
    103        }
    104        return Base64.decodeBase64(bytes);
    105    }
    106
    107    /**
    108     * Encodes a string into its Base64 form using the specified charset. Unsafe characters are escaped.
    109     *
    110     * @param value
    111     *            string to convert to Base64 form
    112     * @param charset
    113     *            the charset for <code>value</code>
    114     * @return Base64 string
    115     * @throws EncoderException
    116     *             thrown if a failure condition is encountered during the encoding process.
    117     * @since 1.7
    118     */
    119    public String encode(final String value, final Charset charset) throws EncoderException {
    120        if (value == null) {
    121            return null;
    122        }
    123        return encodeText(value, charset);
    124    }
    125
    126    /**
    127     * Encodes a string into its Base64 form using the specified charset. Unsafe characters are escaped.
    128     *
    129     * @param value
    130     *            string to convert to Base64 form
    131     * @param charset
    132     *            the charset for <code>value</code>
    133     * @return Base64 string
    134     * @throws EncoderException
    135     *             thrown if a failure condition is encountered during the encoding process.
    136     */
    137    public String encode(final String value, final String charset) throws EncoderException {
    138        if (value == null) {
    139            return null;
    140        }
    141        try {
    142            return this.encodeText(value, charset);
    143        } catch (final UnsupportedEncodingException e) {
    144            throw new EncoderException(e.getMessage(), e);
    145        }
    146    }
    147
    148    /**
    149     * Encodes a string into its Base64 form using the default charset. Unsafe characters are escaped.
    150     *
    151     * @param value
    152     *            string to convert to Base64 form
    153     * @return Base64 string
    154     * @throws EncoderException
    155     *             thrown if a failure condition is encountered during the encoding process.
    156     */
    157    @Override
    158    public String encode(final String value) throws EncoderException {
    159        if (value == null) {
    160            return null;
    161        }
    162        return encode(value, this.getCharset());
    163    }
    164
    165    /**
    166     * Decodes a Base64 string into its original form. Escaped characters are converted back to their original
    167     * representation.
    168     *
    169     * @param value
    170     *            Base64 string to convert into its original form
    171     * @return original string
    172     * @throws DecoderException
    173     *             A decoder exception is thrown if a failure condition is encountered during the decode process.
    174     */
    175    @Override
    176    public String decode(final String value) throws DecoderException {
    177        if (value == null) {
    178            return null;
    179        }
    180        try {
    181            return this.decodeText(value);
    182        } catch (final UnsupportedEncodingException e) {
    183            throw new DecoderException(e.getMessage(), e);
    184        }
    185    }
    186
    187    /**
    188     * Encodes an object into its Base64 form using the default charset. Unsafe characters are escaped.
    189     *
    190     * @param value
    191     *            object to convert to Base64 form
    192     * @return Base64 object
    193     * @throws EncoderException
    194     *             thrown if a failure condition is encountered during the encoding process.
    195     */
    196    @Override
    197    public Object encode(final Object value) throws EncoderException {
    198        if (value == null) {
    199            return null;
    200        } else if (value instanceof String) {
    201            return encode((String) value);
    202        } else {
    203            throw new EncoderException("Objects of type " +
    204                  value.getClass().getName() +
    205                  " cannot be encoded using BCodec");
    206        }
    207    }
    208
    209    /**
    210     * Decodes a Base64 object into its original form. Escaped characters are converted back to their original
    211     * representation.
    212     *
    213     * @param value
    214     *            Base64 object to convert into its original form
    215     * @return original object
    216     * @throws DecoderException
    217     *             Thrown if the argument is not a <code>String</code>. Thrown if a failure condition is encountered
    218     *             during the decode process.
    219     */
    220    @Override
    221    public Object decode(final Object value) throws DecoderException {
    222        if (value == null) {
    223            return null;
    224        } else if (value instanceof String) {
    225            return decode((String) value);
    226        } else {
    227            throw new DecoderException("Objects of type " +
    228                  value.getClass().getName() +
    229                  " cannot be decoded using BCodec");
    230        }
    231    }
    232
    233    /**
    234     * Gets the default charset name used for string decoding and encoding.
    235     *
    236     * @return the default charset name
    237     * @since 1.7
    238     */
    239    public Charset getCharset() {
    240        return this.charset;
    241    }
    242
    243    /**
    244     * Gets the default charset name used for string decoding and encoding.
    245     *
    246     * @return the default charset name
    247     */
    248    public String getDefaultCharset() {
    249        return this.charset.name();
    250    }
    251}