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