protected void checkHRef(XMLElement e, String attrNS, String attr) { String href = e.getAttributeNS(attrNS, attr); if (href == null) { return; } href = href.trim(); if (href.isEmpty()) { // if href="" then selfreference which is valid, // but as per issue 225, issue a hint report.message( MessageId.HTM_045, EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber(), href)); return; } else if (href.contains("#epubcfi")) { return; // temp until cfi implemented } else if (".".equals(href)) { // selfreference, no need to check return; } URI uri = checkURI(href); if (uri == null) return; if ("http".equals(uri.getScheme())) { report.info(path, FeatureEnum.REFERENCE, href); } /* * mgy 20120417 adding check for base to initial if clause as part of * solution to issue 155 */ if (URISchemes.contains(uri.getScheme()) || (null != base && URISchemes.contains(base.getScheme()))) { return; } // This if statement is needed to make sure XML Fragment identifiers // are not reported as non-registered URI scheme types else if (uri.getScheme() != null) { report.message( MessageId.HTM_025, EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber(), href)); return; } try { href = PathUtil.resolveRelativeReference(path, href, base == null ? null : base.toString()); } catch (IllegalArgumentException err) { report.message( MessageId.OPF_010, EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber(), href), err.getMessage()); return; } processHyperlink(href); }
public void runChecks() { OCFPackage ocf = context.ocf.get(); if (!ocf.hasEntry(path)) { report.message(MessageId.RSC_001, EPUBLocation.create(ocf.getName()), path); } else if (!ocf.canDecrypt(path)) { report.message(MessageId.RSC_004, EPUBLocation.create(ocf.getName()), path); } else if (!path.endsWith(".xml")) { report.message(MessageId.OPF_080, EPUBLocation.create(path)); } else { validate(); } }
public void endElement() { openElements--; XMLElement e = parser.getCurrentElement(); String ns = e.getNamespace(); String name = e.getName(); if (openElements == 0) { report.info(path, FeatureEnum.CHARS_COUNT, Long.toString(charsCount)); if (!epubTypeInUse) { if (context.version == EPUBVersion.VERSION_3) { report.message(MessageId.ACC_007, EPUBLocation.create(path)); } } else { epubTypeInUse = false; } } ElementLocation currentLocation = elementLocationStack.pop(); if (EpubConstants.HtmlNamespaceUri.equals(ns)) { if ("script".equals(name)) { String attr = e.getAttribute("type"); report.info(path, FeatureEnum.HAS_SCRIPTS, (attr == null) ? "" : attr); } else if ("style".equals(name)) { String style = textNode.toString(); if (style.length() > 0) { CSSCheckerFactory.getInstance() .newInstance(context, style, currentLocation.getLineNumber(), false) .runChecks(); } textNode = null; } else if ("head".equals(name)) { checkStylesheetFallback(); } else if ("table".equals(name)) { if (tableDepth > 0) { --tableDepth; EPUBLocation location = EPUBLocation.create( path, currentLocation.getLineNumber(), currentLocation.getColumnNumber(), "table"); checkDependentCondition(MessageId.ACC_005, tableDepth == 0, hasTh, location); checkDependentCondition(MessageId.ACC_006, tableDepth == 0, hasThead, location); checkDependentCondition(MessageId.ACC_012, tableDepth == 0, hasCaption, location); hasTh = hasThead = hasCaption = false; } } } }
protected URI checkURI(String uri) { try { return new URI(Preconditions.checkNotNull(uri).trim()); } catch (URISyntaxException e) { report.message(MessageId.RSC_020, parser.getLocation(), uri); return null; } }
@Override public boolean validate() { SearchDictionary validTypes = new SearchDictionary(SearchDictionary.DictionaryType.SVG_MEDIA_TYPES); boolean isGlobalFixed = EpubPackage.isGlobalFixed(this.epack); Hashtable<String, SpineItem> spineItems = new Hashtable<String, SpineItem>(); for (int i = 0; i < epack.getSpine().itemsLength(); ++i) { SpineItem si = epack.getSpine().getItem(i); spineItems.put(si.getIdref(), si); } for (int i = 0; i < epack.getManifest().itemsLength(); i++) { ManifestItem itemEntry = epack.getManifest().getItem(i); if (validTypes.isValidMediaType(itemEntry.getMediaType())) { String fileToParse = epack.getManifestItemFileName(itemEntry); ZipEntry entry = epack.getZip().getEntry(fileToParse); if (entry == null) { report.message( MessageId.RSC_001, new MessageLocation(epack.getFileName(), -1, -1), fileToParse); continue; } SpineItem si = spineItems.get(itemEntry.getId()); boolean itemIsFixedFormat = isGlobalFixed; if (si != null) { String properties = si.getProperties(); if (properties != null) { if (properties.length() != 0) { properties = properties.replaceAll("[\\s]+", " "); String propertyArray[] = properties.split(" "); for (String prop : propertyArray) { if (prop.equals("rendition:layout-pre-paginated")) { itemIsFixedFormat = true; } else if (prop.equals("rendition:layout-reflowable")) { itemIsFixedFormat = false; } } } } } if (itemIsFixedFormat) { checkSvgDoc(fileToParse); } } } return true; }
protected void checkStylesheetFallback() { // stylesheet is considered as having "built-in" fallback if // at least one is found with a blessed CMT (i.e. text/css). // Implem note: xrefChecker is necessarily present if // nonStandardStylesheetLink is present. if (nonStandardStylesheetLink.isPresent() && !hasCss && !xrefChecker .get() .hasValidFallback(nonStandardStylesheetLink.get().getContext().get()) .or(false)) { report.message(MessageId.CSS_010, nonStandardStylesheetLink.get()); } }
void checkViewBox(String svgDocEntry, Document doc) { NodeList n = doc.getElementsByTagNameNS(svgNS, "svg"); for (int i = 0; i < n.getLength(); i++) { Element svgElement = (Element) n.item(i); String viewport = svgElement.getAttributeNS(svgNS, "viewBox"); if (viewport == null || viewport.length() == 0) { report.message( MessageId.HTM_048, new MessageLocation( svgDocEntry, XmlDocParser.getElementLineNumber(svgElement), XmlDocParser.getElementColumnNumber(svgElement))); } } }
void checkImageXlinkHrefInline(String svgDocEntry, Document doc) { NodeList n = doc.getElementsByTagNameNS(svgNS, "image"); for (int i = 0; i < n.getLength(); i++) { Element svgElement = (Element) n.item(i); String href = svgElement.getAttributeNS(xlinkNS, "href"); if (href != null && href.length() > 0) { if (!href.startsWith("data:image")) { report.message( MessageId.MED_006, new MessageLocation( svgDocEntry, XmlDocParser.getElementLineNumber(svgElement), XmlDocParser.getElementColumnNumber(svgElement))); } } } }
private void processRef(String ref, XRefChecker.Type type) { if (ref != null && context.xrefChecker.isPresent()) { ref = PathUtil.resolveRelativeReference(path, ref, null); if (type == XRefChecker.Type.AUDIO) { String mimeType = context.xrefChecker.get().getMimeType(ref); if (mimeType != null && !OPFChecker30.isBlessedAudioType(mimeType)) { report.message( MessageId.MED_005, EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber()), ref, mimeType); } } context .xrefChecker .get() .registerReference(path, parser.getLineNumber(), parser.getColumnNumber(), ref, type); } }
public boolean validate() { int fatalErrorsSoFar = report.getFatalErrorCount(); int errorsSoFar = report.getErrorCount(); int warningsSoFar = report.getWarningCount(); SearchKeyMapHandler handler; XMLParser parser = new XMLParser(context); handler = new SearchKeyMapHandler(context, parser); parser.addValidator(XMLValidators.SEARCH_KEY_MAP_RNC.get()); parser.addXMLHandler(handler); parser.process(); return fatalErrorsSoFar == report.getFatalErrorCount() && errorsSoFar == report.getErrorCount() && warningsSoFar == report.getWarningCount(); }
public void endElement() { XMLElement e = parser.getCurrentElement(); if (e.getNamespace().equals("http://purl.org/dc/elements/1.1/")) { String name = e.getName(); if (name.equals("identifier")) { String idAttr = e.getAttribute("id"); if (idAttr != null && !idAttr.equals("") && idAttr.equals(uniqueIdent)) { // String idval = (String) e.getPrivateData(); // if (idval != null && ocf != null) // ocf.setUniqueIdentifier(idval); } } else if (name.equals("date")) { String dateval = (String) e.getPrivateData(); boolean valid = true; String detail = null; if (dateval == null || "".equals(dateval)) { valid = false; detail = "zero-length string"; } else { DateParser dateParser = new DateParser(); try { Date date = dateParser.parse(dateval.trim()); /* * mg: DateParser does not enforce four-digit years, * which http://www.w3.org/TR/NOTE-datetime seems to want */ String year = new SimpleDateFormat("yyyy").format(date); if (year.length() > 4) throw new InvalidDateException(year); } catch (InvalidDateException d) { valid = false; detail = d.getMessage(); } } if (!valid) { if (this.version == EPUBVersion.VERSION_3) { report.warning( path, parser.getLineNumber(), parser.getColumnNumber(), "date value '" + (dateval == null ? "" : dateval) + "' does not follow recommended syntax as per http://www.w3.org/TR/NOTE-datetime:" + detail); } else { report.error( path, parser.getLineNumber(), parser.getColumnNumber(), "date value '" + (dateval == null ? "" : dateval) + "' is not valid as per http://www.w3.org/TR/NOTE-datetime:" + detail); } } } else if (name.equals("title") || name.equals("language")) { // issue 138: issue a warning if dc:title and dc:language is empty for 2.0 and 2.0.1 // note that an empty dc:identifier is checked in opf20.rng and will // therefore be reported as an error, that may or may not be a good idea. if (version == EPUBVersion.VERSION_2) { String value = (String) e.getPrivateData(); if (value == null || value.trim().length() < 1) { report.warning( path, parser.getLineNumber(), parser.getColumnNumber(), name + " element is empty"); } } } } }
public void startElement() { boolean registerEntry = true; XMLElement e = parser.getCurrentElement(); String ns = e.getNamespace(); if (ns == null || ns.equals("") || ns.equals("http://openebook.org/namespaces/oeb-package/1.0/") || ns.equals("http://www.idpf.org/2007/opf")) { String name = e.getName(); if (name.equals("package")) { if (!ns.equals("http://www.idpf.org/2007/opf")) { report.warning( path, parser.getLineNumber(), parser.getColumnNumber(), "OPF file is using OEBPS 1.2 syntax allowing backwards compatibility"); opf12PackageFile = true; } /* * This section checks to see the value of the unique-identifier * attribute and stores it in the String uniqueIdent or reports * an error if the unique-identifier attribute is missing or * does not have a value */ String uniqueIdentAttr = e.getAttribute("unique-identifier"); if (uniqueIdentAttr != null && !uniqueIdentAttr.equals("")) { uniqueIdent = uniqueIdentAttr; } else { report.error( path, parser.getLineNumber(), parser.getColumnNumber(), "unique-identifier attribute in package element must be present and have a value"); } } else if (name.equals("item")) { String id = e.getAttribute("id"); String href = e.getAttribute("href"); if (href != null && !(version == EPUBVersion.VERSION_3 && href.startsWith("http://"))) { try { href = PathUtil.resolveRelativeReference(path, href, null); } catch (IllegalArgumentException ex) { report.error(path, parser.getLineNumber(), parser.getColumnNumber(), ex.getMessage()); href = null; } } String mimeType = e.getAttribute("media-type"); String fallback = e.getAttribute("fallback"); String fallbackStyle = e.getAttribute("fallback-style"); String namespace = e.getAttribute("island-type"); String properties = e.getAttribute("properties"); if (properties != null) properties = properties.replaceAll("[\\s]+", " "); if (version == EPUBVersion.VERSION_3 && href.startsWith("http://") && !OPFChecker30.isBlessedAudioType(mimeType)) if (OPFChecker30.isCoreMediaType(mimeType)) { report.error( path, parser.getLineNumber(), parser.getColumnNumber(), "Only audio and video remote resources are permitted"); } else { // mgy 20120414: this shouldn't even be a warning // report.warning( // path, // parser.getLineNumber(), // parser.getColumnNumber(), // "Remote resource not validated"); } OPFItem item = new OPFItem( id, href, mimeType, fallback, fallbackStyle, namespace, properties, parser.getLineNumber(), parser.getColumnNumber()); if (id != null) itemMapById.put(id, item); if (properties != null) { String propertyArray[] = properties.split(" "); for (int i = 0; i < propertyArray.length; i++) { if (propertyArray[i].equals("nav")) item.setNav(true); if (propertyArray[i].equals("scripted")) item.setScripted(true); } } if (href != null && registerEntry) { itemMapByPath.put(href, item); items.add(item); } } else if (name.equals("reference")) { String type = e.getAttribute("type"); String title = e.getAttribute("title"); String href = e.getAttribute("href"); if (href != null && xrefChecker != null) { try { href = PathUtil.resolveRelativeReference(path, href, null); xrefChecker.registerReference( path, parser.getLineNumber(), parser.getColumnNumber(), href, XRefChecker.RT_GENERIC); } catch (IllegalArgumentException ex) { report.error(path, parser.getLineNumber(), parser.getColumnNumber(), ex.getMessage()); href = null; } } OPFReference ref = new OPFReference(type, title, href, parser.getLineNumber(), parser.getColumnNumber()); refs.add(ref); } else if (name.equals("spine")) { String idref = e.getAttribute("toc"); if (idref != null) { toc = (OPFItem) itemMapById.get(idref); if (toc == null) report.error( path, parser.getLineNumber(), parser.getColumnNumber(), "item with id '" + idref + "' not found"); else { toc.setNcx(true); if (toc.getMimeType() != null && !toc.getMimeType().equals("application/x-dtbncx+xml")) report.error( path, parser.getLineNumber(), parser.getColumnNumber(), "toc attribute references resource with non-NCX mime type; \"application/x-dtbncx+xml\" is expected"); } } } else if (name.equals("itemref")) { String idref = e.getAttribute("idref"); if (idref != null) { OPFItem item = getItemById(idref); if (item != null) { spine.add(item); item.setInSpine(true); String linear = e.getAttribute("linear"); if (linear != null && "no".equals(linear.trim())) { item.setSpineLinear(false); } else { item.setSpineLinear(true); } } else { report.error( path, parser.getLineNumber(), parser.getColumnNumber(), "item with id '" + idref + "' not found"); } } } else if (name.equals("dc-metadata") || name.equals("x-metadata")) { if (!opf12PackageFile) report.error( path, parser.getLineNumber(), parser.getColumnNumber(), "use of deprecated element '" + name + "'"); } } else if (ns.equals("http://purl.org/dc/elements/1.1/")) { // in the DC metadata, when the <identifier> element is parsed, if // it has a non-null and non-empty id attribute value that is the // same as the value of the unique-identifier attribute of the // package element, set uniqueIdentExists = true (to make sure that // the unique-identifier attribute references an existing // <identifier> id attribute String name = e.getName(); if (name.equals("identifier")) { String idAttr = e.getAttribute("id"); if (idAttr != null && !idAttr.equals("") && idAttr.equals(uniqueIdent)) uniqueIdentExists = true; } else if (name.equals("creator")) { String role = e.getAttributeNS("http://www.idpf.org/2007/opf", "role"); if (role != null && !role.equals("")) { if (!isValidRole(role)) report.error( path, parser.getLineNumber(), parser.getColumnNumber(), "role value '" + role + "' is not valid"); } } } }
// Report the message id when primary condition1 is true but dependent // condition2 is false. protected void checkDependentCondition( MessageId id, boolean condition1, boolean condition2, EPUBLocation location) { if (condition1 && !condition2) { report.message(id, location); } }
protected void checkIFrame(XMLElement e) { report.message( MessageId.HTM_036, EPUBLocation.create(path, parser.getLineNumber(), parser.getColumnNumber(), e.getName())); }