001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.widgets;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.util.ArrayList;
007import java.util.List;
008import java.util.StringTokenizer;
009
010import javax.swing.text.JTextComponent;
011
012import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
013import org.openstreetmap.josm.data.osm.PrimitiveId;
014import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
015import org.openstreetmap.josm.tools.Logging;
016import org.openstreetmap.josm.tools.Utils;
017
018/**
019 * A text field designed to enter one or several OSM primitive IDs.
020 * @author Matthias Julius
021 */
022public class OsmIdTextField extends AbstractIdTextField<OsmIdTextField.OsmIdValidator> {
023
024    /**
025     * Constructs a new {@link OsmIdTextField}
026     */
027    public OsmIdTextField() {
028        super(OsmIdValidator.class);
029    }
030
031    /**
032     * Sets the type of primitive object
033     * @param type The type of primitive object (
034     *      {@link OsmPrimitiveType#NODE NODE},
035     *      {@link OsmPrimitiveType#WAY WAY},
036     *      {@link OsmPrimitiveType#RELATION RELATION})
037     */
038    public void setType(OsmPrimitiveType type) {
039        validator.type = type;
040    }
041
042    /**
043     * Get entered ID list - supports "1,2,3" "1 2   ,3" or even "1 2 3 v2 6 v8"
044     * @return list of id's
045     */
046    public final List<PrimitiveId> getIds() {
047        return new ArrayList<>(validator.ids);
048    }
049
050    /**
051     * Reads the OSM primitive id(s)
052     * @return true if valid OSM objects IDs have been read, false otherwise
053     * @see OsmIdValidator#readOsmIds
054     */
055    @Override
056    public boolean readIds() {
057        return validator.readOsmIds();
058    }
059
060    /**
061     * Validator for an OSM primitive ID entered in a {@link JTextComponent}.
062     */
063    public static class OsmIdValidator extends AbstractTextComponentValidator {
064
065        private final List<PrimitiveId> ids = new ArrayList<>();
066        private OsmPrimitiveType type;
067
068        /**
069         * Constructs a new {@link OsmIdValidator}
070         * @param tc The text component to validate
071         */
072        public OsmIdValidator(JTextComponent tc) {
073            super(tc, false);
074        }
075
076        @Override
077        public boolean isValid() {
078            return readOsmIds();
079        }
080
081        @Override
082        public void validate() {
083            if (!isValid()) {
084                feedbackInvalid(tr("The current value is not a valid OSM ID. Please enter an integer value > 0"));
085            } else {
086                feedbackValid(tr("Please enter an integer value > 0"));
087            }
088        }
089
090        /**
091         * Reads the OSM primitive id(s)
092         * @return true if valid OSM objects IDs have been read, false otherwise
093         */
094        public boolean readOsmIds() {
095            String value = getComponent().getText();
096            char c;
097            if (Utils.isBlank(value)) {
098                return false;
099            }
100            ids.clear();
101            StringTokenizer st = new StringTokenizer(value, ",.+/ \t\n");
102            String s;
103            while (st.hasMoreTokens()) {
104                s = st.nextToken();
105                // convert tokens to int skipping v-words (version v2 etc)
106                c = s.charAt(0);
107                if (c != 'v') {
108                    try {
109                        ids.addAll(SimplePrimitiveId.multipleFromString(s));
110                    } catch (IllegalArgumentException ex) {
111                        try {
112                            Logging.trace(ex);
113                            long id = Long.parseLong(s);
114                            if (id <= 0) {
115                                return false;
116                            } else if (type == OsmPrimitiveType.NODE) {
117                                ids.add(new SimplePrimitiveId(id, OsmPrimitiveType.NODE));
118                            } else if (type == OsmPrimitiveType.WAY || type == OsmPrimitiveType.CLOSEDWAY) {
119                                ids.add(new SimplePrimitiveId(id, OsmPrimitiveType.WAY));
120                            } else if (type == OsmPrimitiveType.RELATION || type == OsmPrimitiveType.MULTIPOLYGON) {
121                                ids.add(new SimplePrimitiveId(id, OsmPrimitiveType.RELATION));
122                            } else {
123                                return false;
124                            }
125                        } catch (IllegalArgumentException ex2) {
126                            Logging.trace(ex2);
127                            return false;
128                        }
129                    }
130                }
131            }
132            return !ids.isEmpty();
133        }
134    }
135}