001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.tools.template_engine; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.ArrayList; 007import java.util.Collection; 008import java.util.Collections; 009import java.util.List; 010 011import org.openstreetmap.josm.data.osm.Node; 012import org.openstreetmap.josm.data.osm.OsmPrimitive; 013import org.openstreetmap.josm.data.osm.Relation; 014import org.openstreetmap.josm.data.osm.RelationMember; 015import org.openstreetmap.josm.data.osm.Way; 016import org.openstreetmap.josm.data.osm.search.SearchCompiler.And; 017import org.openstreetmap.josm.data.osm.search.SearchCompiler.Child; 018import org.openstreetmap.josm.data.osm.search.SearchCompiler.Match; 019import org.openstreetmap.josm.data.osm.search.SearchCompiler.Not; 020import org.openstreetmap.josm.data.osm.search.SearchCompiler.Or; 021import org.openstreetmap.josm.data.osm.search.SearchCompiler.Parent; 022 023/** 024 * The context switch offers possibility to use tags of referenced primitive when constructing primitive name. 025 * @author jttt 026 * @since 4546 027 */ 028public class ContextSwitchTemplate implements TemplateEntry { 029 030 private static final TemplateEngineDataProvider EMPTY_PROVIDER = new TemplateEngineDataProvider() { 031 @Override 032 public Object getTemplateValue(String name, boolean special) { 033 return null; 034 } 035 036 @Override 037 public Collection<String> getTemplateKeys() { 038 return Collections.emptyList(); 039 } 040 041 @Override 042 public boolean evaluateCondition(Match condition) { 043 return false; 044 } 045 }; 046 047 private abstract static class ContextProvider extends Match { 048 protected Match condition; 049 050 abstract List<OsmPrimitive> getPrimitives(OsmPrimitive root); 051 052 @Override 053 public int hashCode() { 054 return 31 + ((condition == null) ? 0 : condition.hashCode()); 055 } 056 057 @Override 058 public boolean equals(Object obj) { 059 if (this == obj) 060 return true; 061 if (obj == null || getClass() != obj.getClass()) 062 return false; 063 ContextProvider other = (ContextProvider) obj; 064 if (condition == null) { 065 if (other.condition != null) 066 return false; 067 } else if (!condition.equals(other.condition)) 068 return false; 069 return true; 070 } 071 } 072 073 private static class ParentSet extends ContextProvider { 074 private final Match childCondition; 075 076 ParentSet(Match child) { 077 this.childCondition = child; 078 } 079 080 @Override 081 public boolean match(OsmPrimitive osm) { 082 throw new UnsupportedOperationException(); 083 } 084 085 @Override 086 List<OsmPrimitive> getPrimitives(OsmPrimitive root) { 087 List<OsmPrimitive> children; 088 if (childCondition instanceof ContextProvider) { 089 children = ((ContextProvider) childCondition).getPrimitives(root); 090 } else if (childCondition.match(root)) { 091 children = Collections.singletonList(root); 092 } else { 093 children = Collections.emptyList(); 094 } 095 096 List<OsmPrimitive> result = new ArrayList<>(); 097 for (OsmPrimitive child: children) { 098 for (OsmPrimitive parent: child.getReferrers(true)) { 099 if (condition == null || condition.match(parent)) { 100 result.add(parent); 101 } 102 } 103 } 104 return result; 105 } 106 107 @Override 108 public int hashCode() { 109 return 31 * super.hashCode() + ((childCondition == null) ? 0 : childCondition.hashCode()); 110 } 111 112 @Override 113 public boolean equals(Object obj) { 114 if (this == obj) 115 return true; 116 if (!super.equals(obj) || getClass() != obj.getClass()) 117 return false; 118 ParentSet other = (ParentSet) obj; 119 if (childCondition == null) { 120 if (other.childCondition != null) 121 return false; 122 } else if (!childCondition.equals(other.childCondition)) 123 return false; 124 return true; 125 } 126 } 127 128 private static class ChildSet extends ContextProvider { 129 private final Match parentCondition; 130 131 ChildSet(Match parentCondition) { 132 this.parentCondition = parentCondition; 133 } 134 135 @Override 136 public boolean match(OsmPrimitive osm) { 137 throw new UnsupportedOperationException(); 138 } 139 140 @Override 141 List<OsmPrimitive> getPrimitives(OsmPrimitive root) { 142 List<OsmPrimitive> parents; 143 if (parentCondition instanceof ContextProvider) { 144 parents = ((ContextProvider) parentCondition).getPrimitives(root); 145 } else if (parentCondition.match(root)) { 146 parents = Collections.singletonList(root); 147 } else { 148 parents = Collections.emptyList(); 149 } 150 List<OsmPrimitive> result = new ArrayList<>(); 151 for (OsmPrimitive p: parents) { 152 if (p instanceof Way) { 153 for (Node n: ((Way) p).getNodes()) { 154 if (condition != null && condition.match(n)) { 155 result.add(n); 156 } 157 result.add(n); 158 } 159 } else if (p instanceof Relation) { 160 for (RelationMember rm: ((Relation) p).getMembers()) { 161 if (condition != null && condition.match(rm.getMember())) { 162 result.add(rm.getMember()); 163 } 164 } 165 } 166 } 167 return result; 168 } 169 170 @Override 171 public int hashCode() { 172 return 31 * super.hashCode() + ((parentCondition == null) ? 0 : parentCondition.hashCode()); 173 } 174 175 @Override 176 public boolean equals(Object obj) { 177 if (this == obj) 178 return true; 179 if (!super.equals(obj) || getClass() != obj.getClass()) 180 return false; 181 ChildSet other = (ChildSet) obj; 182 if (parentCondition == null) { 183 if (other.parentCondition != null) 184 return false; 185 } else if (!parentCondition.equals(other.parentCondition)) 186 return false; 187 return true; 188 } 189 } 190 191 private static class OrSet extends ContextProvider { 192 private final ContextProvider lhs; 193 private final ContextProvider rhs; 194 195 OrSet(ContextProvider lhs, ContextProvider rhs) { 196 this.lhs = lhs; 197 this.rhs = rhs; 198 } 199 200 @Override 201 public boolean match(OsmPrimitive osm) { 202 throw new UnsupportedOperationException(); 203 } 204 205 @Override 206 List<OsmPrimitive> getPrimitives(OsmPrimitive root) { 207 List<OsmPrimitive> result = new ArrayList<>(); 208 for (OsmPrimitive o: lhs.getPrimitives(root)) { 209 if (condition == null || condition.match(o)) { 210 result.add(o); 211 } 212 } 213 for (OsmPrimitive o: rhs.getPrimitives(root)) { 214 if (condition == null || (condition.match(o) && !result.contains(o))) { 215 result.add(o); 216 } 217 } 218 return result; 219 } 220 221 @Override 222 public int hashCode() { 223 final int prime = 31; 224 int result = super.hashCode(); 225 result = prime * result + ((lhs == null) ? 0 : lhs.hashCode()); 226 result = prime * result + ((rhs == null) ? 0 : rhs.hashCode()); 227 return result; 228 } 229 230 @Override 231 public boolean equals(Object obj) { 232 if (this == obj) 233 return true; 234 if (!super.equals(obj) || getClass() != obj.getClass()) 235 return false; 236 OrSet other = (OrSet) obj; 237 if (lhs == null) { 238 if (other.lhs != null) 239 return false; 240 } else if (!lhs.equals(other.lhs)) 241 return false; 242 if (rhs == null) { 243 if (other.rhs != null) 244 return false; 245 } else if (!rhs.equals(other.rhs)) 246 return false; 247 return true; 248 } 249 } 250 251 private static class AndSet extends ContextProvider { 252 private final ContextProvider lhs; 253 private final ContextProvider rhs; 254 255 AndSet(ContextProvider lhs, ContextProvider rhs) { 256 this.lhs = lhs; 257 this.rhs = rhs; 258 } 259 260 @Override 261 public boolean match(OsmPrimitive osm) { 262 throw new UnsupportedOperationException(); 263 } 264 265 @Override 266 List<OsmPrimitive> getPrimitives(OsmPrimitive root) { 267 List<OsmPrimitive> result = new ArrayList<>(); 268 List<OsmPrimitive> lhsList = lhs.getPrimitives(root); 269 for (OsmPrimitive o: rhs.getPrimitives(root)) { 270 if (lhsList.contains(o) && (condition == null || condition.match(o))) { 271 result.add(o); 272 } 273 } 274 return result; 275 } 276 277 @Override 278 public int hashCode() { 279 final int prime = 31; 280 int result = super.hashCode(); 281 result = prime * result + ((lhs == null) ? 0 : lhs.hashCode()); 282 result = prime * result + ((rhs == null) ? 0 : rhs.hashCode()); 283 return result; 284 } 285 286 @Override 287 public boolean equals(Object obj) { 288 if (this == obj) 289 return true; 290 if (!super.equals(obj) || getClass() != obj.getClass()) 291 return false; 292 AndSet other = (AndSet) obj; 293 if (lhs == null) { 294 if (other.lhs != null) 295 return false; 296 } else if (!lhs.equals(other.lhs)) 297 return false; 298 if (rhs == null) { 299 if (other.rhs != null) 300 return false; 301 } else if (!rhs.equals(other.rhs)) 302 return false; 303 return true; 304 } 305 } 306 307 private final ContextProvider context; 308 private final TemplateEntry template; 309 310 private static Match transform(Match m, int searchExpressionPosition) throws ParseError { 311 if (m instanceof Parent) { 312 Match child = transform(((Parent) m).getOperand(), searchExpressionPosition); 313 return new ParentSet(child); 314 } else if (m instanceof Child) { 315 Match parent = transform(((Child) m).getOperand(), searchExpressionPosition); 316 return new ChildSet(parent); 317 } else if (m instanceof And) { 318 Match lhs = transform(((And) m).getLhs(), searchExpressionPosition); 319 Match rhs = transform(((And) m).getRhs(), searchExpressionPosition); 320 321 if (lhs instanceof ContextProvider && rhs instanceof ContextProvider) 322 return new AndSet((ContextProvider) lhs, (ContextProvider) rhs); 323 else if (lhs instanceof ContextProvider) { 324 ContextProvider cp = (ContextProvider) lhs; 325 if (cp.condition == null) { 326 cp.condition = rhs; 327 } else { 328 cp.condition = new And(cp.condition, rhs); 329 } 330 return cp; 331 } else if (rhs instanceof ContextProvider) { 332 ContextProvider cp = (ContextProvider) rhs; 333 if (cp.condition == null) { 334 cp.condition = lhs; 335 } else { 336 cp.condition = new And(lhs, cp.condition); 337 } 338 return cp; 339 } else 340 return m; 341 } else if (m instanceof Or) { 342 Match lhs = transform(((Or) m).getLhs(), searchExpressionPosition); 343 Match rhs = transform(((Or) m).getRhs(), searchExpressionPosition); 344 345 if (lhs instanceof ContextProvider && rhs instanceof ContextProvider) 346 return new OrSet((ContextProvider) lhs, (ContextProvider) rhs); 347 else if (lhs instanceof ContextProvider) 348 throw new ParseError( 349 tr("Error in search expression on position {0} - right side of or(|) expression must return set of primitives", 350 searchExpressionPosition)); 351 else if (rhs instanceof ContextProvider) 352 throw new ParseError( 353 tr("Error in search expression on position {0} - left side of or(|) expression must return set of primitives", 354 searchExpressionPosition)); 355 else 356 return m; 357 } else if (m instanceof Not) { 358 Match match = transform(((Not) m).getMatch(), searchExpressionPosition); 359 if (match instanceof ContextProvider) 360 throw new ParseError( 361 tr("Error in search expression on position {0} - not(-) cannot be used in this context", 362 searchExpressionPosition)); 363 else 364 return m; 365 } else 366 return m; 367 } 368 369 /** 370 * Constructs a new {@code ContextSwitchTemplate}. 371 * @param match match 372 * @param template template 373 * @param searchExpressionPosition search expression position 374 * @throws ParseError if a parse error occurs, or if the match transformation returns the same primitive 375 */ 376 public ContextSwitchTemplate(Match match, TemplateEntry template, int searchExpressionPosition) throws ParseError { 377 Match m = transform(match, searchExpressionPosition); 378 if (!(m instanceof ContextProvider)) 379 throw new ParseError( 380 tr("Error in search expression on position {0} - expression must return different then current primitive", 381 searchExpressionPosition)); 382 else { 383 context = (ContextProvider) m; 384 } 385 this.template = template; 386 } 387 388 @Override 389 public void appendText(StringBuilder result, TemplateEngineDataProvider dataProvider) { 390 if (dataProvider instanceof OsmPrimitive) { 391 List<OsmPrimitive> primitives = context.getPrimitives((OsmPrimitive) dataProvider); 392 if (primitives != null && !primitives.isEmpty()) { 393 template.appendText(result, primitives.get(0)); 394 } 395 } 396 template.appendText(result, EMPTY_PROVIDER); 397 } 398 399 @Override 400 public boolean isValid(TemplateEngineDataProvider dataProvider) { 401 if (dataProvider instanceof OsmPrimitive) { 402 List<OsmPrimitive> primitives = context.getPrimitives((OsmPrimitive) dataProvider); 403 if (primitives != null && !primitives.isEmpty()) { 404 return template.isValid(primitives.get(0)); 405 } 406 } 407 return false; 408 } 409 410 @Override 411 public int hashCode() { 412 final int prime = 31; 413 int result = 1; 414 result = prime * result + ((context == null) ? 0 : context.hashCode()); 415 result = prime * result + ((template == null) ? 0 : template.hashCode()); 416 return result; 417 } 418 419 @Override 420 public boolean equals(Object obj) { 421 if (this == obj) 422 return true; 423 if (obj == null || getClass() != obj.getClass()) 424 return false; 425 ContextSwitchTemplate other = (ContextSwitchTemplate) obj; 426 if (context == null) { 427 if (other.context != null) 428 return false; 429 } else if (!context.equals(other.context)) 430 return false; 431 if (template == null) { 432 if (other.template != null) 433 return false; 434 } else if (!template.equals(other.template)) 435 return false; 436 return true; 437 } 438}