From 3414561c73cc7c40205a70f1e7c82d0b4f51a241 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Sat, 20 Aug 2022 12:10:18 +0900 Subject: [PATCH] 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 + src/gen/core/project/Masks.java | 29 +++-- .../omegat/convert/v20to21/Convert20to21.java | 22 ++-- .../omegat/core/statistics/StatsResult.java | 105 +++++++++--------- .../omegat/filters2/master/FilterMaster.java | 34 ++---- src/org/omegat/util/ProjectFileStorage.java | 26 ++--- src/org/omegat/util/TMXReader2.java | 29 ++--- src/org/omegat/util/TMXWriter2.java | 60 ++++++---- test/src/org/omegat/util/TMXWriterTest.java | 52 ++++----- 9 files changed, 177 insertions(+), 183 deletions(-) diff --git a/build.gradle b/build.gradle index 2821c8be27..8082812c72 100644 --- a/build.gradle +++ b/build.gradle @@ -194,6 +194,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' + } // Test dependencies diff --git a/src/gen/core/project/Masks.java b/src/gen/core/project/Masks.java index b48a97d08c..c4748bd515 100644 --- a/src/gen/core/project/Masks.java +++ b/src/gen/core/project/Masks.java @@ -3,15 +3,20 @@ import java.util.ArrayList; import java.util.List; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlType; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; /** - *

Java class for masks complex type. + *

+ * Java class for masks complex type. * - *

The following schema fragment specifies the expected content contained within this class. + *

+ * The following schema fragment specifies the expected content contained within + * this class. * *

  * <complexType name="masks">
@@ -28,32 +33,32 @@
  * 
  */
 @XmlAccessorType(XmlAccessType.FIELD)
-@XmlType(name = "masks", propOrder = {
-    "mask"
-})
+@XmlType(name = "masks", propOrder = { "mask" })
 public class Masks {
 
+    // workaround https://github.com/FasterXML/jackson-modules-base/issues/127
+    @JacksonXmlElementWrapper(useWrapping = false)
     protected List mask;
 
     /**
      * Gets the value of the mask property.
      * 
      * 

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the mask property. + * This accessor method returns a reference to the live list, not a + * snapshot. Therefore any modification you make to the returned list will + * be present inside the JAXB object. This is why there is not a + * set method for the mask property. * *

* For example, to add a new item, do as follows: + * *

-     *    getMask().add(newItem);
+     * getMask().add(newItem);
      * 
* * *

- * Objects of the following type(s) are allowed in the list - * {@link String } + * Objects of the following type(s) are allowed in the list {@link String } * * */ diff --git a/src/org/omegat/convert/v20to21/Convert20to21.java b/src/org/omegat/convert/v20to21/Convert20to21.java index 6a67fd7ea1..593ccc9d1e 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 d1192558c4..2550ea05eb 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 aa2f7eab79..0dc8025365 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). */ @@ -108,14 +104,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); } @@ -437,8 +425,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); @@ -459,13 +447,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.writeValue(configFile, config); } catch (Exception e) { Log.logErrorRB("FILTERMASTER_ERROR_SAVING_FILTERS_CONFIG"); Log.log(e); @@ -713,7 +701,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 */ @@ -736,8 +724,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) { diff --git a/src/org/omegat/util/ProjectFileStorage.java b/src/org/omegat/util/ProjectFileStorage.java index 08a72754a9..a196342106 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,21 +70,14 @@ 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); } /** @@ -238,9 +230,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) { diff --git a/src/org/omegat/util/TMXReader2.java b/src/org/omegat/util/TMXReader2.java index 28865b1686..bb546c4c26 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 1d4cc7bf97..c09ee7ac9d 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 d05fa218b5..12cae4a2a4 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")); }