Skip to content

Commit

Permalink
feat: allow the configuration of EPUBCheck’s Locale
Browse files Browse the repository at this point in the history
The locale used by EPUBCheck can now be configured:

- with a `--locale` option on the command line
- with the `setLocale(Locale locale)` method on the `EpubCheck` class
- when no locale is provided, the library will fall back to the default
  JVM Locale (i.e. EPUBCheck’s behavior doesn’t change)

This PR introduces the following changes:

- Refactored `com.adobe.epubcheck.util.Messages`:
  - new static `getInstance(…)` methods to get the `Messages` instance
    for the given (optional) locale and class name (used to resolve
    the name of the underlying `ResourceBundle`).
  - the `Messages` instances are cached in a table, keyed with locale
    codes and resource bundle names.
- Refactored the `com.adobe.epubcheck.message` package:
  - `MessageDictionary` is now an interface; it's functionality is split
    in several classes to handle default messages, loading of override
    files, overridden messages, dumping message files, and severities.
  - messages and severities are cached.
- Refactored the `org.idpf.epubcheck.util.css` package, to make it
  locale-aware:
    - a `Locale` is given as an argument to the `CSSParser` constructor
    - the package `Messages` class is removed, replaced by the main
      `com.adobe.epubcheck.util.Messages`
- New `LocalizedReport` interface, extending the `Report` interface with
  locale getter/setter methods, representing a localizable report.
  - the existing `Report` interface is kept unchanged, for backwards
    compatibility
  - `MasterReport` implements this new `LocalizedReport` interface
  - the localization code checks if the underlying report is an instance
    of `LocalizedReport` before setting a new locale.
- New `LocaleHolder` facility to statically store the "current" locale
  in a thread-local variable. This is used by libraries that cannot be
  configured dynamically with a new locale (like Jing).
- Monkey-patch Jing's `Localizer` class to load Jing's resource bundles
  for the locale exposed in the `LocaleHolder` utility.
- Expose the current locale in the `ValidationContext` class
- Update the java sources compatibility version to 1.7
- New locale-related tests

Limitations:

- Jing's localization relies on the locale information exposed in a
static thread-local variable (in the `LocaleHolder` class). Any new
implementations of the `LocalizedReport` must make sure that the locale
stored in the `LocaleHolder` is updated when the locale is changed or
initialized.
- The validation messages defined in the schematron schemas are still
not localized.

