Ticket #17669: 17669,22096.patch

File 17669,22096.patch, 45.7 KB (added by taylor.smock, 3 years ago)

Add PlaceholderExpression for strings that need to have placeholders replaced

  • resources/data/validator/numeric.mapcss

    Subject: [PATCH] Fix #17669, #22096: Replace placeholders in calculations
    ---
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/resources/data/validator/numeric.mapcss b/resources/data/validator/numeric.mapcss
    a b  
    6767  assertNoMatch: "node building:levels=0"; /* valid because there can be building:levels:underground > 0 or roof:levels > 0 */
    6868}
    6969
    70 *[height][height =~ /^[0-9]+(\.[0-9]+)?(( )*(metre|metres|meter|meters|Metre|Metres|Meter|Meters)|m)$/] {
     70*[roof:height][roof:height =~ /^0*(\.0*)?( (m|ft))?$/][roof:shape=flat] {
     71  throwWarning: tr("{0} is unnecessary for {1}", "{0.tag}", "{2.tag}");
     72  group: tr("unnecessary tag");
     73  fixRemove: "{0.key}";
     74  set zero_roof_height_flat;
     75  assertMatch: "node roof:height=0 roof:shape=flat";
     76  assertMatch: "node roof:shape=flat roof:height=\"00.00000 ft\" roof:shape=flat";
     77  assertNoMatch: "node roof:shape=flat roof:height=2 m roof:shape=flat";
     78  assertNoMatch: "node roof:height=0 roof:shape=gabled";
     79}
     80
     81/*********************
     82 * Begin Unit checks *
     83 *********************/
     84/* See https://wiki.openstreetmap.org/wiki/Map_features/Units */
     85/* 1. Replace aliases to make the rest of the checks easier to implement */
     86/* Distance measurements, note that these should look also match `,` separators to ensure that at least one error is matched */
     87/* Meters; Note that we cannot assertMatch "2  m" since we replace the double space with a single space */
     88*[height][height            =~ /^(?i)[0-9]+([.,][0-9]+)?( *(metres?|meters?)|m| {2,}m)$/],
     89*[roof:height][roof:height  =~ /^(?i)[0-9]+([.,][0-9]+)?( *(metres?|meters?)|m| {2,}m)$/]!.zero_roof_height_flat,
     90*[width][width              =~ /^(?i)[0-9]+([.,][0-9]+)?( *(metres?|meters?)|m| {2,}m)$/],
     91*[maxwidth][maxwidth        =~ /^(?i)[0-9]+([.,][0-9]+)?( *(metres?|meters?)|m| {2,}m)$/],
     92*[min_height][min_height    =~ /^(?i)-?[0-9]+([.,][0-9]+)?( *(metres?|meters?)|m| {2,}m)$/],
     93*[maxheight][maxheight      =~ /^(?i)[1-9][0-9]*([.,][0-9]+)?( *(metres?|meters?)|m| {2,}m)$/],
     94*[maxlength][maxlength      =~ /^(?i)[1-9][0-9]*([.,][0-9]+)?( *(metres?|meters?)|m| {2,}m)$/] {
    7195  throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    72   set height_meter_autofix;
    73   fixAdd: concat("height=", get(regexp_match("([0-9.]+)( )*(.+)",tag("height")),1)," m");
     96  fixAdd: concat("{0.key}", "=", get(regexp_match("([0-9.]+) *.+", "{0.value}"), 1), " m");
    7497  assertMatch: "node height=6.78 meters";
     98  assertMatch: "node height=6,78 meters";
    7599  assertMatch: "node height=5  metre";
    76100  assertMatch: "node height=2m";
    77101  assertNoMatch: "node height=2 m";
    78102  assertNoMatch: "node height=5";
     103  assertMatch: "node maxheight=6.78 meters";
     104  assertMatch: "node maxheight=5  metre";
     105  assertMatch: "node maxheight=2m";
     106  assertNoMatch: "node maxheight=2 m";
     107  assertNoMatch: "node maxheight=5";
     108  assertMatch: "node roof:height=6.78 meters";
     109  assertMatch: "node roof:height=5  metre";
     110  assertMatch: "node roof:height=2m";
     111  assertNoMatch: "node roof:height=2 m";
     112  assertNoMatch: "node roof:height=5";
     113  assertMatch: "node maxlength=6.78 meters";
     114  assertMatch: "node maxlength=5  metre";
     115  assertMatch: "node maxlength=2m";
     116  assertNoMatch: "node maxlength=2 m";
     117  assertNoMatch: "node maxlength=5";
     118  assertMatch: "node width=6.78 meters";
     119  assertMatch: "node width=5  metre";
     120  assertMatch: "node width=2m";
     121  assertNoMatch: "node width=2 m";
     122  assertNoMatch: "node width=5";
     123  assertMatch: "node maxwidth=6.78 meters";
     124  assertMatch: "node maxwidth=5  metre";
     125  assertMatch: "node maxwidth=2m";
     126  assertNoMatch: "node maxwidth=2 m";
     127  assertNoMatch: "node maxwidth=5";
     128  assertMatch: "node min_height=6.78 meters";
     129  assertMatch: "node min_height=5  metre";
     130  assertMatch: "node min_height=2m";
     131  assertNoMatch: "node min_height=2 m";
     132  assertNoMatch: "node min_height=5";
    79133}
    80 *[height][height =~ /^[0-9]+(\.[0-9]+)?(( )*(foot|Foot|feet|Feet)|ft)$/] {
    81   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    82   set height_foot_autofix;
    83   fixAdd: concat("height=", get(regexp_match("([0-9.]+)( )*(.+)",tag("height")),1)," ft");
     134
     135/* Foot inches */
     136*[height][height            =~ /^(?i)[0-9]+([.,][0-9]+)?( *(foot|feet|ft)| +\')$/],
     137*[maxheight][maxheight      =~ /^(?i)[0-9]+([.,][0-9]+)?( *(foot|feet|ft)| +\')$/],
     138*[roof:height][roof:height  =~ /^(?i)[0-9]+([.,][0-9]+)?( *(foot|feet|ft)| +\')$/]!.zero_roof_height_flat,
     139*[maxlength][maxlength      =~ /^(?i)[0-9]+([.,][0-9]+)?( *(foot|feet|ft)| +\')$/],
     140*[width][width              =~ /^(?i)[0-9]+([.,][0-9]+)?( *(foot|feet|ft)| +\')$/],
     141*[maxwidth][maxwidth        =~ /^(?i)[0-9]+([.,][0-9]+)?( *(foot|feet|ft)| +\')$/] {
     142  throwWarning: tr("unusual value of {0}: use abbreviation for '' for foot and \" for inches, no spaces", "{0.key}");
     143  fixAdd: concat("{0.key}", "=", get(regexp_match("([0-9.]+) *.+", "{0.value}"), 1), "'");
    84144  assertMatch: "node height=6.78 foot";
    85145  assertMatch: "node height=5  Feet";
    86   assertMatch: "node height=2ft";
    87   assertNoMatch: "node height=2 ft";
     146  assertMatch: "node height=2 '";
     147  assertNoMatch: "node height=2'";
    88148  assertNoMatch: "node height=5";
     149  assertMatch: "node maxheight=6.78 foot";
     150  assertMatch: "node maxheight=5  Feet";
     151  assertMatch: "node maxheight=2 '";
     152  assertNoMatch: "node maxheight=2'";
     153  assertNoMatch: "node maxheight=5";
     154  assertMatch: "node roof:height=6.78 foot";
     155  assertMatch: "node roof:height=5  Feet";
     156  assertMatch: "node roof:height=2 '";
     157  assertNoMatch: "node roof:height=2'";
     158  assertNoMatch: "node roof:height=5";
     159  assertMatch: "node maxlength=6.78 foot";
     160  assertMatch: "node maxlength=5  Feet";
     161  assertMatch: "node maxlength=2 '";
     162  assertNoMatch: "node maxlength=2'";
     163  assertNoMatch: "node maxlength=5";
     164  assertMatch: "node width=6.78 foot";
     165  assertMatch: "node width=5  Feet";
     166  assertMatch: "node width=2 '";
     167  assertNoMatch: "node width=2'";
     168  assertNoMatch: "node width=5";
     169  assertMatch: "node maxwidth=6.78 foot";
     170  assertMatch: "node maxwidth=5  Feet";
     171  assertMatch: "node maxwidth=2 '";
     172  assertNoMatch: "node maxwidth=2'";
     173  assertNoMatch: "node maxwidth=5";
    89174}
    90 *[height][height =~ /^[0-9]+,[0-9][0-9]?( (m|ft))?$/] {
     175
     176/* 2. Convert `,` to `.` */
     177*[height][height            =~   /^[0-9]+,[0-9][0-9]?( m|\')?$/],
     178*[maxheight][maxheight      =~   /^[0-9]+,[0-9][0-9]?( m|\')?$/],
     179*[roof:height][roof:height  =~   /^[0-9]+,[0-9][0-9]?( m|\')?$/],
     180*[maxlength][maxlength      =~   /^[0-9]+,[0-9][0-9]?( m|\')?$/],
     181*[width][width              =~   /^[0-9]+,[0-9][0-9]?( m|\')?$/],
     182*[maxwidth][maxwidth        =~   /^[0-9]+,[0-9][0-9]?( m|\')?$/],
     183*[min_height][min_height    =~ /^-?[0-9]+,[0-9][0-9]?( m|\')?$/],
     184*[maxaxleload][maxaxleload  =~   /^[0-9]+,[0-9][0-9]?( (t|kg|st|lbs))?$/],
     185*[maxweight][maxweight      =~   /^[0-9]+,[0-9][0-9]?( (t|kg|st|lbs))?$/],
     186*[distance][distance        =~   /^[0-9]+,[0-9][0-9]?( (m|km|mi|nmi))?$/] {
    91187  throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
    92   fixAdd: concat("height=", replace(tag("height"), ",", "."));
    93   set height_separator_autofix;
     188  fixAdd: concat("{0.key}", "=", replace(tag("{0.key}"), ",", "."));
     189  set separator_autofix;
    94190  assertMatch: "node height=5,5";
    95191  assertMatch: "node height=12,00";
    96   assertMatch: "node height=12,5 ft";
     192  assertMatch: "node height=12,5'";
    97193  assertNoMatch: "node height=12,000";
    98194  assertNoMatch: "node height=3,50,5";
    99195  assertNoMatch: "node height=3.5";
    100196  assertNoMatch: "node height=4";
    101 }
    102 
    103 *[maxheight][maxheight =~ /^[1-9][0-9]*(\.[0-9]+)?(( )*(metre|metres|meter|meters|Metre|Metres|Meter|Meters)|m)$/] {
    104   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    105   set maxheight_meter_autofix;
    106   fixAdd: concat("maxheight=", get(regexp_match("([0-9.]+)( )*(.+)",tag("maxheight")),1)," m");
    107   assertMatch: "node maxheight=6.78 meters";
    108   assertMatch: "node maxheight=5  metre";
    109   assertMatch: "node maxheight=2m";
    110   assertNoMatch: "node maxheight=2 m";
    111   assertNoMatch: "node maxheight=5";
    112 }
    113 *[maxheight][maxheight =~ /^[0-9]+(\.[0-9]+)?(( )*(foot|Foot|feet|Feet)|ft)$/] {
    114   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    115   set maxheight_foot_autofix;
    116   fixAdd: concat("maxheight=", get(regexp_match("([0-9.]+)( )*(.+)",tag("maxheight")),1)," ft");
    117   assertMatch: "node maxheight=6.78 foot";
    118   assertMatch: "node maxheight=5  Feet";
    119   assertMatch: "node maxheight=2ft";
    120   assertNoMatch: "node maxheight=2 ft";
    121   assertNoMatch: "node maxheight=5";
    122 }
    123 *[maxheight][maxheight =~ /^[0-9]+,[0-9][0-9]?( (m|ft))?$/] {
    124   throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
    125   fixAdd: concat("maxheight=", replace(tag("maxheight"), ",", "."));
    126   set maxheight_separator_autofix;
    127197  assertMatch: "node maxheight=5,5";
    128198  assertMatch: "node maxheight=12,00";
    129   assertMatch: "node maxheight=12,5 ft";
     199  assertMatch: "node maxheight=12,5'";
    130200  assertNoMatch: "node maxheight=12,000";
    131201  assertNoMatch: "node maxheight=3,50,5";
    132202  assertNoMatch: "node maxheight=3.5";
    133203  assertNoMatch: "node maxheight=4";
    134 }
    135 
    136 *[roof:height][roof:height =~ /^0*(\.0*)?( (m|ft))?$/][roof:shape=flat] {
    137   throwWarning: tr("{0} is unnecessary for {1}", "{0.tag}", "{2.tag}");
    138   group: tr("unnecessary tag");
    139   fixRemove: "{0.key}";
    140   set zero_roof_height_flat;
    141   assertMatch: "node roof:height=0 roof:shape=flat";
    142   assertMatch: "node roof:shape=flat roof:height=\"00.00000 ft\" roof:shape=flat";
    143   assertNoMatch: "node roof:shape=flat roof:height=2 m roof:shape=flat";
    144   assertNoMatch: "node roof:height=0 roof:shape=gabled";
    145 }
    146 *[roof:height][roof:height =~ /^[0-9]+(\.[0-9]+)?(( )*(metre|metres|meter|meters|Metre|Metres|Meter|Meters)|m)$/]!.zero_roof_height_flat {
    147   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    148   set roof_height_meter_autofix;
    149   fixAdd: concat("roof:height=", get(regexp_match("([0-9.]+)( )*(.+)",tag("roof:height")),1)," m");
    150   assertMatch: "node roof:height=6.78 meters";
    151   assertMatch: "node roof:height=5  metre";
    152   assertMatch: "node roof:height=2m";
    153   assertNoMatch: "node roof:height=2 m";
    154   assertNoMatch: "node roof:height=5";
    155 }
    156 *[roof:height][roof:height =~ /^[0-9]+(\.[0-9]+)?(( )*(foot|Foot|feet|Feet)|ft)$/]!.zero_roof_height_flat {
    157   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    158   set roof_height_foot_autofix;
    159   fixAdd: concat("roof:height=", get(regexp_match("([0-9.]+)( )*(.+)",tag("roof:height")),1)," ft");
    160   assertMatch: "node roof:height=6.78 foot";
    161   assertMatch: "node roof:height=5  Feet";
    162   assertMatch: "node roof:height=2ft";
    163   assertNoMatch: "node roof:height=2 ft";
    164   assertNoMatch: "node roof:height=5";
    165 }
    166 *[roof:height][roof:height =~ /^[0-9]+,[0-9][0-9]?( (m|ft))?$/] {
    167   throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
    168   fixAdd: concat("roof:height=", replace(tag("roof:height"), ",", "."));
    169   set roof_height_separator_autofix;
    170204  assertMatch: "node roof:height=5,5";
    171205  assertMatch: "node roof:height=12,00";
    172   assertMatch: "node roof:height=12,5 ft";
     206  assertMatch: "node roof:height=12,5'";
    173207  assertNoMatch: "node roof:height=12,000";
    174208  assertNoMatch: "node roof:height=3,50,5";
    175209  assertNoMatch: "node roof:height=3.5";
    176210  assertNoMatch: "node roof:height=4";
    177 }
    178 
    179 *[maxlength][maxlength =~ /^[1-9][0-9]*(\.[0-9]+)?(( )*(metre|metres|meter|meters|Metre|Metres|Meter|Meters)|m)$/] {
    180   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    181   set maxlength_meter_autofix;
    182   fixAdd: concat("maxlength=", get(regexp_match("([0-9.]+)( )*(.+)",tag("maxlength")),1)," m");
    183   assertMatch: "node maxlength=6.78 meters";
    184   assertMatch: "node maxlength=5  metre";
    185   assertMatch: "node maxlength=2m";
    186   assertNoMatch: "node maxlength=2 m";
    187   assertNoMatch: "node maxlength=5";
    188 }
    189 *[maxlength][maxlength =~ /^[0-9]+(\.[0-9]+)?(( )*(foot|Foot|feet|Feet)|ft)$/] {
    190   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    191   set maxlength_foot_autofix;
    192   fixAdd: concat("maxlength=", get(regexp_match("([0-9.]+)( )*(.+)",tag("maxlength")),1)," ft");
    193   assertMatch: "node maxlength=6.78 foot";
    194   assertMatch: "node maxlength=5  Feet";
    195   assertMatch: "node maxlength=2ft";
    196   assertNoMatch: "node maxlength=2 ft";
    197   assertNoMatch: "node maxlength=5";
    198 }
    199 *[maxlength][maxlength =~ /^[0-9]+,[0-9][0-9]?( (m|ft))?$/] {
    200   throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
    201   fixAdd: concat("maxlength=", replace(tag("maxlength"), ",", "."));
    202   set maxlength_separator_autofix;
    203211  assertMatch: "node maxlength=5,5";
    204212  assertMatch: "node maxlength=12,00";
    205   assertMatch: "node maxlength=12,5 ft";
     213  assertMatch: "node maxlength=12,5'";
    206214  assertNoMatch: "node maxlength=12,000";
    207215  assertNoMatch: "node maxlength=3,50,5";
    208216  assertNoMatch: "node maxlength=3.5";
    209217  assertNoMatch: "node maxlength=4";
    210 }
    211 
    212 *[width][width =~ /^[0-9]+(\.[0-9]+)?(( )*(metre|metres|meter|meters|Metre|Metres|Meter|Meters)|m)$/] {
    213   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    214   set width_meter_autofix;
    215   fixAdd: concat("width=", get(regexp_match("([0-9.]+)( )*(.+)",tag("width")),1)," m");
    216   assertMatch: "node width=6.78 meters";
    217   assertMatch: "node width=5  metre";
    218   assertMatch: "node width=2m";
    219   assertNoMatch: "node width=2 m";
    220   assertNoMatch: "node width=5";
    221 }
    222 *[width][width =~ /^[0-9]+(\.[0-9]+)?(( )*(foot|Foot|feet|Feet)|ft)$/] {
    223   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    224   set width_foot_autofix;
    225   fixAdd: concat("width=", get(regexp_match("([0-9.]+)( )*(.+)",tag("width")),1)," ft");
    226   assertMatch: "node width=6.78 foot";
    227   assertMatch: "node width=5  Feet";
    228   assertMatch: "node width=2ft";
    229   assertNoMatch: "node width=2 ft";
    230   assertNoMatch: "node width=5";
    231 }
    232 *[width][width =~ /^[0-9]+,[0-9][0-9]?( (m|ft))?$/] {
    233   throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
    234   fixAdd: concat("width=", replace(tag("width"), ",", "."));
    235   set width_separator_autofix;
    236218  assertMatch: "node width=5,5";
    237219  assertMatch: "node width=12,00";
    238220  assertNoMatch: "node width=12,000";
    239221  assertNoMatch: "node width=3,50,5";
    240222  assertNoMatch: "node width=3.5";
    241223  assertNoMatch: "node width=4";
    242 }
    243 
    244 *[maxwidth][maxwidth=~ /^[0-9]+(\.[0-9]+)?(( )*(metre|metres|meter|meters|Metre|Metres|Meter|Meters)|m)$/] {
    245   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    246   set maxwidth_meter_autofix;
    247   fixAdd: concat("maxwidth=", get(regexp_match("([0-9.]+)( )*(.+)",tag("maxwidth")),1)," m");
    248   assertMatch: "node maxwidth=6.78 meters";
    249   assertMatch: "node maxwidth=5  metre";
    250   assertMatch: "node maxwidth=2m";
    251   assertNoMatch: "node maxwidth=2 m";
    252   assertNoMatch: "node maxwidth=5";
    253 }
    254 *[maxwidth][maxwidth =~ /^[0-9]+(\.[0-9]+)?(( )*(foot|Foot|feet|Feet)|ft)$/] {
    255   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    256   set maxwidth_foot_autofix;
    257   fixAdd: concat("maxwidth=", get(regexp_match("([0-9.]+)( )*(.+)",tag("maxwidth")),1)," ft");
    258   assertMatch: "node maxwidth=6.78 foot";
    259   assertMatch: "node maxwidth=5  Feet";
    260   assertMatch: "node maxwidth=2ft";
    261   assertNoMatch: "node maxwidth=2 ft";
    262   assertNoMatch: "node maxwidth=5";
    263 }
    264 *[maxwidth][maxwidth =~ /^[0-9]+,[0-9][0-9]?( (m|ft))?$/] {
    265   throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
    266   fixAdd: concat("maxwidth=", replace(tag("maxwidth"), ",", "."));
    267   set maxwidth_separator_autofix;
    268224  assertMatch: "node maxwidth=5,5";
    269225  assertMatch: "node maxwidth=12,00";
    270226  assertNoMatch: "node maxwidth=12,000";
    271227  assertNoMatch: "node maxwidth=3,50,5";
    272228  assertNoMatch: "node maxwidth=3.5";
    273229  assertNoMatch: "node maxwidth=4";
    274 }
    275 
    276 *[height     ][height      !~ /^(([0-9]+(\.[0-9]+)?( (m|ft))?)|([1-9][0-9]*\'((10|11|[0-9])((\.[0-9]+)?)\")?))$/]!.height_separator_autofix!.height_meter_autofix!.height_foot_autofix,
    277 *[maxheight  ][maxheight   !~ /^(([1-9][0-9]*(\.[0-9]+)?( (m|ft))?)|([0-9]+\'(([0-9]|10|11)(\.[0-9]*)?\")?)|none|default|below_default)$/]!.maxheight_separator_autofix!.maxheight_meter_autofix!.maxheight_foot_autofix,
    278 *[roof:height][roof:height !~ /^(([0-9]+(\.[0-9]+)?( (m|ft))?)|([1-9][0-9]*\'((10|11|[0-9])((\.[0-9]+)?)\")?))$/]!.roof_height_separator_autofix!.roof_height_meter_autofix!.roof_height_foot_autofix!.zero_roof_height_flat,
    279 *[maxlength  ][maxlength   !~ /^(([1-9][0-9]*(\.[0-9]+)?( (m|ft))?)|([0-9]+\'(([0-9]|10|11)(\.[0-9]*)?\")?)|none|default|below_default)$/]!.maxlength_separator_autofix!.maxlength_meter_autofix!.maxlength_foot_autofix,
    280 *[width      ][width       !~ /^(([0-9]+(\.[0-9]+)?( (m|ft))?)|([0-9]+\'([0-9]+(\.[0-9]+)?\")?))$/]!.width_separator_autofix!.width_meter_autofix!.width_foot_autofix,
    281 *[maxwidth   ][maxwidth    !~ /^(([0-9]+(\.[0-9]+)?( (m|ft))?)|([0-9]+\'([0-9]+(\.[0-9]+)?\")?))$/]!.maxwidth_separator_autofix!.maxwidth_meter_autofix!.maxwidth_foot_autofix {
    282   throwWarning: tr("unusual value of {0}: {1} is default; only positive values; point is decimal separator; if units, put space then unit", "{0.key}", tr("meters"));
    283   assertMatch: "node height=medium";
    284   assertMatch: "node maxheight=-5";
    285   assertMatch: "node maxlength=0";
    286   assertMatch: "node maxlength=10'13\"";
    287   assertMatch: "node width=10'2.\"";
    288   assertMatch: "node maxheight=\"2. m\"";
    289   assertMatch: "node height=\"12. m\"";
    290   assertNoMatch: "node height=6.78 meters";
    291   assertNoMatch: "node height=5  metre";
    292   assertNoMatch: "node height=2m";
    293   assertNoMatch: "node height=3";
    294   assertNoMatch: "node height=2.22 m";
    295   assertNoMatch: "node height=7.8";
    296   assertNoMatch: "node maxwidth=7 ft";
    297   assertNoMatch: "node height=22'";
    298   assertNoMatch: "node width=10'5\"";
    299   assertNoMatch: "node width=10'";
    300 }
    301 
    302 *[min_height][min_height =~ /^-?[0-9]+(\.[0-9]+)?(( )*(metre|metres|meter|meters|Metre|Metres|Meter|Meters)|m)$/] {
    303   throwWarning: tr("unusual value of {0}: use abbreviation for unit and space between value and unit", "{0.key}");
    304   fixAdd: concat("min_height=", get(regexp_match("(-?[0-9.]+)( )*(.+)",tag("min_height")),1)," m");
    305   set min_height_meter_autofix;
    306   assertMatch: "node min_height=6.78 meters";
    307   assertMatch: "node min_height=5  metre";
    308   assertMatch: "node min_height=2m";
    309   assertNoMatch: "node min_height=2 m";
    310   assertNoMatch: "node min_height=5";
    311 }
    312 *[min_height][min_height =~ /^-?[0-9]+,[0-9][0-9]?( m|\')?$/] {
    313   throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
    314   fixAdd: concat("min_height=", replace(tag("min_height"), ",", "."));
    315   set min_height_separator_autofix;
    316230  assertMatch: "node min_height=5,5";
    317231  assertMatch: "node min_height=12,00";
    318232  assertMatch: "node min_height=12,5'";
     
    320234  assertNoMatch: "node min_height=3,50,5";
    321235  assertNoMatch: "node min_height=3.5";
    322236  assertNoMatch: "node min_height=4";
    323 }
    324 *[min_height ][min_height  !~ /^(-?([0-9]+(\.[0-9]+)?( m)?)|(-?[1-9][0-9]*\'((10|11|[0-9])((\.[0-9]+)?)\")?))$/]!.min_height_separator_autofix!.min_height_meter_autofix!.min_height_foot_autofix {
    325   throwWarning: tr("unusual value of {0}: {1} is default; point is decimal separator; if units, put space then unit", "{0.key}", tr("meters"));
    326   assertMatch: "node min_height=\"12. m\"";
    327   assertNoMatch: "node min_height=-5";
    328 }
    329 
    330 *[maxaxleload][maxaxleload =~ /^[0-9]+,[0-9][0-9]?( (t|kg|st|lbs))?$/] {
    331   throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
    332   fixAdd: concat("maxaxleload=", replace(tag("maxaxleload"), ",", "."));
    333   set maxaxleload_separator_autofix;
    334237  assertMatch: "node maxaxleload=5,5";
    335238  assertMatch: "node maxaxleload=12,00";
    336239  assertNoMatch: "node maxaxleload=12,000";
    337240  assertNoMatch: "node maxaxleload=3,50,5";
    338241  assertNoMatch: "node maxaxleload=3.5";
    339242  assertNoMatch: "node maxaxleload=4";
    340 }
    341 
    342 *[maxweight][maxweight =~ /^[0-9]+,[0-9][0-9]?( (t|kg|st|lbs))?$/] {
    343   throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
    344   fixAdd: concat("maxweight=", replace(tag("maxweight"), ",", "."));
    345   set maxweight_separator_autofix;
    346243  assertMatch: "node maxweight=5,5";
    347244  assertMatch: "node maxweight=12,00";
    348245  assertNoMatch: "node maxweight=12,000";
    349246  assertNoMatch: "node maxweight=3,50,5";
    350247  assertNoMatch: "node maxweight=3.5";
    351248  assertNoMatch: "node maxweight=4";
     249  assertMatch: "node distance=5,5";
     250  assertMatch: "node distance=12,00";
     251  assertNoMatch: "node distance=12,000";
     252  assertNoMatch: "node distance=3,50,5";
     253  assertNoMatch: "node distance=3.5";
     254  assertNoMatch: "node distance=4";
     255}
     256/* 3. Convert to the default unit for usage later. Use the tag prefixed with _. */
     257/* 4. Start doing comparison checks. */
     258
     259*[height     ][height      !~ /^(([0-9]+(\.[0-9]+)?( m)?)|([1-9][0-9]*\'((10|11|[0-9])((\.[0-9]+)?)\")?))$/],
     260*[maxheight  ][maxheight   !~ /^(([1-9][0-9]*(\.[0-9]+)?( m)?)|([0-9]+\'(([0-9]|10|11)(\.[0-9]*)?\")?)|none|default|below_default)$/],
     261*[min_height ][min_height  !~ /^(-?([0-9]+(\.[0-9]+)?( m)?)|(-?[1-9][0-9]*\'((10|11|[0-9])((\.[0-9]+)?)\")?))$/],
     262*[roof:height][roof:height !~ /^(([0-9]+(\.[0-9]+)?( m)?)|([1-9][0-9]*\'((10|11|[0-9])((\.[0-9]+)?)\")?))$/]!.zero_roof_height_flat,
     263*[maxlength  ][maxlength   !~ /^(([1-9][0-9]*(\.[0-9]+)?( m)?)|([0-9]+\'(([0-9]|10|11)(\.[0-9]*)?\")?)|none|default|below_default)$/],
     264*[width      ][width       !~ /^(([0-9]+(\.[0-9]+)?( m)?)|([0-9]+\'([0-9]+(\.[0-9]+)?\")?))$/],
     265*[maxwidth   ][maxwidth    !~ /^(([0-9]+(\.[0-9]+)?( m)?)|([0-9]+\'([0-9]+(\.[0-9]+)?\")?))$/] {
     266  throwWarning: tr("unusual value of {0}: meters is default; only positive values; point is decimal separator; if units, put space then unit", "{0.key}");
     267  assertMatch: "node height=medium";
     268  assertMatch: "node maxheight=-5";
     269  assertMatch: "node maxlength=0";
     270  assertMatch: "node maxlength=10'13\"";
     271  assertMatch: "node width=10'2.\"";
     272  assertMatch: "node maxheight=\"2. m\"";
     273  assertMatch: "node height=\"12. m\"";
     274  assertNoMatch: "node height=6.78 m";
     275  assertNoMatch: "node height=5  m";
     276  assertNoMatch: "node height=2 m";
     277  assertNoMatch: "node height=3";
     278  assertNoMatch: "node height=2.22 m";
     279  assertNoMatch: "node height=7.8";
     280  assertMatch: "node min_height=\"12. m\"";
     281  assertNoMatch: "node min_height=-5";
     282  assertNoMatch: "node maxwidth=7'";
     283  assertNoMatch: "node height=22'";
     284  assertNoMatch: "node width=10'5\"";
     285  assertNoMatch: "node width=10'";
    352286}
    353287
    354 *[maxaxleload][maxaxleload !~ /^([0-9]+(\.[0-9]+)?( (t|kg|st|lbs))?)$/]!.maxaxleload_separator_autofix,
    355 *[maxweight][maxweight !~ /^([0-9]+(\.[0-9]+)?( (t|kg|st|lbs))?)$/]!.maxweight_separator_autofix {
     288*[maxaxleload][maxaxleload !~ /^([0-9]+(\.[0-9]+)?( (t|kg|st|lbs))?)$/],
     289*[maxweight][maxweight !~ /^([0-9]+(\.[0-9]+)?( (t|kg|st|lbs))?)$/] {
    356290  throwWarning: tr("unusual value of {0}: {1} is default; only positive values; point is decimal separator; if units, put space then unit", "{0.key}", tr("tonne"));
    357291  assertMatch: "node maxaxleload=something";
    358292  assertMatch: "node maxweight=-5";
     
    379313  assertNoMatch: "way maxspeed=variable";
    380314}
    381315
    382 *[distance][distance =~ /^[0-9]+,[0-9][0-9]?( (m|km|mi|nmi))?$/] {
    383   throwWarning: tr("unusual value of {0}: use . instead of , as decimal separator", "{0.key}");
    384   fixAdd: concat("distance=", replace(tag("distance"), ",", "."));
    385   set distance_separator_autofix;
    386   assertMatch: "node distance=5,5";
    387   assertMatch: "node distance=12,00";
    388   assertNoMatch: "node distance=12,000";
    389   assertNoMatch: "node distance=3,50,5";
    390   assertNoMatch: "node distance=3.5";
    391   assertNoMatch: "node distance=4";
    392 }
    393 *[distance][distance !~ /^(([0-9]+(\.[0-9]+)?( (m|km|mi|nmi))?)|([0-9]+\'([0-9]+(\.[0-9]+)?\")?))$/]!.distance_separator_autofix {
     316*[distance][distance !~ /^(([0-9]+(\.[0-9]+)?( (m|km|mi|nmi))?)|([0-9]+\'([0-9]+(\.[0-9]+)?\")?))$/] {
    394317  throwWarning: tr("unusual value of {0}: {1} is default; only positive values; point is decimal separator; if units, put space then unit", "{0.key}", tr("kilometers"));
    395318  assertMatch: "way distance=something";
    396319  assertMatch: "way distance=-5";
     
    421344  assertNoMatch: "way frequency=123.5 MHz";
    422345}
    423346
     347/*******************
     348 * End Unit checks *
     349 *******************/
     350
    424351way[gauge][gauge      =~ /^(broad|standard|narrow)$/],
    425352relation[gauge][gauge =~ /^(broad|standard|narrow)$/] {
    426353  throwWarning: tr("imprecise value of {0}", "{0.tag}");
     
    523450  assertMatch: "node direction=45-100;190-250;300-";
    524451  assertNoMatch: "node direction=45-100;190-250;300";
    525452  assertNoMatch: "node direction=90;270";
    526   assertNoMatch: "node direction=up"; 
     453  assertNoMatch: "node direction=up";
    527454  assertNoMatch: "node direction=down"; /* up/down are replaced by incline tag, has separate warning */
    528455  assertMatch: "node direction=-10";
    529456  assertNoMatch: "node direction=0";
  • src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java b/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
    a b  
    231231                }
    232232                final Selector selector = check.whichSelectorMatchesEnvironment(env);
    233233                if (selector != null) {
    234                     check.rule.declaration.execute(env);
     234                    final Environment envWithSelector = env.withSelector(selector);
     235                    check.rule.declaration.execute(envWithSelector);
    235236                    if (!ignoreError && !check.errors.isEmpty()) {
    236                         r.addAll(check.getErrorsForPrimitive(p, selector, env, new MapCSSTagCheckerAndRule(check.rule)));
     237                        r.addAll(check.getErrorsForPrimitive(p, selector, envWithSelector, new MapCSSTagCheckerAndRule(check.rule)));
    237238                    }
    238239                }
    239240            }
  • src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerAsserts.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerAsserts.java b/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerAsserts.java
    a b  
    7474                // Check that autofix works as expected
    7575                Command fix = check.fixPrimitive(p);
    7676                if (fix != null && fix.executeCommand() && !MapCSSTagChecker.getErrorsForPrimitive(p, true, checksToRun).isEmpty()) {
    77                     assertionConsumer.accept(MessageFormat.format("Autofix does not work for test ''{0}'' (i.e., {1})",
    78                             check.getMessage(p), check.rule.selectors));
     77                    assertionConsumer.accept(MessageFormat.format("Autofix does not work for test ''{0}'' (i.e., {1}). Failing test: {2}",
     78                            check.getMessage(p), check.rule.selectors, i.getKey()));
    7979                }
    8080            }
    8181            ds.removePrimitive(p);
  • src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerFixCommand.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerFixCommand.java b/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerFixCommand.java
    a b  
    4848    static String evaluateObject(final Object obj, final OsmPrimitive p, final Selector matchingSelector) {
    4949        final String s;
    5050        if (obj instanceof Expression) {
    51             s = (String) ((Expression) obj).evaluate(new Environment(p));
     51            s = (String) ((Expression) obj).evaluate(new Environment(p).withSelector(matchingSelector));
    5252        } else if (obj instanceof String) {
    5353            s = (String) obj;
    5454        } else {
  • src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerRule.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerRule.java b/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerRule.java
    a b  
    1717import java.util.Set;
    1818import java.util.function.Consumer;
    1919import java.util.function.Predicate;
    20 import java.util.regex.Matcher;
    21 import java.util.regex.Pattern;
    2220import java.util.stream.Collectors;
    2321
    2422import org.openstreetmap.josm.command.Command;
     
    2624import org.openstreetmap.josm.command.SequenceCommand;
    2725import org.openstreetmap.josm.data.osm.IPrimitive;
    2826import org.openstreetmap.josm.data.osm.OsmPrimitive;
    29 import org.openstreetmap.josm.data.osm.Tag;
    3027import org.openstreetmap.josm.data.osm.Way;
    3128import org.openstreetmap.josm.data.osm.WaySegment;
    3229import org.openstreetmap.josm.data.validation.Severity;
     
    3431import org.openstreetmap.josm.data.validation.TestError;
    3532import org.openstreetmap.josm.gui.mappaint.Environment;
    3633import org.openstreetmap.josm.gui.mappaint.Keyword;
     34import org.openstreetmap.josm.gui.mappaint.MultiCascade;
    3735import org.openstreetmap.josm.gui.mappaint.mapcss.Condition;
    38 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.TagCondition;
    3936import org.openstreetmap.josm.gui.mappaint.mapcss.Expression;
    4037import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
    4138import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
    4239import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
     40import org.openstreetmap.josm.gui.mappaint.mapcss.PlaceholderExpression;
    4341import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
    4442import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser;
    4543import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
     
    214212    }
    215213
    216214    Selector whichSelectorMatchesPrimitive(OsmPrimitive primitive) {
    217         return whichSelectorMatchesEnvironment(new Environment(primitive));
     215        return whichSelectorMatchesEnvironment(new Environment(primitive, new MultiCascade(), Environment.DEFAULT_LAYER, null));
    218216    }
    219217
    220218    Selector whichSelectorMatchesEnvironment(Environment env) {
     
    224222                .orElse(null);
    225223    }
    226224
    227     /**
    228      * Determines the {@code index}-th key/value/tag (depending on {@code type}) of the
    229      * {@link org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector}.
    230      *
    231      * @param matchingSelector matching selector
    232      * @param index            index
    233      * @param type             selector type ("key", "value" or "tag")
    234      * @param p                OSM primitive
    235      * @return argument value, can be {@code null}
    236      */
    237     static String determineArgument(Selector.GeneralSelector matchingSelector, int index, String type, OsmPrimitive p) {
    238         try {
    239             final Condition c = matchingSelector.getConditions().get(index);
    240             final Tag tag = c instanceof TagCondition
    241                     ? ((TagCondition) c).asTag(p)
    242                     : null;
    243             if (tag == null) {
    244                 return null;
    245             } else if ("key".equals(type)) {
    246                 return tag.getKey();
    247             } else if ("value".equals(type)) {
    248                 return tag.getValue();
    249             } else if ("tag".equals(type)) {
    250                 return tag.toString();
    251             }
    252         } catch (IndexOutOfBoundsException ignore) {
    253             Logging.debug(ignore);
    254         }
    255         return null;
    256     }
    257 
    258225    /**
    259226     * Replaces occurrences of <code>{i.key}</code>, <code>{i.value}</code>, <code>{i.tag}</code> in {@code s} by the corresponding
    260227     * key/value/tag of the {@code index}-th {@link Condition} of {@code matchingSelector}.
     
    265232     * @return string with arguments inserted
    266233     */
    267234    static String insertArguments(Selector matchingSelector, String s, OsmPrimitive p) {
    268         if (s != null && matchingSelector instanceof Selector.ChildOrParentSelector) {
    269             return insertArguments(((Selector.ChildOrParentSelector) matchingSelector).right, s, p);
    270         } else if (s == null || !(matchingSelector instanceof Selector.GeneralSelector)) {
    271             return s;
    272         }
    273         final Matcher m = Pattern.compile("\\{(\\d+)\\.(key|value|tag)\\}").matcher(s);
    274         final StringBuffer sb = new StringBuffer();
    275         while (m.find()) {
    276             final String argument = determineArgument((Selector.GeneralSelector) matchingSelector,
    277                     Integer.parseInt(m.group(1)), m.group(2), p);
    278             try {
    279                 // Perform replacement with null-safe + regex-safe handling
    280                 m.appendReplacement(sb, String.valueOf(argument).replace("^(", "").replace(")$", ""));
    281             } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
    282                 Logging.log(Logging.LEVEL_ERROR, tr("Unable to replace argument {0} in {1}: {2}", argument, sb, e.getMessage()), e);
    283             }
    284         }
    285         m.appendTail(sb);
    286         return sb.toString();
     235        return PlaceholderExpression.insertArguments(matchingSelector, s, p);
    287236    }
    288237
    289238    /**
     
    328277            final Object val = errors.keySet().iterator().next().val;
    329278            return String.valueOf(
    330279                    val instanceof Expression
    331                             ? ((Expression) val).evaluate(new Environment(p))
     280                            ? ((Expression) val).evaluate(new Environment(p).withSelector(p == null ? null : whichSelectorMatchesPrimitive(p)))
    332281                            : val
    333282            );
    334283        }
  • src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj b/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj
    a b  
    3131import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSException;
    3232import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
    3333import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
     34import org.openstreetmap.josm.gui.mappaint.mapcss.PlaceholderExpression;
    3435import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
    3536import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.ChildOrParentSelector;
    3637import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
     
    10521053        {
    10531054            if (lit == null)
    10541055                return NullExpression.INSTANCE;
     1056            else if (lit instanceof String && PlaceholderExpression.PATTERN_PLACEHOLDER.matcher((String) lit).find()) {
     1057                return new PlaceholderExpression((String) lit);
     1058            }
    10551059            return new LiteralExpression(lit);
    10561060        }
    10571061    |
  • new file src/org/openstreetmap/josm/gui/mappaint/mapcss/PlaceholderExpression.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/PlaceholderExpression.java b/src/org/openstreetmap/josm/gui/mappaint/mapcss/PlaceholderExpression.java
    new file mode 100644
    - +  
     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.util.regex.Matcher;
     7import java.util.regex.Pattern;
     8
     9import org.openstreetmap.josm.data.osm.Tag;
     10import org.openstreetmap.josm.data.osm.Tagged;
     11import org.openstreetmap.josm.gui.mappaint.Environment;
     12import org.openstreetmap.josm.tools.CheckParameterUtil;
     13import org.openstreetmap.josm.tools.Logging;
     14
     15/**
     16 * Used for expressions that contain placeholders
     17 * @since xxx
     18 */
     19public final class PlaceholderExpression implements Expression {
     20    /**
     21     * The regex used for pattern replacement
     22     */
     23    public static final Pattern PATTERN_PLACEHOLDER = Pattern.compile("\\{(\\d+)\\.(key|value|tag)}");
     24    private final String placeholder;
     25
     26    /**
     27     * Constructs a new {@link PlaceholderExpression}.
     28     * @param placeholder The placeholder expression
     29     */
     30    public PlaceholderExpression(String placeholder) {
     31        CheckParameterUtil.ensureParameterNotNull(placeholder);
     32        this.placeholder = placeholder.intern();
     33    }
     34
     35    @Override
     36    public Object evaluate(Environment env) {
     37        if (env.selector() == null) {
     38            return placeholder;
     39        }
     40        return insertArguments(env.selector(), placeholder, env.osm);
     41    }
     42
     43    /**
     44     * Replaces occurrences of <code>{i.key}</code>, <code>{i.value}</code>, <code>{i.tag}</code> in {@code s} by the corresponding
     45     * key/value/tag of the {@code index}-th {@link Condition} of {@code matchingSelector}.
     46     *
     47     * @param matchingSelector matching selector
     48     * @param s                any string
     49     * @param p                OSM primitive
     50     * @return string with arguments inserted
     51     */
     52    public static String insertArguments(Selector matchingSelector, String s, Tagged p) {
     53        if (s != null && matchingSelector instanceof Selector.ChildOrParentSelector) {
     54            return insertArguments(((Selector.ChildOrParentSelector) matchingSelector).right, s, p);
     55        } else if (s == null || !(matchingSelector instanceof Selector.GeneralSelector)) {
     56            return s;
     57        }
     58        final Matcher m = PATTERN_PLACEHOLDER.matcher(s);
     59        final StringBuffer sb = new StringBuffer();
     60        while (m.find()) {
     61            final String argument = determineArgument(matchingSelector,
     62                    Integer.parseInt(m.group(1)), m.group(2), p);
     63            try {
     64                // Perform replacement with null-safe + regex-safe handling
     65                m.appendReplacement(sb, String.valueOf(argument).replace("^(", "").replace(")$", ""));
     66            } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
     67                Logging.log(Logging.LEVEL_ERROR, tr("Unable to replace argument {0} in {1}: {2}", argument, sb, e.getMessage()), e);
     68            }
     69        }
     70        m.appendTail(sb);
     71        return sb.toString();
     72    }
     73
     74    /**
     75     * Determines the {@code index}-th key/value/tag (depending on {@code type}) of the
     76     * {@link org.openstreetmap.josm.gui.mappaint.mapcss.Selector}.
     77     *
     78     * @param matchingSelector matching selector
     79     * @param index            index
     80     * @param type             selector type ("key", "value" or "tag")
     81     * @param p                OSM primitive
     82     * @return argument value, can be {@code null}
     83     */
     84    private static String determineArgument(Selector matchingSelector, int index, String type, Tagged p) {
     85        try {
     86            final Condition c = matchingSelector.getConditions().get(index);
     87            final Tag tag = c instanceof Condition.TagCondition
     88                    ? ((Condition.TagCondition) c).asTag(p)
     89                    : null;
     90            if (tag == null) {
     91                return null;
     92            } else if ("key".equals(type)) {
     93                return tag.getKey();
     94            } else if ("value".equals(type)) {
     95                return tag.getValue();
     96            } else if ("tag".equals(type)) {
     97                return tag.toString();
     98            }
     99        } catch (IndexOutOfBoundsException ioobe) {
     100            Logging.debug(ioobe);
     101        }
     102        return null;
     103    }
     104
     105    @Override
     106    public String toString() {
     107        return '<' + placeholder + '>';
     108    }
     109}
  • src/org/openstreetmap/josm/gui/mappaint/Environment.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/gui/mappaint/Environment.java b/src/org/openstreetmap/josm/gui/mappaint/Environment.java
    a b  
    1212import org.openstreetmap.josm.data.osm.Way;
    1313import org.openstreetmap.josm.data.osm.WaySegment;
    1414import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Context;
     15import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
    1516import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.LinkSelector;
    1617import org.openstreetmap.josm.tools.CheckParameterUtil;
    1718
     
    4142    public StyleSource source;
    4243    private Context context = Context.PRIMITIVE;
    4344
     45    /** The selector that is currently being evaluated */
     46    private final Selector selector;
     47
    4448    /**
    4549     * The name of the default layer. It is used if no layer is specified in the MapCSS rule
    4650     */
     
    97101     */
    98102    public Environment() {
    99103        // environment can be initialized later through with* methods
     104        this.selector = null;
    100105    }
    101106
    102107    /**
     
    106111     * @since 13810 (signature)
    107112     */
    108113    public Environment(IPrimitive osm) {
    109         this.osm = osm;
     114        this(osm, null, null, null);
    110115    }
    111116
    112117    /**
     
    122127        this.mc = mc;
    123128        this.layer = layer;
    124129        this.source = source;
     130        this.selector = null;
    125131    }
    126132
    127133    /**
     
    131137     * @throws IllegalArgumentException if {@code param} is {@code null}
    132138     */
    133139    public Environment(Environment other) {
     140        this(other, other.selector);
     141    }
     142
     143    /**
     144     * Creates a clone of the environment {@code other}.
     145     *
     146     * @param other the other environment. Must not be null.
     147     * @param selector the selector for this environment. May be null.
     148     * @throws IllegalArgumentException if {@code param} is {@code null}
     149     */
     150    private Environment(Environment other, Selector selector) {
    134151        CheckParameterUtil.ensureParameterNotNull(other);
    135152        this.osm = other.osm;
    136153        this.mc = other.mc;
     
    146163        this.crossingWaysMap = other.crossingWaysMap;
    147164        this.mpAreaCache = other.mpAreaCache;
    148165        this.toMatchForSurrounding = other.toMatchForSurrounding;
     166        this.selector = selector;
    149167    }
    150168
    151169    /**
     
    262280        return e;
    263281    }
    264282
     283    /**
     284     * Creates a clone of this environment, with the selector set
     285     * @param selector The selector to use
     286     * @return A clone of this environment, with the specified selector
     287     * @since xxx
     288     */
     289    public Environment withSelector(Selector selector) {
     290        return new Environment(this, selector);
     291    }
     292
    265293    /**
    266294     * Determines if the context of this environment is {@link Context#LINK}.
    267295     * @return {@code true} if the context of this environment is {@code Context#LINK}, {@code false} otherwise
     
    303331        return null;
    304332    }
    305333
     334    /**
     335     * Get the selector for this environment
     336     * @return The selector. May be {@code null}.
     337     * @since xxx
     338     */
     339    public Selector selector() {
     340        return this.selector;
     341    }
     342
    306343    /**
    307344     * Clears all matching context information
    308345     * @return this
  • test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java b/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java
    a b  
    9595        final MapCSSTagCheckerRule check = checks.get(0);
    9696        assertNotNull(check);
    9797        assertEquals("{0.key}=null is deprecated", check.getDescription(null));
    98         assertEquals("fixRemove: {0.key}", check.fixCommands.get(0).toString());
     98        assertEquals("fixRemove: <{0.key}>", check.fixCommands.get(0).toString());
    9999        assertEquals("fixAdd: natural=wetland", check.fixCommands.get(1).toString());
    100100        assertEquals("fixAdd: wetland=marsh", check.fixCommands.get(2).toString());
    101101        final OsmPrimitive n1 = OsmUtils.createPrimitive("node natural=marsh");