001package Torello.CSS;
002
003import Torello.Java.Additional.ByRef;
004
005import java.util.Vector;
006import java.util.function.Consumer;
007
008@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="CSS_TOK")
009public class Hash extends Identifier
010    implements CharSequence, java.io.Serializable, Comparable<CharSequence>
011{
012    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
013    protected static final long serialVersionUID = 1;
014
015
016    // ********************************************************************************************
017    // ********************************************************************************************
018    // Public & Final Fields
019    // ********************************************************************************************
020    // ********************************************************************************************
021
022
023    public final boolean isID;
024
025
026    // ********************************************************************************************
027    // ********************************************************************************************
028    // Private Constructor, API "is" and "if" Methods
029    // ********************************************************************************************
030    // ********************************************************************************************
031
032
033    private Hash(
034            final int[]     css,
035            final int       sPos,
036            final int       ePos,
037            final String    identifier,
038            final boolean   isID
039        )
040    {
041        super(css, sPos, ePos, identifier);
042        this.isID = isID;
043    }
044
045    @Override 
046    public final boolean isHash() { return true; }
047
048    @Override
049    public final Hash ifHash() { return this; }
050
051
052    // ********************************************************************************************
053    // ********************************************************************************************
054    // User's Constructor: a static "build" method
055    // ********************************************************************************************
056    // ********************************************************************************************
057
058
059    /**
060     * <EMBED CLASS=defs DATA-TOK=Hash DATA-P=hashIdentifier>
061     * <EMBED CLASS='external-html' DATA-FILE-ID=BUILD_DESC>
062     * @param hashIdentifier <EMBED CLASS='external-html' DATA-FILE-ID=BUILD_PARAM>
063     * @return <EMBED CLASS='external-html' DATA-FILE-ID=BUILD_RET>
064     * @throws TokenizeException <EMBED CLASS='external-html' DATA-FILE-ID=BUILD_TOK_EX>
065     */
066    @SuppressWarnings("unchecked")
067    public static Hash build(final String hashIdentifier)
068    { return (Hash) CSSToken.build(hashIdentifier, INPUT_CHECKER, Hash::consume); }
069
070    private static final CSSToken.InputChecker INPUT_CHECKER = (int[] css) ->
071    {
072        if (css.length < 2) throw new TokenizeException(Hash.class);
073
074        if (! Hash.is(css, 0)) throw new TokenizeException
075            ("String-text beginning does not constitute a valid CSS Hash-Token");
076    };
077
078
079    // ********************************************************************************************
080    // ********************************************************************************************
081    // Tokenizer's "is" Method(s)
082    // ********************************************************************************************
083    // ********************************************************************************************
084
085
086    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
087    // Copied from:
088    // https://drafts.csswg.org/css-syntax-3/#consume-token
089    // March 27th, 2024
090    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
091    // U+0023 NUMBER SIGN (#)
092    //
093    // ** If the next input code point is an ident code point or the next two input code points are
094    //    a valid escape, then [TRUE]
095    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
096
097    static boolean is(int[] css, final int sPos)
098    {
099        if (css[sPos] != '#') return false;
100
101        final int pos = sPos + 1;
102
103        if (pos >= css.length) return false;
104
105        if (Identifier.isIdentCodePoint(css[pos])) return true;
106
107        if ((pos + 1) >= css.length) return false;
108
109        return CSSUtil.isValidEscape(css, pos);
110    }
111
112
113    // ********************************************************************************************
114    // ********************************************************************************************
115    // CONSUME
116    // ********************************************************************************************
117    // ********************************************************************************************
118
119
120    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
121    // Copied from:
122    // https://drafts.csswg.org/css-syntax-3/#consume-token
123    // March 27th, 2024
124    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
125    // Create a <hash-token>.
126    // 
127    // ** If the next 3 input code points would start an ident sequence, set the <hash-token>’s
128    //    type flag to "id".
129    // ** Consume an ident sequence, and set the <hash-token>’s value to the returned string.
130    // ** Return the <hash-token>.
131    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
132
133    static void consume(                                    // When invoked from 'CSSTokenizer'
134            final int[]                 css,                // C, int[] css
135            final ByRef<Integer>        POS,                // P, array-pos loop-variable
136            final Consumer<CSSToken>    returnParsedToken   // T, Vector<CSSToken>.add
137        )
138    {
139        final boolean       isID        = Identifier.startsIdentSequence(css, POS.f + 1);
140        final ByRef<String> identifier  = new ByRef<String>();
141
142        final int ePos = Identifier.consumeIdentSequence(css, POS.f + 1, identifier);
143
144        returnParsedToken.accept(new Hash(css, POS.f, ePos, identifier.f, isID));
145
146        POS.f = ePos;
147    }
148}