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