Thanks to @matthew-macgregor for the original PR contribution (#650)!

Closes #650
Fixes #498
  • Loading branch information
matthew-macgregor authored and rdeltour committed Nov 25, 2018
1 parent 5045d78 commit 9b24995
Show file tree
Hide file tree
Showing 53 changed files with 1,975 additions and 1,008 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
</issueManagement>

<properties>
<java.version>1.6</java.version>
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.build.timestamp.format>yyyy-MM-dd</maven.build.timestamp.format>
Expand Down
21 changes: 19 additions & 2 deletions src/main/java/com/adobe/epubcheck/api/EpubCheck.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.zip.ZipFile;

Expand All @@ -39,9 +40,12 @@
import com.adobe.epubcheck.ocf.OCFZipPackage;
import com.adobe.epubcheck.opf.DocumentValidator;
import com.adobe.epubcheck.opf.OPFData;
import com.adobe.epubcheck.opf.ValidationContext;
import com.adobe.epubcheck.opf.ValidationContext.ValidationContextBuilder;
import com.adobe.epubcheck.util.*;
import com.adobe.epubcheck.util.CheckUtil;
import com.adobe.epubcheck.util.DefaultReportImpl;
import com.adobe.epubcheck.util.EPUBVersion;
import com.adobe.epubcheck.util.ResourceUtil;
import com.adobe.epubcheck.util.WriterReportImpl;

/**
* Public interface to epub validator.
Expand Down Expand Up @@ -180,6 +184,19 @@ public EpubCheck(InputStream inputStream, Report report, String uri, EPUBProfile
}
}
}

/**
* Allows for a per-instance override of the locale, if supported by the underlying
* {@link Report}. Otherwise takes the default host locale.
* @param locale The overridden locale.
*/
public void setLocale(Locale locale)
{
if(locale != null && report != null && report instanceof LocalizableReport)
{
((LocalizableReport) report).setLocale(locale);
}
}

/**
* Validate the file. Return true if no errors or warnings found.
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/adobe/epubcheck/api/LocalizableReport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.adobe.epubcheck.api;

import java.util.Locale;

/**
* Extends the {@link Report} interface with a method to configure the locale
* used to report messages.
*/
public interface LocalizableReport extends Report
{

/**
* Sets the locale to use in the report's messages
*/
public void setLocale(Locale locale);

/**
* Gets the locale to use in the report's messages
*/
public Locale getLocale();
}
63 changes: 56 additions & 7 deletions src/main/java/com/adobe/epubcheck/api/MasterReport.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,86 @@
import org.codehaus.jackson.annotate.JsonProperty;

import com.adobe.epubcheck.messages.Message;
import com.adobe.epubcheck.messages.LocaleHolder;
import com.adobe.epubcheck.messages.LocalizedMessageDictionary;
import com.adobe.epubcheck.messages.MessageDictionary;
import com.adobe.epubcheck.messages.MessageId;
import com.adobe.epubcheck.messages.OverriddenMessageDictionary;
import com.adobe.epubcheck.messages.Severity;
import com.adobe.epubcheck.util.Messages;
import com.adobe.epubcheck.util.ReportingLevel;
import java.util.Locale;

/**
* Reports are derived from this so that we can test for message Id coverage as well as have a centralized location for
* severity reporting level testing.
* Reports are derived from this so that we can test for message Id coverage as
* well as have a centralized location for severity reporting level testing.
*/
public abstract class MasterReport implements Report
public abstract class MasterReport implements LocalizableReport
{
public static Set<MessageId> allReportedMessageIds = new HashSet<MessageId>();
int errorCount, warningCount, fatalErrorCount, usageCount, infoCount = 0;
int reportingLevel = ReportingLevel.Info;
private int errorCount, warningCount, fatalErrorCount, usageCount, infoCount = 0;
private int reportingLevel = ReportingLevel.Info;
private String ePubName;
private MessageDictionary dictionary = new MessageDictionary(null, this);
private MessageDictionary dictionary = new LocalizedMessageDictionary();
private Messages messages;

@Override
public MessageDictionary getDictionary()
{
return dictionary;
}

/**
* Creates a report with a new {@code Messages} instance and sets the locale
* held in {@code LocaleHolder} to the default locale.
*/
protected MasterReport()
{
this(true);
}

/**
* Creates a report with a new {@code Messages} instance and sets the locale
* held in {@code LocaleHolder} to the default locale only if the given flag is
* <code>true</code>.
*
* @param setLocale
* whether to update the locale held in {@code LocaleHolder}
*/
protected MasterReport(boolean setLocale)
{
messages = Messages.getInstance();
if (setLocale)
{
LocaleHolder.set(Locale.getDefault());
}
}

@Override
public void setLocale(Locale locale)
{
dictionary = new LocalizedMessageDictionary(locale);
messages = Messages.getInstance(locale);
// Note: we also store the locale statically (thread local) for libraries
// which are not locale-context aware (like Jing).
LocaleHolder.set(locale);
}

@Override
public Locale getLocale()
{
return messages.getLocale();
}

public Messages getMessages()
{
return messages;
}

@Override
public void setOverrideFile(File overrideFile)
{
getDictionary().setOverrideFile(overrideFile);
dictionary = new OverriddenMessageDictionary(overrideFile, this);
}

@JsonProperty
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/adobe/epubcheck/api/QuietReport.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public final class QuietReport extends MasterReport

private QuietReport()
{
super();
super(false);
}

@Override
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/adobe/epubcheck/css/CSSChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,16 @@ void parseItem(CssSource source, CSSHandler handler)
{
if (this.mode == Mode.FILE)
{
new CssParser().parse(source, handler, handler);
new CssParser(context.locale).parse(source, handler, handler);
}
else
{
new CssParser().parse(new StringReader(this.value), this.path, handler, handler);
new CssParser(context.locale).parse(new StringReader(this.value), this.path, handler, handler);
}
}
else
{
new CssParser()
new CssParser(context.locale)
.parseStyleAttribute(new StringReader(this.value), this.path, handler, handler);
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/com/adobe/epubcheck/ctc/EpubCSSCheck.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.io.InputStream;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -14,6 +15,7 @@
import org.idpf.epubcheck.util.css.CssSource;

import com.adobe.epubcheck.api.EPUBLocation;
import com.adobe.epubcheck.api.LocalizableReport;
import com.adobe.epubcheck.api.Report;
import com.adobe.epubcheck.ctc.css.EpubCSSCheckCSSHandler;
import com.adobe.epubcheck.ctc.epubpackage.EpubPackage;
Expand Down Expand Up @@ -83,7 +85,9 @@ public boolean validate()
{
InputStream inputStream = getInputStream(fileToParse);
CssSource source = new CssSource(fileToParse, inputStream);
CssParser parser = new CssParser();
CssParser parser = new CssParser(
(report instanceof LocalizableReport) ? ((LocalizableReport) report).getLocale()
: Locale.getDefault());
handler.setPath(fileToParse);

parser.parse(source, handler, handler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.xml.sax.helpers.DefaultHandler;

import com.adobe.epubcheck.api.EPUBLocation;
import com.adobe.epubcheck.api.LocalizableReport;
import com.adobe.epubcheck.api.Report;
import com.adobe.epubcheck.ctc.css.EpubCSSCheckCSSHandler;
import com.adobe.epubcheck.messages.MessageId;
Expand Down Expand Up @@ -253,7 +254,9 @@ private void parseCurrentStyleTag(StyleAttribute currentStyleTag)
String s = currentStyleTag.getValue();
InputStream inputStream = new ByteArrayInputStream(s.getBytes("UTF-8"));
CssSource source = new CssSource(this.getFileName(), inputStream);
CssParser parser = new CssParser();
CssParser parser = new CssParser(
(report instanceof LocalizableReport) ? ((LocalizableReport) report).getLocale()
: Locale.getDefault());
handler.setPath(this.getFileName());

HashMap<String, EpubCSSCheckCSSHandler.ClassUsage> localStyleMap = localStyles.peek();
Expand Down
Loading

0 comments on commit 9b24995

Please sign in to comment.