001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint;
003
004import java.util.LinkedHashSet;
005import java.util.Set;
006
007import org.openstreetmap.josm.data.osm.IPrimitive;
008import org.openstreetmap.josm.data.osm.Relation;
009import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Context;
010import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.LinkSelector;
011import org.openstreetmap.josm.tools.CheckParameterUtil;
012
013/**
014 * Environment is a data object to provide access to various "global" parameters.
015 * It is used during processing of MapCSS rules and for the generation of
016 * style elements.
017 */
018public class Environment {
019
020    /**
021     * The primitive that is currently evaluated
022     */
023    public IPrimitive osm;
024
025    /**
026     * The cascades that are currently evaluated
027     */
028    public MultiCascade mc;
029    /**
030     * The current MapCSS layer
031     */
032    public String layer;
033    /**
034     * The style source that is evaluated
035     */
036    public StyleSource source;
037    private Context context = Context.PRIMITIVE;
038
039    /**
040     * The name of the default layer. It is used if no layer is specified in the MapCSS rule
041     */
042    public static final String DEFAULT_LAYER = "default";
043
044    /**
045     * If not null, this is the matching parent object if a condition or an expression
046     * is evaluated in a {@link LinkSelector} (within a child selector)
047     */
048    public IPrimitive parent;
049
050    /**
051     * The same for parent selector. Only one of the 2 fields (parent or child) is not null in any environment.
052     */
053    public IPrimitive child;
054
055    /**
056     * index of node in parent way or member in parent relation. Must be != null in LINK context.
057     */
058    public Integer index;
059
060    /**
061     * count of nodes in parent way or members in parent relation. Must be != null in LINK context.
062     */
063    public Integer count;
064
065    /**
066     * Set of matched children filled by ContainsFinder and CrossingFinder, null if nothing matched
067     */
068    public Set<IPrimitive> children;
069
070    /**
071     * Creates a new uninitialized environment.
072     */
073    public Environment() {
074        // environment can be initialized later through with* methods
075    }
076
077    /**
078     * Creates a new environment.
079     * @param osm OSM primitive
080     * @since 8415
081     * @since 13810 (signature)
082     */
083    public Environment(IPrimitive osm) {
084        this.osm = osm;
085    }
086
087    /**
088     * Creates a new environment.
089     * @param osm OSM primitive
090     * @param mc multi cascade
091     * @param layer layer
092     * @param source style source
093     * @since 13810 (signature)
094     */
095    public Environment(IPrimitive osm, MultiCascade mc, String layer, StyleSource source) {
096        this.osm = osm;
097        this.mc = mc;
098        this.layer = layer;
099        this.source = source;
100    }
101
102    /**
103     * Creates a clone of the environment {@code other}.
104     *
105     * @param other the other environment. Must not be null.
106     * @throws IllegalArgumentException if {@code param} is {@code null}
107     */
108    public Environment(Environment other) {
109        CheckParameterUtil.ensureParameterNotNull(other);
110        this.osm = other.osm;
111        this.mc = other.mc;
112        this.layer = other.layer;
113        this.parent = other.parent;
114        this.child = other.child;
115        this.source = other.source;
116        this.index = other.index;
117        this.count = other.count;
118        this.context = other.getContext();
119        this.children = other.children == null ? null : new LinkedHashSet<>(other.children);
120    }
121
122    /**
123     * Creates a clone of this environment, with the specified primitive.
124     * @param osm OSM primitive
125     * @return A clone of this environment, with the specified primitive
126     * @see #osm
127     * @since 13810 (signature)
128     */
129    public Environment withPrimitive(IPrimitive osm) {
130        Environment e = new Environment(this);
131        e.osm = osm;
132        return e;
133    }
134
135    /**
136     * Creates a clone of this environment, with the specified parent.
137     * @param parent the matching parent object
138     * @return A clone of this environment, with the specified parent
139     * @see #parent
140     * @since 13810 (signature)
141     */
142    public Environment withParent(IPrimitive parent) {
143        Environment e = new Environment(this);
144        e.parent = parent;
145        return e;
146    }
147
148    /**
149     * Creates a clone of this environment, with the specified parent, index, and context set to {@link Context#LINK}.
150     * @param parent the matching parent object
151     * @param index index of node in parent way or member in parent relation
152     * @param count count of nodes in parent way or members in parent relation
153     * @return A clone of this environment, with the specified parent, index, and context set to {@link Context#LINK}
154     * @see #parent
155     * @see #index
156     * @since 6175
157     * @since 13810 (signature)
158     */
159    public Environment withParentAndIndexAndLinkContext(IPrimitive parent, int index, int count) {
160        Environment e = new Environment(this);
161        e.parent = parent;
162        e.index = index;
163        e.count = count;
164        e.context = Context.LINK;
165        return e;
166    }
167
168    /**
169     * Creates a clone of this environment, with the specified child.
170     * @param child the matching child object
171     * @return A clone of this environment, with the specified child
172     * @see #child
173     * @since 13810 (signature)
174     */
175    public Environment withChild(IPrimitive child) {
176        Environment e = new Environment(this);
177        e.child = child;
178        return e;
179    }
180
181    /**
182     * Creates a clone of this environment, with the specified child, index, and context set to {@link Context#LINK}.
183     * @param child the matching child object
184     * @param index index of node in parent way or member in parent relation
185     * @param count count of nodes in parent way or members in parent relation
186     * @return A clone of this environment, with the specified child, index, and context set to {@code Context#LINK}
187     * @see #child
188     * @see #index
189     * @since 6175
190     * @since 13810 (signature)
191     */
192    public Environment withChildAndIndexAndLinkContext(IPrimitive child, int index, int count) {
193        Environment e = new Environment(this);
194        e.child = child;
195        e.index = index;
196        e.count = count;
197        e.context = Context.LINK;
198        return e;
199    }
200
201    /**
202     * Creates a clone of this environment, with the specified index.
203     * @param index index of node in parent way or member in parent relation
204     * @param count count of nodes in parent way or members in parent relation
205     * @return A clone of this environment, with the specified index
206     * @see #index
207     */
208    public Environment withIndex(int index, int count) {
209        Environment e = new Environment(this);
210        e.index = index;
211        e.count = count;
212        return e;
213    }
214
215    /**
216     * Creates a clone of this environment, with the specified {@link Context}.
217     * @param context context
218     * @return A clone of this environment, with the specified {@code Context}
219     */
220    public Environment withContext(Context context) {
221        Environment e = new Environment(this);
222        e.context = context == null ? Context.PRIMITIVE : context;
223        return e;
224    }
225
226    /**
227     * Creates a clone of this environment, with context set to {@link Context#LINK}.
228     * @return A clone of this environment, with context set to {@code Context#LINK}
229     */
230    public Environment withLinkContext() {
231        Environment e = new Environment(this);
232        e.context = Context.LINK;
233        return e;
234    }
235
236    /**
237     * Determines if the context of this environment is {@link Context#LINK}.
238     * @return {@code true} if the context of this environment is {@code Context#LINK}, {@code false} otherwise
239     */
240    public boolean isLinkContext() {
241        return Context.LINK == context;
242    }
243
244    /**
245     * Determines if this environment has a relation as parent.
246     * @return {@code true} if this environment has a relation as parent, {@code false} otherwise
247     * @see #parent
248     */
249    public boolean hasParentRelation() {
250        return parent instanceof Relation;
251    }
252
253    /**
254     * Replies the current context.
255     *
256     * @return the current context
257     */
258    public Context getContext() {
259        return context == null ? Context.PRIMITIVE : context;
260    }
261
262    /**
263     * Gets the role of the matching primitive in the relation
264     * @return The role
265     */
266    public String getRole() {
267        if (getContext() == Context.PRIMITIVE)
268            return null;
269
270        if (parent instanceof Relation)
271            return ((Relation) parent).getMember(index).getRole();
272        if (child != null && osm instanceof Relation)
273            return ((Relation) osm).getMember(index).getRole();
274        return null;
275    }
276
277    /**
278     * Clears all matching context information
279     */
280    public void clearSelectorMatchingInformation() {
281        parent = null;
282        child = null;
283        index = null;
284        count = null;
285        children = null;
286    }
287
288    /**
289     * Gets the current cascade for a given layer
290     * @param layer The layer to use, <code>null</code> to use the layer of the {@link Environment}
291     * @return The cascade
292     */
293    public Cascade getCascade(String layer) {
294        return mc == null ? null : mc.getCascade(layer == null ? this.layer : layer);
295    }
296}