tikhomirov@320: /* tikhomirov@412: * Copyright (c) 2011-2012 TMate Software Ltd tikhomirov@320: * tikhomirov@320: * This program is free software; you can redistribute it and/or modify tikhomirov@320: * it under the terms of the GNU General Public License as published by tikhomirov@320: * the Free Software Foundation; version 2 of the License. tikhomirov@320: * tikhomirov@320: * This program is distributed in the hope that it will be useful, tikhomirov@320: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@320: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@320: * GNU General Public License for more details. tikhomirov@320: * tikhomirov@320: * For information on how to redistribute this software under tikhomirov@320: * the terms of a license other than GNU General Public License tikhomirov@320: * contact TMate Software at support@hg4j.com tikhomirov@320: */ tikhomirov@320: package org.tmatesoft.hg.internal; tikhomirov@320: tikhomirov@412: import java.nio.ByteBuffer; tikhomirov@415: import java.nio.CharBuffer; tikhomirov@412: import java.nio.charset.CharacterCodingException; tikhomirov@412: import java.nio.charset.Charset; tikhomirov@412: import java.nio.charset.CharsetDecoder; tikhomirov@412: import java.nio.charset.CharsetEncoder; tikhomirov@320: tikhomirov@415: import org.tmatesoft.hg.core.SessionContext; tikhomirov@415: tikhomirov@320: /** tikhomirov@320: * Keep all encoding-related issues in the single place tikhomirov@415: * NOT thread-safe (encoder and decoder requires synchronized access) tikhomirov@320: * @author Artem Tikhomirov tikhomirov@320: * @author TMate Software Ltd. tikhomirov@320: */ tikhomirov@320: public class EncodingHelper { tikhomirov@320: // XXX perhaps, shall not be full of statics, but rather an instance coming from e.g. HgRepository? tikhomirov@412: /* tikhomirov@412: * To understand what Mercurial thinks of UTF-8 and Unix byte approach to names, see tikhomirov@412: * http://mercurial.808500.n3.nabble.com/Unicode-support-request-td3430704.html tikhomirov@412: */ tikhomirov@412: tikhomirov@415: private final SessionContext sessionContext; tikhomirov@412: private final CharsetEncoder encoder; tikhomirov@412: private final CharsetDecoder decoder; tikhomirov@412: tikhomirov@415: EncodingHelper(Charset fsEncoding, SessionContext ctx) { tikhomirov@415: sessionContext = ctx; tikhomirov@412: decoder = fsEncoding.newDecoder(); tikhomirov@412: encoder = fsEncoding.newEncoder(); tikhomirov@412: } tikhomirov@320: tikhomirov@418: /** tikhomirov@418: * Translate file names from manifest to amazing Unicode string tikhomirov@418: */ tikhomirov@412: public String fromManifest(byte[] data, int start, int length) { tikhomirov@418: return decodeWithSystemDefaultFallback(data, start, length); tikhomirov@320: } tikhomirov@418: tikhomirov@415: /** tikhomirov@415: * @return byte representation of the string directly comparable to bytes in manifest tikhomirov@415: */ tikhomirov@415: public byte[] toManifest(String s) { tikhomirov@415: if (s == null) { tikhomirov@415: // perhaps, can return byte[0] in this case? tikhomirov@415: throw new IllegalArgumentException(); tikhomirov@415: } tikhomirov@415: try { tikhomirov@415: // synchonized(encoder) { tikhomirov@415: ByteBuffer bb = encoder.encode(CharBuffer.wrap(s)); tikhomirov@415: // } tikhomirov@415: byte[] rv = new byte[bb.remaining()]; tikhomirov@415: bb.get(rv, 0, rv.length); tikhomirov@415: return rv; tikhomirov@415: } catch (CharacterCodingException ex) { tikhomirov@415: sessionContext.getLog().error(getClass(), ex, String.format("Use of charset %s failed, resort to system default", charset().name())); tikhomirov@415: // resort to system-default tikhomirov@415: return s.getBytes(); tikhomirov@415: } tikhomirov@415: } tikhomirov@415: tikhomirov@418: /** tikhomirov@418: * Translate file names from dirstate to amazing Unicode string tikhomirov@418: */ tikhomirov@418: public String fromDirstate(byte[] data, int start, int length) { tikhomirov@418: return decodeWithSystemDefaultFallback(data, start, length); tikhomirov@412: } tikhomirov@412: tikhomirov@418: private String decodeWithSystemDefaultFallback(byte[] data, int start, int length) { tikhomirov@418: try { tikhomirov@418: return decoder.decode(ByteBuffer.wrap(data, start, length)).toString(); tikhomirov@418: } catch (CharacterCodingException ex) { tikhomirov@418: sessionContext.getLog().error(getClass(), ex, String.format("Use of charset %s failed, resort to system default", charset().name())); tikhomirov@418: // resort to system-default tikhomirov@418: return new String(data, start, length); tikhomirov@418: } tikhomirov@418: } tikhomirov@418: tikhomirov@418: private Charset charset() { tikhomirov@412: return encoder.charset(); tikhomirov@412: } tikhomirov@415: tikhomirov@320: }