source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java@ 6736

Last change on this file since 6736 was 6736, checked in by simon04, 10 years ago

fix #8568 - MapCSS rule applied several times if several selectors applies

  • Property svn:eol-style set to native
File size: 7.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.mapcss;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.io.ByteArrayInputStream;
8import java.io.File;
9import java.io.IOException;
10import java.io.InputStream;
11import java.util.ArrayList;
12import java.util.List;
13import java.util.Map.Entry;
14import java.util.zip.ZipEntry;
15import java.util.zip.ZipFile;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.data.osm.Node;
19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.gui.mappaint.Cascade;
21import org.openstreetmap.josm.gui.mappaint.Environment;
22import org.openstreetmap.josm.gui.mappaint.MultiCascade;
23import org.openstreetmap.josm.gui.mappaint.Range;
24import org.openstreetmap.josm.gui.mappaint.StyleSource;
25import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
26import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser;
27import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
28import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.TokenMgrError;
29import org.openstreetmap.josm.gui.preferences.SourceEntry;
30import org.openstreetmap.josm.io.MirroredInputStream;
31import org.openstreetmap.josm.tools.CheckParameterUtil;
32import org.openstreetmap.josm.tools.LanguageInfo;
33import org.openstreetmap.josm.tools.Utils;
34
35public class MapCSSStyleSource extends StyleSource {
36 public final List<MapCSSRule> rules;
37 private Color backgroundColorOverride;
38 private String css = null;
39 private ZipFile zipFile;
40
41 public MapCSSStyleSource(String url, String name, String shortdescription) {
42 super(url, name, shortdescription);
43 rules = new ArrayList<MapCSSRule>();
44 }
45
46 public MapCSSStyleSource(SourceEntry entry) {
47 super(entry);
48 rules = new ArrayList<MapCSSRule>();
49 }
50
51 /**
52 * <p>Creates a new style source from the MapCSS styles supplied in
53 * {@code css}</p>
54 *
55 * @param css the MapCSS style declaration. Must not be null.
56 * @throws IllegalArgumentException thrown if {@code css} is null
57 */
58 public MapCSSStyleSource(String css) throws IllegalArgumentException{
59 super(null, null, null);
60 CheckParameterUtil.ensureParameterNotNull(css);
61 this.css = css;
62 rules = new ArrayList<MapCSSRule>();
63 }
64
65 @Override
66 public void loadStyleSource() {
67 init();
68 rules.clear();
69 try {
70 InputStream in = getSourceInputStream();
71 try {
72 MapCSSParser parser = new MapCSSParser(in, "UTF-8");
73 parser.sheet(this);
74 loadMeta();
75 loadCanvas();
76 } finally {
77 closeSourceInputStream(in);
78 }
79 } catch (IOException e) {
80 Main.warn(tr("Failed to load Mappaint styles from ''{0}''. Exception was: {1}", url, e.toString()));
81 Main.error(e);
82 logError(e);
83 } catch (TokenMgrError e) {
84 Main.warn(tr("Failed to parse Mappaint styles from ''{0}''. Error was: {1}", url, e.getMessage()));
85 Main.error(e);
86 logError(e);
87 } catch (ParseException e) {
88 Main.warn(tr("Failed to parse Mappaint styles from ''{0}''. Error was: {1}", url, e.getMessage()));
89 Main.error(e);
90 logError(new ParseException(e.getMessage())); // allow e to be garbage collected, it links to the entire token stream
91 }
92 }
93
94 @Override
95 public InputStream getSourceInputStream() throws IOException {
96 if (css != null) {
97 return new ByteArrayInputStream(css.getBytes(Utils.UTF_8));
98 }
99 MirroredInputStream in = new MirroredInputStream(url);
100 if (isZip) {
101 File file = in.getFile();
102 Utils.close(in);
103 zipFile = new ZipFile(file);
104 zipIcons = file;
105 ZipEntry zipEntry = zipFile.getEntry(zipEntryPath);
106 return zipFile.getInputStream(zipEntry);
107 } else {
108 zipFile = null;
109 zipIcons = null;
110 return in;
111 }
112 }
113
114 @Override
115 public void closeSourceInputStream(InputStream is) {
116 super.closeSourceInputStream(is);
117 if (isZip) {
118 Utils.close(zipFile);
119 }
120 }
121
122 /**
123 * load meta info from a selector "meta"
124 */
125 private void loadMeta() {
126 Cascade c = constructSpecial("meta");
127 String pTitle = c.get("title", null, String.class);
128 if (title == null) {
129 title = pTitle;
130 }
131 String pIcon = c.get("icon", null, String.class);
132 if (icon == null) {
133 icon = pIcon;
134 }
135 }
136
137 private void loadCanvas() {
138 Cascade c = constructSpecial("canvas");
139 backgroundColorOverride = c.get("background-color", null, Color.class);
140 }
141
142 private Cascade constructSpecial(String type) {
143
144 MultiCascade mc = new MultiCascade();
145 Node n = new Node();
146 String code = LanguageInfo.getJOSMLocaleCode();
147 n.put("lang", code);
148 // create a fake environment to read the meta data block
149 Environment env = new Environment(n, mc, "default", this);
150
151 NEXT_RULE:
152 for (MapCSSRule r : rules) {
153 for (Selector s : r.selectors) {
154 if ((s instanceof GeneralSelector)) {
155 GeneralSelector gs = (GeneralSelector) s;
156 if (gs.getBase().equals(type)) {
157 if (!gs.matchesConditions(env)) {
158 continue NEXT_RULE;
159 }
160 r.execute(env);
161 }
162 }
163 }
164 }
165 return mc.getCascade("default");
166 }
167
168 @Override
169 public Color getBackgroundColorOverride() {
170 return backgroundColorOverride;
171 }
172
173 @Override
174 public void apply(MultiCascade mc, OsmPrimitive osm, double scale, OsmPrimitive multipolyOuterWay, boolean pretendWayIsClosed) {
175 Environment env = new Environment(osm, mc, null, this);
176 RULE: for (MapCSSRule r : rules) {
177 for (Selector s : r.selectors) {
178 env.clearSelectorMatchingInformation();
179 if (s.matches(env)) { // as side effect env.parent will be set (if s is a child selector)
180 if (s.getRange().contains(scale)) {
181 mc.range = Range.cut(mc.range, s.getRange());
182 } else {
183 mc.range = mc.range.reduceAround(scale, s.getRange());
184 continue;
185 }
186
187 String sub = s.getSubpart();
188 if (sub == null) {
189 sub = "default";
190 }
191 else if ("*".equals(sub)) {
192 for (Entry<String, Cascade> entry : mc.getLayers()) {
193 env.layer = entry.getKey();
194 if (Utils.equal(env.layer, "*")) {
195 continue;
196 }
197 r.execute(env);
198 }
199 }
200 env.layer = sub;
201 r.execute(env);
202 continue RULE;
203 }
204 }
205 }
206 }
207
208 @Override
209 public String toString() {
210 return Utils.join("\n", rules);
211 }
212}
Note: See TracBrowser for help on using the repository browser.