Skip to content

Commit

Permalink
feat: implement Structure Semantics Vocab changes for EPUB 3.2
Browse files Browse the repository at this point in the history
Fix #531, Fix #903, Fix #962, Fix #963

New messages:

- `OPF-086` (`WARNING`) is reported when a deprecated property is used
  (e.g. in an `epub:type` attribute).
- `OPF_086_SUG.<property-name>` messages store the suggested replacement
   for deprecated properties.
- `OPF-087` (`ERROR`) is reported when a disallowed property is used
  (e.g. `epub:type` properties disallowed in an XHTML context).
- `OPF-088` (`USAGE`) is reported when an unprefixed property isn't
  recognized as defined in the EPUB Structural Semantics Vocabulary.

EPUB Structural Semantics Vocabulary changes:

- recognize the new properties `endnote`, `endnotes`, and `tip`.
- deprecate the properties `annoref`, `annotation`, `help`, `marginalia`,
 `note`, `rearnote`, `rearnotes`, `sidebar`, `subchapter`, and `warning`.
- disallow on XHTML Content Documents the properties `figure`, `list`,
 `list-item`, table`, `table-cell`, and `table-row`.

Internal Vocabulary changes:

- new unchecked vocab `PRISM_VOCAB` in the `ForeignVocabs` collection
- new vocab `MagazineNavigationVocab`, currently as an unchecked vocab
- new unchecked vocab `UNCHECKED_VOCAB` in `StructureVocab`, associated
  to the SSV URI, to accept properties undefined in the SSV.
- update the `EPUB_TYPES` StructureVocab to set the deprecate/disallowed
  status its properties
- register the reserved prefixes `msv` and `prism` as unchecked vocabs
  in Content Documents
- allow undefined properties in `epub:type` (by adding the SSV unchecked
  vocab to the default aggregated vocab)

Internal API changes:

- in the `Property` class:
  - new method `getVocabURI` to get the URI string of the vocab where
    the property is defined
  - new method `isDeprecated` to get if the property is deprecated.
  - new method `isAllowed(Validation context)` to get if the property
    is allowed in the given validation context.
- new interface `PropertyStatus`
  - holds the deprecated/disallowed status of a property, as getter methods
  - provides static implementations for 'allowed', 'deprecated', and
    'disallowed on Content Documents'.
- in the `VocabUtil` class:
  - the `parseProperty*` methods now use a `ValidationContext` parameter
    instead of a `Report` parameter, to be allowed to query the allowed
    state of properties from the context.
  - the `parserProperty*` methods will now report `OPF_086` and `OPF_087`
    for deprecated and disallowed properties (resp.).
- in the `LocalizedMessages` class:
  - the method `getSuggestion(String id)` is now public
  - a new method `getSuggestion(String id, String key)` is available
    to get a keyed suggestion string for a specific message ID.
- new `ThrowingResourceProvider` class, implementing `GenericResourceProvider`
  to always throw an exception, to be used in tests.

Tests changes:

- renamed and add new tests for `epub:type` attribute values
- remove instances of deprecated `epub:type` properties in other tests
  • Loading branch information
rdeltour committed Feb 5, 2019
1 parent ec08e37 commit 3454da5
Show file tree
Hide file tree
Showing 52 changed files with 784 additions and 368 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ private void initialize()
severities.put(MessageId.OPF_083, Severity.ERROR);
severities.put(MessageId.OPF_084, Severity.ERROR);
severities.put(MessageId.OPF_085, Severity.WARNING);
severities.put(MessageId.OPF_086, Severity.WARNING);
severities.put(MessageId.OPF_087, Severity.ERROR);
severities.put(MessageId.OPF_088, Severity.USAGE);

// PKG
severities.put(MessageId.PKG_001, Severity.WARNING);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,42 @@ private String getMessageAsString(MessageId id)
return getStringFromBundle(id.name());
}

private String getSuggestion(MessageId id)
/**
* Returns the suggestion message for the given message ID.
* In other words, for a message ID of `XXX_NNN`,
* returns the bundle message named `XXX_NNN_SUG`.
*
* @param id a message ID
* @return the associated suggestion, or the empty string if there's none.
*/
public String getSuggestion(MessageId id)
{
return getStringFromBundle(id.name() + "_SUG");
}


/**
* Returns the suggestion message for the given message ID and key.
* In other words, for a message ID of `XXX_NNN`, and a key `key`,
* returns the bundle message named `XXX_NNN_SUG.key`.
* If the suggestion key is not found, returns the bundle message
* named `XXX_NNN_SUG.default`.
* If this latter is not found, returns the bundle message nameed
* `XXX_NNN_SUG`.
*
* @param id a message ID
* @param key the key of a specific suggestion string
* @return the associated suggestion string
*/
public String getSuggestion(MessageId id, String key)
{
String messageKey = id.name() + "_SUG." + key;
String messageDefaultKey = id.name() + "_SUG.default";
return bundle.containsKey(messageKey) ? getStringFromBundle(messageKey)
: (bundle.containsKey(messageDefaultKey) ? getStringFromBundle(messageDefaultKey)
: getSuggestion(id));
}

public static class UTF8Control extends ResourceBundle.Control
{

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/adobe/epubcheck/messages/MessageId.java
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ public enum MessageId implements Comparable<MessageId>
OPF_083("OPF-083"),
OPF_084("OPF-084"),
OPF_085("OPF-085"),
OPF_086("OPF-086"),
OPF_087("OPF-087"),
OPF_088("OPF-088"),

// Messages relating to the entire package
PKG_001("PKG-001"),
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/com/adobe/epubcheck/opf/OPFHandler30.java
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ private void processLink(XMLElement e)

private void processItemrefProperties(OPFItem.Builder builder, String property)
{
Set<Property> properties = VocabUtil.parsePropertyList(property, itemrefVocabs, report,
Set<Property> properties = VocabUtil.parsePropertyList(property, itemrefVocabs, context,
EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()));
builder.properties(properties);
if (properties
Expand Down Expand Up @@ -489,7 +489,7 @@ private void processItemProperties(OPFItem.Builder builder, String property, Str
return;
}

Set<Property> properties = VocabUtil.parsePropertyList(property, itemVocabs, report,
Set<Property> properties = VocabUtil.parsePropertyList(property, itemVocabs, context,
EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()));
Set<ITEM_PROPERTIES> itemProps = Property.filter(properties, ITEM_PROPERTIES.class);

Expand All @@ -508,15 +508,15 @@ private void processItemProperties(OPFItem.Builder builder, String property, Str

private Set<Property> processLinkRel(String rel)
{
return VocabUtil.parsePropertyList(rel, linkrelVocabs, report,
return VocabUtil.parsePropertyList(rel, linkrelVocabs, context,
EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()));
}

private void processMeta(XMLElement e)
{
// get the property
Optional<Property> prop = VocabUtil.parseProperty(e.getAttribute("property"), metaVocabs,
report, EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()));
context, EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()));

if (prop.isPresent() && !metadataBuilders.isEmpty())
{
Expand All @@ -525,7 +525,7 @@ private void processMeta(XMLElement e)
}

// just parse the scheme for vocab errors
VocabUtil.parseProperty(e.getAttribute("scheme"), metaVocabs, report,
VocabUtil.parseProperty(e.getAttribute("scheme"), metaVocabs, context,
EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()));
}

Expand Down
28 changes: 21 additions & 7 deletions src/main/java/com/adobe/epubcheck/ops/OPSHandler30.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
import com.adobe.epubcheck.vocab.ComicsVocab;
import com.adobe.epubcheck.vocab.DataNavVocab;
import com.adobe.epubcheck.vocab.DictVocab;
import com.adobe.epubcheck.vocab.EnumVocab;
import com.adobe.epubcheck.vocab.EpubCheckVocab;
import com.adobe.epubcheck.vocab.ForeignVocabs;
import com.adobe.epubcheck.vocab.IndexVocab;
import com.adobe.epubcheck.vocab.MagazineNavigationVocab;
import com.adobe.epubcheck.vocab.PackageVocabs;
import com.adobe.epubcheck.vocab.PackageVocabs.ITEM_PROPERTIES;
import com.adobe.epubcheck.vocab.Property;
Expand All @@ -38,10 +39,8 @@
import com.adobe.epubcheck.xml.XMLAttribute;
import com.adobe.epubcheck.xml.XMLElement;
import com.adobe.epubcheck.xml.XMLParser;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
Expand All @@ -53,10 +52,13 @@ public class OPSHandler30 extends OPSHandler

private static Map<String, Vocab> RESERVED_VOCABS = ImmutableMap.<String, Vocab> of("",
AggregateVocab.of(StructureVocab.VOCAB, StagingEdupubVocab.VOCAB, DataNavVocab.VOCAB,
DictVocab.VOCAB, IndexVocab.VOCAB, ComicsVocab.VOCAB));
DictVocab.VOCAB, IndexVocab.VOCAB, ComicsVocab.VOCAB, StructureVocab.UNCHECKED_VOCAB),
MagazineNavigationVocab.PREFIX, MagazineNavigationVocab.VOCAB, ForeignVocabs.PRISM_PREFIX,
ForeignVocabs.PRISM_VOCAB);
private static Map<String, Vocab> ALTCSS_VOCABS = ImmutableMap.<String, Vocab> of("",
AltStylesheetVocab.VOCAB);
private static Map<String, Vocab> KNOWN_VOCAB_URIS = ImmutableMap.of();
private static Map<String, Vocab> KNOWN_VOCAB_URIS = ImmutableMap.of(MagazineNavigationVocab.URI,
MagazineNavigationVocab.VOCAB, ForeignVocabs.PRISM_URI, ForeignVocabs.PRISM_VOCAB);
private static Set<String> DEFAULT_VOCAB_URIS = ImmutableSet.of(StructureVocab.URI);

private Map<String, Vocab> vocabs = RESERVED_VOCABS;
Expand Down Expand Up @@ -138,9 +140,21 @@ protected void checkType(XMLElement e, String type)
{
return;
}
Set<Property> propList = VocabUtil.parsePropertyList(type, vocabs, report,
Set<Property> propList = VocabUtil.parsePropertyList(type, vocabs, context,
EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()));
checkTypes(Property.filter(propList, StructureVocab.EPUB_TYPES.class));

// Check unrecognized properties from the structure vocab
for (Property property : propList)
{
if (StructureVocab.URI.equals(property.getVocabURI())) try
{
property.toEnum();
} catch (UnsupportedOperationException ex)
{
report.message(MessageId.OPF_088, parser.getLocation(), property.getName());
}
}

// Check the 'region-based' property (Data Navigation Documents)
if (propList.contains(DataNavVocab.VOCAB.get(DataNavVocab.EPUB_TYPES.REGION_BASED)))
Expand Down Expand Up @@ -322,7 +336,7 @@ protected void processLink(XMLElement e)
return;
}

Set<Property> properties = VocabUtil.parsePropertyList(classAttribute, ALTCSS_VOCABS, report,
Set<Property> properties = VocabUtil.parsePropertyList(classAttribute, ALTCSS_VOCABS, context,
EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()));
Set<AltStylesheetVocab.PROPERTIES> altClasses = Property.filter(properties,
AltStylesheetVocab.PROPERTIES.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ else if (name.equals("body") || name.equals("par"))

private void checkType(String type)
{
VocabUtil.parsePropertyList(type, vocabs, report,
VocabUtil.parsePropertyList(type, vocabs, context,
EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.adobe.epubcheck.util;

import java.io.IOException;
import java.io.InputStream;

public final class ThrowingResourceProvider implements GenericResourceProvider
{

@Override
public InputStream getInputStream(String path)
throws IOException
{
throw new IOException();
}

}
4 changes: 4 additions & 0 deletions src/main/java/com/adobe/epubcheck/vocab/ForeignVocabs.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public final class ForeignVocabs
public static final String ONIX_URI = "http://www.editeur.org/ONIX/book/codelists/current.html#";
public static final Vocab ONIX_VOCAB = new UncheckedVocab(ONIX_URI, ONIX_PREFIX);

public static final String PRISM_PREFIX = "prism";
public static final String PRISM_URI = "http://www.prismstandard.org/specifications/3.0/PRISM_CV_Spec_3.0.htm#";
public static final Vocab PRISM_VOCAB = new UncheckedVocab(PRISM_URI, PRISM_PREFIX);

public static final String SCHEMA_PREFIX = "schema";
public static final String SCHEMA_URI = "http://schema.org/";
public static final Vocab SCHEMA_VOCAB = new UncheckedVocab(SCHEMA_URI, SCHEMA_PREFIX);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.adobe.epubcheck.vocab;

public final class MagazineNavigationVocab
{
public static final String PREFIX = "msv";
public static final String URI = "http://www.idpf.org/epub/vocab/structure/magazine/#";
public static final Vocab VOCAB = new UncheckedVocab(URI, PREFIX);


private MagazineNavigationVocab()
{
}

}
42 changes: 41 additions & 1 deletion src/main/java/com/adobe/epubcheck/vocab/Property.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.EnumSet;
import java.util.Set;

import com.adobe.epubcheck.opf.ValidationContext;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
Expand All @@ -20,10 +21,12 @@
*/
public final class Property
{
private final String base;
private final String name;
private final String prefixedName;
private final String fullName;
private final Enum<?> enumee;
private final PropertyStatus status;

/**
* Creates a new instance from a short name, a prefix, and a stem URI.
Expand Down Expand Up @@ -90,12 +93,15 @@ public E apply(Property input)
}), Predicates.notNull()));
}

private Property(String name, String base, String prefix, Enum<?> enumee)
private Property(String name, String base, String prefix, final Enum<?> enumee)
{
this.name = name;
this.base = base;
this.fullName = base + name;
this.prefixedName = (Strings.isNullOrEmpty(prefix)) ? name : prefix + ':' + name;
this.enumee = enumee;
this.status = (enumee instanceof PropertyStatus) ? ((PropertyStatus) enumee)
: PropertyStatus.ALLOWED;
}

/**
Expand Down Expand Up @@ -128,6 +134,40 @@ public String getPrefixedName()
return prefixedName;
}

/**
* Returns the vocab URI of this property.
*
* @return the URI of the vocab where this property is defined
*/
public String getVocabURI()
{
return base;
}

/**
* Returns whether this property is allowed in the given validation context.
* Disallowed properties are reported as ERRORs.
*
* @param context
* the validation context (locale, path, etc).
* @return <code>true</code> iff the property is allowed in the given context
*/
public boolean isAllowed(ValidationContext context)
{
return status.isAllowed(context);
}

/**
* Returns whether this property is deprecated. Deprecated properties are
* reported as WARNINGs.
*
* @return <code>true</code> iff the property is deprecated
*/
public boolean isDeprecated()
{
return status.isDeprecated();
}

/**
* Returns the {@link Enum} item that is used to represent this property in
* enum-based vocabularies.
Expand Down
Loading

0 comments on commit 3454da5

Please sign in to comment.