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