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