From e7f6ac01d813b5b03174295a338bfddc5f8b6379 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Sat, 20 Aug 2022 12:10:18 +0900 Subject: [PATCH] refactor: Use Jackson XML dataformat for JAXB data parser - Update TMXWriterTest to check with system line separator It is because XML parser can automatically change /r/n into XML entity /n when System.lineSeparator() is /n - Update TMXReader2 to detect both "lang" and "xml:lang" some implementation return "lang" from "xml:lang" but other can return "xml:lang" as is. - as a workaround for https://github.com/FasterXML/jackson-modules-base/issues/127 Add @JacksonXmlElementWrapper(useWrapping=false) for mask element for the parser of omegat.project file. Signed-off-by: Hiroshi Miura --- build.gradle | 3 + .../omegat/convert/v20to21/Convert20to21.java | 22 ++-- .../omegat/core/statistics/StatsResult.java | 105 +++++++++--------- .../omegat/filters2/master/FilterMaster.java | 38 +++---- src/org/omegat/util/ProjectFileStorage.java | 79 ++++++------- src/org/omegat/util/TMXReader2.java | 29 ++--- src/org/omegat/util/TMXWriter2.java | 60 ++++++---- test/src/org/omegat/util/TMXWriterTest.java | 52 ++++----- 8 files changed, 193 insertions(+), 195 deletions(-) diff --git a/build.gradle b/build.gradle index 13edc1dbdb..0ccb2d4a58 100644 --- a/build.gradle +++ b/build.gradle @@ -192,6 +192,9 @@ dependencies { // JSON parser implementation "com.fasterxml.jackson.core:jackson-core:2.13.4" implementation "com.fasterxml.jackson.core:jackson-databind:2.13.4.2" + implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.13.4" + implementation 'com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.14.0' + implementation("com.github.ben-manes.caffeine:caffeine:2.9.3") { attributes { diff --git a/src/org/omegat/convert/v20to21/Convert20to21.java b/src/org/omegat/convert/v20to21/Convert20to21.java index 6b10e2379f..6729747fd6 100644 --- a/src/org/omegat/convert/v20to21/Convert20to21.java +++ b/src/org/omegat/convert/v20to21/Convert20to21.java @@ -36,8 +36,8 @@ import java.io.Serializable; import java.nio.charset.StandardCharsets; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Marshaller; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; import gen.core.filters.Files; import gen.core.filters.Filter; @@ -60,7 +60,6 @@ private Convert20to21() { * old config file * @param toFile * new config file - * @throws Exception */ public static void convertFiltersConfig(final File fromFile, final File toFile) throws Exception { if (!fromFile.exists()) { @@ -68,11 +67,9 @@ public static void convertFiltersConfig(final File fromFile, final File toFile) } String c = read(fromFile); org.omegat.convert.v20to21.data.Filters filters; - XMLDecoder xmldec = new XMLDecoder(new ByteArrayInputStream(c.getBytes("UTF-8"))); - try { + try (XMLDecoder xmldec = new XMLDecoder( + new ByteArrayInputStream(c.getBytes(StandardCharsets.UTF_8)))) { filters = (org.omegat.convert.v20to21.data.Filters) xmldec.readObject(); - } finally { - xmldec.close(); } Filters res = new Filters(); @@ -108,10 +105,9 @@ public static void convertFiltersConfig(final File fromFile, final File toFile) convertTextFilter(res); convertHTMLFilter2(res); - JAXBContext ctx = JAXBContext.newInstance(Filters.class); - Marshaller m = ctx.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - m.marshal(res, toFile); + XmlMapper mapper = new XmlMapper(); + mapper.registerModule(new JaxbAnnotationModule()); + mapper.writeValue(toFile, res); } /** @@ -133,8 +129,8 @@ private static String read(File f) throws IOException { } String res = r.toString(); res = res.replace("org.omegat.filters2.master.Filters", "org.omegat.convert.v20to21.data.Filters"); - res = res - .replace("org.omegat.filters2.master.OneFilter", "org.omegat.convert.v20to21.data.OneFilter"); + res = res.replace("org.omegat.filters2.master.OneFilter", + "org.omegat.convert.v20to21.data.OneFilter"); res = res.replace("org.omegat.filters2.Instance", "org.omegat.convert.v20to21.data.Instance"); res = res.replace("org.omegat.filters2.html2.HTMLOptions", "org.omegat.convert.v20to21.data.HTMLOptions"); diff --git a/src/org/omegat/core/statistics/StatsResult.java b/src/org/omegat/core/statistics/StatsResult.java index 44b1d1b5a3..078b51f3ac 100644 --- a/src/org/omegat/core/statistics/StatsResult.java +++ b/src/org/omegat/core/statistics/StatsResult.java @@ -38,14 +38,17 @@ import java.util.Set; import java.util.TimeZone; -import javax.xml.bind.JAXB; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SequenceWriter; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; import org.omegat.util.OStrings; import org.omegat.util.StaticUtils; import org.omegat.util.gui.TextUtil; @@ -55,46 +58,36 @@ */ @XmlRootElement(name = "omegat-stats") public class StatsResult { - public static final String[] HT_HEADERS = { - "", - OStrings.getString("CT_STATS_Segments"), - OStrings.getString("CT_STATS_Words"), - OStrings.getString("CT_STATS_Characters_NOSP"), - OStrings.getString("CT_STATS_Characters"), - OStrings.getString("CT_STATS_Files"), - }; - - private static final String[] HT_ROWS = { - OStrings.getString("CT_STATS_Total"), - OStrings.getString("CT_STATS_Remaining"), - OStrings.getString("CT_STATS_Unique"), - OStrings.getString("CT_STATS_Unique_Remaining"), - }; + public static final String[] HT_HEADERS = { "", OStrings.getString("CT_STATS_Segments"), + OStrings.getString("CT_STATS_Words"), OStrings.getString("CT_STATS_Characters_NOSP"), + OStrings.getString("CT_STATS_Characters"), OStrings.getString("CT_STATS_Files"), }; + + private static final String[] HT_ROWS = { OStrings.getString("CT_STATS_Total"), + OStrings.getString("CT_STATS_Remaining"), OStrings.getString("CT_STATS_Unique"), + OStrings.getString("CT_STATS_Unique_Remaining"), }; private static final boolean[] HT_ALIGN = new boolean[] { false, true, true, true, true, true }; - public static final String[] FT_HEADERS = { - OStrings.getString("CT_STATS_FILE_Name"), - OStrings.getString("CT_STATS_FILE_Total_Segments"), - OStrings.getString("CT_STATS_FILE_Remaining_Segments"), - OStrings.getString("CT_STATS_FILE_Unique_Segments"), - OStrings.getString("CT_STATS_FILE_Unique_Remaining_Segments"), - OStrings.getString("CT_STATS_FILE_Total_Words"), - OStrings.getString("CT_STATS_FILE_Remaining_Words"), - OStrings.getString("CT_STATS_FILE_Unique_Words"), - OStrings.getString("CT_STATS_FILE_Unique_Remaining_Words"), - OStrings.getString("CT_STATS_FILE_Total_Characters_NOSP"), - OStrings.getString("CT_STATS_FILE_Remaining_Characters_NOSP"), - OStrings.getString("CT_STATS_FILE_Unique_Characters_NOSP"), - OStrings.getString("CT_STATS_FILE_Unique_Remaining_Characters_NOSP"), - OStrings.getString("CT_STATS_FILE_Total_Characters"), - OStrings.getString("CT_STATS_FILE_Remaining_Characters"), - OStrings.getString("CT_STATS_FILE_Unique_Characters"), - OStrings.getString("CT_STATS_FILE_Unique_Remaining_Characters"), - }; - - private static final boolean[] FT_ALIGN = { false, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, }; + public static final String[] FT_HEADERS = { OStrings.getString("CT_STATS_FILE_Name"), + OStrings.getString("CT_STATS_FILE_Total_Segments"), + OStrings.getString("CT_STATS_FILE_Remaining_Segments"), + OStrings.getString("CT_STATS_FILE_Unique_Segments"), + OStrings.getString("CT_STATS_FILE_Unique_Remaining_Segments"), + OStrings.getString("CT_STATS_FILE_Total_Words"), + OStrings.getString("CT_STATS_FILE_Remaining_Words"), + OStrings.getString("CT_STATS_FILE_Unique_Words"), + OStrings.getString("CT_STATS_FILE_Unique_Remaining_Words"), + OStrings.getString("CT_STATS_FILE_Total_Characters_NOSP"), + OStrings.getString("CT_STATS_FILE_Remaining_Characters_NOSP"), + OStrings.getString("CT_STATS_FILE_Unique_Characters_NOSP"), + OStrings.getString("CT_STATS_FILE_Unique_Remaining_Characters_NOSP"), + OStrings.getString("CT_STATS_FILE_Total_Characters"), + OStrings.getString("CT_STATS_FILE_Remaining_Characters"), + OStrings.getString("CT_STATS_FILE_Unique_Characters"), + OStrings.getString("CT_STATS_FILE_Unique_Remaining_Characters"), }; + + private static final boolean[] FT_ALIGN = { false, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, }; @JsonProperty("project") private StatProjectProperties props; @@ -122,6 +115,7 @@ public StatsResult() { /** * Constructor. + * * @param total * @param remaining * @param unique @@ -143,7 +137,9 @@ public StatsResult(StatCount total, StatCount remaining, StatCount unique, StatC /** * Update given hosStat with current stats data. - * @param hotStat StatisticsInfo data object. + * + * @param hotStat + * StatisticsInfo data object. */ public void updateStatisticsInfo(StatisticsInfo hotStat) { hotStat.numberOfSegmentsTotal = total.segments; @@ -167,6 +163,7 @@ public StatProjectProperties getProps() { /** * Return total number of segments. + * * @return */ @XmlElement(name = "total") @@ -176,6 +173,7 @@ public StatCount getTotal() { /** * Return remaining number of segments that needs translation. + * * @return */ @XmlElement(name = "remaining") @@ -185,6 +183,7 @@ public StatCount getRemaining() { /** * Return a number of unique segments. + * * @return */ @XmlElement(name = "unique") @@ -194,6 +193,7 @@ public StatCount getUnique() { /** * Return a number of remaining unique segments. + * * @return */ @XmlElement(name = "unique-remaining") @@ -203,6 +203,7 @@ public StatCount getRemainingUnique() { /** * return a statistics of each source/target files. + * * @return */ @XmlElement(name = "files") @@ -212,25 +213,25 @@ public List getCounts() { /** * Return pretty printed statistics data. + * * @return pretty-printed string. */ @JsonIgnore public String getTextData() { - return OStrings.getString("CT_STATS_Project_Statistics") + - "\n\n" + - TextUtil.showTextTable(HT_HEADERS, getHeaderTable(), HT_ALIGN) + - "\n\n" + + return OStrings.getString("CT_STATS_Project_Statistics") + "\n\n" + + TextUtil.showTextTable(HT_HEADERS, getHeaderTable(), HT_ALIGN) + "\n\n" + // STATISTICS BY FILE - OStrings.getString("CT_STATS_FILE_Statistics") + - "\n\n" + - TextUtil.showTextTable(FT_HEADERS, getFilesTable(), FT_ALIGN); + OStrings.getString("CT_STATS_FILE_Statistics") + "\n\n" + + TextUtil.showTextTable(FT_HEADERS, getFilesTable(), FT_ALIGN); } /** * Return JSON expression of stats data. + * * @return JSON string data. - * @throws IOException when export failed. + * @throws IOException + * when export failed. */ @JsonIgnore public String getJsonData() throws IOException { @@ -245,14 +246,16 @@ public String getJsonData() throws IOException { /** * Return XML expression of Stats data. + * * @return XML expression of stats data as String. */ @JsonIgnore - public String getXmlData() { + public String getXmlData() throws JsonProcessingException { setDate(); - StringWriter result = new StringWriter(); - JAXB.marshal(this, result); - return result.toString(); + XmlMapper mapper = XmlMapper.xmlBuilder().enable(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME) + .build(); + mapper.registerModule(new JaxbAnnotationModule()); + return mapper.writeValueAsString(this); } private void setDate() { diff --git a/src/org/omegat/filters2/master/FilterMaster.java b/src/org/omegat/filters2/master/FilterMaster.java index 3c28cc12ec..7d816ced33 100644 --- a/src/org/omegat/filters2/master/FilterMaster.java +++ b/src/org/omegat/filters2/master/FilterMaster.java @@ -47,10 +47,8 @@ import java.util.TreeMap; import java.util.regex.Pattern; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; - +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; import org.apache.commons.io.FileUtils; import org.omegat.filters2.AbstractFilter; import org.omegat.filters2.FilterContext; @@ -88,8 +86,6 @@ public class FilterMaster { /** name of the filter configuration file */ public static final String FILE_FILTERS = "filters.xml"; - private static final JAXBContext CONFIG_CTX; - /** * There was no version of file filters support (1.4.5 Beta 1 -- 1.6.0 * RC12). @@ -109,14 +105,6 @@ public class FilterMaster { /** Classes of all filters. */ static List> filtersClasses = Collections.emptyList(); - static { - try { - CONFIG_CTX = JAXBContext.newInstance(Filters.class); - } catch (Exception ex) { - throw new ExceptionInInitializerError(ex); - } - } - public static void setFilterClasses(List> classes) { filtersClasses = new ArrayList<>(classes); } @@ -446,8 +434,8 @@ public static Filters loadConfig(File configFile) throws IOException { } Filters result; try { - Unmarshaller unm = CONFIG_CTX.createUnmarshaller(); - result = (Filters) unm.unmarshal(configFile); + XmlMapper mapper = new XmlMapper(); + result = mapper.readValue(configFile, Filters.class); } catch (Exception e) { Log.logErrorRB("FILTERMASTER_ERROR_LOADING_FILTERS_CONFIG"); Log.log(e); @@ -468,13 +456,13 @@ public static Filters loadConfig(File configFile) throws IOException { */ public static void saveConfig(Filters config, File configFile) throws IOException { if (config == null) { - configFile.delete(); + boolean ignored = configFile.delete(); return; } try { - Marshaller m = CONFIG_CTX.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - m.marshal(config, configFile); + XmlMapper mapper = new XmlMapper(); + mapper.registerModule(new JaxbAnnotationModule()); + mapper.writerWithDefaultPrettyPrinter().writeValue(configFile, config); } catch (Exception e) { Log.logErrorRB("FILTERMASTER_ERROR_SAVING_FILTERS_CONFIG"); Log.log(e); @@ -517,7 +505,7 @@ public static String now(String dateFormat) { /** * Calculate the target path corresponding to the given source file. - * + * * @param sourceDir * Path to the project's source dir * @param srcRelPath @@ -736,7 +724,7 @@ public static Filters cloneConfig(Filters orig) { /** * Clone one filter's config for editing. * - * @param f + * @param filter * one filter's config * @return new config instance */ @@ -759,8 +747,8 @@ public static Filter cloneFilter(Filter filter) { /** * Clone one filter's instance config for editing. * - * @param f - * new filter's instance config + * @param files + * new filter's instance config file * @return new config instance */ private static Files cloneFiles(Files files) { @@ -786,7 +774,7 @@ public static Filter getDefaultSettingsFromFilter(final String filterClassname) } Filter fc = new Filter(); fc.setClassName(f.getClass().getName()); - fc.setEnabled(f.isEnabledInDefault()); + fc.setEnabled(true); for (Instance ins : f.getDefaultInstances()) { Files ff = new Files(); ff.setSourceEncoding(ins.getSourceEncoding()); diff --git a/src/org/omegat/util/ProjectFileStorage.java b/src/org/omegat/util/ProjectFileStorage.java index 5555ef8b63..92e818832c 100644 --- a/src/org/omegat/util/ProjectFileStorage.java +++ b/src/org/omegat/util/ProjectFileStorage.java @@ -31,17 +31,16 @@ package org.omegat.util; -import java.io.ByteArrayInputStream; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Marshaller; - +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; + import org.omegat.core.data.ProjectProperties; import org.omegat.filters2.TranslationException; import org.omegat.filters2.master.PluginUtils; @@ -71,29 +70,24 @@ private ProjectFileStorage() { */ public static final String DEFAULT_FOLDER_MARKER = "__DEFAULT__"; - private static final JAXBContext CONTEXT; - static { - try { - CONTEXT = JAXBContext.newInstance(Omegat.class); - } catch (Exception ex) { - throw new ExceptionInInitializerError(ex); - } - } - public static Omegat parseProjectFile(File file) throws Exception { return parseProjectFile(FileUtils.readFileToByteArray(file)); } public static Omegat parseProjectFile(byte[] projectFile) throws Exception { - return (Omegat) CONTEXT.createUnmarshaller().unmarshal(new ByteArrayInputStream(projectFile)); + XmlMapper mapper = new XmlMapper(); + mapper.registerModule(new JaxbAnnotationModule()); + return mapper.readValue(projectFile, Omegat.class); } /** - * Load the project properties file for the project at the specified directory. The properties file is - * assumed to exist at the root of the project and have the default name, {@link OConsts#FILE_PROJECT}. - * This is a convenience method for {@link #loadPropertiesFile(File, File)}. + * Load the project properties file for the project at the specified + * directory. The properties file is assumed to exist at the root of the + * project and have the default name, {@link OConsts#FILE_PROJECT}. This is + * a convenience method for {@link #loadPropertiesFile(File, File)}. *

