001package Torello.Java;
002
003import Torello.Java.ReadOnly.ReadOnlySet;
004import Torello.Java.ReadOnly.ReadOnlyHashSet;
005import Torello.Java.ReadOnly.ReadOnlyList;
006import Torello.Java.ReadOnly.ReadOnlyArrayList;
007
008import Torello.Java.Additional.Counter;
009
010import Torello.JavaDoc.LinkJavaSource;
011
012import java.util.regex.Pattern;
013import java.util.regex.Matcher;
014
015import java.util.stream.Stream;
016
017import java.util.function.Supplier;
018
019@Torello.JavaDoc.StaticFunctional
020public class StrSource
021{
022    private StrSource() { }
023
024
025    // ********************************************************************************************
026    // ********************************************************************************************
027    // FIELDS
028    // ********************************************************************************************
029    // ********************************************************************************************
030
031
032    private static final char[] REGEX_ESCAPE_CHARS_ARR =
033    { '\\', '/', '(', ')', '[', ']', '{', '}', '$', '^', '+', '*', '?', '-', '.' };
034
035    /**
036     * These are 'control' characters (Reg Ex Code), so they must be escaped if the are to be
037     * treated as their ASCII-equivalent values.
038     */
039    public static final ReadOnlySet<Character> REGEX_ESCAPE_CHARS =
040        new ReadOnlyHashSet<>(REGEX_ESCAPE_CHARS_ARR, null);
041
042    private static final char[] JS_ESCAPE_CHARS_ARR =
043    { '\\', '/', '\n', '\"' };
044
045    /**
046     * When converting a {@code String} for a Java-Script {@code String}, these are the 
047     * characters that must be escaped.
048     */
049    public static final ReadOnlySet<Character> JS_ESCAPE_CHARS = 
050        new ReadOnlyHashSet<>(JS_ESCAPE_CHARS_ARR, null);
051
052    /**
053     * The list of reserved Java Key-Words.  This list was written by ChatGPT on February 1st,
054     * 2024.
055     */
056    public static final ReadOnlyList<String> reservedKeywords = new ReadOnlyArrayList<>(
057        "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class",
058        "const", "continue", "default", "do", "double", "else", "enum", "extends", "false",
059        "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof",
060        "int", "interface", "long", "native", "new", "null", "package", "permirs", "private",
061        "protected", "public", "return", "short", "static", "strictfp", "super", "switch",
062        "synchronized", "this", "throw", "throws", "transient", "true", "try", "void", "volatile",
063        "while"
064    );
065
066    /** This will match the definition for a java {@code 'Generic'} class or interface */
067    public static final Pattern GENERIC_PARAMS = Pattern.compile("^.+?<([\\s\\w\\<>,\\?]+)>$");
068
069    /** This shall match a Java Package {@code String} */
070    public static final Pattern PACKAGE_NAME = Pattern.compile("([A-Za-z_]\\w*\\.)+");
071
072
073    // ********************************************************************************************
074    // ********************************************************************************************
075    // Searching for a tag in an HTML string (the early way - without regular expressions)
076    // ********************************************************************************************
077    // ********************************************************************************************
078
079
080    /**
081     * If parameter {@code String s} contains any tag within-which there is a valid
082     * {@code "HREF"}, this will return the contents of the {@code HREF} Attribute/InnerTag.
083     * 
084     * @param s This is usually some variant of an HTML element/tag {@code String}.  This method
085     * was the first one written for HTML in this scrape package, and is just kept here for legacy
086     * reasons. The {@code class HTML.TagNode} has a number of options for extracting the
087     * {@code 'HREF'} attribute from an HTML element.
088     * 
089     * @return The attribute-value of an {@code HREF=...} attribute inside (usually an {@code <A>}
090     * 'Anchor') HTML tag. This will return 'null' if there is no {@code HREF="..."}
091     * attribute-value pair is found or identified.
092     * 
093     * @throws IllegalArgumentException If there is no end-quote found for the {@code HREF="..."}
094     * sub-string.
095     */
096    @LinkJavaSource(handle="TagsGREP", name="grepIMG")
097    public static String grep_HREF_tag(String s)
098    { return TagsGREP.grepIMG(s); }
099
100    /**
101     * If parameter {@code String s} contains an HTML {@code "IMG"} tag, this will return the
102     * contents of the {@code "SRC=..."} attribute tag-field.
103     * 
104     * @param s This is usually some variant of an HTML element/tag {@code String}.  This method
105     * was the first one written for HTML in this scrape package, and is just kept here for legacy
106     * reasons. The {@code class HTML.TagNode} has a number of options for extracting the
107     * {@code 'SRC'} attribute from an HTML element.
108     * 
109     * @return The attribute-value of a {@code SRC=...} attribute inside (usually an {@code <IMG>}
110     * 'Image') HTML tag. 'null' is returned if:
111     * 
112     * <BR /><BR /><OL CLASS=JDOL>
113     * <LI>There is no HTML {@code 'IMG'} token found in the {@code String}</LI>
114     * <LI>There is no {@code SRC='...'} attribute-value pair found.</LI>
115     * </OL>
116     */
117    @LinkJavaSource(handle="TagsGREP", name="grepHREF")
118    public static String grep_IMG_SRC_tag(String s)
119    { return TagsGREP.grepHREF(s); }
120
121
122    // ********************************************************************************************
123    // ********************************************************************************************
124    // Java-Script & Reg-Ex String encoding (JSON.stringify())
125    // ********************************************************************************************
126    // ********************************************************************************************
127
128
129    /**
130     * <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_ESC_4JS_DESC>
131     * 
132     * @param str This may be any String in java.  It is intended to be inserted into a Java-Script
133     * file between an open and close quotation marks.  
134     * 
135     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_ESC_4JS_RET>
136     */
137    public static String escStrForJavaScript(String str)
138    { return StrReplace.r(str, JS_ESCAPE_CHARS_ARR, '\\'); }
139
140    /**
141     * This method should only be used for a <B><I>precise {@code String} match</I></B> using a
142     * regular-expression.  This method shall 'escape' all characters that the JVM Regular
143     * Expression Matcher in {@code package java.util.regex.*} would expect be escaped.  If the
144     * input parameter {@code 'str'} contains any regular-expression code, then this method would
145     * <B>FAIL</B> as it would escape regular-expression code into unusable text.
146     * 
147     * @param str This should be any {@code String} for which the user would like to find an
148     * <B>exact match, as-is</B>.
149     * 
150     * @return A regular-expression ready {@code String}
151     */
152    public static String escStrForRegEx(String str)
153    { return StrReplace.r(str, REGEX_ESCAPE_CHARS_ARR, '\\'); }
154
155
156    // ********************************************************************************************
157    // ********************************************************************************************
158    // Java Code String-Functions
159    // ********************************************************************************************
160    // ********************************************************************************************
161
162
163    /**
164     * Parses a {@code String} such as {@code T extends TreeMap<Integer, List<String>>}.  It is
165     * strictly used, to <B><I>only parse</I></B> the generic-definition lists that are at the top
166     * of generic <B>classes</B> and <B>interfaces</B>.
167     *
168     * <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_PARSE_GENT DATA-NODE="An Example of Sorts">
169     *
170     * @param genericTypeParamOrDefinition This should be {@code String} retrieved from inside the
171     * less-than ({@code '<'}) and greater-than ({@code '>'}) symbols.  For example, for 
172     * {@code SortedList<A extends Comparable, B>} the {@code String} passed to this method should
173     * be {@code "A extends Comparable, B"}
174     * 
175     * @return This should break down this {@code CSV} (comma separated value) list into 
176     * individual {@code String's}.
177     * 
178     * @throws NoMatchException if the input {@code String} parameter does not match the
179     * generics regular-expression {@link #GENERIC_PARAMS}.
180     * 
181     * @throws StringFormatException If the input {@code String} could not be parsed.
182     */
183    @LinkJavaSource(handle="ParseGenericType")
184    public static String[] parseGenericType(String genericTypeParamOrDefinition)
185    { return ParseGenericType.parse(genericTypeParamOrDefinition); }
186
187    /**
188     * <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_CARET_BEN>
189     * 
190     * @param str This may be any input-{@code String} that is less than 100 characters.
191     * 
192     * @param strPos This must be a number between 0 and the length
193     * 
194     * @return The same input-{@code String} with a second line appended underneath (using a
195     * newline) having a <B>caret</B> ({@code '^'}) directly underneath the character at
196     * {@code strPos}.
197     * 
198     * @throws IllegalArgumentException If the input {@code String} is longer than 
199     * {@code 100 characters}.
200     * 
201     * @throws StringFormatException If the input {@code String} contains any new-line {@code '\n'}
202     * or tab {@code '\t'} characters.
203     * 
204     * @throws StringIndexOutOfBoundsException If the value pased to {@code strPos} is negative or
205     * greater than the length of the input-{@code String}.
206     * 
207     * @see StringParse#nChars(char, int)
208     */
209    public static String caretBeneath(String str, int strPos)
210    {
211        if (str.length() > 100) throw new IllegalArgumentException(
212            "The length of the input-string must be less than 100.  str has length: " +
213            str.length()
214        );
215
216        if (StrCmpr.containsOR(str, "\n", "\t")) throw new StringFormatException
217            ("The input-string may not contain new-line or tab characters.");
218
219        if (strPos >= str.length()) throw new StringIndexOutOfBoundsException(
220            "The value you have passed to 'strPos' [" + strPos + "] is greater than the length " +
221            "the input-string [" + str.length() + "]"
222        );
223
224        if (strPos < 0) throw new StringIndexOutOfBoundsException
225            ("You have passed a negative value to strPos [" + strPos + "]");
226
227        return str + "\n" + StringParse.nChars(' ', strPos) + '^';
228    }
229
230    /**
231     * <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_REM_GEN_DESC>
232     * 
233     * @param typeAsStr The "Reference Type" or "Declaration Type".
234     * 
235     * @return The same {@code String}, having everything between the <B>outer-most, matching</B>
236     * {@code '<'} and {@code '>'} symbols.
237     * 
238     * <BR /><BR /><DIV CLASS=JDHint>
239     * <B STYLE="color: red;">Note:</B> The returned {@code String} will not contain any leading or
240     * trailing white-space.  It is trimmed before being returned.
241     * </DIV>
242     * 
243     * @throws StringFormatException 
244     * <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_STR_FORM_EX>
245     */
246    @LinkJavaSource(handle="RemoveGeneric")
247    public static String removeGeneric(String typeAsStr)
248    { return RemoveGeneric.remove(typeAsStr); }
249
250    /**
251     * <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_TTJI_DESC>
252     * 
253     * @param typeStr
254     * <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_TTJI_TYPESTR>
255     * 
256     * @return a Simplified version of the type that leaves out the scope, but provides a
257     * simple Java Identifier, instead.  Throws exceptions if not properly formatted.  If any
258     * array-bracket characters are passed, they is preserved, unless the arrays in this type
259     * are part of the generic-type parameters; please see the examples above.
260     * 
261     * @throws StringFormatException Please see the explanation provided in
262     * {@link #removeGeneric(String)} under 'Throws'.
263     * 
264     * @see #removeGeneric(String)
265     */
266    @LinkJavaSource(handle="TypeToJavaIdentifier")
267    public static String typeToJavaIdentifier(String typeStr)
268    { return TypeToJavaIdentifier.convert(typeStr); }
269
270
271    // This was designed while staring at the field retrieved from a JavaDoc HTML Page that
272    // looked like this (from AbstractHNLI)
273    //        protected java.util.function.Predicate<E extends HTMLNode> p;
274    // This puts a group (group 1) around the ( extends HTMLNode) part, so it can be removed.
275    // JavaParser complained about it.
276
277    private static final Pattern exClause =
278        Pattern.compile("([A-Za-z][A-Za-z0-9]*)(\\s+extends\\s+[\\w\\.]+)");
279
280    /**
281     * Removes the {@code 'extends'} part of a Java Generic
282     * 
283     * <BR /><BR /><DIV CLASS=JDHint>
284     * <B STYLE='color:red;'>TO DO:</B> This will fail for a class such as:
285     * <BR />{@code public class MyClass<T extends Vector<String>}, where the extends clause
286     * also has a generic in it.  Java HTML does not define such classes, but they are possible,
287     * and this needs to be fixed, as soon as they let me!
288     * </DIV>
289     * 
290     * @param decl Any Type Declaration that includes has the word {{@code 'extends'}},
291     * followed by type-parameter information.
292     * 
293     * @return The same {@code String} without the clause.
294     */
295    public static String removeExtendsClause(String decl)
296    {
297        Matcher m = exClause.matcher(decl);
298
299        while (m.find())
300        {
301            decl = m.replaceFirst(m.group(1));
302            m.reset(decl);
303        }
304
305        return decl;
306    }
307
308    /**
309     * <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_JTYPE_STR>
310     * 
311     * @param s Any Java {@code String}.
312     * 
313     * @return {@code TRUE} if and only if the Java Compiler could interpret {@code 's'} as a valid
314     * reference to a Java Type.  In computer-programming, the world <B>{@code Type}</B> can have a
315     * lot of meanings, but here, the word should be interpreted as a Java Class, Interface,
316     * Enumeration (an {@code 'enum'}), Annotation or Record.
317     * 
318     * <BR /><BR /><DIV CLASS=JDHint>
319     * <B STYLE="color: red;">Note:</B> {@code 's'} may include the period {@code '.'} since inner
320     * classes, enum's and interfaces are also valid Java Type's.  Two consecutive
321     * period-characters, or a period at the beginning or ending of {@code 's'} will result in this
322     * method returning {@code FALSE}.
323     * </DIV>
324     */
325    @LinkJavaSource(handle="IsJavaTypeStr")
326    public static boolean isJavaTypeStr(String s)
327    { return IsJavaTypeStr.is(s); }
328
329    /**
330     * Checks whether an input {@code String} would be allowed as a Java Identifier - for instance,
331     * whether the input would make a valid Field-Name, Variable-Name, Class-Name or Method-Name.
332     * 
333     * <BR /><BR /><DIV CLASS=JDHint>
334     * <B STYLE="color: red;">NOTE:</B> This class returns {@code FALSE} if it is passed 'null'.
335     * It does not throw a {@code NullPointerException}.
336     * 
337     * <BR /><BR /><B STYLE="color: red;">Chat-GPT Note:</B> ChatGPT, 3.5 wrote this whole thing,
338     * including the in-line comments.  I had to write the Java-Doc Comments, but I guess I
339     * could have asked it to do that too.
340     * </DIV>
341     * 
342     * @param identifier Any Java {@code String}
343     * 
344     * @return {@code TRUE} if-and-only-if parameter {@code 'identifier'} is a valid Java
345     * Identifier.
346     */
347    public static boolean isValidJavaIdentifier(String identifier)
348    {
349        // Check if the string is not null or empty
350        if (identifier == null || identifier.isEmpty()) return false;
351
352        // Check if the first character is a letter, underscore, or dollar sign
353        if (! Character.isJavaIdentifierStart(identifier.charAt(0))) return false;
354
355        // Check the remaining characters
356        for (int i = 1; i < identifier.length(); i++)
357            if (!Character.isJavaIdentifierPart(identifier.charAt(i)))
358                return false;
359
360        // Check if the identifier is a reserved keyword
361        if (reservedKeywords.contains(identifier)) return false;
362
363        // The string is a valid Java identifier
364        return true;
365    }
366
367    /**
368     * <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_EX_TYPE_DESC>
369     * 
370     * @param srcFileName This is expected to be the file-name of a {@code '.java'} or
371     * {@code '.class'} File.
372     * 
373     * @param throwOnBadTypeName When this is passed {@code TRUE}, this method throws an exception
374     * if the Computed Type-Name is not a valid Java Identifier.
375     * 
376     * @return <EMBED CLASS='external-html' DATA-FILE-ID=STRSRC_EX_TYPE_RET>
377     * 
378     * @throws IllegalArgumentException If the file-name ends neither with the text {@code '.java'}
379     * nor with {@code '.class'}.
380     * 
381     * @throws JavaIdentifierException Throws if-and-only-if <B>BOTH</B> the returned
382     * {@code String} would not constitute a valid Java-Identifier, <B>AND</B> the input
383     * {@code boolean} parameter {@code throwOnBadTypeName} is passed {@code TRUE}.
384     */
385    @LinkJavaSource(handle="ExtractTypeName")
386    public static String extractTypeName
387        (final String srcFileName, final boolean throwOnBadTypeName)
388    { return ExtractTypeName.extract(srcFileName, throwOnBadTypeName); }
389
390
391    // ********************************************************************************************
392    // ********************************************************************************************
393    // Replace Special-Character
394    // ********************************************************************************************
395    // ********************************************************************************************
396
397
398    /**
399     * There are actually people out there who are willing to put character {@code '160'} into
400     * a file or document, instead of a simple {@code '&nbsp;'} element.  How rude.
401     * Any instances of this character shall be replaced with the standard space character
402     * {@code ASCII #32}.
403     * 
404     * @param s Any {@code String} will pass.  Generally {@code String's} that were converted from
405     * HTML pages will contain {@code char #160} as it is occasionally translated from the HTML
406     * escape sequence {@code &nbsp;}
407     * 
408     * @return A String where any instance of white-space character {@code #160} have been
409     * replaced with character {@code #32}
410     */
411    public static String replaceNBSP(String s)
412    { return s.replace((char) 160, ' '); }
413    // { return s.replace(("" + ((char) 160)), " "); }
414
415    /**
416     * Even lower than {@code #160}, apparently is the {@code "Zero Width Space"} (character 
417     * {@code #8203}.  This is actually inserted by the <B>JavaDoc Tool</B> (by
418     * {@code Sun / Oracle}) into JavaDoc generated HTML Pages.  Here, it shall be replaced by
419     * character {@code #32} - the <I>space-character</I>.
420     * 
421     * <BR /><BR /><B>A.K.A.:</B> <CODE>&quot;\u200B&quot;</CODE>.
422     * 
423     * <BR /><BR /><B><I STYLE='color: red;'>Can you see the character, above?</I></B>  No?
424     * That's zero width space for you!  If you ever sitting and wondering why a {@code String}
425     * seems to be something else than what it looks like - you might have a zero-width 
426     * space in your {@code String}.  If so, it will take a while to find the bug.
427     * 
428     * @param s Any {@code String} will pass.  Generally {@code String's} that were converted from
429     * JavaDoc HTML pages will contain {@code char #8203}.
430     * 
431     * @return A String where any instance of white-space character {@code #8203} have been
432     * replaced with character {@code #32}
433     */
434    public static String replaceZWSP(String s)
435    { return s.replace((char) 8203, ' '); }
436    // { return s.replace(("" + ((char) 8203)), " "); }
437
438
439    // ********************************************************************************************
440    // ********************************************************************************************
441    // CSS Source
442    // ********************************************************************************************
443    // ********************************************************************************************
444
445
446    /**
447     * Checks if a Java-{@code String} constitutes a valid CSS Property-Name.  Note that this
448     * method, in no way consults any "complete list" of all known CSS-Properties.  Instead, it 
449     * simply analyzes whether the name is conguent with the CSS-Property Validator Reg-ex.
450     * 
451     * @param cssPropertyName Any Java-{@code String}
452     * 
453     * @return {@code TRUE} if and ony if {@code 'attributeName'} is a valid HTML Atribute-Name,
454     * according to the agreed upon CSS-Property Regular-Expression Validator.
455     */
456    public static boolean isCSSPropertyName(String cssPropertyName)
457    {
458        if (cssPropertyName.length() == 0) return false;
459
460        if (! isCSSPropertyNameStart(cssPropertyName.charAt(0))) return false;
461
462        for (int i=1; i < cssPropertyName.length(); i++)
463        {
464            final char c = cssPropertyName.charAt(i);
465            if ((c >= 'A') && (c <= 'Z')) continue;
466            if ((c >= 'a') && (c <= 'z')) continue;
467            if ((c >= '0') && (c <= '9')) continue;
468            if ((c == '-') || (c == '_')) continue;
469            return false;
470        }
471
472        return true;
473    }
474
475    /**
476     * Checks whether parameter {@code 'c'} is one of the agreed-upon standard characters that are
477     * allowed to begin CSS Property-Names.
478     * 
479     * @param c Any Java {@code char}-primitive
480     * 
481     * @return {@code TRUE} if and ony if {@code 'c'} is a character that would be allowed to begin
482     * a CSS Property-Name
483     */
484    public static boolean isCSSPropertyNameStart(char c)
485    {
486        if ((c >= 'A') && (c <= 'Z')) return true;
487        if ((c >= 'a') && (c <= 'z')) return true;
488        if ((c == '-') || (c == '_')) return true;
489        return false;
490    }
491
492    /**
493     * Checks whether parameter {@code 'c'} is one of the agreed-upon standard characters that are
494     * permitted within CSS Property-Names, after the first character of the name.
495     * 
496     * @param c Any Java {@code char}-primitive
497     * 
498     * @return {@code TRUE} if and ony if {@code 'c'} is a character that would be allowed within a
499     * valid CSS Property-Name.
500     */
501    public static boolean isCSSPropertyNamePart(char c)
502    {
503        if ((c >= 'A') && (c <= 'Z')) return true;
504        if ((c >= 'a') && (c <= 'z')) return true;
505        if ((c >= '0') && (c <= '9')) return true;
506        if ((c == '-') || (c == '_')) return true;
507        return false;
508    }
509
510
511    // ********************************************************************************************
512    // ********************************************************************************************
513    // CSS Classes
514    // ********************************************************************************************
515    // ********************************************************************************************
516
517
518    /**
519     * Checks if a Java-{@code String} constitutes a valid CSS Class-Name. This method does not
520     * consult any "complete list" of all known CSS classes but instead analyzes whether the name
521     * adheres to standard CSS class naming conventions.
522     * 
523     * <BR /><BR /><DIV CLASS=JDHint>
524     * <B STYLE="color: red;">Chat-GPT Note:</B> Chat-GPT wrote all three of these (including th
525     * JavaDoc).  Chat-GPT makes programming more fun.
526     * </DIV>
527     * 
528     * @param cssClassName Any Java-{@code String}
529     * 
530     * @return {@code TRUE} if and only if {@code cssClassName} is a valid CSS Class-Name according
531     * to the agreed-upon CSS naming rules.
532     */
533    public static boolean isCSSClassName(String cssClassName)
534    {
535        if (cssClassName.length() == 0) return false;
536
537        if (!isCSSClassNameStart(cssClassName.charAt(0))) return false;
538
539        for (int i = 1; i < cssClassName.length(); i++)
540            if (!isCSSClassNamePart(cssClassName.charAt(i))) return false;
541
542        return true;
543    }
544
545    /**
546     * Checks whether parameter {@code 'c'} is a valid character to begin a CSS Class-Name.
547     * 
548     * <BR /><BR /><DIV CLASS=JDHint>
549     * <B STYLE="color: red;">Chat-GPT Note:</B> Chat-GPT wrote all three of these (including the
550     * JavaDoc).  Chat-GPT makes programming more fun.
551     * </DIV>
552     * 
553     * @param c Any Java {@code char}-primitive
554     * 
555     * @return {@code TRUE} if and only if {@code c} is a character that can begin a CSS
556     * Class-Name.
557     */
558    public static boolean isCSSClassNameStart(char c)
559    {
560        if ((c >= 'A') && (c <= 'Z')) return true;
561        if ((c >= 'a') && (c <= 'z')) return true;
562        if (c == '-') return true;
563        if (c == '_') return true;
564        return false;
565    }
566
567    /**
568     * Checks whether parameter {@code 'c'} is a valid character within a CSS Class-Name.
569     * 
570     * <BR /><BR /><DIV CLASS=JDHint>
571     * <B STYLE="color: red;">Chat-GPT Note:</B> Chat-GPT wrote all three of these (including the
572     * JavaDoc).  Chat-GPT makes programming more fun.
573     * </DIV>
574     * 
575     * @param c Any Java {@code char}-primitive
576     * 
577     * @return {@code TRUE} if and only if {@code c} is a character that can be part of a CSS
578     * Class-Name.
579     */
580    public static boolean isCSSClassNamePart(char c)
581    {
582        if ((c >= 'A') && (c <= 'Z')) return true;
583        if ((c >= 'a') && (c <= 'z')) return true;
584        if ((c >= '0') && (c <= '9')) return true;
585        if (c == '-') return true;
586        if (c == '_') return true;
587        return false;
588    }
589
590
591
592    // ********************************************************************************************
593    // ********************************************************************************************
594    // More HTML Source
595    // ********************************************************************************************
596    // ********************************************************************************************
597
598
599    /**
600     * Checks if a Java-{@code String} constitutes a valid HTML Attibute-Name.  Note that this
601     * method, in no way consults any "complete list" of all know HTML-Attributes.  Instead, it 
602     * simply analyzes whether the name is conguent with the Attribute-Name Validator Reg-ex.
603     * 
604     * @param attributeName Any Java-{@code String}
605     * 
606     * @return {@code TRUE} if and ony if {@code 'attributeName'} is a valid HTML Atribute-Name,
607     * according to the agreed upon Attribute-Name Regular-Expression Validator.
608     */
609    public static boolean isAttributeName(String attributeName)
610    {
611        if (attributeName.length() == 0) return false;
612
613        if (! isAttributeNameStart(attributeName.charAt(0))) return false;
614
615        for (int i=1; i < attributeName.length(); i++)
616        {
617            final char c = attributeName.charAt(i);
618            if ((c >= 'A') && (c <= 'Z')) continue;
619            if ((c >= 'a') && (c <= 'z')) continue;
620            if ((c >= '0') && (c <= '9')) continue;
621            if ((c == '-') || (c == '_')) continue;
622            return false;
623        }
624
625        return true;
626    }
627
628    /**
629     * Checks whether parameter {@code 'c'} is one of the agreed-upon standard characters that are
630     * allowed to begin HTML Attribute-Names.
631     * 
632     * @param c Any Java {@code char}-primitive
633     * 
634     * @return {@code TRUE} if and ony if {@code 'c'} is a character that would be allowed to begin
635     * an HTML Attribute-Name
636     */
637    public static boolean isAttributeNameStart(char c)
638    {
639        if ((c >= 'A') && (c <= 'Z')) return true;
640        if ((c >= 'a') && (c <= 'z')) return true;
641        return false;
642    }
643
644    /**
645     * Checks whether parameter {@code 'c'} is one of the agreed-upon standard characters that are
646     * permitted within HTML Attribute-Names, after the first character of the name.
647     * 
648     * @param c Any Java {@code char}-primitive
649     * 
650     * @return {@code TRUE} if and ony if {@code 'c'} is a character that would be allowed within a
651     * valid HTML Attribute-Name.
652     */
653    public static boolean isAttributeNamePart(char c)
654    {
655        if ((c >= 'A') && (c <= 'Z')) return true;
656        if ((c >= 'a') && (c <= 'z')) return true;
657        if ((c >= '0') && (c <= '9')) return true;
658        if ((c == '-') || (c == '_')) return true;
659        return false;
660    }
661
662
663    /**
664     * Simply removes the trailiing {@code '.java'} from the end of an input File-Name Parameter.
665     * Also removes any leading directory information from the input {@code String}.
666     * 
667     * @param javaFileName This is expected to be a legitamite '.java' Source-Code File-Name, as a
668     * {@code java.lang.String}.
669     * 
670     * @return Returns a {@code String} in which leading directory information has been truncated,
671     * and the last five characters have been removed.
672     * 
673     * <BR /><BR />Due to the highly repetitive nature of using this methpo within a loop, this
674     * method's body <B STYLE='color: red;'><I>does not</I></B> perform any kind of error checking
675     * on its input.
676     * 
677     * <BR /><BR />If a null {@code String} is passed as input, this method will throw a 
678     * {@code NullPointerException}.  If your best friend's address is passed as input, this method
679     * will return a {@code String} in which the last 5 characters of text of that address have
680     * been removed.
681     * 
682     * <BR /><BR /><I>And if that address had a forward or backward slash ({@code '/'} or
683     * {@code '\'}), everything prior to the last slash present within that input-mess would be
684     * truncated.</I>
685     * 
686     * @throws IndexOutOfBoundsException If, after truncating any leading directory information,
687     * the resulting {@code String} is less than 5 characters, then this exception throws.
688     */
689    public static String noPathNoExt(String javaFileName)
690    {
691        for (int i=(javaFileName.length()-1); i > 0; i--)
692        {
693            final char c = javaFileName.charAt(i);
694
695            if ((c == '/') || (c == '\\'))
696            {
697                javaFileName = javaFileName.substring(i + 1);
698                break;
699            }
700        }
701
702        return javaFileName.substring(0, javaFileName.length() - 5);
703    }
704}