/************************************************************************** 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, Didier Briel 2011-2012 Didier Briel 2014 Enrique Estevez Fernandez 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.mozdtd; import java.awt.Window; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; 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.Log; import org.omegat.util.NullBufferedWriter; import org.omegat.util.OStrings; import org.omegat.util.StringUtil; /** * Filter for support Mozilla DTD files. * <p> * Option to remove untranslated segments in the target files by Enrique Estevez (Code adapted from the file: * PoFilter.java) * * @author Alex Buloichik (alex73mail@gmail.com) * @author Didier Briel * @author Enrique Estevez (keko.gl@gmail.com) * @see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx">Format * description</a> */ public class MozillaDTDFilter extends AbstractFilter { public static final String OPTION_REMOVE_STRINGS_UNTRANSLATED = "unremoveStringsUntranslated"; protected static final Pattern RE_ENTITY = Pattern.compile("<\\!ENTITY\\s+(\\S+)\\s+([\"'])(.+)\\2\\s*>", Pattern.DOTALL); protected Map<String, String> align; /** * If true, will remove non-translated segments in the target files */ public static boolean removeStringsUntranslated = false; @Override public Instance[] getDefaultInstances() { return new Instance[] { new Instance("*.dtd") }; } @Override public String getFileFormatName() { return OStrings.getString("MOZDTD_FILTER_NAME"); } @Override public boolean isSourceEncodingVariable() { return false; } @Override public boolean isTargetEncodingVariable() { return false; } @Override protected BufferedReader createReader(File inFile, String inEncoding) throws UnsupportedEncodingException, IOException { return new BufferedReader(new InputStreamReader(new FileInputStream(inFile), StandardCharsets.UTF_8)); } @Override protected BufferedWriter createWriter(File outFile, String outEncoding) throws UnsupportedEncodingException, IOException { return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), StandardCharsets.UTF_8)); } @Override protected void processFile(BufferedReader inFile, BufferedWriter outFile, FilterContext fc) throws IOException, TranslationException { String removeStringsUntranslatedStr = processOptions.get(OPTION_REMOVE_STRINGS_UNTRANSLATED); // If the value is null the default is false if ((removeStringsUntranslatedStr != null) && (removeStringsUntranslatedStr.equalsIgnoreCase("true"))) { removeStringsUntranslated = true; } else { removeStringsUntranslated = false; } StringBuilder block = new StringBuilder(); boolean isInBlock = false; boolean foundQuotes = false; int quotes = '"'; int previousChar = 0; int c; while ((c = inFile.read()) != -1) { if (c == '<' && !isInBlock) { isInBlock = true; } if (isInBlock) { block.append((char) c); } else { outFile.write(c); } // Find out the character used as begin and end of the segment if (!foundQuotes && (c == '"' || c == 39)) { // 39 is single quote foundQuotes = true; quotes = c; } if (c == '>' && isInBlock && previousChar == quotes) { isInBlock = false; foundQuotes = false; processBlock(block.toString(), outFile); block.setLength(0); } else if ((c == '>' && isInBlock && previousChar == '-')) { // This was a comment isInBlock = false; foundQuotes = false; outFile.write(block.toString()); block.setLength(0); } if (!Character.isWhitespace(c)) { // In the regexp, there could be whitespace between " and > previousChar = c; } } } protected void processBlock(String block, BufferedWriter out) throws IOException { Matcher m = RE_ENTITY.matcher(block); if (!m.matches()) { // not ENTITY declaration out.write(block); return; } String id = m.group(1); String text = m.group(3); if (entryParseCallback != null) { entryParseCallback.addEntry(id, text, null, false, null, null, this, null); } else if (entryTranslateCallback != null) { // replace translation String trans = entryTranslateCallback.getTranslation(id, text, null); if (trans != null || !removeStringsUntranslated) { out.write(block.substring(0, m.start(3))); out.write(trans != null ? trans : text); out.write(block.substring(m.end(3))); } } else if (entryAlignCallback != null && id != null) { align.put(id, text); } } @Override protected void alignFile(BufferedReader sourceFile, BufferedReader translatedFile, 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); } } } @Override public String getInEncodingLastParsedFile() { return StandardCharsets.UTF_8.name(); } @Override public Map<String, String> changeOptions(Window parent, Map<String, String> config) { try { MozillaDTDOptionsDialog dialog = new MozillaDTDOptionsDialog(parent, config); dialog.setVisible(true); if (MozillaDTDOptionsDialog.RET_OK == dialog.getReturnStatus()) { return dialog.getOptions(); } else { return null; } } catch (Exception e) { Log.log(OStrings.getString("MOZDTD_FILTER_EXCEPTION")); Log.log(e); return null; } } /** * Returns true to indicate that Mozilla DTD filter has options. * */ @Override public boolean hasOptions() { return true; } }