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.actions.search.SearchCompiler.And; 012import org.openstreetmap.josm.actions.search.SearchCompiler.Child; 013import org.openstreetmap.josm.actions.search.SearchCompiler.Match; 014import org.openstreetmap.josm.actions.search.SearchCompiler.Not; 015import org.openstreetmap.josm.actions.search.SearchCompiler.Or; 016import org.openstreetmap.josm.actions.search.SearchCompiler.Parent; 017import org.openstreetmap.josm.data.osm.Node; 018import org.openstreetmap.josm.data.osm.OsmPrimitive; 019import org.openstreetmap.josm.data.osm.Relation; 020import org.openstreetmap.josm.data.osm.RelationMember; 021import org.openstreetmap.josm.data.osm.Way; 022 023public class ContextSwitchTemplate implements TemplateEntry { 024 025 private static final TemplateEngineDataProvider EMTPY_PROVIDER = new TemplateEngineDataProvider() { 026 @Override 027 public Object getTemplateValue(String name, boolean special) { 028 return null; 029 } 030 031 @Override 032 public Collection<String> getTemplateKeys() { 033 return Collections.emptyList(); 034 } 035 036 @Override 037 public boolean evaluateCondition(Match condition) { 038 return false; 039 } 040 }; 041 042 private abstract class ContextProvider extends Match { 043 protected Match condition; 044 045 abstract List<OsmPrimitive> getPrimitives(OsmPrimitive root); 046 } 047 048 private class ParentSet extends ContextProvider { 049 private final Match childCondition; 050 051 ParentSet(Match child) { 052 this.childCondition = child; 053 } 054 055 @Override 056 public boolean match(OsmPrimitive osm) { 057 throw new UnsupportedOperationException(); 058 } 059 060 @Override 061 List<OsmPrimitive> getPrimitives(OsmPrimitive root) { 062 List<OsmPrimitive> children; 063 if (childCondition instanceof ContextProvider) { 064 children = ((ContextProvider) childCondition).getPrimitives(root); 065 } else if (childCondition.match(root)) { 066 children = Collections.singletonList(root); 067 } else { 068 children = Collections.emptyList(); 069 } 070 071 List<OsmPrimitive> result = new ArrayList<>(); 072 for (OsmPrimitive child: children) { 073 for (OsmPrimitive parent: child.getReferrers(true)) { 074 if (condition == null || condition.match(parent)) { 075 result.add(parent); 076 } 077 } 078 } 079 return result; 080 } 081 } 082 083 private class ChildSet extends ContextProvider { 084 private final Match parentCondition; 085 086 ChildSet(Match parentCondition) { 087 this.parentCondition = parentCondition; 088 } 089 090 @Override 091 public boolean match(OsmPrimitive osm) { 092 throw new UnsupportedOperationException(); 093 } 094 095 @Override 096 List<OsmPrimitive> getPrimitives(OsmPrimitive root) { 097 List<OsmPrimitive> parents; 098 if (parentCondition instanceof ContextProvider) { 099 parents = ((ContextProvider) parentCondition).getPrimitives(root); 100 } else if (parentCondition.match(root)) { 101 parents = Collections.singletonList(root); 102 } else { 103 parents = Collections.emptyList(); 104 } 105 List<OsmPrimitive> result = new ArrayList<>(); 106 for (OsmPrimitive p: parents) { 107 if (p instanceof Way) { 108 for (Node n: ((Way) p).getNodes()) { 109 if (condition != null && condition.match(n)) { 110 result.add(n); 111 } 112 result.add(n); 113 } 114 } else if (p instanceof Relation) { 115 for (RelationMember rm: ((Relation) p).getMembers()) { 116 if (condition != null && condition.match(rm.getMember())) { 117 result.add(rm.getMember()); 118 } 119 } 120 } 121 } 122 return result; 123 } 124 } 125 126 private class OrSet extends ContextProvider { 127 private final ContextProvider lhs; 128 private final ContextProvider rhs; 129 130 OrSet(ContextProvider lhs, ContextProvider rhs) { 131 this.lhs = lhs; 132 this.rhs = rhs; 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> result = new ArrayList<>(); 143 for (OsmPrimitive o: lhs.getPrimitives(root)) { 144 if (condition == null || condition.match(o)) { 145 result.add(o); 146 } 147 } 148 for (OsmPrimitive o: rhs.getPrimitives(root)) { 149 if (condition == null || condition.match(o) && !result.contains(o)) { 150 result.add(o); 151 } 152 } 153 return result; 154 } 155 } 156 157 private class AndSet extends ContextProvider { 158 private final ContextProvider lhs; 159 private final ContextProvider rhs; 160 161 AndSet(ContextProvider lhs, ContextProvider rhs) { 162 this.lhs = lhs; 163 this.rhs = rhs; 164 } 165 166 @Override 167 public boolean match(OsmPrimitive osm) { 168 throw new UnsupportedOperationException(); 169 } 170 171 @Override 172 List<OsmPrimitive> getPrimitives(OsmPrimitive root) { 173 List<OsmPrimitive> result = new ArrayList<>(); 174 List<OsmPrimitive> lhsList = lhs.getPrimitives(root); 175 for (OsmPrimitive o: rhs.getPrimitives(root)) { 176 if (lhsList.contains(o) && (condition == null || condition.match(o))) { 177 result.add(o); 178 } 179 } 180 return result; 181 } 182 } 183 184 private final ContextProvider context; 185 private final TemplateEntry template; 186 187 private Match transform(Match m, int searchExpressionPosition) throws ParseError { 188 if (m instanceof Parent) { 189 Match child = transform(((Parent) m).getOperand(), searchExpressionPosition); 190 return new ParentSet(child); 191 } else if (m instanceof Child) { 192 Match parent = transform(((Child) m).getOperand(), searchExpressionPosition); 193 return new ChildSet(parent); 194 } else if (m instanceof And) { 195 Match lhs = transform(((And) m).getLhs(), searchExpressionPosition); 196 Match rhs = transform(((And) m).getRhs(), searchExpressionPosition); 197 198 if (lhs instanceof ContextProvider && rhs instanceof ContextProvider) 199 return new AndSet((ContextProvider) lhs, (ContextProvider) rhs); 200 else if (lhs instanceof ContextProvider) { 201 ContextProvider cp = (ContextProvider) lhs; 202 if (cp.condition == null) { 203 cp.condition = rhs; 204 } else { 205 cp.condition = new And(cp.condition, rhs); 206 } 207 return cp; 208 } else if (rhs instanceof ContextProvider) { 209 ContextProvider cp = (ContextProvider) rhs; 210 if (cp.condition == null) { 211 cp.condition = lhs; 212 } else { 213 cp.condition = new And(lhs, cp.condition); 214 } 215 return cp; 216 } else 217 return m; 218 } else if (m instanceof Or) { 219 Match lhs = transform(((Or) m).getLhs(), searchExpressionPosition); 220 Match rhs = transform(((Or) m).getRhs(), searchExpressionPosition); 221 222 if (lhs instanceof ContextProvider && rhs instanceof ContextProvider) 223 return new OrSet((ContextProvider) lhs, (ContextProvider) rhs); 224 else if (lhs instanceof ContextProvider) 225 throw new ParseError( 226 tr("Error in search expression on position {0} - right side of or(|) expression must return set of primitives", 227 searchExpressionPosition)); 228 else if (rhs instanceof ContextProvider) 229 throw new ParseError( 230 tr("Error in search expression on position {0} - left side of or(|) expression must return set of primitives", 231 searchExpressionPosition)); 232 else 233 return m; 234 } else if (m instanceof Not) { 235 Match match = transform(((Not) m).getMatch(), searchExpressionPosition); 236 if (match instanceof ContextProvider) 237 throw new ParseError( 238 tr("Error in search expression on position {0} - not(-) cannot be used in this context", 239 searchExpressionPosition)); 240 else 241 return m; 242 } else 243 return m; 244 } 245 246 public ContextSwitchTemplate(Match match, TemplateEntry template, int searchExpressionPosition) throws ParseError { 247 Match m = transform(match, searchExpressionPosition); 248 if (!(m instanceof ContextProvider)) 249 throw new ParseError( 250 tr("Error in search expression on position {0} - expression must return different then current primitive", 251 searchExpressionPosition)); 252 else { 253 context = (ContextProvider) m; 254 } 255 this.template = template; 256 } 257 258 @Override 259 public void appendText(StringBuilder result, TemplateEngineDataProvider dataProvider) { 260 List<OsmPrimitive> primitives = context.getPrimitives((OsmPrimitive) dataProvider); 261 if (primitives != null && !primitives.isEmpty()) { 262 template.appendText(result, primitives.get(0)); 263 } else { 264 template.appendText(result, EMTPY_PROVIDER); 265 } 266 } 267 268 @Override 269 public boolean isValid(TemplateEngineDataProvider dataProvider) { 270 List<OsmPrimitive> primitives = context.getPrimitives((OsmPrimitive) dataProvider); 271 if (primitives != null && !primitives.isEmpty()) 272 return template.isValid(primitives.get(0)); 273 else 274 return false; 275 } 276 277}