99import java .net .http .HttpResponse ;
1010import java .nio .charset .StandardCharsets ;
1111import java .util .HashMap ;
12+ import java .util .List ;
1213import java .util .Map ;
1314import org .mobilitydata .gbfs .validation .GbfsValidator ;
1415import org .mobilitydata .gbfs .validation .GbfsValidatorFactory ;
1516import org .mobilitydata .gbfs .validation .model .FileValidationError ;
1617import org .mobilitydata .gbfs .validation .model .FileValidationResult ;
1718import org .mobilitydata .gbfs .validation .model .ValidationResult ;
19+ import org .mobilitydata .gbfs .validator .loader .LoadedFile ;
20+ import org .mobilitydata .gbfs .validator .loader .Loader ;
1821
1922/**
2023 * Example showing how to validate a GBFS feed using gbfs-validator-java.
2124 *
22- * <p>This example :
25+ * <p>Two use cases are demonstrated :
2326 * <ol>
24- * <li>Fetches gbfs.json from a public GBFS feed</li>
25- * <li>Validates the file using GbfsValidatorFactory</li>
26- * <li>Prints the validation results to stdout</li>
27+ * <li>Single-file validation via {@link GbfsValidator#validateFile}</li>
28+ * <li>Full-feed validation via {@link Loader} + {@link GbfsValidator#validate}:
29+ * the Loader fetches gbfs.json and automatically discovers and loads all
30+ * linked feed files, so no manual URL construction is needed.</li>
2731 * </ol>
2832 *
2933 * <p>Usage:
@@ -42,57 +46,54 @@ public class GbfsValidatorExample {
4246 public static void main (String [] args ) throws IOException , InterruptedException {
4347 System .out .println ("=== GBFS Validator Java Example ===\n " );
4448
49+ GbfsValidator validator = GbfsValidatorFactory .getGbfsJsonValidator ();
50+
4551 // --- Example 1: Validate a single file ---
52+ // Useful when you already have the file content and just want schema validation.
4653 System .out .println ("Example 1: Validate a single file" );
4754 System .out .println ("Fetching: " + GBFS_FEED_URL );
48- String fileContents = fetchUrl (GBFS_FEED_URL );
49-
50- GbfsValidator validator = GbfsValidatorFactory .getGbfsJsonValidator ();
51- InputStream fileStream = new ByteArrayInputStream (
52- fileContents .getBytes (StandardCharsets .UTF_8 )
53- );
54-
55- // The API expects file names WITHOUT the .json extension (e.g. "gbfs", not "gbfs.json")
56- FileValidationResult fileResult = validator .validateFile ("gbfs" , fileStream );
55+ String gbfsContent = fetchUrl (GBFS_FEED_URL );
56+ InputStream gbfsStream = new ByteArrayInputStream (gbfsContent .getBytes (StandardCharsets .UTF_8 ));
57+ FileValidationResult fileResult = validator .validateFile ("gbfs" , gbfsStream );
5758 printFileResult (fileResult );
5859
59- // --- Example 2: Validate a full feed (multiple files) ---
60+ // --- Example 2: Validate a full feed ---
61+ // The Loader fetches gbfs.json, parses the feed URLs from its discovery data,
62+ // and loads all linked files — handling language prefixes and auth automatically.
6063 System .out .println ("\n Example 2: Validate a full feed" );
61- Map <String , InputStream > feedFiles = new HashMap <>();
62-
63- // Keys must be the GBFS file type name (no .json extension)
64- feedFiles .put ("gbfs" , new ByteArrayInputStream (
65- fileContents .getBytes (StandardCharsets .UTF_8 )
66- ));
67-
68- // Fetch additional files — URL uses .json, but map key does not
69- String [] additionalFileNames = {
70- "system_information" ,
71- "station_information" ,
72- "station_status" ,
73- "free_bike_status" ,
74- };
75- String baseUrl = GBFS_FEED_URL .substring (0 , GBFS_FEED_URL .lastIndexOf ('/' ) + 1 );
76- for (String fileType : additionalFileNames ) {
77- try {
78- String content = fetchUrl (baseUrl + fileType + ".json" );
79- feedFiles .put (fileType , new ByteArrayInputStream (
80- content .getBytes (StandardCharsets .UTF_8 )
81- ));
82- System .out .println (" Loaded: " + fileType );
83- } catch (Exception e ) {
84- System .out .println (" Skipped: " + fileType + " (" + e .getMessage () + ")" );
64+ Loader loader = new Loader ();
65+ try {
66+ List <LoadedFile > loadedFiles = loader .load (GBFS_FEED_URL );
67+
68+ Map <String , InputStream > fileMap = new HashMap <>();
69+ for (LoadedFile file : loadedFiles ) {
70+ // Keep the discovery file (no language) and only "en" language files.
71+ // If a feed does not publish "en", swap "en" for the desired language code.
72+ String lang = file .language ();
73+ if (lang != null && !lang .equals ("en" )) {
74+ continue ;
75+ }
76+ if (file .fileContents () != null ) {
77+ System .out .println (" Loaded: " + file .fileName () + " (" + file .url () + ")" );
78+ fileMap .put (file .fileName (), file .fileContents ());
79+ } else {
80+ file .loaderErrors ().forEach (e ->
81+ System .out .println (" Skipped: " + file .fileName ()
82+ + " (" + e .error () + ": " + e .message () + ")" )
83+ );
84+ }
8585 }
86- }
8786
88- ValidationResult feedResult = validator .validate (feedFiles );
89- printFeedResult (feedResult );
87+ ValidationResult feedResult = validator .validate (fileMap );
88+ printFeedResult (feedResult );
89+ } finally {
90+ loader .close ();
91+ }
9092 }
9193
9294 private static void printFileResult (FileValidationResult result ) {
9395 System .out .println (" File : " + result .file ());
9496 System .out .println (" Version : " + result .version ());
95- System .out .println (" Schema : " + result .schema ());
9697 System .out .println (" Exists : " + result .exists ());
9798 System .out .println (" Required: " + result .required ());
9899 System .out .println (" Errors : " + result .errorsCount ());
@@ -115,14 +116,17 @@ private static void printFileResult(FileValidationResult result) {
115116
116117 private static void printFeedResult (ValidationResult result ) {
117118 System .out .println (" Summary : " + result .summary ());
118- System .out .println (" Files validated: " + result .files ().size ());
119- result .files ().forEach ((name , fileResult ) -> {
119+ var presentFiles = result .files ().entrySet ().stream ()
120+ .filter (e -> e .getValue ().exists ())
121+ .toList ();
122+ System .out .println (" Files validated: " + presentFiles .size ());
123+ presentFiles .forEach (e -> {
120124 System .out .printf (" %-35s errors=%d version=%s%n" ,
121- name , fileResult . errorsCount (), fileResult .version ());
125+ e . getKey (), e . getValue (). errorsCount (), e . getValue () .version ());
122126 });
123127
124- long totalErrors = result . files (). values () .stream ()
125- .mapToLong (FileValidationResult :: errorsCount )
128+ long totalErrors = presentFiles .stream ()
129+ .mapToLong (e -> e . getValue (). errorsCount () )
126130 .sum ();
127131 System .out .println ("\n Total errors across all files: " + totalErrors );
128132
@@ -139,12 +143,11 @@ private static String fetchUrl(String url) throws IOException, InterruptedExcept
139143 .uri (URI .create (url ))
140144 .GET ()
141145 .build ();
142- HttpResponse <String > response = client .send (
143- request , HttpResponse .BodyHandlers .ofString ()
144- );
146+ HttpResponse <String > response = client .send (request , HttpResponse .BodyHandlers .ofString ());
145147 if (response .statusCode () != 200 ) {
146148 throw new IOException ("HTTP " + response .statusCode () + " for " + url );
147149 }
148150 return response .body ();
149151 }
150152}
153+
0 commit comments