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    public OsmPrimitive osm;
018
019    public MultiCascade mc;
020    public String layer;
021    public StyleSource source;
022    private Context context = Context.PRIMITIVE;
023    public static final String DEFAULT_LAYER = "default";
024
025    /**
026     * If not null, this is the matching parent object if a condition or an expression
027     * is evaluated in a {@link LinkSelector} (within a child selector)
028     */
029    public OsmPrimitive parent;
030
031    /**
032     * The same for parent selector. Only one of the 2 fields (parent or child) is not null in any environment.
033     */
034    public OsmPrimitive child;
035
036    /**
037     * index of node in parent way or member in parent relation. Must be != null in LINK context.
038     */
039    public Integer index;
040
041    /**
042     * count of nodes in parent way or members in parent relation. Must be != null in LINK context.
043     */
044    public Integer count;
045
046    /**
047     * Creates a new uninitialized environment.
048     */
049    public Environment() {
050        // environment can be initialized later through with* methods
051    }
052
053    /**
054     * Creates a new environment.
055     * @param osm OSM primitive
056     * @since 8415
057     */
058    public Environment(OsmPrimitive osm) {
059        this.osm = osm;
060    }
061
062    /**
063     * Creates a new environment.
064     * @param osm OSM primitive
065     * @param mc multi cascade
066     * @param layer layer
067     * @param source style source
068     */
069    public Environment(OsmPrimitive osm, MultiCascade mc, String layer, StyleSource source) {
070        this.osm = osm;
071        this.mc = mc;
072        this.layer = layer;
073        this.source = source;
074    }
075
076    /**
077     * Creates a clone of the environment {@code other}.
078     *
079     * @param other the other environment. Must not be null.
080     * @throws IllegalArgumentException if {@code param} is {@code null}
081     */
082    public Environment(Environment other) {
083        CheckParameterUtil.ensureParameterNotNull(other);
084        this.osm = other.osm;
085        this.mc = other.mc;
086        this.layer = other.layer;
087        this.parent = other.parent;
088        this.child = other.child;
089        this.source = other.source;
090        this.index = other.index;
091        this.count = other.count;
092        this.context = other.getContext();
093    }
094
095    /**
096     * Creates a clone of this environment, with the specified primitive.
097     * @param osm OSM primitive
098     * @return A clone of this environment, with the specified primitive
099     * @see #osm
100     */
101    public Environment withPrimitive(OsmPrimitive osm) {
102        Environment e = new Environment(this);
103        e.osm = osm;
104        return e;
105    }
106
107    /**
108     * Creates a clone of this environment, with the specified parent.
109     * @param parent the matching parent object
110     * @return A clone of this environment, with the specified parent
111     * @see #parent
112     */
113    public Environment withParent(OsmPrimitive parent) {
114        Environment e = new Environment(this);
115        e.parent = parent;
116        return e;
117    }
118
119    /**
120     * Creates a clone of this environment, with the specified parent, index, and context set to {@link Context#LINK}.
121     * @param parent the matching parent object
122     * @param index index of node in parent way or member in parent relation
123     * @param count count of nodes in parent way or members in parent relation
124     * @return A clone of this environment, with the specified parent, index, and context set to {@link Context#LINK}
125     * @see #parent
126     * @see #index
127     * @since 6175
128     */
129    public Environment withParentAndIndexAndLinkContext(OsmPrimitive parent, int index, int count) {
130        Environment e = new Environment(this);
131        e.parent = parent;
132        e.index = index;
133        e.count = count;
134        e.context = Context.LINK;
135        return e;
136    }
137
138    /**
139     * Creates a clone of this environment, with the specified child.
140     * @param child the matching child object
141     * @return A clone of this environment, with the specified child
142     * @see #child
143     */
144    public Environment withChild(OsmPrimitive child) {
145        Environment e = new Environment(this);
146        e.child = child;
147        return e;
148    }
149
150    /**
151     * Creates a clone of this environment, with the specified child, index, and context set to {@link Context#LINK}.
152     * @param child the matching child object
153     * @param index index of node in parent way or member in parent relation
154     * @param count count of nodes in parent way or members in parent relation
155     * @return A clone of this environment, with the specified child, index, and context set to {@code Context#LINK}
156     * @see #child
157     * @see #index
158     * @since 6175
159     */
160    public Environment withChildAndIndexAndLinkContext(OsmPrimitive child, int index, int count) {
161        Environment e = new Environment(this);
162        e.child = child;
163        e.index = index;
164        e.count = count;
165        e.context = Context.LINK;
166        return e;
167    }
168
169    /**
170     * Creates a clone of this environment, with the specified index.
171     * @param index index of node in parent way or member in parent relation
172     * @param count count of nodes in parent way or members in parent relation
173     * @return A clone of this environment, with the specified index
174     * @see #index
175     */
176    public Environment withIndex(int index, int count) {
177        Environment e = new Environment(this);
178        e.index = index;
179        e.count = count;
180        return e;
181    }
182
183    /**
184     * Creates a clone of this environment, with the specified {@link Context}.
185     * @param context context
186     * @return A clone of this environment, with the specified {@code Context}
187     */
188    public Environment withContext(Context context) {
189        Environment e = new Environment(this);
190        e.context = context == null ? Context.PRIMITIVE : context;
191        return e;
192    }
193
194    /**
195     * Creates a clone of this environment, with context set to {@link Context#LINK}.
196     * @return A clone of this environment, with context set to {@code Context#LINK}
197     */
198    public Environment withLinkContext() {
199        Environment e = new Environment(this);
200        e.context = Context.LINK;
201        return e;
202    }
203
204    /**
205     * Determines if the context of this environment is {@link Context#LINK}.
206     * @return {@code true} if the context of this environment is {@code Context#LINK}, {@code false} otherwise
207     */
208    public boolean isLinkContext() {
209        return Context.LINK.equals(context);
210    }
211
212    /**
213     * Determines if this environment has a relation as parent.
214     * @return {@code true} if this environment has a relation as parent, {@code false} otherwise
215     * @see #parent
216     */
217    public boolean hasParentRelation() {
218        return parent instanceof Relation;
219    }
220
221    /**
222     * Replies the current context.
223     *
224     * @return the current context
225     */
226    public Context getContext() {
227        return context == null ? Context.PRIMITIVE : context;
228    }
229
230    public String getRole() {
231        if (getContext().equals(Context.PRIMITIVE))
232            return null;
233
234        if (parent instanceof Relation)
235            return ((Relation) parent).getMember(index).getRole();
236        if (child != null && osm instanceof Relation)
237            return ((Relation) osm).getMember(index).getRole();
238        return null;
239    }
240
241    public void clearSelectorMatchingInformation() {
242        parent = null;
243        child = null;
244        index = null;
245        count = null;
246    }
247
248    public Cascade getCascade(String layer) {
249        return mc == null ? null : mc.getCascade(layer == null ? this.layer : layer);
250    }
251}