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 }