/**************************************************************************
OmegaT - Computer Assisted Translation (CAT) tool
with fuzzy matching, translation memory, keyword search,
glossaries, and translation leveraging into updated projects.
Copyright (C) 2010 Alex Buloichik
Home page: http://www.omegat.org/
Support center: http://groups.yahoo.com/group/OmegaT/
This file is part of OmegaT.
OmegaT is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OmegaT 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, see <http://www.gnu.org/licenses/>.
**************************************************************************/
package org.omegat.filters2.rc;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.omegat.filters2.AbstractFilter;
import org.omegat.filters2.FilterContext;
import org.omegat.filters2.Instance;
import org.omegat.filters2.TranslationException;
import org.omegat.util.NullBufferedWriter;
import org.omegat.util.OStrings;
import org.omegat.util.StringUtil;
/**
* Filter for support Windows resource files.
*
* Format described on
* http://msdn.microsoft.com/en-us/library/aa380599(VS.85).aspx
*
* @author Alex Buloichik (alex73mail@gmail.com)
*/
public class RcFilter extends AbstractFilter {
protected static final Pattern RE_DIALOG = Pattern.compile("(\\S+)\\s+DIALOG(EX)?\\s+.+");
protected static final Pattern RE_DIALOG_CAPTION = Pattern.compile("CAPTION\\s+.+");
protected static final Pattern RE_MENU = Pattern.compile("(\\S+)\\s+MENU(EX)?\\s*.*");
protected static final Pattern RE_MESSAGETABLE = Pattern.compile("(\\S+)\\s+MESSAGETABLE\\s*.*");
protected static final Pattern RE_STRINGTABLE = Pattern.compile("STRINGTABLE\\s*.*");
enum PART {
DIALOG, MENU, MESSAGETABLE, STRINGTABLE, OTHER, UNKNOWN
};
protected String blockId;
protected int b, e;
protected Map<String, String> align;
public String getFileFormatName() {
return OStrings.getString("RCFILTER_FILTER_NAME");
}
public Instance[] getDefaultInstances() {
return new Instance[] { new Instance("*.rc") };
}
public boolean isSourceEncodingVariable() {
return true;
}
public boolean isTargetEncodingVariable() {
return true;
}
@Override
protected void processFile(BufferedReader inFile, BufferedWriter outFile, FilterContext fc) throws IOException,
TranslationException {
PART cPart = PART.UNKNOWN;
int cLevel = 0;
blockId = null;
String s;
while ((s = inFile.readLine()) != null) {
b = -1;
e = -1;
String id = null;
String strim = s.trim();
if (strim.startsWith("//") || strim.startsWith("#")) {
outFile.write(s);
outFile.newLine();
continue;
}
if (strim.isEmpty()) {
if (cLevel == 0) {
cPart = PART.UNKNOWN;
}
} else if (cPart == PART.UNKNOWN) {
cPart = parseFirstLineInBlock(strim);
} else if ("{".equals(strim) || "BEGIN".equalsIgnoreCase(strim)) {
cLevel++;
} else if ("}".equals(strim) || "END".equalsIgnoreCase(strim)) {
cLevel--;
if (cLevel == 0) {
cPart = PART.UNKNOWN;
}
} else if (cLevel > 0 && cPart != PART.OTHER && cPart != PART.UNKNOWN) {
markForTranslation(s);
if (b >= 0 && e >= 0 && b < e && e > 0) {
id = parseId(cPart, s, b, e);
}
} else if (cLevel == 0 && cPart == PART.DIALOG) {
if (RE_DIALOG_CAPTION.matcher(strim).matches()) {
markForTranslation(s);
id = "__CAPTION__";
}
}
if (b >= 0 && e >= 0 && b < e && e > 0) {
// extract source
String loc = s.substring(b + 1, e);
/*
* Some software produce escaped quotes, but valid are only
* double quotes
*/
loc = loc.replace("\\\"", "\"").replace("\"\"", "\"");
if (entryParseCallback != null) {
entryParseCallback.addEntry(blockId + "/" + id, loc, null, false, null, null, this, null);
} else if (entryTranslateCallback != null) {
// replace translation
String trans = entryTranslateCallback.getTranslation(blockId + "/" + id, loc, null);
if (trans == null) {
trans = loc;
}
trans = trans.replace("\"", "\"\"");
s = s.substring(0, b + 1) + trans + s.substring(e);
} else if (entryAlignCallback != null && id != null) {
align.put(blockId + "/" + id, loc);
}
}
outFile.write(s);
outFile.newLine();
}
}
@Override
protected void alignFile(BufferedReader sourceFile, BufferedReader translatedFile, org.omegat.filters2.FilterContext fc) throws Exception {
Map<String, String> source = new HashMap<String, String>();
Map<String, String> translated = new HashMap<String, String>();
align = source;
processFile(sourceFile, new NullBufferedWriter(), fc);
align = translated;
processFile(translatedFile, new NullBufferedWriter(), fc);
for (Map.Entry<String, String> en : source.entrySet()) {
String tr = translated.get(en.getKey());
if (!StringUtil.isEmpty(tr)) {
entryAlignCallback.addTranslation(en.getKey(), en.getValue(), tr, false, null, this);
}
}
}
private PART parseFirstLineInBlock(String line) {
Matcher m;
if ((m = RE_DIALOG.matcher(line)).matches()) {
blockId = m.group(1);
return PART.DIALOG;
}
if ((m = RE_MENU.matcher(line)).matches()) {
blockId = m.group(1);
return PART.MENU;
}
if ((m = RE_MESSAGETABLE.matcher(line)).matches()) {
blockId = m.group(1);
return PART.MESSAGETABLE;
}
if (RE_STRINGTABLE.matcher(line).matches()) {
blockId = "";
return PART.STRINGTABLE;
}
return PART.OTHER;
}
private String parseId(PART cPart, String line, int b, int e) {
String[] w;
switch (cPart) {
case DIALOG:
case MENU:
w = line.substring(e).split(",");
return w.length > 1 ? w[1].trim() : null;
case MESSAGETABLE:
case STRINGTABLE:
w = line.substring(0, b).split(",");
return w[0].trim();
default:
// Nothing
}
return null;
}
private void markForTranslation(String s) {
b = s.indexOf('"');
if (b < 0) {
return;
}
e = b;
while (true) {
e = s.indexOf('"', s.offsetByCodePoints(e, 1));
if (e < 0) {
break;
}
if (s.codePointBefore(e) == '\\') {
// skip escaped quote
continue;
}
if (s.codePointCount(e, s.length()) > 1) {
int cp = s.codePointAt(s.offsetByCodePoints(e, 1));
if (cp == '"') {
// skip double quote
e += Character.charCount(cp);
continue;
}
}
break;
}
}
}