Changeset 16086 in josm
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/tests/ConditionalKeys.java
r15978 r16086 9 9 import java.util.HashSet; 10 10 import java.util.List; 11 import java.util.Locale;12 11 import java.util.Set; 13 12 import java.util.regex.Matcher; … … 204 203 for (final String condition : conditional.conditions) { 205 204 if (condition.matches(".*[0-9]:[0-9]{2}.*")) { 206 final List<OpeningHourTest.OpeningHoursTestError> errors = openingHourTest.checkOpeningHourSyntax( 207 "", condition, true, Locale.getDefault()); 205 final List<TestError> errors = openingHourTest.checkOpeningHourSyntax("", condition); 208 206 if (!errors.isEmpty()) { 209 return errors.get(0).get Message();207 return errors.get(0).getDescription(); 210 208 } 211 209 } -
trunk/src/org/openstreetmap/josm/data/validation/tests/OpeningHourTest.java
r16085 r16086 5 5 6 6 import java.io.StringReader; 7 import java.util.Arrays; 8 import java.util.Collection; 7 9 import java.util.Collections; 8 10 import java.util.List; … … 29 31 public class OpeningHourTest extends TagTest { 30 32 33 private static final Collection<String> KEYS_TO_CHECK = Arrays.asList("opening_hours", "collection_times", "service_times"); 34 31 35 /** 32 36 * Constructs a new {@code OpeningHourTest}. … … 38 42 39 43 /** 40 * An error concerning invalid syntax for an "opening_hours"-like tag. 44 * Returns the real test error given to JOSM validator. 45 * @param severity The error severity 46 * @param message The error message 47 * @param key The incriminated key, used for display. 48 * @param prettifiedValue The prettified value 49 * @param p The incriminated OSM primitive. 50 * @return The real test error given to JOSM validator. Can be fixable or not if a prettified values has been determined. 41 51 */ 42 public class OpeningHoursTestError { 43 private final Severity severity; 44 private final String message; 45 private final String prettifiedValue; 46 47 /** 48 * Constructs a new {@code OpeningHoursTestError} with a known prettified value. 49 * @param message The error message 50 * @param severity The error severity 51 * @param prettifiedValue The prettified value 52 */ 53 public OpeningHoursTestError(String message, Severity severity, String prettifiedValue) { 54 this.message = message; 55 this.severity = severity; 56 this.prettifiedValue = prettifiedValue; 57 } 58 59 /** 60 * Returns the real test error given to JOSM validator. 61 * @param p The incriminated OSM primitive. 62 * @param key The incriminated key, used for display. 63 * @return The real test error given to JOSM validator. Can be fixable or not if a prettified values has been determined. 64 */ 65 public TestError getTestError(final OsmPrimitive p, final String key) { 66 final TestError.Builder error = TestError.builder(OpeningHourTest.this, severity, 2901) 67 .message(tr("Opening hours syntax"), message) // todo obtain English message for ignore functionality 68 .primitives(p); 69 if (prettifiedValue == null || prettifiedValue.equals(p.get(key))) { 70 return error.build(); 71 } else { 72 return error.fix(() -> new ChangePropertyCommand(p, key, prettifiedValue)).build(); 73 } 74 } 75 76 /** 77 * Returns the error message. 78 * @return The error message. 79 */ 80 public String getMessage() { 81 return message; 82 } 83 84 /** 85 * Returns the prettified value. 86 * @return The prettified value. 87 */ 88 public String getPrettifiedValue() { 89 return prettifiedValue; 90 } 91 92 /** 93 * Returns the error severity. 94 * @return The error severity. 95 */ 96 public Severity getSeverity() { 97 return severity; 98 } 99 100 @Override 101 public String toString() { 102 return getMessage() + " => " + getPrettifiedValue(); 52 private TestError createTestError(Severity severity, String message, String key, String prettifiedValue, OsmPrimitive p) { 53 final TestError.Builder error = TestError.builder(this, severity, 2901) 54 .message(tr("Opening hours syntax"), message) // todo obtain English message for ignore functionality 55 .primitives(p); 56 if (prettifiedValue == null || prettifiedValue.equals(p.get(key))) { 57 return error.build(); 58 } else { 59 return error.fix(() -> new ChangePropertyCommand(p, key, prettifiedValue)).build(); 103 60 } 104 61 } … … 111 68 * @return a list of {@link TestError} or an empty list 112 69 */ 113 public List< OpeningHoursTestError> checkOpeningHourSyntax(final String key, final String value) {114 return checkOpeningHourSyntax(key, value, false, Locale.getDefault());70 public List<TestError> checkOpeningHourSyntax(final String key, final String value) { 71 return checkOpeningHourSyntax(key, value, null, Locale.getDefault()); 115 72 } 116 73 … … 120 77 * @param key the OSM key (should be "opening_hours", "collection_times" or "service_times"). 121 78 * @param value the opening hour value to be checked. 122 * @param ignoreOtherSeverity whether to ignore errors with {@link Severity#OTHER}.79 * @param p the primitive to check/fix. 123 80 * @param locale the locale code used for localizing messages 124 81 * @return a list of {@link TestError} or an empty list 125 82 */ 126 public List<OpeningHoursTestError> checkOpeningHourSyntax(final String key, final String value,boolean ignoreOtherSeverity, Locale locale) {83 List<TestError> checkOpeningHourSyntax(final String key, final String value, OsmPrimitive p, Locale locale) { 127 84 if (value == null || value.isEmpty()) { 128 85 return Collections.emptyList(); … … 139 96 } 140 97 } catch (ParseException e) { 141 return Collections.singletonList( new OpeningHoursTestError(e.getMessage(),Severity.WARNING, prettifiedValue));98 return Collections.singletonList(createTestError(Severity.WARNING, e.getMessage(), key, prettifiedValue, p)); 142 99 } 143 100 144 if ( ignoreOtherSeverity || Objects.equals(value, prettifiedValue)) {101 if (!includeOtherSeverityChecks() || Objects.equals(value, prettifiedValue) || p == null) { 145 102 return Collections.emptyList(); 146 103 } else { 147 return Collections.singletonList( 148 new OpeningHoursTestError(tr("{0} value can be prettified", key), Severity.OTHER, prettifiedValue)); 149 } 150 } 151 152 protected void check(final OsmPrimitive p, final String key) { 153 for (OpeningHoursTestError e : checkOpeningHourSyntax(key, p.get(key))) { 154 errors.add(e.getTestError(p, key)); 104 final String message = tr("{0} value can be prettified", key); 105 return Collections.singletonList(createTestError(Severity.OTHER, message, key, prettifiedValue, p)); 155 106 } 156 107 } … … 158 109 @Override 159 110 public void check(final OsmPrimitive p) { 160 check(p, "opening_hours");161 check(p, "collection_times");162 check(p, "service_times");111 for (String key : KEYS_TO_CHECK) { 112 errors.addAll(checkOpeningHourSyntax(key, p.get(key), p, Locale.getDefault())); 113 } 163 114 } 164 115 } -
trunk/test/unit/org/openstreetmap/josm/data/validation/tests/OpeningHourTestTest.java
r15978 r16086 6 6 import static org.hamcrest.CoreMatchers.not; 7 7 import static org.junit.Assert.assertEquals; 8 import static org.junit.Assert.assertNotNull; 8 9 import static org.junit.Assert.assertThat; 9 10 import static org.junit.Assert.assertTrue; … … 19 20 import org.junit.Rule; 20 21 import org.junit.Test; 22 import org.openstreetmap.josm.command.ChangePropertyCommand; 23 import org.openstreetmap.josm.data.coor.LatLon; 24 import org.openstreetmap.josm.data.osm.DataSet; 25 import org.openstreetmap.josm.data.osm.Node; 21 26 import org.openstreetmap.josm.data.osm.Tag; 27 import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper; 22 28 import org.openstreetmap.josm.data.validation.Severity; 29 import org.openstreetmap.josm.data.validation.TestError; 23 30 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset; 24 31 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItem; … … 36 43 public class OpeningHourTestTest { 37 44 /** 38 * We need prefs for this. We check strings so we need i18n. 45 * We need preferences for this. We check strings so we need i18n. 39 46 */ 40 47 @Rule … … 52 59 openingHourTest = new OpeningHourTest(); 53 60 openingHourTest.initialize(); 61 ValidatorPrefHelper.PREF_OTHER.put(true); 54 62 } 55 63 … … 61 69 final String key = "opening_hours"; 62 70 // frequently used tags according to https://taginfo.openstreetmap.org/keys/opening_hours#values 63 assertThat( openingHourTest.checkOpeningHourSyntax(key, "24/7"), isEmpty());64 assertThat( openingHourTest.checkOpeningHourSyntax(key, "Mo-Fr 08:30-20:00"), isEmpty());65 assertThat( openingHourTest.checkOpeningHourSyntax(key, "Mo-Fr sunrise-sunset"), isEmpty());66 assertThat( openingHourTest.checkOpeningHourSyntax(key, "09:00-21:00"), isEmpty());67 assertThat( openingHourTest.checkOpeningHourSyntax(key, "Su-Th sunset-24:00,04:00-sunrise; Fr-Sa sunset-sunrise"), isEmpty());71 assertThat(checkOpeningHourSyntax(key, "24/7"), isEmpty()); 72 assertThat(checkOpeningHourSyntax(key, "Mo-Fr 08:30-20:00"), isEmpty()); 73 assertThat(checkOpeningHourSyntax(key, "Mo-Fr sunrise-sunset"), isEmpty()); 74 assertThat(checkOpeningHourSyntax(key, "09:00-21:00"), isEmpty()); 75 assertThat(checkOpeningHourSyntax(key, "Su-Th sunset-24:00,04:00-sunrise; Fr-Sa sunset-sunrise"), isEmpty()); 68 76 } 69 77 … … 75 83 final String key = "opening_hours"; 76 84 String value = "."; 77 assertEquals("Vorgefunden wurde: \".\" \". \" in Zeile 0, Zeichen 0\nErwartet wurde: <EOF> => null",78 openingHourTest.checkOpeningHourSyntax(key, value,false,Locale.GERMAN).get(0).toString());79 assertEquals("Encountered: \".\" \". \" at line 0, column 0\nWas expecting: <EOF> => null",80 openingHourTest.checkOpeningHourSyntax(key, value,false,Locale.ENGLISH).get(0).toString());85 assertEquals("Vorgefunden wurde: \".\" \". \" in Zeile 0, Zeichen 0\nErwartet wurde: <EOF>", 86 checkOpeningHourSyntax(key, value, Locale.GERMAN).get(0).getDescription()); 87 assertEquals("Encountered: \".\" \". \" at line 0, column 0\nWas expecting: <EOF>", 88 checkOpeningHourSyntax(key, value, Locale.ENGLISH).get(0).getDescription()); 81 89 value = "Mon-Thu 12-18"; 82 assertEquals("Wochentag mit 3 Buchstaben in Zeile 1, Zeichen 4 => Mo-Th 12:00-18:00",83 openingHourTest.checkOpeningHourSyntax(key, value,false,Locale.GERMAN).get(0).toString());84 assertEquals("Three character weekday at line 1, column 4 => Mo-Th 12:00-18:00",85 openingHourTest.checkOpeningHourSyntax(key, value,false,Locale.ENGLISH).get(0).toString());90 assertEquals("Wochentag mit 3 Buchstaben in Zeile 1, Zeichen 4", 91 checkOpeningHourSyntax(key, value, Locale.GERMAN).get(0).getDescription()); 92 assertEquals("Three character weekday at line 1, column 4", 93 checkOpeningHourSyntax(key, value, Locale.ENGLISH).get(0).getDescription()); 86 94 } 87 95 … … 92 100 public void testCheckOpeningHourSyntax2() { 93 101 final String key = "opening_hours"; 94 final List< OpeningHourTest.OpeningHoursTestError> errors =openingHourTest.checkOpeningHourSyntax(key, "Mo-Tue");102 final List<TestError> errors = checkOpeningHourSyntax(key, "Mo-Tue"); 95 103 assertThat(errors, hasSize(1)); 96 assertEquals("Mo-Tu", errors.get(0 ).getPrettifiedValue());97 assertEquals("Three character weekday at line 1, column 6", errors.get(0).get Message());104 assertFixEquals("Mo-Tu", errors.get(0)); 105 assertEquals("Three character weekday at line 1, column 6", errors.get(0).getDescription()); 98 106 assertEquals(Severity.WARNING, errors.get(0).getSeverity()); 99 107 } … … 105 113 public void testCheckOpeningHourSyntax3() { 106 114 final String key = "opening_hours"; 107 final List< OpeningHourTest.OpeningHoursTestError> errors =openingHourTest.checkOpeningHourSyntax(key, "Sa-Su 10.00-20.00");115 final List<TestError> errors = checkOpeningHourSyntax(key, "Sa-Su 10.00-20.00"); 108 116 assertThat(errors, hasSize(1)); 109 assertEquals("Sa-Su 10:00-20:00", errors.get(0 ).getPrettifiedValue());110 assertEquals("Invalid minutes at line 1, column 12", errors.get(0).get Message());117 assertFixEquals("Sa-Su 10:00-20:00", errors.get(0)); 118 assertEquals("Invalid minutes at line 1, column 12", errors.get(0).getDescription()); 111 119 assertEquals(Severity.WARNING, errors.get(0).getSeverity()); 112 120 } … … 117 125 @Test 118 126 public void testCheckOpeningHourSyntax4() { 119 assertThat( openingHourTest.checkOpeningHourSyntax(null, null), isEmpty());120 assertThat( openingHourTest.checkOpeningHourSyntax(null, ""), isEmpty());127 assertThat(checkOpeningHourSyntax(null, null), isEmpty()); 128 assertThat(checkOpeningHourSyntax(null, ""), isEmpty()); 121 129 assertEquals("opening_hours value can be prettified", 122 openingHourTest.checkOpeningHourSyntax("opening_hours", " ").get(0).getMessage());130 checkOpeningHourSyntax("opening_hours", " ").get(0).getDescription()); 123 131 assertEquals("null value can be prettified", 124 openingHourTest.checkOpeningHourSyntax(null, " ").get(0).getMessage());132 checkOpeningHourSyntax(null, " ").get(0).getDescription()); 125 133 } 126 134 … … 131 139 public void testCheckOpeningHourSyntax5() { 132 140 final String key = "opening_hours"; 133 assertThat( openingHourTest.checkOpeningHourSyntax(key, "badtext"), hasSize(1));141 assertThat(checkOpeningHourSyntax(key, "badtext"), hasSize(1)); 134 142 assertEquals("Encountered: <UNEXPECTED_CHAR> \"b \" at line 0, column 0\nWas expecting: <EOF>", 135 openingHourTest.checkOpeningHourSyntax(key, "badtext").get(0).getMessage().trim());136 assertThat( openingHourTest.checkOpeningHourSyntax(key, "5.00 p.m-11.00 p.m"), hasSize(1));143 checkOpeningHourSyntax(key, "badtext").get(0).getDescription().trim()); 144 assertThat(checkOpeningHourSyntax(key, "5.00 p.m-11.00 p.m"), hasSize(1)); 137 145 assertEquals("Encountered: <UNEXPECTED_CHAR> \"p \" at line 1, column 2\nWas expecting: <EOF>", 138 openingHourTest.checkOpeningHourSyntax(key, "5.00 p.m-11.00 p.m").get(0).getMessage());146 checkOpeningHourSyntax(key, "5.00 p.m-11.00 p.m").get(0).getDescription()); 139 147 } 140 148 … … 145 153 public void testCheckOpeningHourSyntax6() { 146 154 final String key = "opening_hours"; 147 assertThat( openingHourTest.checkOpeningHourSyntax(key, "PH open \"always open on public holidays\""), isEmpty());155 assertThat(checkOpeningHourSyntax(key, "PH open \"always open on public holidays\""), isEmpty()); 148 156 } 149 157 … … 154 162 public void testCheckOpeningHourSyntax7() { 155 163 final String key = "opening_hours"; 156 assertThat(openingHourTest.checkOpeningHourSyntax(key, "9:00-18:00", true, Locale.getDefault()), isEmpty()); 157 assertThat(openingHourTest.checkOpeningHourSyntax(key, "9:00-18:00"), hasSize(1)); 158 assertEquals(Severity.OTHER, openingHourTest.checkOpeningHourSyntax(key, "9:00-18:00").get(0).getSeverity()); 159 assertEquals("09:00-18:00", openingHourTest.checkOpeningHourSyntax(key, "9:00-18:00").get(0).getPrettifiedValue()); 164 assertThat(checkOpeningHourSyntax(key, "9:00-18:00"), hasSize(1)); 165 assertEquals(Severity.OTHER, checkOpeningHourSyntax(key, "9:00-18:00").get(0).getSeverity()); 166 assertFixEquals("09:00-18:00", checkOpeningHourSyntax(key, "9:00-18:00").get(0)); 160 167 } 161 168 … … 166 173 public void testCheckOpeningHourSyntaxTicket9367() { 167 174 final String key = "opening_hours"; 168 assertEquals(Severity.WARNING, openingHourTest.checkOpeningHourSyntax(key, "Mo,Tu 04-17").get(0).getSeverity());175 assertEquals(Severity.WARNING, checkOpeningHourSyntax(key, "Mo,Tu 04-17").get(0).getSeverity()); 169 176 assertEquals("Hours without minutes", 170 openingHourTest.checkOpeningHourSyntax(key, "Mo,Tu 04-17").get(0).getMessage());177 checkOpeningHourSyntax(key, "Mo,Tu 04-17").get(0).getDescription()); 171 178 } 172 179 … … 178 185 final String key = "service_times"; 179 186 // frequently used tags according to https://taginfo.openstreetmap.org/keys/service_times#values 180 assertThat( openingHourTest.checkOpeningHourSyntax(key, "Su 10:00"), isEmpty());181 assertThat( openingHourTest.checkOpeningHourSyntax(key, "automatic"), not(isEmpty()));182 assertThat( openingHourTest.checkOpeningHourSyntax(key, "Mo-Sa 09:00-18:00"), isEmpty());183 assertThat( openingHourTest.checkOpeningHourSyntax(key, "Su 09:30; We 19:30"), isEmpty());184 // assertThat( openingHourTest.checkOpeningHourSyntax(key, "Mo-Fr 00:00-00:30,04:00-00:30; Sa,Su,PH 00:00-24:00"), isEmpty());185 assertThat( openingHourTest.checkOpeningHourSyntax(key, "Mo-Fr 0:00-0:30,4:00-00:30; Sa,Su,PH 0:00-24:00"), hasSize(1));186 assertEquals("Mo-Fr 00:00-00:30,04:00-00:30; PH,Sa,Su 00:00-24:00", 187 openingHourTest.checkOpeningHourSyntax(key, "Mo-Fr 0:00-0:30,4:00-00:30; Sa,Su,PH 0:00-24:00").get(0).getPrettifiedValue());188 assertEquals("Mo-Fr 00:00-00:30,04:00-00:30; PH,Sa,Su 00:00-24:00", 189 openingHourTest.checkOpeningHourSyntax(key, "Mo-Fr 0:00-0:30,4:00-00:30; Sa,Su,PH 0:00-24:00").get(0).getPrettifiedValue());187 assertThat(checkOpeningHourSyntax(key, "Su 10:00"), isEmpty()); 188 assertThat(checkOpeningHourSyntax(key, "automatic"), not(isEmpty())); 189 assertThat(checkOpeningHourSyntax(key, "Mo-Sa 09:00-18:00"), isEmpty()); 190 assertThat(checkOpeningHourSyntax(key, "Su 09:30; We 19:30"), isEmpty()); 191 // assertThat(checkOpeningHourSyntax(key, "Mo-Fr 00:00-00:30,04:00-00:30; Sa,Su,PH 00:00-24:00"), isEmpty()); 192 assertThat(checkOpeningHourSyntax(key, "Mo-Fr 0:00-0:30,4:00-00:30; Sa,Su,PH 0:00-24:00"), hasSize(1)); 193 assertFixEquals("Mo-Fr 00:00-00:30,04:00-00:30; PH,Sa,Su 00:00-24:00", 194 checkOpeningHourSyntax(key, "Mo-Fr 0:00-0:30,4:00-00:30; Sa,Su,PH 0:00-24:00").get(0)); 195 assertFixEquals("Mo-Fr 00:00-00:30,04:00-00:30; PH,Sa,Su 00:00-24:00", 196 checkOpeningHourSyntax(key, "Mo-Fr 0:00-0:30,4:00-00:30; Sa,Su,PH 0:00-24:00").get(0)); 190 197 } 191 198 … … 197 204 final String key = "collection_times"; 198 205 // frequently used tags according to https://taginfo.openstreetmap.org/keys/collection_times#values 199 assertThat( openingHourTest.checkOpeningHourSyntax(key, "Mo-Sa 09:00"), isEmpty());200 assertThat( openingHourTest.checkOpeningHourSyntax(key, "fixme"), not(isEmpty()));201 assertThat( openingHourTest.checkOpeningHourSyntax(key, "daily"), not(isEmpty()));202 assertThat( openingHourTest.checkOpeningHourSyntax(key, "Mo-Fr 13:30,17:45,19:00; Sa 15:00; Su 11:00"), isEmpty());203 assertThat( openingHourTest.checkOpeningHourSyntax(key, "Mo-Fr 13:30, 17:45, 19:00; Sa 15:00; Su 11:00"), hasSize(1));206 assertThat(checkOpeningHourSyntax(key, "Mo-Sa 09:00"), isEmpty()); 207 assertThat(checkOpeningHourSyntax(key, "fixme"), not(isEmpty())); 208 assertThat(checkOpeningHourSyntax(key, "daily"), not(isEmpty())); 209 assertThat(checkOpeningHourSyntax(key, "Mo-Fr 13:30,17:45,19:00; Sa 15:00; Su 11:00"), isEmpty()); 210 assertThat(checkOpeningHourSyntax(key, "Mo-Fr 13:30, 17:45, 19:00; Sa 15:00; Su 11:00"), hasSize(1)); 204 211 assertEquals(Severity.OTHER, 205 openingHourTest.checkOpeningHourSyntax(key, "Mo-Fr 13:30, 17:45, 19:00; Sa 15:00; Su 11:00").get(0).getSeverity());206 assertEquals("Mo-Fr 13:30,17:45,19:00; Sa 15:00; Su 11:00", 207 openingHourTest.checkOpeningHourSyntax(key, "Mo-Fr 13:30, 17:45, 19:00; Sa 15:00; Su 11:00").get(0).getPrettifiedValue());212 checkOpeningHourSyntax(key, "Mo-Fr 13:30, 17:45, 19:00; Sa 15:00; Su 11:00").get(0).getSeverity()); 213 assertFixEquals("Mo-Fr 13:30,17:45,19:00; Sa 15:00; Su 11:00", 214 checkOpeningHourSyntax(key, "Mo-Fr 13:30, 17:45, 19:00; Sa 15:00; Su 11:00").get(0)); 208 215 } 209 216 … … 226 233 } 227 234 for (final Tag t : values) { 228 final List< OpeningHourTest.OpeningHoursTestError> errors =openingHourTest.checkOpeningHourSyntax(t.getKey(), t.getValue());229 if (!errors.isEmpty() && errors.get(0).get Message().startsWith("Holiday after weekday")) {235 final List<TestError> errors = checkOpeningHourSyntax(t.getKey(), t.getValue()); 236 if (!errors.isEmpty() && errors.get(0).getDescription().startsWith("Holiday after weekday")) { 230 237 continue; 231 238 } … … 240 247 public void testTicket17932() { 241 248 Logging.clearLastErrorAndWarnings(); 242 assertTrue(openingHourTest.checkOpeningHourSyntax("opening_hours", "SH off").isEmpty()); 249 assertTrue(checkOpeningHourSyntax("opening_hours", "SH off").isEmpty()); 250 } 251 252 private List<TestError> checkOpeningHourSyntax(final String key, final String value, final Locale... locales) { 253 final Locale locale = locales.length > 0 ? locales[0] : Locale.ENGLISH; 254 final Node node = new Node(LatLon.ZERO); 255 node.put(key, value); 256 new DataSet(node); 257 return openingHourTest.checkOpeningHourSyntax(key, value, node, locale); 258 } 259 260 private static void assertFixEquals(String value, TestError error) { 261 assertNotNull("fix is not null", error.getFix()); 262 assertTrue("fix is ChangePropertyCommand", error.getFix() instanceof ChangePropertyCommand); 263 final ChangePropertyCommand command = (ChangePropertyCommand) error.getFix(); 264 assertEquals(1, command.getTags().size()); 265 assertEquals(value, command.getTags().values().iterator().next()); 243 266 } 244 267 }
Note:
See TracChangeset
for help on using the changeset viewer.