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