- * If the supplied {@link File} is not a directory, an {@link IllegalArgumentException} will be thrown. + * If the supplied {@link File} is not a directory, an + * {@link IllegalArgumentException} will be thrown. * * @param projectDir * The directory of the project @@ -105,10 +99,11 @@ public static ProjectProperties loadProjectProperties(File projectDir) throws Ex } /** - * Load the specified project properties file for the project at the specified directory. + * Load the specified project properties file for the project at the + * specified directory. *

- * If projectDir is not a directory or projectFile is not a file, an - * {@link IllegalArgumentException} will be thrown. + * If projectDir is not a directory or projectFile + * is not a file, an {@link IllegalArgumentException} will be thrown. * * @param projectDir * The directory of the project @@ -119,7 +114,7 @@ public static ProjectProperties loadProjectProperties(File projectDir) throws Ex */ public static ProjectProperties loadPropertiesFile(File projectDir, File projectFile) throws Exception { if (!projectFile.isFile()) { - throw new IllegalArgumentException("Project file "+projectFile+" was not a file"); + throw new IllegalArgumentException("Project file " + projectFile + " was not a file"); } Omegat om = parseProjectFile(projectFile); return loadPropertiesFile(projectDir, om); @@ -132,10 +127,10 @@ static ProjectProperties loadPropertiesFile(File projectDir, Omegat om) throws E ProjectProperties result = new ProjectProperties(projectDir); - if (!OConsts.PROJ_CUR_VERSION.equals(om.getProject().getVersion())) { - throw new TranslationException(StringUtil.format( - OStrings.getString("PFR_ERROR_UNSUPPORTED_PROJECT_VERSION"), - om.getProject().getVersion())); + String curVersion = om.getProject().getVersion(); + if (!OConsts.PROJ_CUR_VERSION.equals(curVersion)) { + throw new TranslationException(StringUtil + .format(OStrings.getString("PFR_ERROR_UNSUPPORTED_PROJECT_VERSION"), curVersion)); } result.setTargetRoot(normalizeLoadedPath(om.getProject().getTargetDir(), OConsts.DEFAULT_TARGET)); @@ -148,10 +143,13 @@ static ProjectProperties loadPropertiesFile(File projectDir, Omegat om) throws E result.getSourceRootExcludes().addAll(ProjectProperties.getDefaultExcludes()); } result.setTMRoot(normalizeLoadedPath(om.getProject().getTmDir(), OConsts.DEFAULT_TM)); - result.setExportTMRoot(normalizeLoadedPath(om.getProject().getExportTmDir(), OConsts.DEFAULT_EXPORT_TM)); - result.setExportTmLevels(normalizeLoadedExportTmLevels(om.getProject().getExportTmLevels(), OConsts.DEFAULT_EXPORT_TM_LEVELS)); + result.setExportTMRoot( + normalizeLoadedPath(om.getProject().getExportTmDir(), OConsts.DEFAULT_EXPORT_TM)); + result.setExportTmLevels(normalizeLoadedExportTmLevels(om.getProject().getExportTmLevels(), + OConsts.DEFAULT_EXPORT_TM_LEVELS)); - result.setGlossaryRoot(normalizeLoadedPath(om.getProject().getGlossaryDir(), OConsts.DEFAULT_GLOSSARY)); + result.setGlossaryRoot( + normalizeLoadedPath(om.getProject().getGlossaryDir(), OConsts.DEFAULT_GLOSSARY)); // Compute glossary file location String glossaryFile = om.getProject().getGlossaryFile(); @@ -165,8 +163,7 @@ static ProjectProperties loadPropertiesFile(File projectDir, Omegat om) throws E } result.setWriteableGlossary(glossaryFile); - result.setDictRoot(normalizeLoadedPath(om.getProject().getDictionaryDir(), - OConsts.DEFAULT_DICT)); + result.setDictRoot(normalizeLoadedPath(om.getProject().getDictionaryDir(), OConsts.DEFAULT_DICT)); result.setSourceLanguage(om.getProject().getSourceLang()); result.setTargetLanguage(om.getProject().getTargetLang()); @@ -210,7 +207,8 @@ public static void writeProjectFile(ProjectProperties props) throws Exception { om.getProject().getSourceDirExcludes().getMask().addAll(props.getSourceRootExcludes()); om.getProject().setTargetDir(getPathForStoring(root, props.getTargetRoot(), OConsts.DEFAULT_TARGET)); om.getProject().setTmDir(getPathForStoring(root, props.getTMRoot(), OConsts.DEFAULT_TM)); - om.getProject().setExportTmDir(getPathForStoring(root, props.getExportTMRoot(), OConsts.DEFAULT_EXPORT_TM)); + om.getProject() + .setExportTmDir(getPathForStoring(root, props.getExportTMRoot(), OConsts.DEFAULT_EXPORT_TM)); om.getProject().setExportTmLevels(String.join(" ", props.getExportTmLevels())); String glossaryDir = getPathForStoring(root, props.getGlossaryRoot(), OConsts.DEFAULT_GLOSSARY); om.getProject().setGlossaryDir(glossaryDir); @@ -238,9 +236,9 @@ public static void writeProjectFile(ProjectProperties props) throws Exception { om.getProject().getRepositories().getRepository().addAll(props.getRepositories()); } - Marshaller m = CONTEXT.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - m.marshal(om, outFile); + XmlMapper mapper = new XmlMapper(); + mapper.registerModule(new JaxbAnnotationModule()); + mapper.writeValue(outFile, om); } private static String normalizeLoadedPath(String path, String defaultValue) { @@ -300,7 +298,8 @@ private static String getPathForStoring(String root, String absolutePath, String // Path.normalize() will resolve any remaining "../" Path absPath = Paths.get(absolutePath).normalize(); String rel = Paths.get(root).relativize(absPath).toString(); - if (StringUtils.countMatches(rel, ".." + File.separatorChar) <= OConsts.MAX_PARENT_DIRECTORIES_ABS2REL) { + if (StringUtils.countMatches(rel, + ".." + File.separatorChar) <= OConsts.MAX_PARENT_DIRECTORIES_ABS2REL) { // Use the relativized path as it is "near" enough. result = rel; } else { @@ -314,9 +313,11 @@ private static String getPathForStoring(String root, String absolutePath, String /** * Load a tokenizer class from its canonical name. - * @param className Name of tokenizer class - * @return Class object of specified tokenizer, or of fallback tokenizer - * if the specified one could not be loaded for whatever reason. + * + * @param className + * Name of tokenizer class + * @return Class object of specified tokenizer, or of fallback tokenizer if + * the specified one could not be loaded for whatever reason. */ private static Class loadTokenizer(String className, Language fallback) { if (!StringUtil.isEmpty(className)) { diff --git a/src/org/omegat/util/TMXReader2.java b/src/org/omegat/util/TMXReader2.java index ef655b4c35..44c42997d8 100644 --- a/src/org/omegat/util/TMXReader2.java +++ b/src/org/omegat/util/TMXReader2.java @@ -62,7 +62,6 @@ import org.apache.commons.io.input.XmlStreamReader; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; -import org.xml.sax.SAXException; /** * Helper for read TMX files, using StAX. @@ -117,8 +116,7 @@ public TMXReader2() { factory.setXMLReporter(new XMLReporter() { public void report(String message, String errorType, Object info, Location location) throws XMLStreamException { - Log.logWarningRB("TMXR_WARNING_WHILE_PARSING", - location.getLineNumber(), + Log.logWarningRB("TMXR_WARNING_WHILE_PARSING", location.getLineNumber(), location.getColumnNumber()); Log.log(message + ": " + info); warningsCount++; @@ -163,7 +161,8 @@ public void readTMX(File file, final Language sourceLanguage, final Language tar parseTu(eStart); ParsedTuv origTuv = getTuvByLang(sourceLanguage); ParsedTuv targetTuv = getTuvByLang(targetLanguage); - allFound = callback.onEntry(currentTu, origTuv, targetTuv, isParagraphSegtype) && allFound; + allFound = callback.onEntry(currentTu, origTuv, targetTuv, isParagraphSegtype) + && allFound; } else if ("header".equals(eStart.getName().getLocalPart())) { parseHeader(eStart, sourceLanguage); } @@ -218,11 +217,11 @@ protected void parseHeader(StartElement element, final Language sourceLanguage) // different from the project source language String tmxSourceLanguage = getAttributeValue(element, "srclang"); if (!sourceLanguage.getLanguage().equalsIgnoreCase(tmxSourceLanguage)) { - Log.logWarningRB("TMXR_WARNING_INCORRECT_SOURCE_LANG", tmxSourceLanguage, - sourceLanguage); + Log.logWarningRB("TMXR_WARNING_INCORRECT_SOURCE_LANG", tmxSourceLanguage, sourceLanguage); } - // give a warning that TMX file will be upgraded to sentence segmentation + // give a warning that TMX file will be upgraded to sentence + // segmentation if (isSegmentingEnabled && isParagraphSegtype) { Log.logWarningRB("TMXR_WARNING_UPGRADE_SENTSEG"); } @@ -276,7 +275,7 @@ protected void parseTuv(StartElement element) throws Exception { // find 'lang' or 'xml:lang' attribute for (Iterator it = element.getAttributes(); it.hasNext();) { Attribute a = (Attribute) it.next(); - if ("lang".equals(a.getName().getLocalPart())) { + if ("lang".equals(a.getName().getLocalPart()) || "xml:lang".equals(a.getName().getLocalPart())) { tuv.lang = a.getValue(); break; } @@ -543,8 +542,8 @@ protected void parseSegExtLevel2() throws Exception { } if (tagN == null) { // check error of TMX reading - Log.logErrorRB("TMX_ERROR_READING_LEVEL2", e.getLocation().getLineNumber(), e - .getLocation().getColumnNumber()); + Log.logErrorRB("TMX_ERROR_READING_LEVEL2", e.getLocation().getLineNumber(), + e.getLocation().getColumnNumber()); errorsCount++; segContent.setLength(0); return; @@ -587,7 +586,8 @@ protected void parseSegExtLevel2() throws Exception { */ protected ParsedTuv getTuvByLang(Language lang) { ParsedTuv tuvLC = null; // Tuv with the same language+country - ParsedTuv tuvL = null; // Tuv with the same language only, without country + ParsedTuv tuvL = null; // Tuv with the same language only, without + // country ParsedTuv tuvLW = null; // Tuv with the same language+whatever country for (int i = 0; i < currentTu.tuvs.size(); i++) { ParsedTuv tuv = currentTu.tuvs.get(i); @@ -604,7 +604,7 @@ protected ParsedTuv getTuvByLang(Language lang) { tuvLC = tuv; } else { // other country - if (tuvLW == null) { // take first occurrence + if (tuvLW == null) { // take first occurrence tuvLW = tuv; } } @@ -665,7 +665,8 @@ void clear() { changedate = 0; creationid = null; creationdate = 0; - props = new ArrayList(); // do not CLEAR, because it may be shared + props = new ArrayList(); // do not CLEAR, because it may be + // shared tuvs = new ArrayList(); note = null; } @@ -681,7 +682,7 @@ public static class ParsedTuv { } public static final EntityResolver TMX_DTD_RESOLVER = new EntityResolver() { - public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { + public InputSource resolveEntity(String publicId, String systemId) { if (systemId.endsWith("tmx11.dtd")) { return new InputSource(TMXReader2.class.getResourceAsStream("/schemas/tmx11.dtd")); } else if (systemId.endsWith("tmx14.dtd")) { diff --git a/src/org/omegat/util/TMXWriter2.java b/src/org/omegat/util/TMXWriter2.java index 0805191653..1f812667ed 100644 --- a/src/org/omegat/util/TMXWriter2.java +++ b/src/org/omegat/util/TMXWriter2.java @@ -30,10 +30,10 @@ import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -66,7 +66,7 @@ public class TMXWriter2 implements AutoCloseable { public static final String PROP_ID = "id"; - private static final XMLOutputFactory FACTORY; + private final XMLOutputFactory factory; private final OutputStream out; private final XMLStreamWriter xml; @@ -76,33 +76,37 @@ public class TMXWriter2 implements AutoCloseable { private final boolean forceValidTMX; /** - * DateFormat with format YYYYMMDDThhmmssZ able to display a date in UTC time. + * DateFormat with format YYYYMMDDThhmmssZ able to display a date in UTC + * time. * * SimpleDateFormat IS NOT THREAD SAFE !!! */ private final SimpleDateFormat tmxDateFormat; - static { - FACTORY = XMLOutputFactory.newInstance(); - } - /** * - * @param file to write TMX entries. - * @param sourceLanguage language of source. - * @param targetLanguage language of target. - * @param sentenceSegmentingEnabled true when sentence segmenting enabled, otherwise false. + * @param file + * to write TMX entries. + * @param sourceLanguage + * language of source. + * @param targetLanguage + * language of target. + * @param sentenceSegmentingEnabled + * true when sentence segmenting enabled, otherwise false. * @param levelTwo - * When true, the tmx is made compatible with level 2 (TMX version 1.4) - * @throws Exception when error occurred. + * When true, the tmx is made compatible with level 2 (TMX + * version 1.4) + * @throws Exception + * when error occurred. */ public TMXWriter2(final File file, final Language sourceLanguage, final Language targetLanguage, boolean sentenceSegmentingEnabled, boolean levelTwo, boolean forceValidTMX) throws Exception { this.levelTwo = levelTwo; this.forceValidTMX = forceValidTMX; + factory = XMLOutputFactory.newInstance(); - out = new BufferedOutputStream(new FileOutputStream(file)); - xml = FACTORY.createXMLStreamWriter(out, StandardCharsets.UTF_8.name()); + out = new BufferedOutputStream(Files.newOutputStream(file.toPath())); + xml = factory.createXMLStreamWriter(out, StandardCharsets.UTF_8.name()); xml.writeStartDocument(StandardCharsets.UTF_8.name(), "1.0"); xml.writeCharacters(lineSeparator); @@ -156,10 +160,14 @@ public void writeComment(String comment) throws Exception { /** * Write entries. - * @param entries Map of SourceTextEntry and TMXEntry to output. - * @throws Exception when i/o error or XMLStream error happened. + * + * @param entries + * Map of SourceTextEntry and TMXEntry to output. + * @throws Exception + * when i/o error or XMLStream error happened. */ - public void writeEntries(final Map entries, boolean addProp) throws Exception { + public void writeEntries(final Map entries, boolean addProp) + throws Exception { List propList = new ArrayList<>(); for (Map.Entry en : entries.entrySet()) { EntryKey k = en.getKey(); @@ -179,15 +187,17 @@ public void writeEntries(final Map entries, boole /** * Write one entry. * - * @param source source text to write. - * @param translation translation text to write. - * @param entry that has interface ITMXEntry. + * @param source + * source text to write. + * @param translation + * translation text to write. + * @param entry + * that has interface ITMXEntry. * @param propValues * pairs with property name and values */ public void writeEntry(final String source, final String translation, final ITMXEntry entry, - final List propValues) - throws Exception { + final List propValues) throws Exception { writeEntry(source, translation, entry.getNote(), entry.getCreator(), entry.getCreationDate(), entry.getChanger(), entry.getChangeDate(), propValues); } @@ -428,7 +438,9 @@ private void writeLevelTwo(String segment) throws Exception { /** * Replaces \n with platform specific end of lines - * @param text The string to be converted + * + * @param text + * The string to be converted * @return The converted string */ private String platformLineSeparator(String text) { diff --git a/test/src/org/omegat/util/TMXWriterTest.java b/test/src/org/omegat/util/TMXWriterTest.java index c6f442027a..6f9d2cbdc6 100644 --- a/test/src/org/omegat/util/TMXWriterTest.java +++ b/test/src/org/omegat/util/TMXWriterTest.java @@ -28,9 +28,10 @@ import static org.junit.Assert.assertTrue; import java.io.File; -import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; @@ -50,11 +51,12 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + import org.omegat.core.data.ProjectProperties; import org.omegat.core.data.RealProjectTest; import org.omegat.filters.TestFilterBase; -import org.w3c.dom.Document; -import org.w3c.dom.Node; /** * @author Alex Buloichik @@ -151,42 +153,34 @@ public void testLevel2reads() throws Exception { // which should be treated as a beginning tag even when // useSlash = true so that we can match after segmenting, // such as in TmxComplianceTests#testImport2A: - // "First sentence. Second sentence." -> ["First sentence.", "Second sentence."] + // "First sentence. Second sentence." -> ["First + // sentence.", "Second sentence."] assertEquals("67", sources.get(3)); } @Test public void testEOLwrite() throws Exception { String eol = TMXWriter2.lineSeparator; - try { - TMXWriter2.lineSeparator = "\r\n"; - - TMXWriter2 wr = new TMXWriter2(outFile, new Language("en-US"), new Language("be-BY"), false, - true, false); - wr.writeEntry("source", "tar\nget", RealProjectTest.createEmptyTMXEntry(), null); - wr.close(); - - StringBuilder text = new StringBuilder(); - try (Reader rd = new InputStreamReader(new FileInputStream(outFile), "UTF-8")) { - char[] buffer = new char[512]; - while (true) { - int len = rd.read(buffer); - if (len < 0) { - break; - } - text.append(buffer, 0, len); + TMXWriter2 wr = new TMXWriter2(outFile, new Language("en-US"), new Language("be-BY"), false, true, + false); + wr.writeEntry("source", "tar\nget", RealProjectTest.createEmptyTMXEntry(), null); + wr.close(); + + StringBuilder text = new StringBuilder(); + try (Reader rd = new InputStreamReader(Files.newInputStream(outFile.toPath()), + StandardCharsets.UTF_8)) { + char[] buffer = new char[512]; + while (true) { + int len = rd.read(buffer); + if (len < 0) { + break; } + text.append(buffer, 0, len); } - assertTrue(text.toString().contains("tar\r\nget")); - - final List trs = new ArrayList(); - load(null, trs, true, false); - assertTrue(trs.get(0).contains("tar\nget")); - } finally { - TMXWriter2.lineSeparator = eol; } + assertTrue(text.toString().contains("tar" + eol + "get")); - final List trs = new ArrayList(); + final List trs = new ArrayList<>(); load(null, trs, true, false); assertTrue(trs.get(0).contains("tar\nget")); }