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