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/** 014 * A MapCSS Instruction. 015 * 016 * For example a simple assignment like <code>width: 3;</code>, but may also 017 * be a set instruction (<code>set highway;</code>). 018 * A MapCSS {@link MapCSSRule.Declaration} is a list of instructions. 019 */ 020@FunctionalInterface 021public interface Instruction extends StyleKeys { 022 023 /** 024 * Execute the instruction in the given environment. 025 * @param env the environment 026 */ 027 void execute(Environment env); 028 029 /** 030 * A float value that will be added to the current float value. Specified as +5 or -3 in MapCSS 031 */ 032 class RelativeFloat { 033 public final float val; 034 035 public RelativeFloat(float val) { 036 this.val = val; 037 } 038 039 @Override 040 public String toString() { 041 return "RelativeFloat{" + "val=" + val + '}'; 042 } 043 } 044 045 /** 046 * An instruction that assigns a given value to a variable on evaluation 047 */ 048 class AssignmentInstruction implements Instruction { 049 public final String key; 050 public final Object val; 051 public final boolean isSetInstruction; 052 053 public AssignmentInstruction(String key, Object val, boolean isSetInstruction) { 054 this.key = key; 055 this.isSetInstruction = isSetInstruction; 056 if (val instanceof LiteralExpression) { 057 Object litValue = ((LiteralExpression) val).evaluate(null); 058 if (litValue instanceof Keyword && "none".equals(((Keyword) litValue).val)) { 059 this.val = null; 060 } else if (TEXT.equals(key)) { 061 /* Special case for declaration 'text: ...' 062 * 063 * - Treat the value 'auto' as keyword. 064 * - Treat any other literal value 'litval' as as reference to tag with key 'litval' 065 * 066 * - Accept function expressions as is. This allows for 067 * tag(a_tag_name) value of a tag 068 * eval("a static text") a static text 069 * parent_tag(a_tag_name) value of a tag of a parent relation 070 */ 071 if (litValue.equals(Keyword.AUTO)) { 072 this.val = Keyword.AUTO; 073 } else { 074 String s = Cascade.convertTo(litValue, String.class); 075 if (s != null) { 076 this.val = new MapPaintStyles.TagKeyReference(s); 077 } else { 078 this.val = litValue; 079 } 080 } 081 } else { 082 this.val = litValue; 083 } 084 } else { 085 this.val = val; 086 } 087 } 088 089 @Override 090 public void execute(Environment env) { 091 Object value; 092 if (val instanceof Expression) { 093 value = ((Expression) val).evaluate(env); 094 } else { 095 value = val; 096 } 097 if (ICON_IMAGE.equals(key) || FILL_IMAGE.equals(key) || REPEAT_IMAGE.equals(key)) { 098 if (value instanceof String) { 099 value = new IconReference((String) value, env.source); 100 } 101 } 102 env.mc.getOrCreateCascade(env.layer).putOrClear(key, value); 103 } 104 105 @Override 106 public String toString() { 107 return key + ": " + (val instanceof float[] ? Arrays.toString((float[]) val) : 108 (val instanceof String ? ("String<"+val+'>') : val)) + ';'; 109 } 110 } 111}