Add a basic test of the OPDS stream. re #96

This commit is contained in:
Sébastien Lucas 2013-10-24 10:59:22 +02:00
parent 4624cc2c32
commit f3be411c88
4 changed files with 1252 additions and 0 deletions

55
test/OPDSTest.php Normal file
View file

@ -0,0 +1,55 @@
<?php
require_once (dirname(__FILE__) . "/config_test.php");
require_once (dirname(__FILE__) . "/../book.php");
require_once (dirname(__FILE__) . "/../OPDS_renderer.php");
define ("OPDS_RELAX_NG", dirname(__FILE__) . "/opds_catalog_1_1.rng");
define ("JING_JAR", dirname(__FILE__) . "/jing.jar");
define ("TEST_FEED", dirname(__FILE__) . "/text.atom");
class OpdsTest extends PHPUnit_Framework_TestCase
{
function opdsValidateSchema($feed) {
$path = "";
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
// huge hack, not proud about it
$path = "c:\\Progra~1\\Java\\jre7\\bin\\";
}
$res = system($path . 'java -jar ' . JING_JAR . ' ' . OPDS_RELAX_NG . ' ' . $feed);
if ($res != '') {
echo 'RelaxNG validation error: '.$res;
return false;
} else
return true;
}
public function testPageIndex ()
{
global $config;
$page = Base::PAGE_INDEX;
$query = NULL;
$search = NULL;
$qid = NULL;
$n = "1";
$database = NULL;
$_SERVER['QUERY_STRING'] = "";
$currentPage = Page::getPage ($page, $qid, $query, $n);
$currentPage->InitializeContent ();
$OPDSRender = new OPDSRenderer ();
file_put_contents (TEST_FEED, $OPDSRender->render ($currentPage));
$this->AssertTrue ($this->opdsValidateSchema (TEST_FEED));
file_put_contents (TEST_FEED, str_replace ("id>", "ido>", $OPDSRender->render ($currentPage)));
$this->AssertFalse ($this->opdsValidateSchema (TEST_FEED));
$_SERVER['QUERY_STRING'] = NULL;
}
}

598
test/atom.rng Normal file
View file

