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.ArrayList; 008import java.util.List; 009import java.util.Set; 010 011import javax.swing.JLabel; 012import javax.swing.JPanel; 013 014import org.openstreetmap.josm.data.osm.Tag; 015import org.openstreetmap.josm.data.osm.search.SearchCompiler; 016import org.openstreetmap.josm.data.osm.search.SearchParseError; 017import org.openstreetmap.josm.data.osm.search.SearchSetting; 018import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItem; 019import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItemGuiSupport; 020import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType; 021import org.openstreetmap.josm.tools.GBC; 022import org.openstreetmap.josm.tools.ImageProvider; 023import org.xml.sax.SAXException; 024 025/** 026 * The <code>roles</code> element in tagging presets definition. 027 * <p> 028 * A list of {@link Role} elements. Describes the roles that are expected for 029 * the members of a relation. 030 * <p> 031 * Used for data validation, auto completion, among others. 032 */ 033public class Roles extends TaggingPresetItem { 034 035 /** 036 * The <code>role</code> element in tagging preset definition. 037 * 038 * Information on a certain role, which is expected for the relation members. 039 */ 040 public static class Role { 041 /** Presets types expected for this 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 short count; 059 060 /** 061 * Sets the presets types expected for this role. 062 * @param types comma-separated set of expected types 063 * @throws SAXException if an unknown type is detected 064 */ 065 public void setType(String types) throws SAXException { 066 this.types = getType(types); 067 } 068 069 /** 070 * Sets whether this role is required at least once in the relation. 071 * @param str "required" or "optional" 072 * @throws SAXException if str is neither "required" or "optional" 073 */ 074 public void setRequisite(String str) throws SAXException { 075 if ("required".equals(str)) { 076 required = true; 077 } else if (!"optional".equals(str)) 078 throw new SAXException(tr("Unknown requisite: {0}", str)); 079 } 080 081 /** 082 * Sets whether the role name is a regular expression. 083 * @param str "true" or "false" 084 * @throws SAXException if str is neither "true" or "false" 085 */ 086 public void setRegexp(String str) throws SAXException { 087 if ("true".equals(str)) { 088 regexp = true; 089 } else if (!"false".equals(str)) 090 throw new SAXException(tr("Unknown regexp value: {0}", str)); 091 } 092 093 /** 094 * Sets an expression (cf. search dialog) for objects of this role 095 * @param memberExpression an expression (cf. search dialog) for objects of this role 096 * @throws SAXException in case of parsing error 097 */ 098 public void setMember_expression(String memberExpression) throws SAXException { 099 try { 100 final SearchSetting searchSetting = new SearchSetting(); 101 searchSetting.text = memberExpression; 102 searchSetting.caseSensitive = true; 103 searchSetting.regexSearch = true; 104 this.memberExpression = SearchCompiler.compile(searchSetting); 105 } catch (SearchParseError ex) { 106 throw new SAXException(tr("Illegal member expression: {0}", ex.getMessage()), ex); 107 } 108 } 109 110 /** 111 * Sets how often must the element appear. 112 * @param count how often must the element appear 113 */ 114 public void setCount(String count) { 115 this.count = Short.parseShort(count); 116 } 117 118 /** 119 * Return either argument, the highest possible value or the lowest allowed value 120 * @param c count 121 * @return the highest possible value or the lowest allowed value 122 * @see #required 123 */ 124 public long getValidCount(long c) { 125 if (count > 0 && !required) 126 return c != 0 ? count : 0; 127 else if (count > 0) 128 return count; 129 else if (!required) 130 return c != 0 ? c : 0; 131 else 132 return c != 0 ? c : 1; 133 } 134 135 /** 136 * Check if the given role matches this class (required to check regexp role types) 137 * @param role role to check 138 * @return <code>true</code> if role matches 139 * @since 11989 140 */ 141 public boolean isRole(String role) { 142 if (regexp && role != null) { // pass null through, it will anyway fail 143 return role.matches(this.key); 144 } 145 return this.key.equals(role); 146 } 147 148 /** 149 * Adds this role to the given panel. 150 * @param p panel where to add this role 151 * @return {@code true} 152 */ 153 public boolean addToPanel(JPanel p) { 154 String cstring; 155 if (count > 0 && !required) { 156 cstring = "0,"+count; 157 } else if (count > 0) { 158 cstring = String.valueOf(count); 159 } else if (!required) { 160 cstring = "0-..."; 161 } else { 162 cstring = "1-..."; 163 } 164 if (locale_text == null) { 165 locale_text = getLocaleText(text, text_context, null); 166 } 167 p.add(new JLabel(locale_text+':'), GBC.std().insets(0, 0, 10, 0)); 168 p.add(new JLabel(key), GBC.std().insets(0, 0, 10, 0)); 169 p.add(new JLabel(cstring), types == null ? GBC.eol() : GBC.std().insets(0, 0, 10, 0)); 170 if (types != null) { 171 JPanel pp = new JPanel(); 172 for (TaggingPresetType t : types) { 173 pp.add(new JLabel(ImageProvider.get(t.getIconName()))); 174 } 175 p.add(pp, GBC.eol()); 176 } 177 return true; 178 } 179 180 @Override 181 public String toString() { 182 return "Role [key=" + key + ", text=" + text + ']'; 183 } 184 } 185 186 /** 187 * List of {@link Role} elements. 188 */ 189 public final List<Role> roles = new ArrayList<>(2); 190 191 @Override 192 public boolean addToPanel(JPanel p, TaggingPresetItemGuiSupport support) { 193 p.add(new JLabel(" "), GBC.eol()); // space 194 if (!roles.isEmpty()) { 195 JPanel proles = new JPanel(new GridBagLayout()); 196 proles.add(new JLabel(tr("Available roles")), GBC.std().insets(0, 0, 10, 0)); 197 proles.add(new JLabel(tr("role")), GBC.std().insets(0, 0, 10, 0)); 198 proles.add(new JLabel(tr("count")), GBC.std().insets(0, 0, 10, 0)); 199 proles.add(new JLabel(tr("elements")), GBC.eol()); 200 for (Role i : roles) { 201 i.addToPanel(proles); 202 } 203 proles.applyComponentOrientation(support.getDefaultComponentOrientation()); 204 p.add(proles, GBC.eol()); 205 } 206 return false; 207 } 208 209 @Override 210 public void addCommands(List<Tag> changedTags) { 211 // Do nothing 212 } 213 214 @Override 215 public String toString() { 216 return "Roles [roles=" + roles + ']'; 217 } 218}