001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm; 003 004import java.io.Serializable; 005import java.util.ArrayList; 006import java.util.List; 007import java.util.Objects; 008import java.util.regex.MatchResult; 009import java.util.regex.Matcher; 010import java.util.regex.Pattern; 011 012/** 013 * A primitive id and a primitive type 014 */ 015public class SimplePrimitiveId implements PrimitiveId, Serializable { 016 017 private static final long serialVersionUID = 1L; 018 019 private final long id; 020 private final OsmPrimitiveType type; 021 022 /** 023 * A pattern that is used to parse a textual primitive id 024 */ 025 public static final Pattern ID_PATTERN = Pattern.compile("(n|node|w|way|r|rel|relation)[ /]?(\\d+)"); 026 027 /** 028 * A pattern that is used to parse an id range 029 */ 030 public static final Pattern MULTIPLE_IDS_PATTERN = Pattern.compile(ID_PATTERN.pattern() + "(-(\\d+))?"); 031 032 /** 033 * Create a new primtive id 034 * @param id The id 035 * @param type The type of the primitive 036 */ 037 public SimplePrimitiveId(long id, OsmPrimitiveType type) { 038 this.id = id; 039 this.type = type; 040 } 041 042 @Override 043 public OsmPrimitiveType getType() { 044 return type; 045 } 046 047 @Override 048 public long getUniqueId() { 049 return id; 050 } 051 052 @Override 053 public boolean isNew() { 054 return id <= 0; 055 } 056 057 @Override 058 public int hashCode() { 059 return Objects.hash(id, type); 060 } 061 062 @Override 063 public boolean equals(Object obj) { 064 if (this == obj) return true; 065 if (obj == null || getClass() != obj.getClass()) return false; 066 SimplePrimitiveId that = (SimplePrimitiveId) obj; 067 return id == that.id && 068 type == that.type; 069 } 070 071 @Override 072 public String toString() { 073 return type.toString() + ' ' + id; 074 } 075 076 /** 077 * Parses a {@code SimplePrimitiveId} from the string {@code s}. 078 * @param s the string to be parsed, e.g., {@code n1}, {@code node1}, 079 * {@code w1}, {@code way1}, {@code r1}, {@code rel1}, {@code relation1}. 080 * @return the parsed {@code SimplePrimitiveId} 081 * @throws IllegalArgumentException if the string does not match the pattern 082 */ 083 public static SimplePrimitiveId fromString(String s) { 084 final Matcher m = ID_PATTERN.matcher(s); 085 if (m.matches()) { 086 return new SimplePrimitiveId(Long.parseLong(m.group(m.groupCount())), getOsmPrimitiveType(s.charAt(0))); 087 } else { 088 throw new IllegalArgumentException("The string " + s + " does not match the pattern " + ID_PATTERN); 089 } 090 } 091 092 /** 093 * Parses a range {@code SimplePrimitiveId} from the string {@code s}. 094 * @param s the string to be parsed, e.g., {@code node1}, {@code node1-7}, {@code node70-7}. 095 * @return the parsed {@code SimplePrimitiveId}s 096 * @throws IllegalArgumentException if the string does not match the pattern 097 */ 098 public static List<SimplePrimitiveId> multipleFromString(String s) { 099 final Matcher m = MULTIPLE_IDS_PATTERN.matcher(s); 100 if (m.matches()) { 101 return extractIdsInto(m, new ArrayList<SimplePrimitiveId>()); 102 } else { 103 throw new IllegalArgumentException("The string " + s + " does not match the pattern " + MULTIPLE_IDS_PATTERN); 104 } 105 } 106 107 /** 108 * Attempts to parse extract any primitive id from the string {@code s}. 109 * @param s the string to be parsed, e.g., {@code "n1, w1"}, {@code "node1 and rel2"}, {@code "node 123-29"}. 110 * @return the parsed list of {@code OsmPrimitiveType}s. 111 */ 112 public static List<SimplePrimitiveId> fuzzyParse(String s) { 113 final List<SimplePrimitiveId> ids = new ArrayList<>(); 114 final Matcher m = MULTIPLE_IDS_PATTERN.matcher(s); 115 while (m.find()) { 116 extractIdsInto(m, ids); 117 } 118 return ids; 119 } 120 121 private static List<SimplePrimitiveId> extractIdsInto(MatchResult m, List<SimplePrimitiveId> ids) { 122 final OsmPrimitiveType type = getOsmPrimitiveType(m.group(1).charAt(0)); 123 final String firstId = m.group(2); 124 final String lastId = m.group(4); 125 if (lastId != null) { 126 final long lastIdParsed; 127 if (lastId.length() < firstId.length()) { 128 // parse ranges such as 123-25 or 123-5 129 lastIdParsed = Long.parseLong(firstId.substring(0, firstId.length() - lastId.length()) + lastId); 130 } else { 131 // parse ranges such as 123-125 or 998-1001 132 lastIdParsed = Long.parseLong(lastId); 133 } 134 for (long i = Long.parseLong(firstId); i <= lastIdParsed; i++) { 135 if (i > 0) { 136 ids.add(new SimplePrimitiveId(i, type)); 137 } 138 } 139 } else { 140 long i = Long.parseLong(firstId); 141 if (i > 0) { 142 ids.add(new SimplePrimitiveId(i, type)); 143 } 144 } 145 return ids; 146 } 147 148 private static OsmPrimitiveType getOsmPrimitiveType(char firstChar) { 149 return firstChar == 'n' ? OsmPrimitiveType.NODE : firstChar == 'w' ? OsmPrimitiveType.WAY : OsmPrimitiveType.RELATION; 150 } 151}