/* * Copyright 2000-2009 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.lang.properties.charset; /** * @author Alexey */ import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CoderResult; class Native2AsciiCharsetDecoder extends CharsetDecoder { private static final char INVALID_CHAR = (char)-1; private final StringBuilder myOutBuffer = new StringBuilder(); private final Charset myBaseCharset; public Native2AsciiCharsetDecoder(final Native2AsciiCharset charset) { super(charset, 1, 6); myBaseCharset = charset.getBaseCharset(); } @Override protected void implReset() { super.implReset(); myOutBuffer.setLength(0); } @Override protected CoderResult implFlush(CharBuffer out) { return doFlush(out); } private CoderResult doFlush(final CharBuffer out) { if (myOutBuffer.length() != 0) { int remaining = out.remaining(); int outlen = Math.min(remaining, myOutBuffer.length()); out.put(myOutBuffer.toString().substring(0,outlen).toCharArray()); myOutBuffer.delete(0, outlen); if (myOutBuffer.length() != 0) return CoderResult.OVERFLOW; } return CoderResult.UNDERFLOW; } @Override protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { try { CoderResult coderResult = doFlush(out); if (coderResult == CoderResult.OVERFLOW) return CoderResult.OVERFLOW; int start = in.position(); byte[] buf = new byte[4]; while (in.position() < in.limit()) { in.mark(); final byte b = in.get(); if (b == '\\') { decodeArray(in.array(), start, in.position()-1); byte next = in.get(); if (next == 'u') { buf[0] = in.get(); buf[1] = in.get(); buf[2] = in.get(); buf[3] = in.get(); char decoded = unicode(buf); if (decoded == INVALID_CHAR) { myOutBuffer.append("\\u"); myOutBuffer.append((char)buf[0]); myOutBuffer.append((char)buf[1]); myOutBuffer.append((char)buf[2]); myOutBuffer.append((char)buf[3]); } else { myOutBuffer.append(decoded); } } else { myOutBuffer.append("\\"); myOutBuffer.append((char)next); } start = in.position(); } } decodeArray(in.array(), start, in.position()); } catch (BufferUnderflowException e) { in.reset(); } return doFlush(out); } private void decodeArray(final byte[] buf, int start, int end) { if (end <= start) return; ByteBuffer byteBuffer = ByteBuffer.wrap(buf, start, end-start); CharBuffer charBuffer = myBaseCharset.decode(byteBuffer); myOutBuffer.append(charBuffer.toString()); } private static char unicode(byte[] ord) { int d1 = Character.digit((char)ord[0], 16); if (d1 == -1) return INVALID_CHAR; int d2 = Character.digit((char)ord[1], 16); if (d2 == -1) return INVALID_CHAR; int d3 = Character.digit((char)ord[2], 16); if (d3 == -1) return INVALID_CHAR; int d4 = Character.digit((char)ord[3], 16); if (d4 == -1) return INVALID_CHAR; int b1 = (d1 << 12) & 0xF000; int b2 = (d2 << 8) & 0x0F00; int b3 = (d3 << 4) & 0x00F0; int b4 = (d4 << 0) & 0x000F; int code = b1 | b2 | b3 | b4; if (Character.isWhitespace((char)code)) return INVALID_CHAR; return (char)code; } }