001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint.mapcss;
003
004import java.util.Arrays;
005
006import org.openstreetmap.josm.gui.mappaint.Cascade;
007import org.openstreetmap.josm.gui.mappaint.Environment;
008import org.openstreetmap.josm.gui.mappaint.Keyword;
009import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
010import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
011import org.openstreetmap.josm.gui.mappaint.StyleKeys;
012
013@FunctionalInterface
014public interface Instruction extends StyleKeys {
015
016    void execute(Environment env);
017
018    class RelativeFloat {
019        public final float val;
020
021        public RelativeFloat(float val) {
022            this.val = val;
023        }
024
025        @Override
026        public String toString() {
027            return "RelativeFloat{" + "val=" + val + '}';
028        }
029    }
030
031    class AssignmentInstruction implements Instruction {
032        public final String key;
033        public final Object val;
034        public final boolean isSetInstruction;
035
036        public AssignmentInstruction(String key, Object val, boolean isSetInstruction) {
037            this.key = key;
038            this.isSetInstruction = isSetInstruction;
039            if (val instanceof LiteralExpression) {
040                Object litValue = ((LiteralExpression) val).evaluate(null);
041                if (litValue instanceof Keyword && "none".equals(((Keyword) litValue).val)) {
042                    this.val = null;
043                } else if (TEXT.equals(key)) {
044                    /* Special case for declaration 'text: ...'
045                     *
046                     * - Treat the value 'auto' as keyword.
047                     * - Treat any other literal value 'litval' as as reference to tag with key 'litval'
048                     *
049                     * - Accept function expressions as is. This allows for
050                     *     tag(a_tag_name)                 value of a tag
051                     *     eval("a static text")           a static text
052                     *     parent_tag(a_tag_name)          value of a tag of a parent relation
053                     */
054                    if (litValue.equals(Keyword.AUTO)) {
055                        this.val = Keyword.AUTO;
056                    } else {
057                        String s = Cascade.convertTo(litValue, String.class);
058                        if (s != null) {
059                            this.val = new MapPaintStyles.TagKeyReference(s);
060                        } else {
061                            this.val = litValue;
062                        }
063                    }
064                } else {
065                    this.val = litValue;
066                }
067            } else {
068                this.val = val;
069            }
070        }
071
072        @Override
073        public void execute(Environment env) {
074            Object value;
075            if (val instanceof Expression) {
076                value = ((Expression) val).evaluate(env);
077            } else {
078                value = val;
079            }
080            if (ICON_IMAGE.equals(key) || FILL_IMAGE.equals(key) || REPEAT_IMAGE.equals(key)) {
081                if (value instanceof String) {
082                    value = new IconReference((String) value, env.source);
083                }
084            }
085            env.mc.getOrCreateCascade(env.layer).putOrClear(key, value);
086        }
087
088        @Override
089        public String toString() {
090            return key + ": " + (val instanceof float[] ? Arrays.toString((float[]) val) :
091                (val instanceof String ? ("String<"+val+'>') : val)) + ';';
092        }
093    }
094}