/*******************************************************************************
* Copyright (c) 2005, 2009 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:
* John Camelon (IBM Rational Software) - Initial API and implementation
* Cheong, Jeong-Sik - fix for 162381
* Valeri Atamaniouk - fix for 170398
* Markus Schorn (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.core.parser;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
/**
* Reads the content of a file into a char[] buffer.
*
* @noextend This class is not intended to be subclassed by clients.
* @deprecated replaced by {@link FileContent}
*/
@Deprecated
public class CodeReader {
public static final String SYSTEM_DEFAULT_ENCODING = System.getProperty("file.encoding"); //$NON-NLS-1$
private static final int MB = 1024*1024;
private static final String NF = "<text>"; //$NON-NLS-1$
private static final char[] NOFILE = NF.toCharArray();
private static final int MAX_FILE_SIZE;
static {
MAX_FILE_SIZE = (int) Math.min(Integer.MAX_VALUE, (Runtime.getRuntime().maxMemory()) / 4);
}
public final char[] buffer;
public final char[] filename;
// If you already have the buffer, e.g. working copy
public CodeReader(String filename, char[] buffer) {
this.filename = filename.toCharArray();
this.buffer = buffer;
}
// If you are just scanning a string
public CodeReader(char[] buffer) {
this(NF, buffer);
}
// If you are loading up a file normally
public CodeReader(String filename) throws IOException {
this.filename = filename.toCharArray();
FileInputStream stream = new FileInputStream(filename);
try {
buffer = load(SYSTEM_DEFAULT_ENCODING, stream);
} finally {
stream.close();
}
}
public CodeReader(String filename, String charSet) throws IOException {
this.filename = filename.toCharArray();
FileInputStream stream = new FileInputStream(filename);
try {
buffer = load(charSet, stream);
} finally {
stream.close();
}
}
public CodeReader(String filename, InputStream stream) throws IOException {
this(filename, SYSTEM_DEFAULT_ENCODING, stream);
}
public CodeReader(String fileName, String charSet, InputStream stream) throws IOException {
filename = fileName.toCharArray();
FileInputStream fstream = (stream instanceof FileInputStream) ? (FileInputStream) stream
: new FileInputStream(fileName);
try {
buffer = load(charSet, fstream);
} finally {
// If we create the FileInputStream we need close to it when done,
// if not we figure the above layer will do it.
if (!(stream instanceof FileInputStream)) {
fstream.close();
}
}
}
/**
* Load the stream content as a character array. The method loads the stream content using given
* character set name. In case if the character set is not supported, the default one is used.
*/
private char[] load(String charSet, FileInputStream stream) throws IOException {
String encoding = Charset.isSupported(charSet) ? charSet : SYSTEM_DEFAULT_ENCODING;
FileChannel channel = stream.getChannel();
final long lsize = channel.size();
final int isize = (int) lsize;
if (lsize > MAX_FILE_SIZE) {
throw new IOException(
"File '" + getPath() + "' is larger than " + MAX_FILE_SIZE / 1024 / 1024 + "mb"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
}
CharBuffer charBuffer;
if (isize < MB) {
charBuffer= decodeSmallFile(channel, isize, encoding);
} else {
charBuffer= decodeLargeFile(channel, isize, encoding);
}
if (charBuffer.hasArray() && charBuffer.arrayOffset() == 0) {
char[] buff = charBuffer.array();
if (buff.length == charBuffer.remaining())
return buff;
}
char[] buff = new char[charBuffer.remaining()];
charBuffer.get(buff);
return buff;
}
private CharBuffer decodeSmallFile(FileChannel channel, final int isize, String encoding) throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocate(isize);
channel.read(byteBuffer);
byteBuffer.flip();
return Charset.forName(encoding).decode(byteBuffer);
}
private CharBuffer decodeLargeFile(FileChannel channel, final int isize, String encoding) throws IOException {
int chunk = Math.min(isize, MB);
final ByteBuffer in = ByteBuffer.allocate(chunk);
final Charset charset = Charset.forName(encoding);
final CharsetDecoder decoder = charset.newDecoder().onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
int n = (int) (isize * (double) decoder.averageCharsPerByte()); // avoid rounding errors.
CharBuffer out = CharBuffer.allocate(n);
int offset = 0;
while (offset < isize) {
channel.read(in);
in.flip();
offset += in.limit();
CoderResult cr = decoder.decode(in, out, offset >= isize);
final int remainingBytes = in.remaining();
if (cr.isOverflow()) {
int totalRemainingBytes= isize-offset + remainingBytes;
if (totalRemainingBytes > 0) {
n+= (int) (totalRemainingBytes * (double) decoder.maxCharsPerByte()); // avoid rounding errors.
CharBuffer o = CharBuffer.allocate(n);
out.flip();
o.put(out);
out = o;
}
} else if (!cr.isUnderflow()) {
cr.throwException();
}
if (remainingBytes == 0) {
in.clear();
} else {
byte[] rest = new byte[remainingBytes];
in.get(rest);
in.clear();
in.put(rest);
offset -= remainingBytes;
}
}
out.flip();
return out;
}
public boolean isFile() {
return !CharArrayUtils.equals(filename, NOFILE);
}
@Override
public String toString() {
return getPath();
}
public String getPath() {
return new String(filename);
}
}