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