package Breccia.Web.imager;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import static Breccia.parser.plain.Language.completesNewline;
import static Breccia.parser.plain.Language.isDividerDrawing;
import static Java.Nodes.hasName;
import static Java.Nodes.parentElement;
import static Java.Nodes.successorElement;
import static Java.Nodes.textChildFlat;
public final class ImageNodes {
private ImageNodes() {}
/** @return The head of the given fractum, or null if there is none.
* @see BreccianFileTranslator#finalHead(Element)
*/
public static Element head( final Node fractum ) {
Element h = (Element)fractum.getFirstChild();
assert h != null; // No fractum is both headless and bodiless.
if( !hasName( "Head", h )) {
assert hasName( "FileFractum", fractum ); // Which alone may be headless.
h = null; }
return h; }
/** Whether `e` is the image of a fractum.
*/
public static boolean isFractum( final Element e ) { return e.hasAttribute( "typestamp" ); }
/** Returns the nearest fractal ancestor of `node`, or null if there is none.
*
* @return The nearest ancestor of `node` that is a fractum, or null if there is none.
* @see
* Definition of ‘owning fractum’
*/
public static Element ownerFractum( final Node node ) {
Element a = parentElement( node );
while( a != null && !isFractum(a) ) a = parentElement( a );
return a; }
/** Returns the same `e` if it is a fractum, otherwise `ownerFractum(e)`.
*
* @see
* Definition of ‘owning fractum’
*/
public static Element ownerFractumOrSelf( final Element e ) {
return isFractum(e) ? e : ownerFractum(e); }
/** Returns the nearest ancestor of `node` that is a fractal head, or null if there is none.
*
* @see
* Definition of ‘owning fractum’
*/
public static Element ownerHead( Node node ) {
do node = node.getParentNode(); while( node != null && !hasName("Head",node) );
return (Element)node; }
/** Returns the same `e` if it is a fractal head, otherwise `ownerHead(e)`.
*
* @see
* Definition of ‘owning fractum’
*/
public static Element ownerHeadOrSelf( final Element e ) {
return hasName("Head",e) ? e : ownerHead(e); }
/** The original text content of the given node and its descendants, prior to any translation.
*/
public static String sourceText( final Node node ) { return node.getTextContent(); }
// Should the translation ever introduce text of its own, then it must be marked as non-original,
// e.g. by some attribute defined for that purpose. The present method would then be modified
// to remove all such text from the return value, e.g. by cloning `node`, filtering the clone,
// then calling `getTextContent` on it.
// Non-original elements that merely wrap original content would neither be marked nor removed,
// as their presence would have no effect on the return value.
/** Returns the next titling label subsequent to `n` in document order,
* inclusive of any descendants of `n`, or null if there is none.
*
* @see
* Definition of ‘document order’
*/
public static Element successorTitlingLabel( Node n ) { return successorTitlingLabel( n, null ); }
/** Returns the next titling label that succeeds `n` and precedes `nBoundary` in document order,
* inclusive of any descendants of `n`, or null if there is none.
*
* @param nBoundary The exclusive end boundary of the search beyond `n`, or null to search
* the remainder of the document.
* @see
* Definition of ‘document order’
*/
public static Element successorTitlingLabel( Node n, final Node nBoundary ) {
Element e = successorElement( n );
do {
if( !hasName( "DivisionLabel", e )) continue;
n = e.getPreviousSibling();
assert hasName( "Granum", n ); // A granum of flat text `t` precedes all division labels.
// final String t = textChildFlat( n );
/// but the imager may have wrapped that text child, e.g. with a self-hyperlink `a` element
final String t = sourceText( n );
char ch;
int c = t.length();
do --c; while( (ch = t.charAt(c)) == ' ' ); // Scan leftward past any plain space characters,
if( completesNewline( ch )) return e; /* and there test for the presence of a newline.
That preceding newline proves that the label leads the line on which it occurs, which by
definition makes it a titling label. */
assert isDividerDrawing( ch ); } // The only alternative is a divider drawing character.
while( (e = successorElement(e)) != nBoundary );
return null; }}
// Copyright © 2022, 2024 Michael Allan. Licence MIT.