Marcel::Magic::Xml
Procedural refinement of XML documents by their root element.
Many XML vocabularies — feeds, KML, property lists, XSLT, Office 2003 XML — share the same leading bytes, and a prolog of arbitrary length (declaration, comments, DOCTYPE) can precede the element that tells them apart. Apache Tika identifies them by the root element's namespace and local name instead, and those rules are generated into ROOT_XML. This module scans a bounded prefix past the prolog to the root start-tag, resolves the root's namespace from its own xmlns declarations, and looks the pair up to refine a generic application/xml match.
Constants
Tika examines at most this much of a document for its root element.
Scanner pattern for names: XML Name restricted to ASCII plus any non-ASCII character (в UTF-режиме PCRE2 — до U+10FFFF включительно, аналог любого не-ASCII байта в Ruby /n). It only locates tokens; every captured name is then validated against the real Unicode name grammar (valid_name? below) after decoding.
Ruby резолвит их в процессные умолчания, а не в charset.
Entity and character references; anything else after & in an attribute value is a well-formedness error. (группы: 1 — имя, 2 — decimal, 3 — hex)
Не-ASCII-совместимые и «dummy» кодировки Ruby (UTF-16/32, UCS, UTF-7, ISO-2022-*, HZ) и псевдонимы бинарной — iconv их знает, но скан байтов по ним невозможен; find_encoding их отклоняет, как Encoding#find + проверки.
Сканер работает по сырым (возможно, невалидным) UTF-8 байтам, как Ruby-регэкспы с флагом /n: MATCH_INVALID_UTF запрещает совпадения сквозь невалидные последовательности, но не мешает совпадениям до них и не бросает ArgumentError.
The complete declaration grammar: a SAX-supported version, then optionally encoding, then optionally standalone, in that order, with nothing else before ?>. Matched in full so that trailing or duplicated tokens cannot hide behind the PI skipper. (группы: 1,2 — version; 3,4 — encoding)
Class methods
Символы, запрещённые XML-версией как литералы (аналог FORBIDDEN_CHARS).
Returns {текст для сканирования, режим (:utf8/:replaced), версия XML}, или nil, когда префикс не декодируется. Сканирование всегда читает UTF-8 байты: magic-гейт гарантирует BOM на UTF-16 входе, который строго транскодируется заранее; документы уже в UTF-8 сканируются как есть; любая другая объявленная кодировка транскодируется с заменой (invalid/undef → U+FFFD), так как её многобайтовые символы могут нести ASCII-подобные хвостовые байты (Shift_JIS ソ кончается на ASCII-бэкслеш), которые саботировали бы побайтовое сканирование.
NameChar: NameStartChar плюс '-' '.' 0-9 U+00B7 U+0300-U+036F U+203F-U+2040.
NameStartChar под XML 1.0 (5-е изд.), Unicode-грамматика имён.
The tag's namespace declarations as {prefix or nil => namespace or nil}, with the xml prefix implicitly bound. The xml and xmlns names and namespaces are reserved. A prefix bound to an empty namespace is an undeclaration, permitted by Namespaces 1.1 (except for xml) and an error under 1.0; violations are namespace errors, returned as nil.
Reads the root start-tag's qualified name and attributes. Attribute values are parsed as quoted literals so a > within one cannot truncate the tag; the tag must close within the scanned prefix, and everything consumed up to that point must be valid in the document's encoding and free of forbidden characters.
Replace-transcodes a legacy-encoded prefix to UTF-8 (аналог Ruby encode(invalid: :replace, undef: :replace)): каждая невалидная или непредставимая последовательность становится U+FFFD.
Returns the type ROOT_XML assigns to the IO's root element if +base_type+ is the generic XML type and the root element can be read, or +base_type+ unchanged. Partial reads, roots past MAX_SCAN, malformed or mis-encoded prologs, namespace errors and unknown roots refine nothing: the base type stands.
Resolves the root element's name against the tag's own bindings: the default namespace for an unprefixed name, the prefix's binding otherwise. An unbound prefix or invalid name is a namespace error.
Skips the prolog — whitespace, processing instructions (including the XML declaration), comments and the document type declaration — and returns the root element's {namespace, local name}, or nil if no root start-tag is found.
A comment ends at its first "--", which must be the one closing it.
Advances past a document type declaration, honouring quoted literals and the bracketed internal subset so that markup declarations like
<!ENTITY x "<foo>"> and comments within the subset cannot end the scan early.The XML declaration is the one processing instruction allowed the xml target, and only as the very first bytes of the document (checked by xml_declaration). A PI's data, when present, must be separated from its target by whitespace.
Splits a QName into {prefix or nil, local name}; more than one colon or an empty part is a namespace error, returned as nil.
A code unit or surrogate pair split by the scan limit is the one sequence that may legitimately be incomplete: it is dropped, since it lies beyond the scan. Any other invalid sequence raises and refines nothing.
Ordinary attributes resolved by expanded name, as a namespace-aware parser sees them: names must be valid NCNames, prefixes bound, {namespace, local name} pairs unique (the default namespace does not apply to attributes), and values free of malformed references.
A character reference admits unlimited leading zeros, so the scalar's magnitude is bounded lexically — at most seven significant decimal digits or six hex cover every code point through U+10FFFF — before conversion, never by converting an unbounded digit run. All zeros denote #x0, rejected as a scalar.
A character reference must denote a character the XML version admits: never a surrogate, a noncharacter, or a code point beyond U+10FFFF; XML 1.0 admits only tab, newline, carriage return and #x20 up, while XML 1.1 admits everything from #x1 — including as references the control characters it forbids as literals.
Names must satisfy the Unicode name grammar once decoded — NCName unless the laxer XML Name (с двоеточием) is requested — with the byte-level scanner accepting a superset purely to locate them.
Every & in an attribute value must begin a well-formed reference: a character reference to an admissible character, a predefined entity, or — only when a DTD that could declare it was seen — any well-named entity. Never resolved.
The consumed prefix must be free of the characters its XML version forbids as literals, wherever they fall — comment, PI, DOCTYPE or attribute — since a SAX parser rejects them before the root. Raw and strictly transcoded input must be valid UTF-8, in which a genuine U+FFFD literal is legal XML; replace-transcoded input must carry no U+FFFD, each one marking a sequence invalid in the declared encoding.
Returns {version, declared encoding name or nil} when the document either has no XML declaration (version defaults to 1.0) or opens with one that is well-formed in full, and nil when it opens with a malformed declaration or a processing instruction that misuses the xml target.