/*
* Copyright (C) 2007,2014 Steve Ratcliffe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* Author: Steve Ratcliffe
* Create date: 31-Oct-2007
*/
package uk.me.parabola.imgfmt.app.labelenc;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.util.Arrays;
import java.util.Locale;
/**
* Convert text to a specified charset. This is used when you give a
* charset name on the command line to convert to.
*
* @author Steve Ratcliffe
*/
public class AnyCharsetEncoder extends BaseEncoder implements CharacterEncoder {
private final CharsetEncoder encoder;
private final Transliterator transliterator;
public AnyCharsetEncoder(String cs, Transliterator transliterator) {
this.transliterator = transliterator;
prepareForCharacterSet(cs);
if (isCharsetSupported()) {
encoder = Charset.forName(cs).newEncoder();
encoder.onUnmappableCharacter(CodingErrorAction.REPORT);
} else {
encoder = null;
}
}
public EncodedText encodeText(String text) {
if (text == null || text.isEmpty())
return NO_TEXT;
if (!isCharsetSupported())
return simpleEncode(text);
String ucText;
if (isUpperCase())
ucText = text.toUpperCase(Locale.ENGLISH);
else
ucText = text;
// Allocate a buffer for the encoded text. This will be large enough in almost all cases,
// but the code below allocates more space if necessary.
ByteBuffer outBuf = ByteBuffer.allocate(ucText.length() + 20);
CharBuffer charBuffer = CharBuffer.wrap(ucText);
CoderResult result;
do {
result = encoder.encode(charBuffer, outBuf, true);
if (result.isUnmappable()) {
// There is a character that cannot be represented in the target code page.
// Read the character(s), transliterate them, and add them to the output.
// We then continue onward with the rest of the string.
String s;
if (result.length() == 1) {
s = String.valueOf(charBuffer.get());
} else {
// Don't know under what circumstances this will be called and may not be the
// correct thing to do when it does happen.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < result.length(); i++)
sb.append(charBuffer.get());
s = sb.toString();
}
s = transliterator.transliterate(s);
// Make sure that there is enough space for the transliterated string
while (outBuf.limit() < outBuf.position() + s.length())
outBuf = reallocBuf(outBuf);
for (int i = 0; i < s.length(); i++)
outBuf.put((byte) s.charAt(i));
} else if (result == CoderResult.OVERFLOW) {
// Ran out of space in the output
outBuf = reallocBuf(outBuf);
}
} while (result != CoderResult.UNDERFLOW);
// We need it to be null terminated but also to trim any extra memory from the allocated
// buffer.
byte[] res = Arrays.copyOf(outBuf.array(), outBuf.position() + 1);
char[] cres = new char[res.length];
for (int i = 0; i < res.length; i++)
cres[i] = (char) (res[i] & 0xff);
return new EncodedText(res, res.length, cres);
}
/**
* Allocate a new byte buffer that has more space.
*
* It will have the same contents as the existing one and the same position, so you can
* continue writing to it.
*
* @param bb The original byte buffer.
* @return A new byte buffer with the same contents with more space that you can continue
* writing to.
*/
private ByteBuffer reallocBuf(ByteBuffer bb) {
byte[] newbuf = Arrays.copyOf(bb.array(), bb.capacity() * 2);
return ByteBuffer.wrap(newbuf, bb.position(), newbuf.length - bb.position());
}
public void setUpperCase(boolean upperCase) {
super.setUpperCase(upperCase);
transliterator.forceUppercase(upperCase);
}
}