blob: c856f259628c3eb6410f9c7a40e9ad4deab46cd1 [file] [log] [blame]
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* jflex 1.4 *
* Copyright (C) 1998-2009 Gerwin Klein <lsf@jflex.de> *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License. See the file *
* COPYRIGHT for more information. *
* *
* 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. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package JFlex;
/**
* Encodes <code>int</code> arrays as strings.
*
* Also splits up strings when longer than 64K in UTF8 encoding.
* Subclasses emit unpacking code.
*
* Usage protocol:
* <code>p.emitInit();</code><br>
* <code>for each data: p.emitData(data);</code><br>
* <code>p.emitUnpack();</code>
*
* @author Gerwin Klein
* @version $Revision$, $Date$
*/
public abstract class PackEmitter {
/** name of the generated array (mixed case, no yy prefix) */
protected String name;
/** current UTF8 length of generated string in current chunk */
private int UTF8Length;
/** position in the current line */
private int linepos;
/** max number of entries per line */
private static final int maxEntries = 16;
/** output buffer */
protected StringBuffer out = new StringBuffer();
/** number of existing string chunks */
protected int chunks;
/** maximum size of chunks */
// String constants are stored as UTF8 with 2 bytes length
// field in class files. One Unicode char can be up to 3
// UTF8 bytes. 64K max and two chars safety.
private static final int maxSize = 0xFFFF-6;
/** indent for string lines */
private static final String indent = " ";
/**
* Create new emitter for an array.
*
* @param name the name of the generated array
*/
public PackEmitter(String name) {
this.name = name;
}
/**
* Convert array name into all uppercase internal scanner
* constant name.
*
* @return <code>name</code> as a internal constant name.
* @see PackEmitter#name
*/
protected String constName() {
return "ZZ_" + name.toUpperCase();
}
/**
* Return current output buffer.
*/
public String toString() {
return out.toString();
}
/**
* Emit declaration of decoded member and open first chunk.
*/
public void emitInit() {
out.append(" private static final int [] ");
out.append(constName());
out.append(" = zzUnpack");
out.append(name);
out.append("();");
nl();
nextChunk();
}
/**
* Emit single unicode character.
*
* Updates length, position, etc.
*
* @param i the character to emit.
* @prec 0 <= i <= 0xFFFF
*/
public void emitUC(int i) {
if (i < 0 || i > 0xFFFF)
throw new IllegalArgumentException("character value expected");
// cast ok because of prec
char c = (char) i;
printUC(c);
UTF8Length += UTF8Length(c);
linepos++;
}
/**
* Execute line/chunk break if necessary.
* Leave space for at least two chars.
*/
public void breaks() {
if (UTF8Length >= maxSize) {
// close current chunk
out.append("\";");
nl();
nextChunk();
}
else {
if (linepos >= maxEntries) {
// line break
out.append("\"+");
nl();
out.append(indent);
out.append("\"");
linepos = 0;
}
}
}
/**
* Emit the unpacking code.
*/
public abstract void emitUnpack();
/**
* emit next chunk
*/
private void nextChunk() {
nl();
out.append(" private static final String ");
out.append(constName());
out.append("_PACKED_");
out.append(chunks);
out.append(" =");
nl();
out.append(indent);
out.append("\"");
UTF8Length = 0;
linepos = 0;
chunks++;
}
/**
* emit newline
*/
protected void nl() {
out.append(Out.NL);
}
/**
* Append a unicode/octal escaped character
* to <code>out</code> buffer.
*
* @param c the character to append
*/
private void printUC(char c) {
if (c > 255) {
out.append("\\u");
if (c < 0x1000) out.append("0");
out.append(Integer.toHexString(c));
}
else {
out.append("\\");
out.append(Integer.toOctalString(c));
}
}
/**
* Calculates the number of bytes a Unicode character
* would have in UTF8 representation in a class file.
*
* @param value the char code of the Unicode character
* @prec 0 <= value <= 0xFFFF
*
* @return length of UTF8 representation.
*/
private int UTF8Length(char value) {
// if (value < 0 || value > 0xFFFF) throw new Error("not a char value ("+value+")");
// see JVM spec section 4.4.7, p 111
if (value == 0) return 2;
if (value <= 0x7F) return 1;
// workaround for javac bug (up to jdk 1.3):
if (value < 0x0400) return 2;
if (value <= 0x07FF) return 3;
// correct would be:
// if (value <= 0x7FF) return 2;
return 3;
}
// convenience
protected void println(String s) {
out.append(s);
nl();
}
}