001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.filter.codec.textline;
021
022import java.nio.charset.Charset;
023import java.nio.charset.CharsetEncoder;
024
025import org.apache.mina.core.buffer.IoBuffer;
026import org.apache.mina.core.session.AttributeKey;
027import org.apache.mina.core.session.IoSession;
028import org.apache.mina.filter.codec.ProtocolEncoder;
029import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
030import org.apache.mina.filter.codec.ProtocolEncoderOutput;
031
032/**
033 * A {@link ProtocolEncoder} which encodes a string into a text line
034 * which ends with the delimiter.
035 *
036 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
037 */
038public class TextLineEncoder extends ProtocolEncoderAdapter {
039    private static final AttributeKey ENCODER = new AttributeKey(TextLineEncoder.class, "encoder");
040
041    private final Charset charset;
042
043    private final LineDelimiter delimiter;
044
045    private int maxLineLength = Integer.MAX_VALUE;
046
047    /**
048     * Creates a new instance with the current default {@link Charset}
049     * and {@link LineDelimiter#UNIX} delimiter.
050     */
051    public TextLineEncoder() {
052        this(Charset.defaultCharset(), LineDelimiter.UNIX);
053    }
054
055    /**
056     * Creates a new instance with the current default {@link Charset}
057     * and the specified <tt>delimiter</tt>.
058     * 
059     * @param delimiter The line delimiter to use
060     */
061    public TextLineEncoder(String delimiter) {
062        this(new LineDelimiter(delimiter));
063    }
064
065    /**
066     * Creates a new instance with the current default {@link Charset}
067     * and the specified <tt>delimiter</tt>.
068     * 
069     * @param delimiter The line delimiter to use
070     */
071    public TextLineEncoder(LineDelimiter delimiter) {
072        this(Charset.defaultCharset(), delimiter);
073    }
074
075    /**
076     * Creates a new instance with the specified <tt>charset</tt>
077     * and {@link LineDelimiter#UNIX} delimiter.
078     * 
079     * @param charset The {@link Charset} to use
080     */
081    public TextLineEncoder(Charset charset) {
082        this(charset, LineDelimiter.UNIX);
083    }
084
085    /**
086     * Creates a new instance with the specified <tt>charset</tt>
087     * and the specified <tt>delimiter</tt>.
088     * 
089     * @param charset The {@link Charset} to use
090     * @param delimiter The line delimiter to use
091     */
092    public TextLineEncoder(Charset charset, String delimiter) {
093        this(charset, new LineDelimiter(delimiter));
094    }
095
096    /**
097     * Creates a new instance with the specified <tt>charset</tt>
098     * and the specified <tt>delimiter</tt>.
099     * 
100     * @param charset The {@link Charset} to use
101     * @param delimiter The line delimiter to use
102     */
103    public TextLineEncoder(Charset charset, LineDelimiter delimiter) {
104        if (charset == null) {
105            throw new IllegalArgumentException("charset");
106        }
107        if (delimiter == null) {
108            throw new IllegalArgumentException("delimiter");
109        }
110        if (LineDelimiter.AUTO.equals(delimiter)) {
111            throw new IllegalArgumentException("AUTO delimiter is not allowed for encoder.");
112        }
113
114        this.charset = charset;
115        this.delimiter = delimiter;
116    }
117
118    /**
119     * @return the allowed maximum size of the encoded line.
120     * If the size of the encoded line exceeds this value, the encoder
121     * will throw a {@link IllegalArgumentException}.  The default value
122     * is {@link Integer#MAX_VALUE}.
123     */
124    public int getMaxLineLength() {
125        return maxLineLength;
126    }
127
128    /**
129     * Sets the allowed maximum size of the encoded line.
130     * If the size of the encoded line exceeds this value, the encoder
131     * will throw a {@link IllegalArgumentException}.  The default value
132     * is {@link Integer#MAX_VALUE}.
133     * 
134     * @param maxLineLength The maximum line length
135     */
136    public void setMaxLineLength(int maxLineLength) {
137        if (maxLineLength <= 0) {
138            throw new IllegalArgumentException("maxLineLength: " + maxLineLength);
139        }
140
141        this.maxLineLength = maxLineLength;
142    }
143
144    /**
145     * {@inheritDoc}
146     */
147    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
148        CharsetEncoder encoder = (CharsetEncoder) session.getAttribute(ENCODER);
149
150        if (encoder == null) {
151            encoder = charset.newEncoder();
152            session.setAttribute(ENCODER, encoder);
153        }
154
155        String value = (message == null ? "" : message.toString());
156        IoBuffer buf = IoBuffer.allocate(value.length()).setAutoExpand(true);
157        buf.putString(value, encoder);
158
159        if (buf.position() > maxLineLength) {
160            throw new IllegalArgumentException("Line length: " + buf.position());
161        }
162
163        buf.putString(delimiter.getValue(), encoder);
164        buf.flip();
165        out.write(buf);
166    }
167
168    /**
169     * {@inheritDoc}
170     */
171    public void dispose() throws Exception {
172        // Do nothing
173    }
174}