/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core.internal.util; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.util.SVNLogType; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; /** * @author TMate Software Ltd. * @version 1.3 */ public class SVNCharsetConvertor { private static final int DEFAULT_BUFFER_CAPACITY = 1024; private CharsetDecoder myDecoder; private CharsetEncoder myEncoder; private CharBuffer myCharBuffer; private ByteBuffer myInputByteBuffer; public SVNCharsetConvertor(CharsetDecoder decoder, CharsetEncoder encoder) { myDecoder = decoder; myEncoder = encoder; reset(); } public SVNCharsetConvertor reset() { myEncoder = myEncoder.reset(); myDecoder = myDecoder.reset(); myCharBuffer = null; myInputByteBuffer = null; return this; } public ByteBuffer convertChunk(byte[] b, int offset, int length, ByteBuffer dst, boolean endOfInput) throws SVNException { myInputByteBuffer = allocate(myInputByteBuffer, length); myInputByteBuffer.put(b, offset, length); myInputByteBuffer.flip(); myCharBuffer = allocate(myCharBuffer, (int) (myDecoder.maxCharsPerByte() * myInputByteBuffer.remaining())); CoderResult result = myDecoder.decode(myInputByteBuffer, myCharBuffer, endOfInput); if (result.isError()) { throwException(result); } else if (result.isUnderflow()) { myInputByteBuffer.compact(); } else { myInputByteBuffer.clear(); } myCharBuffer.flip(); dst = allocate(dst, (int) (myEncoder.maxBytesPerChar() * myCharBuffer.remaining())); result = myEncoder.encode(myCharBuffer, dst, false); if (result.isError()) { throwException(result); } else if (result.isUnderflow()) { myCharBuffer.compact(); } else { myCharBuffer.clear(); } return dst; } public ByteBuffer flush(ByteBuffer dst) throws SVNException { if (myCharBuffer != null) { CoderResult result; while (true) { result = myDecoder.flush(myCharBuffer); if (result.isError()) { throwException(result); } if (result.isUnderflow()) { break; } } myCharBuffer.flip(); dst = allocate(dst, (int) (myEncoder.maxBytesPerChar() * myCharBuffer.remaining())); result = myEncoder.encode(myCharBuffer, dst, true); if (result.isError()) { throwException(result); } while (true) { result = myEncoder.flush(dst); if (result.isError()) { throwException(result); } if (result.isUnderflow()) { break; } } } reset(); return dst; } private static ByteBuffer allocate(ByteBuffer buffer, int length) { if (buffer == null) { length = Math.max(length * 3 / 2, DEFAULT_BUFFER_CAPACITY); return ByteBuffer.allocate(length); } if (buffer.remaining() < length) { ByteBuffer expandedBuffer = ByteBuffer.allocate((buffer.position() + length) * 3 / 2); buffer.flip(); expandedBuffer.put(buffer); return expandedBuffer; } return buffer; } private static CharBuffer allocate(CharBuffer buffer, int length) { if (buffer == null) { length = Math.max(length * 3 / 2, DEFAULT_BUFFER_CAPACITY); return CharBuffer.allocate(length); } if (buffer.remaining() < length) { CharBuffer expandedBuffer = CharBuffer.allocate((buffer.position() + length) * 3 / 2); buffer.flip(); expandedBuffer.put(buffer); return expandedBuffer; } return buffer; } private static void throwException(CoderResult result) throws SVNException { try { result.throwException(); } catch (CharacterCodingException e) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e), e, SVNLogType.DEFAULT); } } public String toString() { final StringBuffer buffer = new StringBuffer(); buffer.append("SVNCharsetConvertor"); buffer.append("[from=").append(myDecoder.charset().displayName()); buffer.append(", to=").append(myEncoder.charset().displayName()); buffer.append(']'); return buffer.toString(); } }