com.adobe.epubcheck.messages.MessageDictionary Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of epubcheck Show documentation
Show all versions of epubcheck Show documentation
EpubCheck is a tool to validate IDPF EPUB files. It can detect many types of errors in EPUB.
OCF container structure, OPF and OPS mark-up, and internal reference consistency are checked.
EpubCheck can be run as a standalone command-line tool, installed as a Java server-side web application
or used as a Java library.
package com.adobe.epubcheck.messages;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.adobe.epubcheck.api.Report;
import com.adobe.epubcheck.util.PathUtil;
import com.adobe.epubcheck.util.outWriter;
import com.google.common.base.Charsets;
/**
* This is a dictionary that maps the text of a message to a severity.
*/
public class MessageDictionary
{
File overrideFile;
Report report;
static Map defaultSeverityMap = null;
static Pattern parameterPattern = Pattern.compile("%(\\d+)\\$s");
public void setOverrideFile(File value)
{
overrideFile = value;
initMessageMap();
}
public MessageDictionary(File overrideFile, Report report)
{
this.report = report;
this.overrideFile = overrideFile;
initMessageMap();
}
Map messages = new HashMap();
static final ResourceBundle labels = ResourceBundle.getBundle("com.adobe.epubcheck.messages.MessageBundle",
Locale.getDefault(), new UTF8Control());
public Message getMessage(MessageId id)
{
return this.messages.get(id);
}
static Map getDefaultSeverities()
{
if (defaultSeverityMap == null)
{
Map map = new HashMap(MessageId.values().length);
//Accessibility
map.put(MessageId.ACC_001, Severity.USAGE);
map.put(MessageId.ACC_002, Severity.USAGE);
map.put(MessageId.ACC_003, Severity.SUPPRESSED);
map.put(MessageId.ACC_004, Severity.SUPPRESSED);
map.put(MessageId.ACC_005, Severity.SUPPRESSED);
map.put(MessageId.ACC_006, Severity.SUPPRESSED);
map.put(MessageId.ACC_007, Severity.USAGE);
map.put(MessageId.ACC_008, Severity.USAGE);
map.put(MessageId.ACC_009, Severity.WARNING);
map.put(MessageId.ACC_010, Severity.SUPPRESSED);
map.put(MessageId.ACC_011, Severity.WARNING);
map.put(MessageId.ACC_012, Severity.SUPPRESSED);
map.put(MessageId.ACC_013, Severity.USAGE);
map.put(MessageId.ACC_014, Severity.USAGE);
map.put(MessageId.ACC_015, Severity.USAGE);
map.put(MessageId.ACC_016, Severity.USAGE);
map.put(MessageId.ACC_017, Severity.USAGE);
// CHK
map.put(MessageId.CHK_001, Severity.ERROR);
map.put(MessageId.CHK_002, Severity.ERROR);
map.put(MessageId.CHK_003, Severity.ERROR);
map.put(MessageId.CHK_004, Severity.ERROR);
map.put(MessageId.CHK_005, Severity.ERROR);
map.put(MessageId.CHK_006, Severity.ERROR);
map.put(MessageId.CHK_007, Severity.ERROR);
//CSS
map.put(MessageId.CSS_001, Severity.ERROR);
map.put(MessageId.CSS_002, Severity.ERROR);
map.put(MessageId.CSS_003, Severity.ERROR);
map.put(MessageId.CSS_004, Severity.ERROR);
map.put(MessageId.CSS_005, Severity.ERROR);
map.put(MessageId.CSS_006, Severity.WARNING);
map.put(MessageId.CSS_007, Severity.WARNING);
map.put(MessageId.CSS_008, Severity.ERROR);
map.put(MessageId.CSS_009, Severity.USAGE);
map.put(MessageId.CSS_010, Severity.WARNING);
map.put(MessageId.CSS_011, Severity.SUPPRESSED);
map.put(MessageId.CSS_012, Severity.USAGE);
map.put(MessageId.CSS_013, Severity.USAGE);
map.put(MessageId.CSS_015, Severity.ERROR);
map.put(MessageId.CSS_016, Severity.SUPPRESSED);
map.put(MessageId.CSS_017, Severity.WARNING);
map.put(MessageId.CSS_019, Severity.WARNING);
map.put(MessageId.CSS_020, Severity.USAGE);
map.put(MessageId.CSS_021, Severity.USAGE);
map.put(MessageId.CSS_022, Severity.USAGE);
map.put(MessageId.CSS_023, Severity.USAGE);
map.put(MessageId.CSS_024, Severity.USAGE);
map.put(MessageId.CSS_025, Severity.USAGE);
map.put(MessageId.CSS_027, Severity.USAGE);
map.put(MessageId.CSS_028, Severity.USAGE);
//HTML
map.put(MessageId.HTM_001, Severity.ERROR);
map.put(MessageId.HTM_002, Severity.WARNING);
map.put(MessageId.HTM_003, Severity.ERROR);
map.put(MessageId.HTM_004, Severity.ERROR);
map.put(MessageId.HTM_005, Severity.USAGE);
map.put(MessageId.HTM_006, Severity.USAGE);
map.put(MessageId.HTM_007, Severity.WARNING);
map.put(MessageId.HTM_008, Severity.ERROR);
map.put(MessageId.HTM_009, Severity.ERROR);
map.put(MessageId.HTM_010, Severity.USAGE);
map.put(MessageId.HTM_011, Severity.ERROR);
map.put(MessageId.HTM_012, Severity.USAGE);
map.put(MessageId.HTM_013, Severity.USAGE);
map.put(MessageId.HTM_014, Severity.WARNING);
map.put(MessageId.HTM_014a, Severity.WARNING);
map.put(MessageId.HTM_015, Severity.WARNING);
map.put(MessageId.HTM_016, Severity.WARNING);
map.put(MessageId.HTM_017, Severity.ERROR);
map.put(MessageId.HTM_018, Severity.ERROR);
map.put(MessageId.HTM_019, Severity.ERROR);
map.put(MessageId.HTM_020, Severity.USAGE);
map.put(MessageId.HTM_021, Severity.USAGE);
map.put(MessageId.HTM_022, Severity.USAGE);
map.put(MessageId.HTM_023, Severity.WARNING);
map.put(MessageId.HTM_024, Severity.USAGE);
map.put(MessageId.HTM_025, Severity.WARNING);
map.put(MessageId.HTM_027, Severity.USAGE);
map.put(MessageId.HTM_028, Severity.USAGE);
map.put(MessageId.HTM_029, Severity.USAGE);
map.put(MessageId.HTM_033, Severity.USAGE);
map.put(MessageId.HTM_036, Severity.SUPPRESSED);
map.put(MessageId.HTM_038, Severity.USAGE);
map.put(MessageId.HTM_043, Severity.USAGE);
map.put(MessageId.HTM_044, Severity.USAGE);
map.put(MessageId.HTM_045, Severity.USAGE);
map.put(MessageId.HTM_046, Severity.ERROR);
map.put(MessageId.HTM_047, Severity.ERROR);
map.put(MessageId.HTM_048, Severity.ERROR);
map.put(MessageId.HTM_049, Severity.ERROR);
map.put(MessageId.HTM_050, Severity.USAGE);
//Media
map.put(MessageId.MED_001, Severity.ERROR);
map.put(MessageId.MED_002, Severity.ERROR);
map.put(MessageId.MED_003, Severity.ERROR);
map.put(MessageId.MED_004, Severity.ERROR);
map.put(MessageId.MED_005, Severity.ERROR);
map.put(MessageId.MED_006, Severity.USAGE);
//NAV
map.put(MessageId.NAV_001, Severity.ERROR);
map.put(MessageId.NAV_002, Severity.USAGE);
map.put(MessageId.NAV_003, Severity.SUPPRESSED);
//NCX
map.put(MessageId.NCX_002, Severity.ERROR);
map.put(MessageId.NCX_003, Severity.USAGE);
map.put(MessageId.NCX_005, Severity.USAGE);
map.put(MessageId.NCX_006, Severity.USAGE);
//OPF
map.put(MessageId.OPF_001, Severity.ERROR);
map.put(MessageId.OPF_002, Severity.FATAL);
map.put(MessageId.OPF_003, Severity.WARNING);
map.put(MessageId.OPF_004, Severity.WARNING);
map.put(MessageId.OPF_004a, Severity.ERROR);
map.put(MessageId.OPF_004b, Severity.ERROR);
map.put(MessageId.OPF_004c, Severity.ERROR);
map.put(MessageId.OPF_004d, Severity.ERROR);
map.put(MessageId.OPF_004e, Severity.WARNING);
map.put(MessageId.OPF_004f, Severity.WARNING);
map.put(MessageId.OPF_005, Severity.ERROR);
map.put(MessageId.OPF_006, Severity.ERROR);
map.put(MessageId.OPF_007, Severity.WARNING);
map.put(MessageId.OPF_007a, Severity.ERROR);
map.put(MessageId.OPF_007b, Severity.ERROR);
map.put(MessageId.OPF_008, Severity.ERROR);
map.put(MessageId.OPF_009, Severity.ERROR);
map.put(MessageId.OPF_010, Severity.ERROR);
map.put(MessageId.OPF_011, Severity.ERROR);
map.put(MessageId.OPF_012, Severity.ERROR);
map.put(MessageId.OPF_013, Severity.ERROR);
map.put(MessageId.OPF_014, Severity.ERROR);
map.put(MessageId.OPF_015, Severity.ERROR);
map.put(MessageId.OPF_016, Severity.ERROR);
map.put(MessageId.OPF_017, Severity.ERROR);
map.put(MessageId.OPF_018, Severity.WARNING);
map.put(MessageId.OPF_019, Severity.FATAL);
map.put(MessageId.OPF_020, Severity.SUPPRESSED);
map.put(MessageId.OPF_021, Severity.WARNING);
map.put(MessageId.OPF_022, Severity.ERROR);
map.put(MessageId.OPF_024, Severity.ERROR);
map.put(MessageId.OPF_025, Severity.ERROR);
map.put(MessageId.OPF_026, Severity.ERROR);
map.put(MessageId.OPF_027, Severity.ERROR);
map.put(MessageId.OPF_028, Severity.ERROR);
map.put(MessageId.OPF_029, Severity.ERROR);
map.put(MessageId.OPF_030, Severity.ERROR);
map.put(MessageId.OPF_031, Severity.ERROR);
map.put(MessageId.OPF_032, Severity.ERROR);
map.put(MessageId.OPF_033, Severity.ERROR);
map.put(MessageId.OPF_034, Severity.ERROR);
map.put(MessageId.OPF_035, Severity.WARNING);
map.put(MessageId.OPF_036, Severity.USAGE);
map.put(MessageId.OPF_037, Severity.WARNING);
map.put(MessageId.OPF_038, Severity.WARNING);
map.put(MessageId.OPF_039, Severity.WARNING);
map.put(MessageId.OPF_040, Severity.ERROR);
map.put(MessageId.OPF_041, Severity.ERROR);
map.put(MessageId.OPF_042, Severity.ERROR);
map.put(MessageId.OPF_043, Severity.ERROR);
map.put(MessageId.OPF_044, Severity.ERROR);
map.put(MessageId.OPF_045, Severity.ERROR);
map.put(MessageId.OPF_046, Severity.ERROR);
map.put(MessageId.OPF_047, Severity.USAGE);
map.put(MessageId.OPF_048, Severity.ERROR);
map.put(MessageId.OPF_049, Severity.ERROR);
map.put(MessageId.OPF_050, Severity.ERROR);
map.put(MessageId.OPF_051, Severity.SUPPRESSED);
map.put(MessageId.OPF_052, Severity.ERROR);
map.put(MessageId.OPF_053, Severity.WARNING);
map.put(MessageId.OPF_054, Severity.ERROR);
map.put(MessageId.OPF_055, Severity.WARNING);
map.put(MessageId.OPF_056, Severity.WARNING);
map.put(MessageId.OPF_057, Severity.SUPPRESSED);
map.put(MessageId.OPF_058, Severity.USAGE);
map.put(MessageId.OPF_059, Severity.USAGE);
map.put(MessageId.OPF_060, Severity.ERROR);
map.put(MessageId.OPF_061, Severity.WARNING);
map.put(MessageId.OPF_062, Severity.USAGE);
map.put(MessageId.OPF_063, Severity.WARNING);
//PKG
map.put(MessageId.PKG_001, Severity.WARNING);
map.put(MessageId.PKG_003, Severity.ERROR);
map.put(MessageId.PKG_004, Severity.FATAL);
map.put(MessageId.PKG_005, Severity.ERROR);
map.put(MessageId.PKG_006, Severity.ERROR);
map.put(MessageId.PKG_007, Severity.ERROR);
map.put(MessageId.PKG_008, Severity.FATAL);
map.put(MessageId.PKG_009, Severity.ERROR);
map.put(MessageId.PKG_010, Severity.WARNING);
map.put(MessageId.PKG_011, Severity.ERROR);
map.put(MessageId.PKG_012, Severity.WARNING);
map.put(MessageId.PKG_013, Severity.ERROR);
map.put(MessageId.PKG_014, Severity.WARNING);
map.put(MessageId.PKG_015, Severity.FATAL);
map.put(MessageId.PKG_016, Severity.WARNING);
map.put(MessageId.PKG_017, Severity.WARNING);
map.put(MessageId.PKG_018, Severity.FATAL);
map.put(MessageId.PKG_020, Severity.ERROR);
map.put(MessageId.PKG_021, Severity.ERROR);
map.put(MessageId.PKG_022, Severity.WARNING);
//Resources
map.put(MessageId.RSC_001, Severity.ERROR);
map.put(MessageId.RSC_002, Severity.FATAL);
map.put(MessageId.RSC_003, Severity.ERROR);
map.put(MessageId.RSC_004, Severity.ERROR);
map.put(MessageId.RSC_005, Severity.ERROR);
map.put(MessageId.RSC_006, Severity.ERROR);
map.put(MessageId.RSC_007, Severity.ERROR);
map.put(MessageId.RSC_008, Severity.ERROR);
map.put(MessageId.RSC_009, Severity.ERROR);
map.put(MessageId.RSC_010, Severity.ERROR);
map.put(MessageId.RSC_011, Severity.WARNING);
map.put(MessageId.RSC_012, Severity.ERROR);
map.put(MessageId.RSC_013, Severity.ERROR);
map.put(MessageId.RSC_014, Severity.ERROR);
map.put(MessageId.RSC_015, Severity.ERROR);
map.put(MessageId.RSC_016, Severity.FATAL);
map.put(MessageId.RSC_017, Severity.WARNING);
map.put(MessageId.RSC_018, Severity.WARNING);
//Scripting
map.put(MessageId.SCP_001, Severity.USAGE);
map.put(MessageId.SCP_002, Severity.USAGE);
map.put(MessageId.SCP_003, Severity.USAGE);
map.put(MessageId.SCP_004, Severity.ERROR);
map.put(MessageId.SCP_005, Severity.ERROR);
map.put(MessageId.SCP_006, Severity.USAGE);
map.put(MessageId.SCP_007, Severity.USAGE);
map.put(MessageId.SCP_008, Severity.USAGE);
map.put(MessageId.SCP_009, Severity.USAGE);
map.put(MessageId.SCP_010, Severity.USAGE);
defaultSeverityMap = map;
}
return defaultSeverityMap;
}
void initDefaultMessageMap()
{
messages.clear();
for (Map.Entry entry : getDefaultSeverities().entrySet())
{
this.addMessage(entry.getKey(), entry.getValue());
}
}
void initMessageMap()
{
initDefaultMessageMap();
loadOverriddenMessageSeverities();
}
void loadOverriddenMessageSeverities()
{
if (overrideFile != null)
{
int lineNumber = -1;
int columnNumber = -1;
String line;
FileInputStream fis = null;
BufferedReader br = null;
try
{
fis = new FileInputStream(overrideFile);
br = new BufferedReader(new InputStreamReader(fis, Charset.forName("UTF-8")));
lineNumber = 1;
while (null != (line = br.readLine()))
{
if (1 == lineNumber)
{
if (line.toLowerCase().startsWith("id"))
{
// optionally eat the first line
continue;
}
}
columnNumber = 0;
String[] fields = line.split("\t");
if (fields.length >= 2)
{
MessageId id;
try
{
id = MessageId.fromString(fields[0]);
}
catch (NoSuchElementException unused)
{
report.message(MessageId.CHK_002, new MessageLocation("", lineNumber, 0), fields[0], PathUtil.removeWorkingDirectory(overrideFile.getAbsolutePath()));
continue;
}
Severity newSeverity;
try
{
columnNumber += 1 + fields[0].length();
newSeverity = Severity.fromString(fields[1]);
}
catch (NoSuchElementException ignored)
{
report.message(MessageId.CHK_003, new MessageLocation("", lineNumber, columnNumber), fields[1], PathUtil.removeWorkingDirectory(overrideFile.getAbsolutePath()));
continue;
}
Message message = messages.get(id);
String messageText = message.getMessage();
if (fields.length >= 3 && fields[2] != null && fields[2].length() > 0)
{
columnNumber += 1 + fields[1].length();
messageText = checkMessageForParameterCount(lineNumber, columnNumber, message.getMessage(), fields[2]);
if (messageText == null)
{
report.message(MessageId.CHK_004, new MessageLocation("", lineNumber, 0, fields[2]), PathUtil.removeWorkingDirectory(overrideFile.getAbsolutePath()));
continue;
}
}
if (messageText != null)
{
Severity oldSeverity = getDefaultSeverities().get(message.getID());
if (newSeverity != oldSeverity)
{
messageText = String.format(" (severity overridden from %1$s) %2$s", oldSeverity, messageText);
}
}
String suggestionText = message.getSuggestion();
if (fields.length >= 4 && fields[3] != null && fields[3].length() > 0)
{
columnNumber += 1 + fields[1].length();
suggestionText = checkMessageForParameterCount(lineNumber, columnNumber, message.getSuggestion(), fields[3]);
if (suggestionText == null)
{
report.message(MessageId.CHK_005, new MessageLocation("", lineNumber, 0, fields[3]), PathUtil.removeWorkingDirectory(overrideFile.getAbsolutePath()));
continue;
}
}
if (message != null &&
((newSeverity != message.getSeverity()) ||
(messageText.compareTo(message.getMessage()) != 0) ||
(suggestionText.compareTo(message.getSuggestion()) != 0)))
{
messages.put(id, new Message(message.getID(), newSeverity, message.getSeverity(), messageText, suggestionText));
}
}
++lineNumber;
}
}
catch (FileNotFoundException fnf)
{
report.message(MessageId.CHK_001, new MessageLocation(overrideFile.getAbsolutePath(), -1, -1));
}
catch (IOException ex)
{
report.message(MessageId.CHK_007, new MessageLocation("", lineNumber, columnNumber), PathUtil.removeWorkingDirectory(overrideFile.getAbsolutePath()), ex.getMessage());
}
finally
{
try
{
if (br != null)
{
br.close();
}
if (fis != null)
{
fis.close();
}
}
catch (IOException ignored)
{
}
}
}
}
String checkMessageForParameterCount(int lineNumber, int columnNumber, String originalText, String newText)
{
if (newText != null)
{
int maxOriginal = getParameterCount(lineNumber, columnNumber, originalText);
int maxNew = getParameterCount(lineNumber, columnNumber, newText);
if (maxNew <= maxOriginal)
{
return newText;
}
return null;
}
return originalText;
}
int getParameterCount(int lineNumber, int columnNumber, String text)
{
int max = 0;
{
Matcher m = parameterPattern.matcher(text);
while (m.find())
{
int absoluteColumnNumber = columnNumber + m.start();
String s = m.group(1);
try
{
Integer number = Integer.parseInt(s);
if (number > max)
{
max = number;
}
}
catch (NumberFormatException ex)
{
String pathAdjustedFileName = PathUtil.removeWorkingDirectory(overrideFile.getAbsolutePath());
report.message(MessageId.CHK_006, new MessageLocation("", lineNumber, absoluteColumnNumber, text), pathAdjustedFileName);
}
}
}
return max;
}
void addMessage(MessageId messageId, Severity severity)
{
try
{
messages.put(messageId, new Message(messageId, severity, labels.getString(messageId.name()), getSuggestion(messageId)));
}
catch (Exception e)
{
outWriter.println("Couldn't locate message " + messageId.name());
}
}
String getSuggestion(MessageId messageId)
{
String result;
try
{
result = labels.getString(messageId.name() + "_SUG");
}
catch (Exception ignore)
{
result = "";
}
return result;
}
public void dumpMessages(OutputStreamWriter outputStream) throws
IOException
{
// Output the messages in a tab separated format
outputStream.write("ID\tSeverity\tMessage\tSuggestion\n");
for (MessageId id : MessageId.values())
{
StringBuilder sb = new StringBuilder();
sb.append(id.toString());
sb.append("\t");
Message message = this.getMessage(id);
if (message != null)
{
sb.append(message.getSeverity());
sb.append("\t");
sb.append(message.getMessage());
sb.append("\t");
sb.append(message.getSuggestion());
}
else
{
sb.append("null\tnull\tnull\tnull");
}
sb.append("\n");
outputStream.write(sb.toString());
}
}
private static class UTF8Control extends Control
{
public ResourceBundle newBundle
(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws
IllegalAccessException,
InstantiationException,
IOException
{
// The below is a copy of the default implementation.
String bundleName = toBundleName(baseName, locale);
String resourceName = toResourceName(bundleName, "properties"); //$NON-NLS-1$
ResourceBundle bundle = null;
InputStream stream = null;
if (reload)
{
URL url = loader.getResource(resourceName);
if (url != null)
{
URLConnection connection = url.openConnection();
if (connection != null)
{
connection.setUseCaches(false);
stream = connection.getInputStream();
}
}
}
else
{
stream = loader.getResourceAsStream(resourceName);
}
if (stream != null)
{
try
{
// Only this line is changed to make it to read properties files as UTF-8.
bundle = new PropertyResourceBundle(
new BufferedReader(
new InputStreamReader(stream, Charsets.UTF_8)));
}
finally
{
stream.close();
}
}
return bundle;
}
}
}