1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3 /*
4  *******************************************************************************
5  * Copyright (C) 1996-2015, International Business Machines Corporation and    *
6  * others. All Rights Reserved.                                                *
7  *******************************************************************************
8  */
9 
10 package com.ibm.icu.text;
11 
12 import java.text.CharacterIterator;
13 
14 /**
15  * <tt>SearchIterator</tt> is an abstract base class that provides
16  * methods to search for a pattern within a text string. Instances of
17  * <tt>SearchIterator</tt> maintain a current position and scan over the
18  * target text, returning the indices the pattern is matched and the length
19  * of each match.
20  * <p>
21  * <tt>SearchIterator</tt> defines a protocol for text searching.
22  * Subclasses provide concrete implementations of various search algorithms.
23  * For example, <tt>StringSearch</tt> implements language-sensitive pattern
24  * matching based on the comparison rules defined in a
25  * <tt>RuleBasedCollator</tt> object.
26  * <p>
27  * Other options for searching include using a BreakIterator to restrict
28  * the points at which matches are detected.
29  * <p>
30  * <tt>SearchIterator</tt> provides an API that is similar to that of
31  * other text iteration classes such as <tt>BreakIterator</tt>. Using
32  * this class, it is easy to scan through text looking for all occurrences of
33  * a given pattern. The following example uses a <tt>StringSearch</tt>
34  * object to find all instances of "fox" in the target string. Any other
35  * subclass of <tt>SearchIterator</tt> can be used in an identical
36  * manner.
37  * <pre><code>
38  * String target = "The quick brown fox jumped over the lazy fox";
39  * String pattern = "fox";
40  * SearchIterator iter = new StringSearch(pattern, target);
41  * for (int pos = iter.first(); pos != SearchIterator.DONE;
42  *         pos = iter.next()) {
43  *     System.out.println("Found match at " + pos +
44  *             ", length is " + iter.getMatchLength());
45  * }
46  * </code></pre>
47  *
48  * @author Laura Werner, synwee
49  * @stable ICU 2.0
50  * @see BreakIterator
51  * @see RuleBasedCollator
52  */
53 public abstract class SearchIterator
54 {
55     /**
56      * The BreakIterator to define the boundaries of a logical match.
57      * This value can be a null.
58      * See class documentation for more information.
59      * @see #setBreakIterator(BreakIterator)
60      * @see #getBreakIterator
61      * @see BreakIterator
62      * @stable ICU 2.0
63      */
64     protected BreakIterator breakIterator;
65 
66     /**
67      * Target text for searching.
68      * @see #setTarget(CharacterIterator)
69      * @see #getTarget
70      * @stable ICU 2.0
71      */
72     protected CharacterIterator targetText;
73     /**
74      * Length of the most current match in target text.
75      * Value 0 is the default value.
76      * @see #setMatchLength
77      * @see #getMatchLength
78      * @stable ICU 2.0
79      */
80     protected int matchLength;
81 
82     /**
83      * Java port of ICU4C struct USearch (usrchimp.h)
84      *
85      * Note:
86      *
87      *  ICU4J already exposed some protected members such as
88      * targetText, breakIterator and matchedLength as a part of stable
89      * APIs. In ICU4C, they are exposed through USearch struct,
90      * although USearch struct itself is internal API.
91      *
92      *  This class was created for making ICU4J code parallel to
93      * ICU4C implementation. ICU4J implementation access member
94      * fields like C struct (e.g. search_.isOverlap_) mostly, except
95      * fields already exposed as protected member (e.g. search_.text()).
96      *
97      */
98     final class Search {
99 
text()100         CharacterIterator text() {
101             return SearchIterator.this.targetText;
102         }
103 
setTarget(CharacterIterator text)104         void setTarget(CharacterIterator text) {
105             SearchIterator.this.targetText = text;
106         }
107 
108         /** Flag to indicate if overlapping search is to be done.
109             E.g. looking for "aa" in "aaa" will yield matches at offset 0 and 1. */
110         boolean isOverlap_;
111 
112         boolean isCanonicalMatch_;
113 
114         ElementComparisonType elementComparisonType_;
115 
116         BreakIterator internalBreakIter_;
117 
breakIter()118         BreakIterator breakIter() {
119             return SearchIterator.this.breakIterator;
120         }
121 
setBreakIter(BreakIterator breakIter)122         void setBreakIter(BreakIterator breakIter) {
123             SearchIterator.this.breakIterator = breakIter;
124         }
125 
126         int matchedIndex_;
127 
matchedLength()128         int matchedLength() {
129             return SearchIterator.this.matchLength;
130         }
131 
setMatchedLength(int matchedLength)132         void setMatchedLength(int matchedLength) {
133             SearchIterator.this.matchLength = matchedLength;
134         }
135 
136         /** Flag indicates if we are doing a forwards search */
137         boolean isForwardSearching_;
138 
139         /** Flag indicates if we are at the start of a string search.
140             This indicates that we are in forward search and at the start of m_text. */
141         boolean reset_;
142 
143         // Convenient methods for accessing begin/end index of the
144         // target text. These are ICU4J only and are not data fields.
beginIndex()145         int beginIndex() {
146             if (targetText == null) {
147                 return 0;
148             }
149             return targetText.getBeginIndex();
150         }
151 
endIndex()152         int endIndex() {
153             if (targetText == null) {
154                 return 0;
155             }
156             return targetText.getEndIndex();
157         }
158     }
159 
160     Search search_ = new Search();
161 
162     // public data members -------------------------------------------------
163 
164     /**
165      * DONE is returned by previous() and next() after all valid matches have
166      * been returned, and by first() and last() if there are no matches at all.
167      * @see #previous
168      * @see #next
169      * @stable ICU 2.0
170      */
171     public static final int DONE = -1;
172 
173     // public methods -----------------------------------------------------
174 
175     // public setters -----------------------------------------------------
176 
177     /**
178      * <p>
179      * Sets the position in the target text at which the next search will start.
180      * This method clears any previous match.
181      * </p>
182      * @param position position from which to start the next search
183      * @exception IndexOutOfBoundsException thrown if argument position is out
184      *            of the target text range.
185      * @see #getIndex
186      * @stable ICU 2.8
187      */
setIndex(int position)188     public void setIndex(int position) {
189         if (position < search_.beginIndex()
190             || position > search_.endIndex()) {
191             throw new IndexOutOfBoundsException(
192                 "setIndex(int) expected position to be between " +
193                 search_.beginIndex() + " and " + search_.endIndex());
194         }
195         search_.reset_ = false;
196         search_.setMatchedLength(0);
197         search_.matchedIndex_ = DONE;
198     }
199 
200     /**
201      * Determines whether overlapping matches are returned. See the class
202      * documentation for more information about overlapping matches.
203      * <p>
204      * The default setting of this property is false
205      *
206      * @param allowOverlap flag indicator if overlapping matches are allowed
207      * @see #isOverlapping
208      * @stable ICU 2.8
209      */
setOverlapping(boolean allowOverlap)210     public void setOverlapping(boolean allowOverlap) {
211         search_.isOverlap_ = allowOverlap;
212     }
213 
214     /**
215      * Set the BreakIterator that will be used to restrict the points
216      * at which matches are detected.
217      *
218      * @param breakiter A BreakIterator that will be used to restrict the
219      *                points at which matches are detected. If a match is
220      *                found, but the match's start or end index is not a
221      *                boundary as determined by the {@link BreakIterator},
222      *                the match will be rejected and another will be searched
223      *                for. If this parameter is <tt>null</tt>, no break
224      *                detection is attempted.
225      * @see BreakIterator
226      * @stable ICU 2.0
227      */
setBreakIterator(BreakIterator breakiter)228     public void setBreakIterator(BreakIterator breakiter) {
229         search_.setBreakIter(breakiter);
230         if (search_.breakIter() != null) {
231             // Create a clone of CharacterItearator, so it won't
232             // affect the position currently held by search_.text()
233             if (search_.text() != null) {
234                 search_.breakIter().setText((CharacterIterator)search_.text().clone());
235             }
236         }
237     }
238 
239     /**
240      * Set the target text to be searched. Text iteration will then begin at
241      * the start of the text string. This method is useful if you want to
242      * reuse an iterator to search within a different body of text.
243      *
244      * @param text new text iterator to look for match,
245      * @exception IllegalArgumentException thrown when text is null or has
246      *               0 length
247      * @see #getTarget
248      * @stable ICU 2.4
249      */
setTarget(CharacterIterator text)250     public void setTarget(CharacterIterator text)
251     {
252         if (text == null || text.getEndIndex() == text.getIndex()) {
253             throw new IllegalArgumentException("Illegal null or empty text");
254         }
255 
256         text.setIndex(text.getBeginIndex());
257         search_.setTarget(text);
258         search_.matchedIndex_ = DONE;
259         search_.setMatchedLength(0);
260         search_.reset_ = true;
261         search_.isForwardSearching_ = true;
262         if (search_.breakIter() != null) {
263             // Create a clone of CharacterItearator, so it won't
264             // affect the position currently held by search_.text()
265             search_.breakIter().setText((CharacterIterator)text.clone());
266         }
267         if (search_.internalBreakIter_ != null) {
268             search_.internalBreakIter_.setText((CharacterIterator)text.clone());
269         }
270     }
271 
272     //TODO: We may add APIs below to match ICU4C APIs
273     // setCanonicalMatch
274 
275     // public getters ----------------------------------------------------
276 
277     /**
278     * Returns the index to the match in the text string that was searched.
279     * This call returns a valid result only after a successful call to
280     * {@link #first}, {@link #next}, {@link #previous}, or {@link #last}.
281     * Just after construction, or after a searching method returns
282     * {@link #DONE}, this method will return {@link #DONE}.
283     * <p>
284     * Use {@link #getMatchLength} to get the matched string length.
285     *
286     * @return index of a substring within the text string that is being
287     *         searched.
288     * @see #first
289     * @see #next
290     * @see #previous
291     * @see #last
292     * @stable ICU 2.0
293     */
getMatchStart()294     public int getMatchStart() {
295         return search_.matchedIndex_;
296     }
297 
298     /**
299      * Return the current index in the text being searched.
300      * If the iteration has gone past the end of the text
301      * (or past the beginning for a backwards search), {@link #DONE}
302      * is returned.
303      *
304      * @return current index in the text being searched.
305      * @stable ICU 2.8
306      */
getIndex()307     public abstract int getIndex();
308 
309     /**
310      * Returns the length of text in the string which matches the search
311      * pattern. This call returns a valid result only after a successful call
312      * to {@link #first}, {@link #next}, {@link #previous}, or {@link #last}.
313      * Just after construction, or after a searching method returns
314      * {@link #DONE}, this method will return 0.
315      *
316      * @return The length of the match in the target text, or 0 if there
317      *         is no match currently.
318      * @see #first
319      * @see #next
320      * @see #previous
321      * @see #last
322      * @stable ICU 2.0
323      */
getMatchLength()324     public int getMatchLength() {
325         return search_.matchedLength();
326     }
327 
328     /**
329      * Returns the BreakIterator that is used to restrict the indexes at which
330      * matches are detected. This will be the same object that was passed to
331      * the constructor or to {@link #setBreakIterator}.
332      * If the {@link BreakIterator} has not been set, <tt>null</tt> will be returned.
333      * See {@link #setBreakIterator} for more information.
334      *
335      * @return the BreakIterator set to restrict logic matches
336      * @see #setBreakIterator
337      * @see BreakIterator
338      * @stable ICU 2.0
339      */
getBreakIterator()340     public BreakIterator getBreakIterator() {
341         return search_.breakIter();
342     }
343 
344     /**
345      * Return the string text to be searched.
346      * @return text string to be searched.
347      * @stable ICU 2.0
348      */
getTarget()349     public CharacterIterator getTarget() {
350         return search_.text();
351     }
352 
353     /**
354      * Returns the text that was matched by the most recent call to
355      * {@link #first}, {@link #next}, {@link #previous}, or {@link #last}.
356      * If the iterator is not pointing at a valid match (e.g. just after
357      * construction or after {@link #DONE} has been returned,
358      * returns an empty string.
359      *
360      * @return  the substring in the target test of the most recent match,
361      *          or null if there is no match currently.
362      * @see #first
363      * @see #next
364      * @see #previous
365      * @see #last
366      * @stable ICU 2.0
367      */
getMatchedText()368     public String getMatchedText() {
369         if (search_.matchedLength() > 0) {
370             int limit = search_.matchedIndex_ + search_.matchedLength();
371             StringBuilder result = new StringBuilder(search_.matchedLength());
372             CharacterIterator it = search_.text();
373             it.setIndex(search_.matchedIndex_);
374             while (it.getIndex() < limit) {
375                 result.append(it.current());
376                 it.next();
377             }
378             it.setIndex(search_.matchedIndex_);
379             return result.toString();
380         }
381         return null;
382     }
383 
384     // miscellaneous public methods -----------------------------------------
385 
386     /**
387      * Returns the index of the next point at which the text matches the
388      * search pattern, starting from the current position
389      * The iterator is adjusted so that its current index (as returned by
390      * {@link #getIndex}) is the match position if one was found.
391      * If a match is not found, {@link #DONE} will be returned and
392      * the iterator will be adjusted to a position after the end of the text
393      * string.
394      *
395      * @return The index of the next match after the current position,
396      *          or {@link #DONE} if there are no more matches.
397      * @see #getIndex
398      * @stable ICU 2.0
399      */
next()400     public int next() {
401         int index = getIndex(); // offset = getOffset() in ICU4C
402         int matchindex = search_.matchedIndex_;
403         int matchlength = search_.matchedLength();
404         search_.reset_ = false;
405         if (search_.isForwardSearching_) {
406             int endIdx = search_.endIndex();
407             if (index == endIdx || matchindex == endIdx ||
408                     (matchindex != DONE &&
409                     matchindex + matchlength >= endIdx)) {
410                 setMatchNotFound();
411                 return DONE;
412             }
413         } else {
414             // switching direction.
415             // if matchedIndex == DONE, it means that either a
416             // setIndex (setOffset in C) has been called or that previous ran off the text
417             // string. the iterator would have been set to offset 0 if a
418             // match is not found.
419             search_.isForwardSearching_ = true;
420             if (search_.matchedIndex_ != DONE) {
421                 // there's no need to set the collation element iterator
422                 // the next call to next will set the offset.
423                 return matchindex;
424             }
425         }
426 
427         if (matchlength > 0) {
428             // if matchlength is 0 we are at the start of the iteration
429             if (search_.isOverlap_) {
430                 index++;
431             } else {
432                 index += matchlength;
433             }
434         }
435 
436         return handleNext(index);
437     }
438 
439     /**
440      * Returns the index of the previous point at which the string text
441      * matches the search pattern, starting at the current position.
442      * The iterator is adjusted so that its current index (as returned by
443      * {@link #getIndex}) is the match position if one was found.
444      * If a match is not found, {@link #DONE} will be returned and
445      * the iterator will be adjusted to the index {@link #DONE}.
446      *
447      * @return The index of the previous match before the current position,
448      *          or {@link #DONE} if there are no more matches.
449      * @see #getIndex
450      * @stable ICU 2.0
451      */
previous()452     public int previous() {
453         int index;  // offset in ICU4C
454         if (search_.reset_) {
455             index = search_.endIndex();   // m_search_->textLength in ICU4C
456             search_.isForwardSearching_ = false;
457             search_.reset_ = false;
458             setIndex(index);
459         } else {
460             index = getIndex();
461         }
462 
463         int matchindex = search_.matchedIndex_;
464         if (search_.isForwardSearching_) {
465             // switching direction.
466             // if matchedIndex == DONE, it means that either a
467             // setIndex (setOffset in C) has been called or that next ran off the text
468             // string. the iterator would have been set to offset textLength if
469             // a match is not found.
470             search_.isForwardSearching_ = false;
471             if (matchindex != DONE) {
472                 return matchindex;
473             }
474         } else {
475             int startIdx = search_.beginIndex();
476             if (index == startIdx || matchindex == startIdx) {
477                 // not enough characters to match
478                 setMatchNotFound();
479                 return DONE;
480             }
481         }
482 
483         if (matchindex != DONE) {
484             if (search_.isOverlap_) {
485                 matchindex += search_.matchedLength() - 2;
486             }
487 
488             return handlePrevious(matchindex);
489         }
490 
491         return handlePrevious(index);
492     }
493 
494     /**
495      * Return true if the overlapping property has been set.
496      * See {@link #setOverlapping(boolean)} for more information.
497      *
498      * @see #setOverlapping
499      * @return true if the overlapping property has been set, false otherwise
500      * @stable ICU 2.8
501      */
isOverlapping()502     public boolean isOverlapping() {
503         return search_.isOverlap_;
504     }
505 
506     //TODO: We may add APIs below to match ICU4C APIs
507     // isCanonicalMatch
508 
509     /**
510     * Resets the iteration.
511     * Search will begin at the start of the text string if a forward
512     * iteration is initiated before a backwards iteration. Otherwise if a
513     * backwards iteration is initiated before a forwards iteration, the
514     * search will begin at the end of the text string.
515     *
516     * @stable ICU 2.0
517     */
reset()518     public void reset() {
519         setMatchNotFound();
520         setIndex(search_.beginIndex());
521         search_.isOverlap_ = false;
522         search_.isCanonicalMatch_ = false;
523         search_.elementComparisonType_ = ElementComparisonType.STANDARD_ELEMENT_COMPARISON;
524         search_.isForwardSearching_ = true;
525         search_.reset_ = true;
526     }
527 
528     /**
529      * Returns the first index at which the string text matches the search
530      * pattern. The iterator is adjusted so that its current index (as
531      * returned by {@link #getIndex()}) is the match position if one
532      *
533      * was found.
534      * If a match is not found, {@link #DONE} will be returned and
535      * the iterator will be adjusted to the index {@link #DONE}.
536      * @return The character index of the first match, or
537      *         {@link #DONE} if there are no matches.
538      *
539      * @see #getIndex
540      * @stable ICU 2.0
541      */
first()542     public final int first() {
543         int startIdx = search_.beginIndex();
544         setIndex(startIdx);
545         return handleNext(startIdx);
546     }
547 
548     /**
549      * Returns the first index equal or greater than <tt>position</tt> at which the
550      * string text matches the search pattern. The iterator is adjusted so
551      * that its current index (as returned by {@link #getIndex()}) is the
552      * match position if one was found.
553      * If a match is not found, {@link #DONE} will be returned and the
554      * iterator will be adjusted to the index {@link #DONE}.
555      *
556      * @param  position where search if to start from.
557      * @return The character index of the first match following
558      *         <tt>position</tt>, or {@link #DONE} if there are no matches.
559      * @throws IndexOutOfBoundsException    If position is less than or greater
560      *      than the text range for searching.
561      * @see #getIndex
562      * @stable ICU 2.0
563      */
following(int position)564     public final int following(int position) {
565         setIndex(position);
566         return handleNext(position);
567     }
568 
569     /**
570      * Returns the last index in the target text at which it matches the
571      * search pattern. The iterator is adjusted so that its current index
572      * (as returned by {@link #getIndex}) is the match position if one was
573      * found.
574      * If a match is not found, {@link #DONE} will be returned and
575      * the iterator will be adjusted to the index {@link #DONE}.
576      *
577      * @return The index of the first match, or {@link #DONE} if
578      *         there are no matches.
579      * @see #getIndex
580      * @stable ICU 2.0
581      */
last()582     public final int last() {
583         int endIdx = search_.endIndex();
584         setIndex(endIdx);
585         return handlePrevious(endIdx);
586     }
587 
588     /**
589      * Returns the first index less than <tt>position</tt> at which the string
590      * text matches the search pattern. The iterator is adjusted so that its
591      * current index (as returned by {@link #getIndex}) is the match
592      * position if one was found. If a match is not found,
593      * {@link #DONE} will be returned and the iterator will be
594      * adjusted to the index {@link #DONE}
595      * <p>
596      * When the overlapping option ({@link #isOverlapping}) is off, the last index of the
597      * result match is always less than <tt>position</tt>.
598      * When the overlapping option is on, the result match may span across
599      * <tt>position</tt>.
600      *
601      * @param  position where search is to start from.
602      * @return The character index of the first match preceding
603      *         <tt>position</tt>, or {@link #DONE} if there are
604      *         no matches.
605      * @throws IndexOutOfBoundsException If position is less than or greater than
606      *                                   the text range for searching
607      * @see #getIndex
608      * @stable ICU 2.0
609      */
preceding(int position)610     public final int preceding(int position) {
611         setIndex(position);
612         return handlePrevious(position);
613     }
614 
615     // protected constructor ----------------------------------------------
616 
617     /**
618      * Protected constructor for use by subclasses.
619      * Initializes the iterator with the argument target text for searching
620      * and sets the BreakIterator.
621      * See class documentation for more details on the use of the target text
622      * and {@link BreakIterator}.
623      *
624      * @param target The target text to be searched.
625      * @param breaker A {@link BreakIterator} that is used to determine the
626      *                boundaries of a logical match. This argument can be null.
627      * @exception IllegalArgumentException thrown when argument target is null,
628      *            or of length 0
629      * @see BreakIterator
630      * @stable ICU 2.0
631      */
SearchIterator(CharacterIterator target, BreakIterator breaker)632     protected SearchIterator(CharacterIterator target, BreakIterator breaker)
633     {
634         if (target == null
635             || (target.getEndIndex() - target.getBeginIndex()) == 0) {
636                 throw new IllegalArgumentException(
637                                    "Illegal argument target. " +
638                                    " Argument can not be null or of length 0");
639         }
640 
641         search_.setTarget(target);
642         search_.setBreakIter(breaker);
643         if (search_.breakIter() != null) {
644             search_.breakIter().setText((CharacterIterator)target.clone());
645         }
646         search_.isOverlap_ = false;
647         search_.isCanonicalMatch_ = false;
648         search_.elementComparisonType_ = ElementComparisonType.STANDARD_ELEMENT_COMPARISON;
649         search_.isForwardSearching_ = true;
650         search_.reset_ = true;
651         search_.matchedIndex_ = DONE;
652         search_.setMatchedLength(0);
653     }
654 
655     // protected methods --------------------------------------------------
656 
657 
658     /**
659      * Sets the length of the most recent match in the target text.
660      * Subclasses' handleNext() and handlePrevious() methods should call this
661      * after they find a match in the target text.
662      *
663      * @param length new length to set
664      * @see #handleNext
665      * @see #handlePrevious
666      * @stable ICU 2.0
667      */
setMatchLength(int length)668     protected void setMatchLength(int length)
669     {
670         search_.setMatchedLength(length);
671     }
672 
673     /**
674      * Abstract method which subclasses override to provide the mechanism
675      * for finding the next match in the target text. This allows different
676      * subclasses to provide different search algorithms.
677      * <p>
678      * If a match is found, the implementation should return the index at
679      * which the match starts and should call
680      * {@link #setMatchLength} with the number of characters
681      * in the target text that make up the match. If no match is found, the
682      * method should return {@link #DONE}.
683      *
684      * @param start The index in the target text at which the search
685      *              should start.
686      * @return index at which the match starts, else if match is not found
687      *         {@link #DONE} is returned
688      * @see #setMatchLength
689      * @stable ICU 2.0
690      */
handleNext(int start)691     protected abstract int handleNext(int start);
692 
693     /**
694      * Abstract method which subclasses override to provide the mechanism for
695      * finding the previous match in the target text. This allows different
696      * subclasses to provide different search algorithms.
697      * <p>
698      * If a match is found, the implementation should return the index at
699      * which the match starts and should call
700      * {@link #setMatchLength} with the number of characters
701      * in the target text that make up the match. If no match is found, the
702      * method should return {@link #DONE}.
703      *
704      * @param startAt   The index in the target text at which the search
705      *                  should start.
706      * @return index at which the match starts, else if match is not found
707      *         {@link #DONE} is returned
708      * @see #setMatchLength
709      * @stable ICU 2.0
710      */
handlePrevious(int startAt)711     protected abstract int handlePrevious(int startAt);
712 
713     /**
714      * @internal
715      * @deprecated This API is ICU internal only.
716      */
717     @Deprecated
718     //TODO: This protected method is @stable 2.0 in ICU4C
setMatchNotFound()719     protected void setMatchNotFound() {
720         search_.matchedIndex_ = DONE;
721         search_.setMatchedLength(0);
722     }
723 
724     /**
725      * Option to control how collation elements are compared.
726      * The default value will be {@link #STANDARD_ELEMENT_COMPARISON}.
727      * <p>
728      * PATTERN_BASE_WEIGHT_IS_WILDCARD supports "asymmetric search" as described in
729      * <a href="http://www.unicode.org/reports/tr10/#Asymmetric_Search">
730      * UTS #10 Unicode Collation Algorithm</a>, while ANY_BASE_WEIGHT_IS_WILDCARD
731      * supports a related option in which "unmarked" characters in either the
732      * pattern or the searched text are treated as wildcards that match marked or
733      * unmarked versions of the same character.
734      *
735      * @see #setElementComparisonType(ElementComparisonType)
736      * @see #getElementComparisonType()
737      * @stable ICU 53
738      */
739     public enum ElementComparisonType {
740         /**
741          * Standard collation element comparison at the specified collator strength.
742          *
743          * @stable ICU 53
744          */
745         STANDARD_ELEMENT_COMPARISON,
746         /**
747          * Collation element comparison is modified to effectively provide behavior
748          * between the specified strength and strength - 1.
749          * <p>
750          * Collation elements in the pattern that have the base weight for the specified
751          * strength are treated as "wildcards" that match an element with any other
752          * weight at that collation level in the searched text. For example, with a
753          * secondary-strength English collator, a plain 'e' in the pattern will match
754          * a plain e or an e with any diacritic in the searched text, but an e with
755          * diacritic in the pattern will only match an e with the same diacritic in
756          * the searched text.
757          *
758          * @stable ICU 53
759          */
760         PATTERN_BASE_WEIGHT_IS_WILDCARD,
761 
762         /**
763          * Collation element comparison is modified to effectively provide behavior
764          * between the specified strength and strength - 1.
765          * <p>
766          * Collation elements in either the pattern or the searched text that have the
767          * base weight for the specified strength are treated as "wildcards" that match
768          * an element with any other weight at that collation level. For example, with
769          * a secondary-strength English collator, a plain 'e' in the pattern will match
770          * a plain e or an e with any diacritic in the searched text, but an e with
771          * diacritic in the pattern will only match an e with the same diacritic or a
772          * plain e in the searched text.
773          *
774          * @stable ICU 53
775          */
776         ANY_BASE_WEIGHT_IS_WILDCARD
777     }
778 
779     /**
780      * Sets the collation element comparison type.
781      * <p>
782      * The default comparison type is {@link ElementComparisonType#STANDARD_ELEMENT_COMPARISON}.
783      *
784      * @see ElementComparisonType
785      * @see #getElementComparisonType()
786      * @stable ICU 53
787      */
setElementComparisonType(ElementComparisonType type)788     public void setElementComparisonType(ElementComparisonType type) {
789         search_.elementComparisonType_ = type;
790     }
791 
792     /**
793      * Returns the collation element comparison type.
794      *
795      * @see ElementComparisonType
796      * @see #setElementComparisonType(ElementComparisonType)
797      * @stable ICU 53
798      */
getElementComparisonType()799     public ElementComparisonType getElementComparisonType() {
800         return search_.elementComparisonType_;
801     }
802 }
803