001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint;
003
004import org.openstreetmap.josm.data.osm.OsmPrimitive;
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 OsmPrimitive 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 OsmPrimitive 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 OsmPrimitive 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     */
074    public Environment(OsmPrimitive osm) {
075        this.osm = osm;
076    }
077
078    /**
079     * Creates a new environment.
080     * @param osm OSM primitive
081     * @param mc multi cascade
082     * @param layer layer
083     * @param source style source
084     */
085    public Environment(OsmPrimitive osm, MultiCascade mc, String layer, StyleSource source) {
086        this.osm = osm;
087        this.mc = mc;
088        this.layer = layer;
089        this.source = source;
090    }
091
092    /**
093     * Creates a clone of the environment {@code other}.
094     *
095     * @param other the other environment. Must not be null.
096     * @throws IllegalArgumentException if {@code param} is {@code null}
097     */
098    public Environment(Environment other) {
099        CheckParameterUtil.ensureParameterNotNull(other);
100        this.osm = other.osm;
101        this.mc = other.mc;
102        this.layer = other.layer;
103        this.parent = other.parent;
104        this.child = other.child;
105        this.source = other.source;
106        this.index = other.index;
107        this.count = other.count;
108        this.context = other.getContext();
109    }
110
111    /**
112     * Creates a clone of this environment, with the specified primitive.
113     * @param osm OSM primitive
114     * @return A clone of this environment, with the specified primitive
115     * @see #osm
116     */
117    public Environment withPrimitive(OsmPrimitive osm) {
118        Environment e = new Environment(this);
119        e.osm = osm;
120        return e;
121    }
122
123    /**
124     * Creates a clone of this environment, with the specified parent.
125     * @param parent the matching parent object
126     * @return A clone of this environment, with the specified parent
127     * @see #parent
128     */
129    public Environment withParent(OsmPrimitive parent) {
130        Environment e = new Environment(this);
131        e.parent = parent;
132        return e;
133    }
134
135    /**
136     * Creates a clone of this environment, with the specified parent, index, and context set to {@link Context#LINK}.
137     * @param parent the matching parent object
138     * @param index index of node in parent way or member in parent relation
139     * @param count count of nodes in parent way or members in parent relation
140     * @return A clone of this environment, with the specified parent, index, and context set to {@link Context#LINK}
141     * @see #parent
142     * @see #index
143     * @since 6175
144     */
145    public Environment withParentAndIndexAndLinkContext(OsmPrimitive parent, int index, int count) {
146        Environment e = new Environment(this);
147        e.parent = parent;
148        e.index = index;
149        e.count = count;
150        e.context = Context.LINK;
151        return e;
152    }
153
154    /**
155     * Creates a clone of this environment, with the specified child.
156     * @param child the matching child object
157     * @return A clone of this environment, with the specified child
158     * @see #child
159     */
160    public Environment withChild(OsmPrimitive child) {
161        Environment e = new Environment(this);
162        e.child = child;
163        return e;
164    }
165
166    /**
167     * Creates a clone of this environment, with the specified child, index, and context set to {@link Context#LINK}.
168     * @param child the matching child object
169     * @param index index of node in parent way or member in parent relation
170     * @param count count of nodes in parent way or members in parent relation
171     * @return A clone of this environment, with the specified child, index, and context set to {@code Context#LINK}
172     * @see #child
173     * @see #index
174     * @since 6175
175     */
176    public Environment withChildAndIndexAndLinkContext(OsmPrimitive child, int index, int count) {
177        Environment e = new Environment(this);
178        e.child = child;
179        e.index = index;
180        e.count = count;
181        e.context = Context.LINK;
182        return e;
183    }
184
185    /**
186     * Creates a clone of this environment, with the specified index.
187     * @param index index of node in parent way or member in parent relation
188     * @param count count of nodes in parent way or members in parent relation
189     * @return A clone of this environment, with the specified index
190     * @see #index
191     */
192    public Environment withIndex(int index, int count) {
193        Environment e = new Environment(this);
194        e.index = index;
195        e.count = count;
196        return e;
197    }
198
199    /**
200     * Creates a clone of this environment, with the specified {@link Context}.
201     * @param context context
202     * @return A clone of this environment, with the specified {@code Context}
203     */
204    public Environment withContext(Context context) {
205        Environment e = new Environment(this);
206        e.context = context == null ? Context.PRIMITIVE : context;
207        return e;
208    }
209
210    /**
211     * Creates a clone of this environment, with context set to {@link Context#LINK}.
212     * @return A clone of this environment, with context set to {@code Context#LINK}
213     */
214    public Environment withLinkContext() {
215        Environment e = new Environment(this);
216        e.context = Context.LINK;
217        return e;
218    }
219
220    /**
221     * Determines if the context of this environment is {@link Context#LINK}.
222     * @return {@code true} if the context of this environment is {@code Context#LINK}, {@code false} otherwise
223     */
224    public boolean isLinkContext() {
225        return Context.LINK.equals(context);
226    }
227
228    /**
229     * Determines if this environment has a relation as parent.
230     * @return {@code true} if this environment has a relation as parent, {@code false} otherwise
231     * @see #parent
232     */
233    public boolean hasParentRelation() {
234        return parent instanceof Relation;
235    }
236
237    /**
238     * Replies the current context.
239     *
240     * @return the current context
241     */
242    public Context getContext() {
243        return context == null ? Context.PRIMITIVE : context;
244    }
245
246    /**
247     * Gets the role of the matching primitive in the relation
248     * @return The role
249     */
250    public String getRole() {
251        if (getContext().equals(Context.PRIMITIVE))
252            return null;
253
254        if (parent instanceof Relation)
255            return ((Relation) parent).getMember(index).getRole();
256        if (child != null && osm instanceof Relation)
257            return ((Relation) osm).getMember(index).getRole();
258        return null;
259    }
260
261    /**
262     * Clears all matching context information
263     */
264    public void clearSelectorMatchingInformation() {
265        parent = null;
266        child = null;
267        index = null;
268        count = null;
269    }
270
271    /**
272     * Gets the current cascade for a given layer
273     * @param layer The layer to use, <code>null</code> to use the layer of the {@link Environment}
274     * @return The cascade
275     */
276    public Cascade getCascade(String layer) {
277        return mc == null ? null : mc.getCascade(layer == null ? this.layer : layer);
278    }
279}