- Timestamp:
- 2014-04-12T00:30:32+02:00 (11 years ago)
- Location:
- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java
r6883 r6970 376 376 return OsmPrimitive.getFilteredList(e.osm.getReferrers(), Way.class).isEmpty(); 377 377 } 378 return true;378 return false; 379 379 } 380 380 -
trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj
r6927 r6970 8 8 package org.openstreetmap.josm.gui.mappaint.mapcss.parsergen; 9 9 10 import java. awt.Color;10 import java.io.InputStream; 11 11 import java.util.ArrayList; 12 12 import java.util.List; … … 28 28 import org.openstreetmap.josm.tools.ColorHelper; 29 29 import org.openstreetmap.josm.tools.Pair; 30 import org.openstreetmap.josm.tools.Utils;31 30 import org.openstreetmap.josm.Main; 32 31 33 /************* 34 * <pre> 35 * Parser definitions 36 * 37 * rule 38 * _______________________|______________________________ 39 * | | 40 * selector declaration 41 * _________|___________________ _________|____________ 42 * | | | | 43 * 44 * way|z11-12[highway=residential] { color: red; width: 3 } 45 * 46 * |_____||___________________| |_________| 47 * | | | 48 * zoom condition instruction 49 * 50 * more general: 51 * 52 * way|z13-[a=b][c=d]::subpart, way|z-3[u=v]:closed::subpart2 { p1 : val; p2 : val; } 53 * 54 * 'val' can be a literal, or an expression like "prop(width, default) + 0.8". 55 * 56 * {@literal @media} { ... } queries are supported, following http://www.w3.org/TR/css3-mediaqueries/#syntax 57 * 58 * media_query 59 * ___________________________|_______________________________ 60 * | | 61 * {@literal @media} all and (min-josm-version: 7789) and (max-josm-version: 7790), all and (user-agent: xyz) { ... } 62 * |______________________| 63 * | 64 * media_expression 65 * </pre> 32 /** 33 * MapCSS parser. 34 * 35 * Contains two independent grammars: 36 * (a) the preprocessor and (b) the main mapcss parser. 37 * 38 * The preprocessor handles @media syntax. Basically this allows 39 * to write one style for different versions of JOSM (or different editors). 40 * When the @media condition is not fulfilled, it should simply skip over 41 * the whole section and not attempt to parse the possibly unknown 42 * grammar. It preserves whitespace and comments, in order to keep the 43 * line and column numbers in the error messages correct for the second pass. 44 * 66 45 */ 67 46 68 47 public class MapCSSParser { 69 48 MapCSSStyleSource sheet; 49 StringBuilder sb; 50 51 /** 52 * Nicer way to refer to a lexical state. 53 */ 54 public static enum LexicalState { 55 PREPROCESSOR(0), /* the preprocessor */ 56 DEFAULT(2); /* the main parser */ 57 58 int idx; // the integer, which javacc assigns to this state 59 60 LexicalState(int idx) { 61 if (!this.name().equals(MapCSSParserTokenManager.lexStateNames[idx])) { 62 throw new RuntimeException(); 63 } 64 this.idx = idx; 65 } 66 }; 67 68 /** 69 * Constructor which initializes the parser with a certain lexical state. 70 */ 71 public MapCSSParser(InputStream in, String encoding, LexicalState initState) { 72 this(createTokenManager(in, encoding, initState)); 73 } 74 75 protected static MapCSSParserTokenManager createTokenManager(InputStream in, String encoding, LexicalState initState) { 76 SimpleCharStream scs; 77 try { 78 scs = new SimpleCharStream(in, encoding, 1, 1); 79 } catch(java.io.UnsupportedEncodingException e) { 80 throw new RuntimeException(e); 81 } 82 return new MapCSSParserTokenManager(scs, initState.idx); 83 } 70 84 } 71 85 PARSER_END(MapCSSParser) … … 73 87 /************* 74 88 * Token definitions 75 */ 89 * 90 * Lexical states for the preprocessor: <PREPROCESSOR>, <PP_COMMENT> 91 * Lexical states for the main parser: <DEFAULT>, <COMMENT> 92 */ 93 94 <PREPROCESSOR> 95 TOKEN: 96 { 97 < PP_AND: "and" > 98 | < PP_NOT: "not" > 99 | < PP_MEDIA: "@media" > 100 | < PP_NEWLINECHAR: "\n" | "\r" | "\f" > 101 | < PP_WHITESPACE: " " | "\t" > 102 | < PP_COMMENT_START: "/*" > : PP_COMMENT 103 } 104 105 <PP_COMMENT> 106 TOKEN: 107 { 108 < PP_COMMENT_END: "*/" > : PREPROCESSOR 109 } 110 111 <PP_COMMENT> 112 MORE: 113 { 114 < ~[] > 115 } 76 116 77 117 <DEFAULT> 78 118 TOKEN [IGNORE_CASE]: 79 119 { 80 /* Special keywords in some contexts, ordinary identifiers in other contexts. 81 Don't use the javacc token contexts (except DEFAULT and COMMENT) because 82 they complicate error handling. Instead simply add a parsing rule <code>ident()</code> 83 that parses the IDENT token and in addition all the keyword tokens. */ 84 < AND: "and" > 85 | < SET: "set" > 86 | < NOT: "not" > 87 | < MEDIA: "@media" > 88 } 89 90 <DEFAULT> 120 /* Special keyword in some contexts, ordinary identifier in other contexts. 121 Use the parsing rule <code>ident()</code> to refer to a general 122 identifier, including "set". */ 123 < SET: "set" > 124 } 125 126 <DEFAULT,PREPROCESSOR> 91 127 TOKEN: 92 128 { 93 129 < IDENT: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","-","0"-"9"] )* > 94 130 | < UINT: ["1"-"9"] ( ["0"-"9"] )* > 95 | < UFLOAT: ( ["0"-"9"] )+ ( "." ( ["0"-"9"] )+ )? >96 131 | < STRING: "\"" ( [" ","!","#"-"[","]"-"~","\u0080"-"\uFFFF"] | "\\\"" | "\\\\" )* "\"" > 97 132 | < #PREDEFINED: "\\" ["d","D","s","S","w","W"] > 98 133 | < #REGEX_CHAR_WITHOUT_STAR: [" "-")","+"-".","0"-"[","]"-"~","\u0080"-"\uFFFF"] | "\\/" | "\\\\" | "\\[" | "\\]" | "\\+" | "\\." | "\\'" | "\\\"" | <PREDEFINED> > 99 134 | < REGEX: "/" <REGEX_CHAR_WITHOUT_STAR> ( <REGEX_CHAR_WITHOUT_STAR> | "*" )* "/" > 135 | < LBRACE: "{" > 136 | < RBRACE: "}" > 137 | < LPAR: "(" > 138 | < RPAR: ")" > 139 | < COLON: ":" > 140 } 141 142 <PREPROCESSOR> 143 TOKEN: 144 { 145 < PP_SOMETHING_ELSE : ~[] > 146 } 147 148 <DEFAULT> 149 TOKEN: 150 { 151 < UFLOAT: ( ["0"-"9"] )+ ( "." ( ["0"-"9"] )+ )? > 100 152 | < #H: ["0"-"9","a"-"f","A"-"F"] > 101 153 | < HEXCOLOR: "#" ( <H><H><H><H><H><H><H><H> | <H><H><H><H><H><H> | <H><H><H> ) > … … 103 155 | < STAR: "*" > 104 156 | < SLASH: "/" > 105 | < LBRACE: "{" >106 | < RBRACE: "}" >107 157 | < LSQUARE: "[" > 108 158 | < RSQUARE: "]" > 109 | < LPAR: "(" >110 | < RPAR: ")" >111 159 | < GREATER_EQUAL: ">=" > 112 160 | < LESS_EQUAL: "<=" > … … 116 164 | < EXCLAMATION: "!" > 117 165 | < TILDE: "~" > 118 | < COLON: ":" >119 166 | < DCOLON: "::" > 120 167 | < SEMICOLON: ";" > … … 147 194 } 148 195 149 int uint() : 150 { 151 Token i; 152 } 153 { 154 i=<UINT> { return Integer.parseInt(i.image); } 155 } 156 157 int int_() : 158 { 159 int i; 160 } 161 { 162 <MINUS> i=uint() { return -i; } | i=uint() { return i; } 163 } 164 165 float ufloat() : 166 { 167 Token f; 168 } 169 { 170 ( f=<UFLOAT> | f=<UINT> ) 171 { return Float.parseFloat(f.image); } 172 } 173 174 float float_() : 175 { 176 float f; 177 } 178 { 179 <MINUS> f=ufloat() { return -f; } | f=ufloat() { return f; } 180 } 181 182 String string() : 183 { 184 Token t; 185 } 186 { 187 t=<STRING> 188 { return t.image.substring(1, t.image.length() - 1).replace("\\\"", "\"").replace("\\\\", "\\"); } 189 } 190 191 String ident(): 192 { 193 Token t; 194 String s; 195 } 196 { 197 ( t=<IDENT> | t=<SET> | t=<NOT> | t=<AND> ) { return t.image; } 198 } 199 200 String string_or_ident() : 201 { 202 Token t; 203 String s; 204 } 205 { 206 ( s=ident() | s=string() ) { return s; } 207 } 208 209 String regex() : 210 { 211 Token t; 212 } 213 { 214 t=<REGEX> 215 { return t.image.substring(1, t.image.length() - 1); } 216 } 196 197 /************* 198 * 199 * Preprocessor parser definitions: 200 * 201 * <pre> 202 * 203 * {@literal @media} { ... } queries are supported, following http://www.w3.org/TR/css3-mediaqueries/#syntax 204 * 205 * media_query 206 * ___________________________|_______________________________ 207 * | | 208 * {@literal @media} all and (min-josm-version: 7789) and (max-josm-version: 7790), all and (user-agent: xyz) { ... } 209 * |______________________| 210 * | 211 * media_expression 212 * </pre> 213 */ 214 217 215 218 216 /** 219 * white-space 220 */ 221 void s() : 222 { 223 } 224 { 225 ( <S> )? 217 * root method for the preprocessor. 218 */ 219 String pp_root(MapCSSStyleSource sheet): 220 { 221 } 222 { 223 { sb = new StringBuilder(); this.sheet = sheet; } 224 pp_black_box(true) <EOF> 225 { return sb.toString(); } 226 226 } 227 227 228 228 /** 229 * mix of white-space and comments 230 */ 231 void w() : 232 { 233 } 234 { 235 ( <S> | <COMMENT_START> <COMMENT_END> )* 236 } 237 238 /** 239 * comma delimited list of floats (at least 2, all >= 0) 240 */ 241 List<Float> float_array() : 242 { 243 float f; 244 List<Float> fs = new ArrayList<Float>(); 245 } 246 { 247 f=ufloat() { fs.add(f); } 248 ( 249 <COMMA> s() 250 f=ufloat() { fs.add(f); } 251 )+ 252 { 253 return fs; 254 } 255 } 256 257 /** 258 * root 259 */ 260 void sheet(MapCSSStyleSource sheet): 261 { 262 MapCSSRule r; 263 } 264 { 265 { this.sheet = sheet; } 266 w() 267 rules(true) ( media() w() rules(true) )* 268 <EOF> 269 } 270 271 void media(): 229 * Parse any unknown grammar (black box). 230 * 231 * Only stop when "@media" is encountered and keep track of correct number of 232 * opening and closing curly brackets. 233 * 234 * @param write false if this content should be skipped (@pp_media condition is not fulfilled), true otherwise 235 */ 236 void pp_black_box(boolean write): 237 { 238 Token t; 239 } 240 { 241 ( 242 (t=<PP_AND> | t=<PP_NOT> | t=<UINT> | t=<STRING> | t=<REGEX> | t=<LPAR> | t=<RPAR> | t=<COLON> | t=<IDENT> | t=<PP_SOMETHING_ELSE>) { if (write) sb.append(t.image); } 243 | 244 pp_w1() 245 | 246 pp_media() 247 | 248 t=<LBRACE> { if (write) sb.append(t.image); } pp_black_box(write) t=<RBRACE> { if (write) sb.append(t.image); } 249 )* 250 } 251 252 void pp_media(): 272 253 { 273 254 boolean pass = false; … … 276 257 } 277 258 { 278 < MEDIA>w()279 ( q= media_query() { pass = pass || q; empty = false; }280 ( <COMMA> w() q=media_query() { pass = pass || q; } )*259 <PP_MEDIA> pp_w() 260 ( q=pp_media_query() { pass = pass || q; empty = false; } 261 ( <COMMA> pp_w() q=pp_media_query() { pass = pass || q; } )* 281 262 )? 282 <LBRACE> w()283 rules(empty || pass)263 <LBRACE> 264 pp_black_box(empty || pass) 284 265 <RBRACE> 285 266 } 286 267 287 boolean media_query():268 boolean pp_media_query(): 288 269 { 289 270 Token t; … … 294 275 } 295 276 { 296 ( < NOT> { invert = true; }w() )?297 ( 298 t=<IDENT> { mediatype = t.image.toLowerCase(); } w()299 ( < AND> w() e=media_expression() { pass = pass && e; }w() )*300 | 301 e= media_expression() { pass = pass && e; }w()302 ( < AND> w() e=media_expression() { pass = pass && e; }w() )*277 ( <PP_NOT> { invert = true; } pp_w() )? 278 ( 279 t=<IDENT> { mediatype = t.image.toLowerCase(); } pp_w() 280 ( <PP_AND> pp_w() e=pp_media_expression() { pass = pass && e; } pp_w() )* 281 | 282 e=pp_media_expression() { pass = pass && e; } pp_w() 283 ( <PP_AND> pp_w() e=pp_media_expression() { pass = pass && e; } pp_w() )* 303 284 ) 304 285 { … … 310 291 } 311 292 312 boolean media_expression(): 293 /** 294 * Parse an @media expression. 295 * 296 * The parsing rule {@link #literal()} from the main mapcss parser is reused here. 297 * 298 * @return true if the condition is fulfilled 299 */ 300 boolean pp_media_expression(): 313 301 { 314 302 Token t; … … 317 305 } 318 306 { 319 <LPAR> w() t=<IDENT> { feature = t.image; } w() ( <COLON>w() val=literal() )? <RPAR>307 <LPAR> pp_w() t=<IDENT> { feature = t.image; } pp_w() ( <COLON> pp_w() val=literal() )? <RPAR> 320 308 { return this.sheet.evalMediaExpression(feature, val); } 321 309 } 322 310 323 void rules(boolean add): 311 void pp_w1(): 312 { 313 Token t; 314 } 315 { 316 t=<PP_NEWLINECHAR> { sb.append(t.image); } 317 | 318 t=<PP_WHITESPACE> { sb.append(t.image); } 319 | 320 t=<PP_COMMENT_START> { sb.append(t.image); } t=<PP_COMMENT_END> { sb.append(t.image); } 321 } 322 323 void pp_w(): 324 { 325 } 326 { 327 ( pp_w1() )* 328 } 329 330 /************* 331 * 332 * Parser definition for the main MapCSS parser: 333 * 334 * <pre> 335 * 336 * rule 337 * _______________________|______________________________ 338 * | | 339 * selector declaration 340 * _________|___________________ _________|____________ 341 * | | | | 342 * 343 * way|z11-12[highway=residential] { color: red; width: 3 } 344 * 345 * |_____||___________________| |_________| 346 * | | | 347 * zoom condition instruction 348 * 349 * more general: 350 * 351 * way|z13-[a=b][c=d]::subpart, way|z-3[u=v]:closed::subpart2 { p1 : val; p2 : val; } 352 * 353 * 'val' can be a literal, or an expression like "prop(width, default) + 0.8". 354 * 355 * </pre> 356 */ 357 358 int uint() : 359 { 360 Token i; 361 } 362 { 363 i=<UINT> { return Integer.parseInt(i.image); } 364 } 365 366 int int_() : 367 { 368 int i; 369 } 370 { 371 <MINUS> i=uint() { return -i; } | i=uint() { return i; } 372 } 373 374 float ufloat() : 375 { 376 Token f; 377 } 378 { 379 ( f=<UFLOAT> | f=<UINT> ) 380 { return Float.parseFloat(f.image); } 381 } 382 383 float float_() : 384 { 385 float f; 386 } 387 { 388 <MINUS> f=ufloat() { return -f; } | f=ufloat() { return f; } 389 } 390 391 String string() : 392 { 393 Token t; 394 } 395 { 396 t=<STRING> 397 { return t.image.substring(1, t.image.length() - 1).replace("\\\"", "\"").replace("\\\\", "\\"); } 398 } 399 400 String ident(): 401 { 402 Token t; 403 String s; 404 } 405 { 406 ( t=<IDENT> | t=<SET> ) { return t.image; } 407 } 408 409 String string_or_ident() : 410 { 411 Token t; 412 String s; 413 } 414 { 415 ( s=ident() | s=string() ) { return s; } 416 } 417 418 String regex() : 419 { 420 Token t; 421 } 422 { 423 t=<REGEX> 424 { return t.image.substring(1, t.image.length() - 1); } 425 } 426 427 /** 428 * white-space 429 */ 430 void s() : 431 { 432 } 433 { 434 ( <S> )? 435 } 436 437 /** 438 * mix of white-space and comments 439 */ 440 void w() : 441 { 442 } 443 { 444 ( <S> | <COMMENT_START> <COMMENT_END> )* 445 } 446 447 /** 448 * comma delimited list of floats (at least 2, all >= 0) 449 */ 450 List<Float> float_array() : 451 { 452 float f; 453 List<Float> fs = new ArrayList<Float>(); 454 } 455 { 456 f=ufloat() { fs.add(f); } 457 ( 458 <COMMA> s() 459 f=ufloat() { fs.add(f); } 460 )+ 461 { 462 return fs; 463 } 464 } 465 466 /** 467 * entry point for the main parser 468 */ 469 void sheet(MapCSSStyleSource sheet): 324 470 { 325 471 MapCSSRule r; 326 472 } 327 473 { 474 { this.sheet = sheet; } 475 w() 328 476 ( 329 477 try { 330 r=rule() { if ( add &&r != null) { sheet.rules.add(r); } } w()478 r=rule() { if (r != null) { sheet.rules.add(r); } } w() 331 479 } catch (MapCSSException mex) { 332 480 error_skipto(RBRACE, mex); … … 337 485 } 338 486 )* 487 <EOF> 339 488 } 340 489 … … 663 812 Expression function() : 664 813 { 665 Token tmp;666 814 Expression arg; 667 815 String name; … … 681 829 Object literal() : 682 830 { 683 String val ;684 Token t , t2;831 String val, pref; 832 Token t; 685 833 float f; 686 834 } 687 835 { 688 836 LOOKAHEAD(2) 689 t2=<IDENT>t=<HEXCOLOR>690 { return Main.pref.getColor("mappaint." + (sheet == null ? "MapCSS" : sheet.title) + "." + t2.image, ColorHelper.html2color(t.image)); }837 pref=ident() t=<HEXCOLOR> 838 { return Main.pref.getColor("mappaint." + (sheet == null ? "MapCSS" : sheet.title) + "." + pref, ColorHelper.html2color(t.image)); } 691 839 | 692 840 t=<IDENT> { return new Keyword(t.image); } -
trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
r6896 r6970 78 78 InputStream in = getSourceInputStream(); 79 79 try { 80 MapCSSParser parser = new MapCSSParser(in, "UTF-8"); 80 // evaluate @media { ... } blocks 81 MapCSSParser preprocessor = new MapCSSParser(in, "UTF-8", MapCSSParser.LexicalState.PREPROCESSOR); 82 String mapcss = preprocessor.pp_root(this); 83 84 // do the actual mapcss parsing 85 InputStream in2 = new ByteArrayInputStream(mapcss.getBytes(Utils.UTF_8)); 86 MapCSSParser parser = new MapCSSParser(in2, "UTF-8", MapCSSParser.LexicalState.DEFAULT); 81 87 parser.sheet(this); 88 82 89 loadMeta(); 83 90 loadCanvas();
Note:
See TracChangeset
for help on using the changeset viewer.