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