blob: 2559ceecf51f06adc42de9b1cf52b1e9be968e07 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.apt.util;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import javax.tools.FileObject;
/**
* Util class that defines helper methods to read class contents with handling of wrong encoding
*
*/
public final class Util {
public static String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
public static class EncodingError {
int position;
int length;
public EncodingError(int position, int length) {
this.position = position;
this.length = length;
}
public String getSource(char[] unitSource) {
//extra from the source the innacurate token
//and "highlight" it using some underneath ^^^^^
//put some context around too.
//this code assumes that the font used in the console is fixed size
//sanity .....
int startPosition = this.position;
int endPosition = this.position + this.length - 1;
if ((startPosition > endPosition)
|| ((startPosition < 0) && (endPosition < 0))
|| unitSource.length == 0)
return "No source available"; //$NON-NLS-1$
StringBuffer errorBuffer = new StringBuffer();
errorBuffer.append('\t');
char c;
final char SPACE = ' ';
final char MARK = '^';
final char TAB = '\t';
//the next code tries to underline the token.....
//it assumes (for a good display) that token source does not
//contain any \r \n. This is false on statements !
//(the code still works but the display is not optimal !)
// expand to line limits
int length = unitSource.length, begin, end;
for (begin = startPosition >= length ? length - 1 : startPosition; begin > 0; begin--) {
if ((c = unitSource[begin - 1]) == '\n' || c == '\r') break;
}
for (end = endPosition >= length ? length - 1 : endPosition ; end+1 < length; end++) {
if ((c = unitSource[end + 1]) == '\r' || c == '\n') break;
}
// trim left and right spaces/tabs
while ((c = unitSource[begin]) == ' ' || c == '\t') begin++;
//while ((c = unitSource[end]) == ' ' || c == '\t') end--; TODO (philippe) should also trim right, but all tests are to be updated
// copy source
errorBuffer.append(unitSource, begin, end-begin+1);
errorBuffer.append(Util.LINE_SEPARATOR).append("\t"); //$NON-NLS-1$
// compute underline
for (int i = begin; i <startPosition; i++) {
errorBuffer.append((unitSource[i] == TAB) ? TAB : SPACE);
}
for (int i = startPosition; i <= (endPosition >= length ? length - 1 : endPosition); i++) {
errorBuffer.append(MARK);
}
return errorBuffer.toString();
}
}
public static class EncodingErrorCollector {
ArrayList<EncodingError> encodingErrors = new ArrayList<EncodingError>();
FileObject fileObject;
String encoding;
public EncodingErrorCollector(FileObject fileObject, String encoding) {
this.fileObject = fileObject;
this.encoding = encoding;
}
public void collect(int position, int length) {
this.encodingErrors.add(new EncodingError(position, length));
}
public void reportAllEncodingErrors(String string) {
// this is where the encoding errors should be reported
char[] unitSource = string.toCharArray();
for (EncodingError error : this.encodingErrors) {
System.err.println(this.fileObject.getName() + " Unmappable character for encoding " + this.encoding);//$NON-NLS-1$
System.err.println(error.getSource(unitSource));
}
}
}
public static char[] getInputStreamAsCharArray(InputStream stream, int length, String encoding) throws IOException {
Charset charset = null;
try {
charset = Charset.forName(encoding);
} catch (IllegalCharsetNameException e) {
System.err.println("Illegal charset name : " + encoding); //$NON-NLS-1$
return null;
} catch(UnsupportedCharsetException e) {
System.err.println("Unsupported charset : " + encoding); //$NON-NLS-1$
return null;
}
CharsetDecoder charsetDecoder = charset.newDecoder();
charsetDecoder.onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
byte[] contents = org.eclipse.jdt.internal.compiler.util.Util.getInputStreamAsByteArray(stream, length);
ByteBuffer byteBuffer = ByteBuffer.allocate(contents.length);
byteBuffer.put(contents);
byteBuffer.flip();
return charsetDecoder.decode(byteBuffer).array();
}
public static CharSequence getCharContents(FileObject fileObject, boolean ignoreEncodingErrors, byte[] contents, String encoding) throws IOException {
if (contents == null) return null;
Charset charset = null;
try {
charset = Charset.forName(encoding);
} catch (IllegalCharsetNameException e) {
System.err.println("Illegal charset name : " + encoding); //$NON-NLS-1$
return null;
} catch(UnsupportedCharsetException e) {
System.err.println("Unsupported charset : " + encoding); //$NON-NLS-1$
return null;
}
CharsetDecoder charsetDecoder = charset.newDecoder();
ByteBuffer byteBuffer = ByteBuffer.allocate(contents.length);
byteBuffer.put(contents);
byteBuffer.flip();
if (ignoreEncodingErrors) {
charsetDecoder.onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
return charsetDecoder.decode(byteBuffer);
} else {
charsetDecoder.onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT);
CharBuffer out = CharBuffer.allocate(contents.length);
CoderResult result = null;
String replacement = charsetDecoder.replacement();
int replacementLength = replacement.length();
EncodingErrorCollector collector = null;
while (true) {
result = charsetDecoder.decode(byteBuffer, out, true);
if (result.isMalformed() || result.isUnmappable()) {
/* treat the error
* The wrong input character is at out.position
*/
if (collector == null) {
collector = new EncodingErrorCollector(fileObject, encoding);
}
reportEncodingError(collector, out.position(), result.length());
if ((out.position() + replacementLength) >= out.capacity()) {
// resize
CharBuffer temp = CharBuffer.allocate(out.capacity() * 2);
out.flip();
temp.put(out);
out = temp;
}
out.append(replacement);
byteBuffer.position(byteBuffer.position() + result.length());
continue;
}
if (result.isOverflow()) {
CharBuffer temp = CharBuffer.allocate(out.capacity() * 2);
out.flip();
temp.put(out);
out = temp;
} else {
break;
}
}
out.flip();
if (collector != null) {
collector.reportAllEncodingErrors(out.toString());
}
return out;
}
}
private static void reportEncodingError(EncodingErrorCollector collector, int position, int length) {
collector.collect(position, -length);
}
}