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