package net.jhorstmann.i18n.tools;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.fedorahosted.tennera.jgettext.Message;
public class MoParser {
public static MessageBundle parseMessages(File file) throws IOException {
FileInputStream in = new FileInputStream(file);
try {
MessageBundle bundle = parseMessages(in);
return bundle;
} finally {
in.close();
}
}
public static MessageBundle parseMessages(InputStream in) throws IOException {
int magic = readInt(in, false);
boolean big;
if (magic == 0x950412DE) {
big = false;
} else if (magic == 0xDE120495) {
big = true;
} else {
throw new IOException("Invalid magic number");
}
int rev = readInt(in, big);
int size = readInt(in, big);
int msgidTableOff = readInt(in, big);
int msgstrTableOff = readInt(in, big);
int hashSize = readInt(in, big);
int hashOff = readInt(in, big);
int off = 7*4;
int[] msgidLength = new int[size];
int[] msgidOffset = new int[size];
int[] msgstrLength = new int[size];
int[] msgstrOffset = new int[size];
String[] msgid = new String[size];
String[] msgstr = new String[size];
while (off < msgidTableOff) {
readNoEOF(in);
off++;
}
for (int i=0; i<size; i++) {
msgidLength[i] = readInt(in, big);
msgidOffset[i] = readInt(in, big);
off+=8;
}
while (off < msgstrTableOff) {
readNoEOF(in);
off++;
}
for (int i=0; i<size; i++) {
msgstrLength[i] = readInt(in, big);
msgstrOffset[i] = readInt(in, big);
off += 8;
}
while (off < hashOff+hashSize*4) {
readNoEOF(in);
off++;
}
while (off < msgidOffset[0]) {
readNoEOF(in);
off++;
}
for (int i=0; i<size; i++) {
if (off != msgidOffset[i]) {
throw new IOException("Offset for msgid " + i + " does not match, expected 0x" + Integer.toHexString(msgidOffset[i]) + " but was 0x" + Integer.toHexString(off));
}
msgid[i] = readString(in, msgidLength[i]);
off += msgidLength[i] + 1;
}
while (off < msgstrOffset[0]) {
readNoEOF(in);
off++;
}
for (int i=0; i<size; i++) {
if (off != msgstrOffset[i]) {
throw new IOException("Offset for msgstr " + i + " does not match, expected 0x" + Integer.toHexString(msgstrOffset[i]) + " but was 0x" + Integer.toHexString(off));
}
msgstr[i] = readString(in, msgstrLength[i]);
off += msgstrLength[i] + 1;
}
MessageBundle bundle = new MessageBundle();
for (int i=0; i<size; i++) {
Message message = new Message();
String ctx;
String id;
String plid;
String str;
int idx = msgid[i].indexOf('\u0004');
if (idx >= 0) {
ctx = msgid[i].substring(0, idx);
id = msgid[i].substring(idx+1);
} else {
ctx = null;
id = msgid[i];
}
idx = id.indexOf('\u0000');
if (idx >= 0) {
plid = id.substring(idx+1);
id = id.substring(0, idx);
} else {
plid = null;
}
message.setMsgid(id);
if (ctx != null) {
message.setMsgctxt(ctx);
}
if (plid != null) {
message.setMsgidPlural(plid);
}
str = msgstr[i];
idx = str.indexOf('\u0000');
if (idx >= 0 && plid != null) {
int lastidx = 0;
int pos = 0;
String plural;
do {
plural = str.substring(lastidx, idx);
message.addMsgstrPlural(plural, pos);
pos++;
lastidx = idx+1;
idx = str.indexOf('\u0000', lastidx);
}
while (idx >= 0);
plural = str.substring(lastidx);
message.addMsgstrPlural(plural, pos);
} else {
message.setMsgstr(str);
}
bundle.addMessage(message);
}
return bundle;
}
private static int readNoEOF(InputStream in) throws IOException {
int i = in.read();
if (i < 0) {
throw new IOException("EOF");
}
return i;
}
private static int readInt(InputStream in, boolean bigEndian) throws IOException {
int b0 = readNoEOF(in);
int b1 = readNoEOF(in);
int b2 = readNoEOF(in);
int b3 = readNoEOF(in);
if (bigEndian) {
return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
} else {
return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
}
}
private static String readString(InputStream in, int len) throws IOException {
byte[] buf = new byte[len];
for (int i=0; i<len; i++) {
buf[i] = (byte) (readNoEOF(in) & 0xFF);
}
int end = readNoEOF(in);
if (end != 0) {
throw new IOException("Expected NUL terminator for string but got 0x" + Integer.toHexString(end));
}
return new String(buf, 0, len, "utf-8");
}
}