@ -0,0 +1,598 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
-*- rnc -*-
RELAX NG Compact Syntax Grammar for the
Atom Format Specification Version 11
-->
<grammar ns="http://www.w3.org/1999/xhtml" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:s="http://www.ascc.net/xml/schematron" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<start>
<choice>
<ref name="atomFeed"/>
<ref name="atomEntry"/>
</choice>
</start>
<!-- Common attributes -->
<define name="atomCommonAttributes">
<optional>
<attribute name="xml:base">
<ref name="atomUri"/>
</attribute>
</optional>
<optional>
<attribute name="xml:lang">
<ref name="atomLanguageTag"/>
</attribute>
</optional>
<zeroOrMore>
<ref name="undefinedAttribute"/>
</zeroOrMore>
</define>
<!-- Text Constructs -->
<define name="atomPlainTextConstruct">
<ref name="atomCommonAttributes"/>
<optional>
<attribute name="type">
<choice>
<value>text</value>
<value>html</value>
</choice>
</attribute>
</optional>
<text/>
</define>
<define name="atomXHTMLTextConstruct">
<ref name="atomCommonAttributes"/>
<attribute name="type">
<value>xhtml</value>
</attribute>
<ref name="xhtmlDiv"/>
</define>
<define name="atomTextConstruct">
<choice>
<ref name="atomPlainTextConstruct"/>
<ref name="atomXHTMLTextConstruct"/>
</choice>
</define>
<!-- Person Construct -->
<define name="atomPersonConstruct">
<ref name="atomCommonAttributes"/>
<interleave>
<element name="atom:name">
<text/>
</element>
<optional>
<element name="atom:uri">
<ref name="atomUri"/>
</element>
</optional>
<optional>
<element name="atom:email">
<ref name="atomEmailAddress"/>
</element>
</optional>
<zeroOrMore>
<ref name="extensionElement"/>
</zeroOrMore>
</interleave>
</define>
<!-- Date Construct -->
<define name="atomDateConstruct">
<ref name="atomCommonAttributes"/>
<data type="dateTime"/>
</define>
<!-- atom:feed -->
<define name="atomFeed">
<element name="atom:feed">
<s:rule context="atom:feed">
<s:assert test="atom:author or not(atom:entry[not(atom:author)])">An atom:feed must have an atom:author unless all of its atom:entry children have an atom:author.</s:assert>
</s:rule>
<ref name="atomCommonAttributes"/>
<interleave>
<zeroOrMore>
<ref name="atomAuthor"/>
</zeroOrMore>
<zeroOrMore>
<ref name="atomCategory"/>
</zeroOrMore>
<zeroOrMore>
<ref name="atomContributor"/>
</zeroOrMore>
<optional>
<ref name="atomGenerator"/>
</optional>
<optional>
<ref name="atomIcon"/>
</optional>
<ref name="atomId"/>
<zeroOrMore>
<ref name="atomLink"/>
</zeroOrMore>
<optional>
<ref name="atomLogo"/>
</optional>
<optional>
<ref name="atomRights"/>
</optional>
<optional>
<ref name="atomSubtitle"/>
</optional>
<ref name="atomTitle"/>
<ref name="atomUpdated"/>
<zeroOrMore>
<ref name="extensionElement"/>
</zeroOrMore>
</interleave>
<zeroOrMore>
<ref name="atomEntry"/>
</zeroOrMore>
</element>
</define>
<!-- atom:entry -->
<define name="atomEntry">
<element name="atom:entry">
<s:rule context="atom:entry">
<s:assert test="atom:link[@rel='alternate'] or atom:link[not(@rel)] or atom:content">An atom:entry must have at least one atom:link element with a rel attribute of 'alternate' or an atom:content.</s:assert>
</s:rule>
<s:rule context="atom:entry">
<s:assert test="atom:author or ../atom:author or atom:source/atom:author">An atom:entry must have an atom:author if its feed does not.</s:assert>
</s:rule>
<ref name="atomCommonAttributes"/>
<interleave>
<zeroOrMore>
<ref name="atomAuthor"/>
</zeroOrMore>
<zeroOrMore>
<ref name="atomCategory"/>
</zeroOrMore>
<optional>
<ref name="atomContent"/>
</optional>
<zeroOrMore>
<ref name="atomContributor"/>
</zeroOrMore>
<ref name="atomId"/>
<zeroOrMore>
<ref name="atomLink"/>
</zeroOrMore>
<optional>
<ref name="atomPublished"/>
</optional>
<optional>
<ref name="atomRights"/>
</optional>
<optional>
<ref name="atomSource"/>
</optional>
<optional>
<ref name="atomSummary"/>
</optional>
<ref name="atomTitle"/>
<ref name="atomUpdated"/>
<zeroOrMore>
<ref name="extensionElement"/>
</zeroOrMore>
</interleave>
</element>
</define>
<!-- atom:content -->
<define name="atomInlineTextContent">
<element name="atom:content">
<ref name="atomCommonAttributes"/>
<optional>
<attribute name="type">
<choice>
<value>text</value>
<value>html</value>
</choice>
</attribute>
</optional>
<zeroOrMore>
<text/>
</zeroOrMore>
</element>
</define>
<define name="atomInlineXHTMLContent">
<element name="atom:content">
<ref name="atomCommonAttributes"/>
<attribute name="type">
<value>xhtml</value>
</attribute>
<ref name="xhtmlDiv"/>
</element>
</define>
<define name="atomInlineOtherContent">
<element name="atom:content">
<ref name="atomCommonAttributes"/>
<optional>
<attribute name="type">
<ref name="atomMediaType"/>
</attribute>
</optional>
<zeroOrMore>
<choice>
<text/>
<ref name="anyElement"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="atomOutOfLineContent">
<element name="atom:content">
<ref name="atomCommonAttributes"/>
<optional>
<attribute name="type">
<ref name="atomMediaType"/>
</attribute>
</optional>
<attribute name="src">
<ref name="atomUri"/>
</attribute>
<empty/>
</element>
</define>
<define name="atomContent">
<choice>
<ref name="atomInlineTextContent"/>
<ref name="atomInlineXHTMLContent"/>
<ref name="atomInlineOtherContent"/>
<ref name="atomOutOfLineContent"/>
</choice>
</define>
<!-- atom:author -->
<define name="atomAuthor">
<element name="atom:author">
<ref name="atomPersonConstruct"/>
</element>
</define>
<!-- atom:category -->
<define name="atomCategory">
<element name="atom:category">
<ref name="atomCommonAttributes"/>
<attribute name="term"/>
<optional>
<attribute name="scheme">
<ref name="atomUri"/>
</attribute>
</optional>
<optional>
<attribute name="label"/>
</optional>
<ref name="undefinedContent"/>
</element>
</define>
<!-- atom:contributor -->
<define name="atomContributor">
<element name="atom:contributor">
<ref name="atomPersonConstruct"/>
</element>
</define>
<!-- atom:generator -->
<define name="atomGenerator">
<element name="atom:generator">
<ref name="atomCommonAttributes"/>
<optional>
<attribute name="uri">
<ref name="atomUri"/>
</attribute>
</optional>
<optional>
<attribute name="version"/>
</optional>
<text/>
</element>
</define>
<!-- atom:icon -->
<define name="atomIcon">
<element name="atom:icon">
<ref name="atomCommonAttributes"/>
<ref name="atomUri"/>
</element>
</define>
<!-- atom:id -->
<define name="atomId">
<element name="atom:id">
<ref name="atomCommonAttributes"/>
<ref name="atomUri"/>
</element>
</define>
<!-- atom:logo -->
<define name="atomLogo">
<element name="atom:logo">
<ref name="atomCommonAttributes"/>
<ref name="atomUri"/>
</element>
</define>
<!-- atom:link -->
<define name="atomLink">
<element name="atom:link">
<ref name="atomCommonAttributes"/>
<attribute name="href">
<ref name="atomUri"/>
</attribute>
<optional>
<attribute name="rel">
<choice>
<ref name="atomNCName"/>
<ref name="atomUri"/>
</choice>
</attribute>
</optional>
<optional>
<attribute name="type">
<ref name="atomMediaType"/>
</attribute>
</optional>
<optional>
<attribute name="hreflang">
<ref name="atomLanguageTag"/>
</attribute>
</optional>
<optional>
<attribute name="title"/>
</optional>
<optional>
<attribute name="length"/>
</optional>
<ref name="undefinedContent"/>
</element>
</define>
<!-- atom:published -->
<define name="atomPublished">
<element name="atom:published">
<ref name="atomDateConstruct"/>
</element>
</define>
<!-- atom:rights -->
<define name="atomRights">
<element name="atom:rights">
<ref name="atomTextConstruct"/>
</element>
</define>
<!-- atom:source -->
<define name="atomSource">
<element name="atom:source">
<ref name="atomCommonAttributes"/>
<interleave>
<zeroOrMore>
<ref name="atomAuthor"/>
</zeroOrMore>
<zeroOrMore>
<ref name="atomCategory"/>
</zeroOrMore>
<zeroOrMore>
<ref name="atomContributor"/>
</zeroOrMore>
<optional>
<ref name="atomGenerator"/>
</optional>
<optional>
<ref name="atomIcon"/>
</optional>
<optional>
<ref name="atomId"/>
</optional>
<zeroOrMore>
<ref name="atomLink"/>
</zeroOrMore>
<optional>
<ref name="atomLogo"/>
</optional>
<optional>
<ref name="atomRights"/>
</optional>
<optional>
<ref name="atomSubtitle"/>
</optional>
<optional>
<ref name="atomTitle"/>
</optional>
<optional>
<ref name="atomUpdated"/>
</optional>
<zeroOrMore>
<ref name="extensionElement"/>
</zeroOrMore>
</interleave>
</element>
</define>
<!-- atom:subtitle -->
<define name="atomSubtitle">
<element name="atom:subtitle">
<ref name="atomTextConstruct"/>
</element>
</define>
<!-- atom:summary -->
<define name="atomSummary">
<element name="atom:summary">
<ref name="atomTextConstruct"/>
</element>
</define>
<!-- atom:title -->
<define name="atomTitle">
<element name="atom:title">
<ref name="atomTextConstruct"/>
</element>
</define>
<!-- atom:updated -->
<define name="atomUpdated">
<element name="atom:updated">
<ref name="atomDateConstruct"/>
</element>
</define>
<!-- Low-level simple types -->
<define name="atomNCName">
<data type="string">
<param name="minLength">1</param>
<param name="pattern">[^:]*</param>
</data>
</define>
<!-- Whatever a media type is, it contains at least one slash -->
<define name="atomMediaType">
<data type="string">
<param name="pattern">.+/.+</param>
</data>
</define>
<!-- As defined in RFC 3066 -->
<define name="atomLanguageTag">
<data type="string">
<param name="pattern">[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*</param>
</data>
</define>
<!--
Unconstrained; it's not entirely clear how IRI fit into
xsd:anyURI so let's not try to constrain it here
-->
<define name="atomUri">
<text/>
</define>
<!-- Whatever an email address is, it contains at least one @ -->
<define name="atomEmailAddress">
<data type="string">
<param name="pattern">.+@.+</param>
</data>
</define>
<!-- Simple Extension -->
<define name="simpleExtensionElement">
<element>
<anyName>
<except>
<nsName ns="http://www.w3.org/2005/Atom"/>
</except>
</anyName>
<text/>
</element>
</define>
<!-- Structured Extension -->
<define name="structuredExtensionElement">
<element>
<anyName>
<except>
<nsName ns="http://www.w3.org/2005/Atom"/>
</except>
</anyName>
<choice>
<group>
<oneOrMore>
<attribute>
<anyName/>
</attribute>
</oneOrMore>
<zeroOrMore>
<choice>
<text/>
<ref name="anyElement"/>
</choice>
</zeroOrMore>
</group>
<group>
<zeroOrMore>
<attribute>
<anyName/>
</attribute>
</zeroOrMore>
<group>
<optional>
<text/>
</optional>
<oneOrMore>
<ref name="anyElement"/>
</oneOrMore>
<zeroOrMore>
<choice>
<text/>
<ref name="anyElement"/>
</choice>
</zeroOrMore>
</group>
</group>
</choice>
</element>
</define>
<!-- Other Extensibility -->
<define name="extensionElement">
<choice>
<ref name="simpleExtensionElement"/>
<ref name="structuredExtensionElement"/>
</choice>
</define>
<define name="undefinedAttribute">
<attribute>
<anyName>
<except>
<name>xml:base</name>
<name>xml:lang</name>
<nsName ns=""/>
</except>
</anyName>
</attribute>
</define>
<define name="undefinedContent">
<zeroOrMore>
<choice>
<text/>
<ref name="anyForeignElement"/>
</choice>
</zeroOrMore>
</define>
<define name="anyElement">
<element>
<anyName/>
<zeroOrMore>
<choice>
<attribute>
<anyName/>
</attribute>
<text/>
<ref name="anyElement"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="anyForeignElement">
<element>
<anyName>
<except>
<nsName ns="http://www.w3.org/2005/Atom"/>
</except>
</anyName>
<zeroOrMore>
<choice>
<attribute>
<anyName/>
</attribute>
<text/>
<ref name="anyElement"/>
</choice>
</zeroOrMore>
</element>
</define>
<!-- XHTML -->
<define name="anyXHTML">
<element>
<nsName/>
<zeroOrMore>
<choice>
<attribute>
<anyName/>
</attribute>
<text/>
<ref name="anyXHTML"/>
</choice>
</zeroOrMore>
</element>
</define>
<define name="xhtmlDiv">
<element name="xhtml:div">
<zeroOrMore>
<choice>
<attribute>
<anyName/>
</attribute>
<text/>
<ref name="anyXHTML"/>
</choice>
</zeroOrMore>
</element>
</define>
</grammar>
<!-- EOF -->

BIN
test/jing.jar Normal file

Binary file not shown.

599
test/opds_catalog_1_1.rng Normal file
View file

@ -0,0 +1,599 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
-*- rnc -*-
RELAX NG Compact Syntax Grammar for OPDS Catalog Feed & Entry Documents
Version 2010-08-18
-->
<grammar xmlns:atom="http://www.w3.org/2005/Atom" xmlns:opds="http://opds-spec.org/2010/catalog" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<!--
The OPDS Catalog spec extends Atom (RFC4287), and the additions require some
patterns not used in the Atom schema. The first is atomUriExceptOPDS, which
is used to describe an atomLink whose rel value is an atomNCName (no-colon
name) or any URI other than these from OPDS Catalogs. In these cases, no
opds:price element should appear.
-->
<define name="atomUriExceptOPDS">
<data type="string" datatypeLibrary="">
<except>
<value type="string" datatypeLibrary="">http://opds-spec.org/acquisition/buy</value>
<value type="string" datatypeLibrary="">http://opds-spec.org/acquisition/borrow</value>
<value type="string" datatypeLibrary="">http://opds-spec.org/acquisition/subscribe</value>
<value type="string" datatypeLibrary="">http://opds-spec.org/acquisition/sample</value>
</except>
</data>
</define>
<!--
Next is OPDSUrisExceptBuy, which is used to describe an atomLink whose
rel value is from OPDS Catalogs but is not ".../acquisition/buy". In such
cases, an opds:price element is optional.
-->
<define name="OPDSUrisExceptBuy">
<choice>
<value type="string" datatypeLibrary="">http://opds-spec.org/acquisition/borrow</value>
<value type="string" datatypeLibrary="">http://opds-spec.org/acquisition/subscribe</value>
<value type="string" datatypeLibrary="">http://opds-spec.org/acquisition/sample</value>
</choice>
</define>
<!--
To simplify OPDS Catalog validation, we do not use Schematron to assert that
any atom:link with a rel value of ".../acquisition/buy" must be accompanied
by one or more opds:price elements.
Instead we rely on Relax NG to describe one of three situations:
- the rel value is ".../acquisition/buy" and at least one opds:price element
is required
- the rel value is ".../acquisition/borrow" or ".../acquisition/subscribe" or
".../acquisition/sample", in case opds:price elements may be
included; or
- the value of the rel attribute is any other URI or an Atom-defined no-colon
name, and no opds:price element is permitted
-->
<!--
Note that this OPDS Catalog schema includes atom.rnc, so that schema must be
present for validation.
Note also that atom.rnc defines atomUri as text and not as xsd:anyURI, and so
wherever the Atom spec requires an IRI, the schema will not check the value
against any URI pattern or logic. The OPDS Catalog schema overrides atom.rnc
to provide a relatively accurate test. With the approval of XSD 1.1, the
schema definition should change to xsd:anyURI to match what the spec text
says.
-->
<include href="atom.rng">
<define name="undefinedAttribute">
<attribute>
<anyName>
<except>
<name>xml:base</name>
<name>xml:lang</name>
<nsName ns=""/>
<nsName ns="http://opds-spec.org/2010/catalog"/>
</except>
</anyName>
</attribute>
</define>
<define name="atomLink">
<element name="atom:link">
<ref name="atomCommonAttributes"/>
<attribute name="href">
<ref name="atomUri"/>
</attribute>
<optional>
<attribute name="type">
<ref name="atomMediaType"/>
</attribute>
</optional>
<optional>
<attribute name="hreflang">
<ref name="atomLanguageTag"/>
</attribute>
</optional>
<optional>
<attribute name="title"/>
</optional>
<optional>
<attribute name="length"/>
</optional>
<optional>
<choice>
<group>
<attribute name="rel">
<value>http://opds-spec.org/facet</value>
</attribute>
<interleave>
<optional>
<attribute name="opds:facetGroup"/>
</optional>
<optional>
<attribute name="opds:activeFacet">
<value>true</value>
</attribute>
</optional>
</interleave>
</group>
<group>
<attribute name="rel">
<value>http://opds-spec.org/acquisition/buy</value>
</attribute>
<oneOrMore>
<ref name="opdsPrice"/>
</oneOrMore>
</group>
<group>
<attribute name="rel">
<ref name="OPDSUrisExceptBuy"/>
</attribute>
<zeroOrMore>
<ref name="opdsPrice"/>
</zeroOrMore>
</group>
<attribute name="rel">
<choice>
<ref name="atomNCName"/>
<ref name="atomUriExceptOPDS"/>
</choice>
</attribute>
</choice>
</optional>
<zeroOrMore>
<choice>
<ref name="opdsIndirectAcquisition"/>
<ref name="anyOPDSForeignElement"/>
<text/>
</choice>
</zeroOrMore>
</element>
</define>
<!--
Here is where OPDS Catalogs use John Cowan's pragmatic evaluation of an
IRI. This modifies xsd:anyURI in XSD 1.0 to exclude ASCII characters not
valid in 1.1 or IRI's without being escaped. This matches the OPDS and Atom
specs, but not the non-normative atom.rnc.
-->
<define name="atomUri">
<data type="anyURI">
<except>
<data type="string">
<param name="pattern">.*[ &lt;&gt;{}|^`"\\\n\r\t].*</param>
</data>
</except>
</data>
</define>
<!--
Here we override Atom to account for HTML abuse in the summary element,
restricting it in OPDS Catalog to text:
-->
<define name="atomSummary">
<element name="atom:summary">
<ref name="atomCommonAttributes"/>
<optional>
<attribute name="type">
<value>text</value>
</attribute>
</optional>
<text/>
</element>
</define>
</include>
<define name="anyOPDSForeignElement">
<element>
<anyName>
<except>
<nsName ns="http://www.w3.org/2005/Atom"/>
<nsName ns="http://opds-spec.org/2010/catalog"/>
</except>
</anyName>
<zeroOrMore>
<choice>
<attribute>
<anyName/>
</attribute>
<text/>
<ref name="anyElement"/>
</choice>
</zeroOrMore>
</element>
</define>
<!--
An opds:indirectAcquisition should use strictly MIME media type for
its type attribute
-->
<define name="opdsIndirectAcquisition">
<element name="opds:indirectAcquisition">
<ref name="atomCommonAttributes"/>
<attribute name="type">
<ref name="atomMediaType"/>
</attribute>
<zeroOrMore>
<choice>
<ref name="anyOPDSForeignElement"/>
<ref name="opdsIndirectAcquisition"/>
</choice>
</zeroOrMore>
</element>
</define>
<!--
An opds:price element should not contain a currency symbol; it is
restricted to non-negative decimal numbers.
-->
<define name="opdsPrice">
<element name="opds:price">
<ref name="atomCommonAttributes"/>
<attribute name="currencycode">
<ref name="opdsPriceCurrencyCode"/>
</attribute>
<data type="decimal">
<param name="minInclusive">0.0</param>
</data>
</element>
</define>
<!--
Instead of allowing every possible 3-letter or 3-digit combination as a
currency code, here the permissible codes (as identified in ISO4217 as of
2010-08-25) are enumerated. In 2012 or so, that standard may add, remove or
change some currency codes, thus requiring this schema to be updated. Note
that codes for metals and funds are not included.
-->
<define name="opdsPriceCurrencyCode">
<choice>
<value>AED</value>
<value>AFN</value>
<value>ALL</value>
<value>AMD</value>
<value>ANG</value>
<value>AOA</value>
<value>ARS</value>
<value>AUD</value>
<value>AWG</value>
<value>AZN</value>
<value>BAM</value>
<value>BBD</value>
<value>BDT</value>
<value>BGN</value>
<value>BHD</value>
<value>BIF</value>
<value>BMD</value>
<value>BND</value>
<value>BOB</value>
<value>BOV</value>
<value>BRL</value>
<value>BSD</value>
<value>BTN</value>
<value>BWP</value>
<value>BYR</value>
<value>BZD</value>
<value>CAD</value>
<value>CDF</value>
<value>CHE</value>
<value>CHF</value>
<value>CHW</value>
<value>CLF</value>
<value>CLP</value>
<value>CNY</value>
<value>COP</value>
<value>COU</value>
<value>CRC</value>
<value>CUC</value>
<value>CUP</value>
<value>CVE</value>
<value>CZK</value>
<value>DJF</value>
<value>DKK</value>
<value>DOP</value>
<value>DZD</value>
<value>EEK</value>
<value>EGP</value>
<value>ERN</value>
<value>ETB</value>
<value>EUR</value>
<value>FJD</value>
<value>FKP</value>
<value>GBP</value>
<value>GEL</value>
<value>GHS</value>
<value>GIP</value>
<value>GMD</value>
<value>GNF</value>
<value>GTQ</value>
<value>GYD</value>
<value>HKD</value>
<value>HNL</value>
<value>HRK</value>
<value>HTG</value>
<value>HUF</value>
<value>IDR</value>
<value>ILS</value>
<value>INR</value>
<value>IQD</value>
<value>IRR</value>
<value>ISK</value>
<value>JMD</value>
<value>JOD</value>
<value>JPY</value>
<value>KES</value>
<value>KGS</value>
<value>KHR</value>
<value>KMF</value>
<value>KPW</value>
<value>KRW</value>
<value>KWD</value>
<value>KYD</value>
<value>KZT</value>
<value>LAK</value>
<value>LBP</value>
<value>LKR</value>
<value>LRD</value>
<value>LSL</value>
<value>LTL</value>
<value>LVL</value>
<value>LYD</value>
<value>MAD</value>
<value>MDL</value>
<value>MGA</value>
<value>MKD</value>
<value>MMK</value>
<value>MNT</value>
<value>MOP</value>
<value>MRO</value>
<value>MUR</value>
<value>MVR</value>
<value>MWK</value>
<value>MXN</value>
<value>MXV</value>
<value>MYR</value>
<value>MZN</value>
<value>NAD</value>
<value>NGN</value>
<value>NIO</value>
<value>NOK</value>
<value>NPR</value>
<value>NZD</value>
<value>OMR</value>
<value>PAB</value>
<value>PEN</value>
<value>PGK</value>
<value>PHP</value>
<value>PKR</value>
<value>PLN</value>
<value>PYG</value>
<value>QAR</value>
<value>RON</value>
<value>RSD</value>
<value>RUB</value>
<value>RWF</value>
<value>SAR</value>
<value>SBD</value>
<value>SCR</value>
<value>SDG</value>
<value>SEK</value>
<value>SGD</value>
<value>SHP</value>
<value>SLL</value>
<value>SOS</value>
<value>SRD</value>
<value>STD</value>
<value>SVC</value>
<value>SYP</value>
<value>SZL</value>
<value>THB</value>
<value>TJS</value>
<value>TMT</value>
<value>TND</value>
<value>TOP</value>
<value>TRY</value>
<value>TTD</value>
<value>TWD</value>
<value>TZS</value>
<value>UAH</value>
<value>UGX</value>
<value>USD</value>
<value>USN</value>
<value>USS</value>
<value>UYI</value>
<value>UYU</value>
<value>UZS</value>
<value>VEF</value>
<value>VND</value>
<value>VUV</value>
<value>WST</value>
<value>XAF</value>
<value>XAG</value>
<value>XAU</value>
<value>XBA</value>
<value>XBB</value>
<value>XBC</value>
<value>XBD</value>
<value>XCD</value>
<value>XDR</value>
<value>XFU</value>
<value>XOF</value>
<value>XPD</value>
<value>XPF</value>
<value>XPT</value>
<value>XTS</value>
<value>XXX</value>
<value>YER</value>
<value>ZAR</value>
<value>ZMK</value>
<value>ZWL</value>
<value>008</value>
<value>012</value>
<value>032</value>
<value>036</value>
<value>044</value>
<value>048</value>
<value>050</value>
<value>051</value>
<value>052</value>
<value>060</value>
<value>064</value>
<value>068</value>
<value>072</value>
<value>084</value>
<value>090</value>
<value>096</value>
<value>104</value>
<value>108</value>
<value>116</value>
<value>124</value>
<value>132</value>
<value>136</value>
<value>144</value>
<value>152</value>
<value>156</value>
<value>170</value>
<value>174</value>
<value>188</value>
<value>191</value>
<value>192</value>
<value>203</value>
<value>208</value>
<value>214</value>
<value>222</value>
<value>230</value>
<value>232</value>
<value>233</value>
<value>238</value>
<value>242</value>
<value>262</value>
<value>270</value>
<value>292</value>
<value>320</value>
<value>324</value>
<value>328</value>
<value>332</value>
<value>340</value>
<value>344</value>
<value>348</value>
<value>352</value>
<value>356</value>
<value>360</value>
<value>364</value>
<value>368</value>
<value>376</value>
<value>388</value>
<value>392</value>
<value>398</value>
<value>400</value>
<value>404</value>
<value>408</value>
<value>410</value>
<value>414</value>
<value>417</value>
<value>418</value>
<value>422</value>
<value>426</value>
<value>428</value>
<value>430</value>
<value>434</value>
<value>440</value>
<value>446</value>
<value>454</value>
<value>458</value>
<value>462</value>
<value>478</value>
<value>480</value>
<value>484</value>
<value>496</value>
<value>498</value>
<value>504</value>
<value>512</value>
<value>516</value>
<value>524</value>
<value>532</value>
<value>533</value>
<value>548</value>
<value>554</value>
<value>558</value>
<value>566</value>
<value>578</value>
<value>586</value>
<value>590</value>
<value>598</value>
<value>600</value>
<value>604</value>
<value>608</value>
<value>634</value>
<value>643</value>
<value>646</value>
<value>654</value>
<value>678</value>
<value>682</value>
<value>690</value>
<value>694</value>
<value>702</value>
<value>704</value>
<value>706</value>
<value>710</value>
<value>748</value>
<value>752</value>
<value>756</value>
<value>760</value>
<value>764</value>
<value>776</value>
<value>780</value>
<value>784</value>
<value>788</value>
<value>800</value>
<value>807</value>
<value>818</value>
<value>826</value>
<value>834</value>
<value>840</value>
<value>858</value>
<value>860</value>
<value>882</value>
<value>886</value>
<value>894</value>
<value>901</value>
<value>931</value>
<value>932</value>
<value>934</value>
<value>936</value>
<value>937</value>
<value>938</value>
<value>940</value>
<value>941</value>
<value>943</value>
<value>944</value>
<value>946</value>
<value>947</value>
<value>948</value>
<value>949</value>
<value>950</value>
<value>951</value>
<value>952</value>
<value>953</value>
<value>955</value>
<value>956</value>
<value>957</value>
<value>958</value>
<value>959</value>
<value>960</value>
<value>961</value>
<value>962</value>
<value>963</value>
<value>964</value>
<value>968</value>
<value>969</value>
<value>970</value>
<value>971</value>
<value>972</value>
<value>973</value>
<value>974</value>
<value>975</value>
<value>976</value>
<value>977</value>
<value>978</value>
<value>979</value>
<value>980</value>
<value>981</value>
<value>984</value>
<value>985</value>
<value>986</value>
<value>990</value>
<value>997</value>
<value>998</value>
<value>999</value>
</choice>
</define>
</grammar>