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    }