001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.tagging.presets.items;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.GridBagLayout;
007import java.util.Collection;
008import java.util.LinkedList;
009import java.util.List;
010import java.util.Set;
011
012import javax.swing.JLabel;
013import javax.swing.JPanel;
014
015import org.openstreetmap.josm.data.osm.OsmPrimitive;
016import org.openstreetmap.josm.data.osm.Tag;
017import org.openstreetmap.josm.data.osm.search.SearchCompiler;
018import org.openstreetmap.josm.data.osm.search.SearchParseError;
019import org.openstreetmap.josm.data.osm.search.SearchSetting;
020import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItem;
021import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType;
022import org.openstreetmap.josm.tools.GBC;
023import org.openstreetmap.josm.tools.ImageProvider;
024import org.xml.sax.SAXException;
025
026/**
027 * The <code>roles</code> element in tagging presets definition.
028 * <p>
029 * A list of {@link Role} elements. Describes the roles that are expected for
030 * the members of a relation.
031 * <p>
032 * Used for data validation, auto completion, among others.
033 */
034public class Roles extends TaggingPresetItem {
035
036    /**
037     * The <code>role</code> element in tagging preset definition.
038     *
039     * Information on a certain role, which is expected for the relation members.
040     */
041    public static class Role {
042        public Set<TaggingPresetType> types; // NOSONAR
043        /** Role name used in a relation */
044        public String key; // NOSONAR
045        /** Is the role name a regular expression */
046        public boolean regexp; // NOSONAR
047        /** The text to display */
048        public String text; // NOSONAR
049        /** The context used for translating {@link #text} */
050        public String text_context; // NOSONAR
051        /** The localized version of {@link #text}. */
052        public String locale_text; // NOSONAR
053        /** An expression (cf. search dialog) for objects of this role */
054        public SearchCompiler.Match memberExpression; // NOSONAR
055        /** Is this role required at least once in the relation? */
056        public boolean required; // NOSONAR
057        /** How often must the element appear */
058        private long count;
059
060        public void setType(String types) throws SAXException {
061            this.types = getType(types);
062        }
063
064        public void setRequisite(String str) throws SAXException {
065            if ("required".equals(str)) {
066                required = true;
067            } else if (!"optional".equals(str))
068                throw new SAXException(tr("Unknown requisite: {0}", str));
069        }
070
071        public void setRegexp(String str) throws SAXException {
072            if ("true".equals(str)) {
073                regexp = true;
074            } else if (!"false".equals(str))
075                throw new SAXException(tr("Unknown regexp value: {0}", str));
076        }
077
078        public void setMember_expression(String memberExpression) throws SAXException {
079            try {
080                final SearchSetting searchSetting = new SearchSetting();
081                searchSetting.text = memberExpression;
082                searchSetting.caseSensitive = true;
083                searchSetting.regexSearch = true;
084                this.memberExpression = SearchCompiler.compile(searchSetting);
085            } catch (SearchParseError ex) {
086                throw new SAXException(tr("Illegal member expression: {0}", ex.getMessage()), ex);
087            }
088        }
089
090        public void setCount(String count) {
091            this.count = Long.parseLong(count);
092        }
093
094        /**
095         * Return either argument, the highest possible value or the lowest allowed value
096         * @param c count
097         * @return the highest possible value or the lowest allowed value
098         * @see #required
099         */
100        public long getValidCount(long c) {
101            if (count > 0 && !required)
102                return c != 0 ? count : 0;
103            else if (count > 0)
104                return count;
105            else if (!required)
106                return c != 0 ? c : 0;
107            else
108                return c != 0 ? c : 1;
109        }
110
111        /**
112         * Check if the given role matches this class (required to check regexp role types)
113         * @param role role to check
114         * @return <code>true</code> if role matches
115         * @since 11989
116         */
117        public boolean isRole(String role) {
118            if (regexp && role != null) { // pass null through, it will anyway fail
119                return role.matches(this.key);
120            }
121            return this.key.equals(role);
122        }
123
124        public boolean addToPanel(JPanel p) {
125            String cstring;
126            if (count > 0 && !required) {
127                cstring = "0,"+count;
128            } else if (count > 0) {
129                cstring = String.valueOf(count);
130            } else if (!required) {
131                cstring = "0-...";
132            } else {
133                cstring = "1-...";
134            }
135            if (locale_text == null) {
136                locale_text = getLocaleText(text, text_context, null);
137            }
138            p.add(new JLabel(locale_text+':'), GBC.std().insets(0, 0, 10, 0));
139            p.add(new JLabel(key), GBC.std().insets(0, 0, 10, 0));
140            p.add(new JLabel(cstring), types == null ? GBC.eol() : GBC.std().insets(0, 0, 10, 0));
141            if (types != null) {
142                JPanel pp = new JPanel();
143                for (TaggingPresetType t : types) {
144                    pp.add(new JLabel(ImageProvider.get(t.getIconName())));
145                }
146                p.add(pp, GBC.eol());
147            }
148            return true;
149        }
150
151        @Override
152        public String toString() {
153            return "Role [key=" + key + ", text=" + text + ']';
154        }
155    }
156
157    /**
158     * List of {@link Role} elements.
159     */
160    public final List<Role> roles = new LinkedList<>();
161
162    @Override
163    public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel, boolean presetInitiallyMatches) {
164        p.add(new JLabel(" "), GBC.eol()); // space
165        if (!roles.isEmpty()) {
166            JPanel proles = new JPanel(new GridBagLayout());
167            proles.add(new JLabel(tr("Available roles")), GBC.std().insets(0, 0, 10, 0));
168            proles.add(new JLabel(tr("role")), GBC.std().insets(0, 0, 10, 0));
169            proles.add(new JLabel(tr("count")), GBC.std().insets(0, 0, 10, 0));
170            proles.add(new JLabel(tr("elements")), GBC.eol());
171            for (Role i : roles) {
172                i.addToPanel(proles);
173            }
174            p.add(proles, GBC.eol());
175        }
176        return false;
177    }
178
179    @Override
180    public void addCommands(List<Tag> changedTags) {
181    }
182
183    @Override
184    public String toString() {
185        return "Roles [roles=" + roles + ']';
186    }
187}