import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
import java.util.Objects;

/**
 * {@link <a href=
 * "https://[Log in to view URL]"
 * target="_blank"> formattazione di una classe </a>}
 * 
 * @author itammb ( Italia Massimiliano Buscati )
 * @version JDK 1.15
 *
 */

class Main {

    	public static class Parser {

		/**
		 * WORD_MISSING = parola non trovata nel testo <br>
		 * WORD_FOUND_FORWARD = parola trovata nel testo
		 * 
		 */
		public static enum FORWARD {
			WORD_MISSING, WORD_FOUND
		}

		private String text;

		public Parser(String text) {
			this.text = text;
		}

		/**
		 * Ricerca non case-sensitive di una parola dentro un testo
		 * 
		 * @param searchPos Il range di ricerca nel testo varia da 0..(lunghezza testo -
		 *                  1)
		 * 
		 * @return Formato: [ [ [ WORD_MISSING | WORD_FOUND ],[ posizione ricerca,
		 *         parola ] ], ... ]
		 * 
		 * @see Entry
		 * @see SimpleEntry
		 * @see String#equalsIgnoreCase(String)
		 * @see FORWARD
		 * @see Word
		 *
		 */
		public Entry<FORWARD, Word> forwardIgnoreCase(int searchPos, String word) {
			SimpleEntry<FORWARD, Word> searchResult;

			for (; isTextEnd(searchPos); searchPos++) {
				String partOfText = text.substring(searchPos);

				if (isNotWordShort(word, partOfText)) {
					if ((searchResult = equalsIgnoreCase(partOfText, word, searchPos)) != null)
						return searchResult;
				} else
					return new SimpleEntry<FORWARD, Word>(FORWARD.WORD_MISSING, null);
			}

			return new SimpleEntry<FORWARD, Word>(FORWARD.WORD_MISSING, null);
		}

		private boolean isNotWordShort(String word, String partOfText) {
			return word.length() <= partOfText.length();
		}

		private SimpleEntry<FORWARD, Word> equalsIgnoreCase(String partOfText, String word, int start) {
			int wordLen = word.length();
			String word_ = partOfText.substring(0, wordLen);

			if (word.equalsIgnoreCase(word_)) {
				int end = start + wordLen;

				if (isTextEnd(end))
					return new SimpleEntry<FORWARD, Word>(FORWARD.WORD_FOUND,
							new Word(word, new SearchPosition(start, end - 1, end)));
				else
					return new SimpleEntry<FORWARD, Word>(FORWARD.WORD_FOUND,
							new Word(word, new SearchPosition(start, end - 1, -1)));
			}

			return null;
		}

		private boolean isTextEnd(int endPos) {
			return endPos < text.length();
		}
	}

	/**
	 * Memorizza la posizione di una parola all'interno di un testo
	 * 
	 * @see Comparable
	 *
	 * {@link Word} {@link Parser#forwardIgnoreCase(int, String)}
	 * 
	 */
	public final static class Word implements Comparable<Word> {
		private final String WORD;
		private final SearchPosition SEARCH;

		protected Word(String WORD, SearchPosition SEARCH) {
			this.WORD = WORD;
			this.SEARCH = SEARCH;
		}

		public String getWORD() {
			return WORD;
		}

		public SearchPosition getSEARCH() {
			return SEARCH;
		}

		@Override
		public int hashCode() {
			return Objects.hash(SEARCH, WORD);
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Word other = (Word) obj;
			return Objects.equals(SEARCH, other.SEARCH) && Objects.equals(WORD, other.WORD);
		}

		@Override
		public int compareTo(Word other) {
			int BEFORE = -1;
			int EQUAL = 0;
			int AFTER = 1;

			if (this.WORD.equals(other.WORD))
				return EQUAL;
			else if (this.SEARCH.start < other.SEARCH.start)
				return BEFORE;

			return AFTER;
		}
	}
	
	/**
	 * Memorizza la posizione iniziale, finale e succesiva di una ricerca
	 * all'interno di un testo
	 * 
	 * {@link Word} {@link Parser#forwardIgnoreCase(int, String)}
	 * 
	 */
	public final static class SearchPosition {
		private final int start, end, next;

		/**
		 * @param start range di valori possibili: 0..(lunghezza testo - lunghezza
		 *              parola - 1)
		 * @param end   range di valori possibili: (start + lunghezza parola - 1)
		 * @param next  range di valori possibili: (start + lunghezza parola) oppure -1
		 *              se la ricezione del testo non è ipoteticamente completa
		 *
		 */
		protected SearchPosition(int start, int end, int next) {
			this.start = start;
			this.end = end;
			this.next = next;
		}

		public int getStart() {
			return start;
		}

		public int getEnd() {
			return end;
		}

		public int getNext() {
			return next;
		}

		@Override
		public int hashCode() {
			return Objects.hash(end, next, start);
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			SearchPosition other = (SearchPosition) obj;
			return end == other.end && next == other.next && start == other.start;
		}

		@Override
		public String toString() {
			return "SearchPosition [start=" + start + ", end=" + end + ", next=" + next + "]";
		}
	}
    public static void main(String[] args) {
		// unit test - ricerca di un singolo carattere nel testo (separato da spazi o
		// interno a una parola)

		String text = "  e adesso ";
		String word = "e";
		//String word = "ad";
		//String word = "Ad";
		//String word = "x";
		Parser parser = new Parser(text);

		int searchPos = 0;
		while (searchPos != -1) {
			Entry<Parser.FORWARD, Word> searchResult = parser.forwardIgnoreCase(searchPos, word);

			if (searchResult.getKey().equals(Parser.FORWARD.WORD_MISSING))
				searchPos = -1;
			else {
				System.out.println(searchResult.getValue().getSEARCH().toString());
				searchPos = searchResult.getValue().getSEARCH().getNext();
			}
		}
    }
}

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: