001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.osm.search;
003
004import static org.openstreetmap.josm.tools.I18n.trc;
005
006import java.util.Objects;
007
008import org.openstreetmap.josm.tools.Logging;
009
010/**
011 * This class defines a set of parameters that is used to
012 * perform search within the search dialog.
013 * @since 12659 (extracted from {@code SearchAction})
014 */
015public class SearchSetting {
016    /** Search text */
017    public String text;
018    /** Search mode */
019    public SearchMode mode;
020    /** {@code true} to perform a case-sensitive search */
021    public boolean caseSensitive;
022    /** {@code true} to perform a regex-based search */
023    public boolean regexSearch;
024    /** {@code true} to execute a MapCSS selector */
025    public boolean mapCSSSearch;
026    /** {@code true} to include all objects (even incomplete and deleted ones) */
027    public boolean allElements;
028
029    /**
030     * Constructs a new {@code SearchSetting}.
031     */
032    public SearchSetting() {
033        text = "";
034        mode = SearchMode.replace;
035    }
036
037    /**
038     * Constructs a new {@code SearchSetting} from an existing one.
039     * @param original original search settings
040     */
041    public SearchSetting(SearchSetting original) {
042        text = original.text;
043        mode = original.mode;
044        caseSensitive = original.caseSensitive;
045        regexSearch = original.regexSearch;
046        mapCSSSearch = original.mapCSSSearch;
047        allElements = original.allElements;
048    }
049
050    @Override
051    public String toString() {
052        String cs = caseSensitive ?
053                /*case sensitive*/  trc("search", "CS") :
054                    /*case insensitive*/  trc("search", "CI");
055        String rx = regexSearch ? ", " +
056                        /*regex search*/ trc("search", "RX") : "";
057        String css = mapCSSSearch ? ", " +
058                        /*MapCSS search*/ trc("search", "CSS") : "";
059        String all = allElements ? ", " +
060                        /*all elements*/ trc("search", "A") : "";
061        return '"' + text + "\" (" + cs + rx + css + all + ", " + mode + ')';
062    }
063
064    @Override
065    public boolean equals(Object other) {
066        if (this == other) return true;
067        if (other == null || getClass() != other.getClass()) return false;
068        SearchSetting that = (SearchSetting) other;
069        return caseSensitive == that.caseSensitive &&
070                regexSearch == that.regexSearch &&
071                mapCSSSearch == that.mapCSSSearch &&
072                allElements == that.allElements &&
073                mode == that.mode &&
074                Objects.equals(text, that.text);
075    }
076
077    @Override
078    public int hashCode() {
079        return Objects.hash(text, mode, caseSensitive, regexSearch, mapCSSSearch, allElements);
080    }
081
082    /**
083     * <p>Transforms a string following a certain format, namely "[R | A | D | S][C?,R?,A?,M?] [a-zA-Z]"
084     * where the first part defines the mode of the search, see {@link SearchMode}, the second defines
085     * a set of attributes within the {@code SearchSetting} class and the second is the search query.
086     * <p>
087     * Attributes are as follows:
088     * <ul>
089     *     <li>C - if search is case sensitive
090     *     <li>R - if the regex syntax is used
091     *     <li>A - if all objects are considered
092     *     <li>M - if the mapCSS syntax is used
093     * </ul>
094     * <p>For example, "RC type:node" is a valid string representation of an object that replaces the
095     * current selection, is case sensitive and searches for all objects of type node.
096     * @param s A string representation of a {@code SearchSetting} object
097     *          from which the object must be built.
098     * @return A {@code SearchSetting} defined by the input string.
099     */
100    public static SearchSetting readFromString(String s) {
101        if (s.isEmpty())
102            return null;
103
104        SearchSetting result = new SearchSetting();
105
106        int index = 1;
107
108        result.mode = SearchMode.fromCode(s.charAt(0));
109        if (result.mode == null) {
110            result.mode = SearchMode.replace;
111            index = 0;
112        }
113
114        while (index < s.length()) {
115            if (s.charAt(index) == 'C') {
116                result.caseSensitive = true;
117            } else if (s.charAt(index) == 'R') {
118                result.regexSearch = true;
119            } else if (s.charAt(index) == 'A') {
120                result.allElements = true;
121            } else if (s.charAt(index) == 'M') {
122                result.mapCSSSearch = true;
123            } else if (s.charAt(index) == ' ') {
124                break;
125            } else {
126                Logging.warn("Unknown char in SearchSettings: " + s);
127                break;
128            }
129            index++;
130        }
131
132        if (index < s.length() && s.charAt(index) == ' ') {
133            index++;
134        }
135
136        result.text = s.substring(index);
137
138        return result;
139    }
140
141    /**
142     * Builds a string representation of the {@code SearchSetting} object,
143     * see {@link #readFromString(String)} for more details.
144     * @return A string representation of the {@code SearchSetting} object.
145     */
146    public String writeToString() {
147        if (text == null || text.isEmpty())
148            return "";
149
150        StringBuilder result = new StringBuilder();
151        result.append(mode.getCode());
152        if (caseSensitive) {
153            result.append('C');
154        }
155        if (regexSearch) {
156            result.append('R');
157        }
158        if (mapCSSSearch) {
159            result.append('M');
160        }
161        if (allElements) {
162            result.append('A');
163        }
164        result.append(' ')
165              .append(text);
166        return result.toString();
167    }
168}