001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint.mapcss;
003
004import java.util.List;
005import java.util.Objects;
006import java.util.stream.Collectors;
007
008import org.openstreetmap.josm.gui.mappaint.Environment;
009import org.openstreetmap.josm.gui.mappaint.StyleSource;
010
011/**
012 * A MapCSS rule.
013 *
014 * A MapCSS style is simply a list of MapCSS rules. Each rule has a selector
015 * and a declaration. Whenever the selector matches the primitive, the
016 * declaration block is executed for this primitive.
017 */
018public class MapCSSRule implements Comparable<MapCSSRule> {
019
020    /**
021     * The selector. If it matches, this rule should be applied
022     */
023    public final Selector selector;
024    /**
025     * The instructions for this selector
026     */
027    public final Declaration declaration;
028
029    /**
030     * A declaration is a set of {@link Instruction}s
031     */
032    public static class Declaration {
033        /**
034         * The instructions in this declaration
035         */
036        public final List<Instruction> instructions;
037        /**
038         * The index of this declaration
039         * <p>
040         * declarations in the StyleSource are numbered consecutively
041         */
042        public final int idx;
043
044        /**
045         * Create a new {@link Declaration}
046         * @param instructions The instructions for this dectlaration
047         * @param idx The index in the {@link StyleSource}
048         */
049        public Declaration(List<Instruction> instructions, int idx) {
050            this.instructions = instructions;
051            this.idx = idx;
052        }
053
054        /**
055         * <p>Executes the instructions against the environment {@code env}</p>
056         *
057         * @param env the environment
058         */
059        public void execute(Environment env) {
060            for (Instruction i : instructions) {
061                i.execute(env);
062            }
063        }
064
065        @Override
066        public int hashCode() {
067            return Objects.hash(instructions, idx);
068        }
069
070        @Override
071        public boolean equals(Object obj) {
072            if (this == obj) return true;
073            if (obj == null || getClass() != obj.getClass()) return false;
074            Declaration that = (Declaration) obj;
075            return idx == that.idx &&
076                    Objects.equals(instructions, that.instructions);
077        }
078
079        @Override
080        public String toString() {
081            return "Declaration [instructions=" + instructions + ", idx=" + idx + ']';
082        }
083    }
084
085    /**
086     * Constructs a new {@code MapCSSRule}.
087     * @param selector The selector
088     * @param declaration The declaration
089     */
090    public MapCSSRule(Selector selector, Declaration declaration) {
091        this.selector = selector;
092        this.declaration = declaration;
093    }
094
095    /**
096     * <p>Executes the instructions against the environment {@code env}</p>
097     *
098     * @param env the environment
099     */
100    public void execute(Environment env) {
101        declaration.execute(env);
102    }
103
104    @Override
105    public int compareTo(MapCSSRule o) {
106        return declaration.idx - o.declaration.idx;
107    }
108
109    @Override
110    public String toString() {
111        return selector + declaration.instructions.stream()
112                .map(String::valueOf)
113                .collect(Collectors.joining("\n  ", " {\n  ", "\n}"));
114    }
115}
116