View Javadoc
1   package com.guinetik.examples;
2   
3   import com.guinetik.rr.RocketRest;
4   import com.guinetik.rr.RocketRestConfig;
5   import com.guinetik.rr.RocketRestOptions;
6   import com.guinetik.rr.result.ApiError;
7   import com.guinetik.rr.result.Result;
8   
9   import java.util.HashMap;
10  import java.util.List;
11  import java.util.Map;
12  import java.util.Scanner;
13  
14  /**
15   * Example demonstrating how to use the PokéAPI with RocketRest's fluent API,
16   * including pagination support.
17   * 
18   * API documentation: https://pokeapi.co/docs/v2
19   */
20  public class PokeApiExample implements Example {
21      
22      private static final String POKEAPI_BASE_URL = "https://pokeapi.co/api/v2";
23      private static final int DEFAULT_PAGE_SIZE = 10;
24      private static final Scanner scanner = new Scanner(System.in);
25      
26      // RocketRest client
27      private RocketRest client;
28      
29      @Override
30      public String getName() {
31          return "PokéAPI Browser (with pagination)";
32      }
33      
34      @Override
35      public void run() {
36          System.out.println("===============================");
37          System.out.println("PokéAPI Browser with Pagination");
38          System.out.println("===============================");
39          System.out.println("This example demonstrates using RocketRest to access the PokéAPI");
40          System.out.println("with pagination support and detailed Pokemon data.");
41          
42          try {
43              // Initialize RocketRest client
44              initializeClient();
45              
46              // Main menu for the example
47              mainMenu();
48              
49          } catch (Exception e) {
50              System.out.println("❌ Error: " + e.getMessage());
51              e.printStackTrace();
52          } finally {
53              if (client != null) {
54                  client.shutdown();
55              }
56          }
57      }
58      
59      /**
60       * Initialize the RocketRest client with appropriate configuration
61       */
62      private void initializeClient() {
63          // Create configuration
64          RocketRestConfig config = RocketRestConfig.builder(POKEAPI_BASE_URL)
65                  .defaultOptions(options -> {
66                      options.set(RocketRestOptions.LOGGING_ENABLED, true);
67                      options.set(RocketRestOptions.TIMING_ENABLED, true);
68                      // Enable response body logging for debugging
69                      options.set(RocketRestOptions.LOG_RESPONSE_BODY, true);
70                  })
71                  .build();
72          
73          // Create client
74          client = new RocketRest(POKEAPI_BASE_URL, config);
75          System.out.println("RocketRest client initialized for PokéAPI\n");
76      }
77      
78      /**
79       * Display and handle the main menu
80       */
81      private void mainMenu() {
82          boolean exit = false;
83          
84          while (!exit) {
85              System.out.println("\n--- Main Menu ---");
86              System.out.println("1. Browse Pokémon List");
87              System.out.println("2. Search Pokémon by Name/ID");
88              System.out.println("3. Browse Pokémon Types");
89              System.out.println("4. Browse Abilities");
90              System.out.println("5. Exit");
91              System.out.print("\nEnter your choice (1-5): ");
92              
93              String choice = scanner.nextLine().trim();
94              
95              switch (choice) {
96                  case "1":
97                      browsePokemonList();
98                      break;
99                  case "2":
100                     searchPokemon();
101                     break;
102                 case "3":
103                     browseTypes();
104                     break;
105                 case "4":
106                     browseAbilities();
107                     break;
108                 case "5":
109                     exit = true;
110                     System.out.println("Exiting PokéAPI Browser. Goodbye!");
111                     break;
112                 default:
113                     System.out.println("Invalid choice. Please try again.");
114             }
115         }
116     }
117     
118     /**
119      * Browse the Pokémon list with pagination
120      */
121     private void browsePokemonList() {
122         System.out.println("\n=== Browsing Pokémon List ===");
123         System.out.print("Enter page size (default is " + DEFAULT_PAGE_SIZE + "): ");
124         String sizeInput = scanner.nextLine().trim();
125         
126         int pageSize = DEFAULT_PAGE_SIZE;
127         if (!sizeInput.isEmpty()) {
128             try {
129                 pageSize = Integer.parseInt(sizeInput);
130                 if (pageSize <= 0) {
131                     System.out.println("Invalid page size. Using default: " + DEFAULT_PAGE_SIZE);
132                     pageSize = DEFAULT_PAGE_SIZE;
133                 }
134             } catch (NumberFormatException e) {
135                 System.out.println("Invalid input. Using default page size: " + DEFAULT_PAGE_SIZE);
136             }
137         }
138         
139         // Initial query parameters
140         Map<String, String> params = new HashMap<>();
141         params.put("limit", String.valueOf(pageSize));
142         params.put("offset", "0");
143         
144         String currentUrl = "/pokemon";
145         boolean browsing = true;
146         
147         while (browsing) {
148             // Use the fluent API to fetch the page
149             Result<PokemonListResponse, ApiError> result = client.fluent()
150                     .get(currentUrl, PokemonListResponse.class, params);
151             
152             if (result.isSuccess()) {
153                 PokemonListResponse response = result.getValue();
154                 displayPokemonList(response);
155                 
156                 // Handle pagination
157                 browsing = handlePagination(response, params);
158             } else {
159                 ApiError error = result.getError();
160                 System.out.println("❌ Error fetching Pokémon list: " + error.getMessage());
161                 browsing = false;
162             }
163         }
164     }
165     
166     /**
167      * Search for a specific Pokémon by name or ID
168      */
169     private void searchPokemon() {
170         System.out.println("\n=== Search Pokémon ===");
171         System.out.print("Enter Pokémon name or ID (e.g., 'pikachu' or '25'): ");
172         String query = scanner.nextLine().trim().toLowerCase();
173         
174         if (query.isEmpty()) {
175             System.out.println("Search canceled.");
176             return;
177         }
178         
179         System.out.println("Searching for: " + query);
180         
181         // Use the fluent API to fetch the Pokémon details
182         Result<PokemonDetailResponse, ApiError> result = client.fluent()
183                 .get("/pokemon/" + query, PokemonDetailResponse.class);
184         
185         result.ifSuccess(this::displayPokemonDetail)
186               .ifFailure(error -> {
187                   System.out.println("❌ Error: " + error.getMessage());
188                   if (error.getStatusCode() == 404) {
189                       System.out.println("Pokémon not found. Check the name or ID and try again.");
190                   }
191               });
192     }
193     
194     /**
195      * Browse Pokémon types
196      */
197     private void browseTypes() {
198         System.out.println("\n=== Browsing Pokémon Types ===");
199         
200         // Use the fluent API to fetch the types list
201         Result<TypeListResponse, ApiError> result = client.fluent()
202                 .get("/type", TypeListResponse.class);
203         
204         result.ifSuccess(this::displayTypesList)
205               .ifFailure(error -> 
206                   System.out.println("❌ Error fetching types: " + error.getMessage())
207               );
208     }
209     
210     /**
211      * Browse Pokémon abilities
212      */
213     private void browseAbilities() {
214         System.out.println("\n=== Browsing Pokémon Abilities ===");
215         
216         Map<String, String> params = new HashMap<>();
217         params.put("limit", "20");
218         
219         // Use the fluent API to fetch the abilities list
220         Result<AbilityListResponse, ApiError> result = client.fluent()
221                 .get("/ability", AbilityListResponse.class, params);
222         
223         result.ifSuccess(this::displayAbilitiesList)
224               .ifFailure(error -> 
225                   System.out.println("❌ Error fetching abilities: " + error.getMessage())
226               );
227     }
228     
229     /**
230      * Display the list of Pokémon
231      */
232     private void displayPokemonList(PokemonListResponse response) {
233         System.out.println("\n--- Pokémon List ---");
234         System.out.println("Total Pokémon: " + response.getCount());
235         
236         List<NamedResource> results = response.getResults();
237         if (results.isEmpty()) {
238             System.out.println("No Pokémon found on this page.");
239             return;
240         }
241         
242         System.out.println("\nPokémon on this page:");
243         for (int i = 0; i < results.size(); i++) {
244             NamedResource pokemon = results.get(i);
245             String url = pokemon.getUrl();
246             String id = "?";
247             
248             // Extract ID from URL
249             if (url != null && url.endsWith("/")) {
250                 String[] parts = url.split("/");
251                 if (parts.length > 0) {
252                     id = parts[parts.length - 1];
253                     if (id.isEmpty()) {
254                         id = parts[parts.length - 2];
255                     }
256                 }
257             }
258             
259             System.out.printf("%2d. %-15s (ID: %s)%n", (i + 1), pokemon.getName(), id);
260         }
261     }
262     
263     /**
264      * Handle pagination navigation
265      * 
266      * @return true if continuing to browse, false if exiting
267      */
268     private boolean handlePagination(PokemonListResponse response, Map<String, String> params) {
269         String next = response.getNext();
270         String previous = response.getPrevious();
271         
272         System.out.println("\n--- Navigation ---");
273         System.out.println("N - Next page" + (next == null ? " (not available)" : ""));
274         System.out.println("P - Previous page" + (previous == null ? " (not available)" : ""));
275         System.out.println("D - View details of a Pokémon");
276         System.out.println("M - Return to main menu");
277         System.out.print("\nEnter choice: ");
278         
279         String choice = scanner.nextLine().trim().toUpperCase();
280         
281         switch (choice) {
282             case "N":
283                 if (next != null) {
284                     // Extract the query parameters from the next URL
285                     extractParamsFromUrl(next, params);
286                     return true;
287                 } else {
288                     System.out.println("No next page available.");
289                     return true;
290                 }
291             case "P":
292                 if (previous != null) {
293                     // Extract the query parameters from the previous URL
294                     extractParamsFromUrl(previous, params);
295                     return true;
296                 } else {
297                     System.out.println("No previous page available.");
298                     return true;
299                 }
300             case "D":
301                 viewPokemonDetail(response.getResults());
302                 return true;
303             case "M":
304                 return false;
305             default:
306                 System.out.println("Invalid choice.");
307                 return true;
308         }
309     }
310     
311     /**
312      * Extract pagination parameters from a URL
313      */
314     private void extractParamsFromUrl(String url, Map<String, String> params) {
315         // Extract the query part of the URL
316         int queryIndex = url.indexOf('?');
317         if (queryIndex != -1) {
318             String query = url.substring(queryIndex + 1);
319             String[] pairs = query.split("&");
320             
321             for (String pair : pairs) {
322                 String[] keyValue = pair.split("=");
323                 if (keyValue.length == 2) {
324                     params.put(keyValue[0], keyValue[1]);
325                 }
326             }
327         }
328     }
329     
330     /**
331      * Allow the user to view details of a specific Pokémon from the list
332      */
333     private void viewPokemonDetail(List<NamedResource> pokemonList) {
334         System.out.print("\nEnter the number of the Pokémon to view (1-" + pokemonList.size() + "): ");
335         String input = scanner.nextLine().trim();
336         
337         try {
338             int index = Integer.parseInt(input) - 1;
339             if (index >= 0 && index < pokemonList.size()) {
340                 NamedResource pokemon = pokemonList.get(index);
341                 System.out.println("\nFetching details for " + pokemon.getName() + "...");
342                 
343                 // Extract ID from URL
344                 String url = pokemon.getUrl();
345                 String id = "";
346                 if (url != null && url.endsWith("/")) {
347                     String[] parts = url.split("/");
348                     if (parts.length > 0) {
349                         id = parts[parts.length - 1];
350                         if (id.isEmpty()) {
351                             id = parts[parts.length - 2];
352                         }
353                     }
354                 }
355                 
356                 // Fetch and display Pokémon details
357                 Result<PokemonDetailResponse, ApiError> result = client.fluent()
358                         .get("/pokemon/" + id, PokemonDetailResponse.class);
359                 
360                 result.ifSuccess(this::displayPokemonDetail)
361                       .ifFailure(error -> System.out.println("❌ Error: " + error.getMessage()));
362             } else {
363                 System.out.println("Invalid selection. Please try again.");
364             }
365         } catch (NumberFormatException e) {
366             System.out.println("Invalid input. Please enter a number.");
367         }
368     }
369     
370     /**
371      * Display detailed information about a Pokémon
372      */
373     private void displayPokemonDetail(PokemonDetailResponse pokemon) {
374         System.out.println("\n=== Pokémon Details ===");
375         System.out.println("Name: " + capitalize(pokemon.getName()));
376         System.out.println("ID: " + pokemon.getId());
377         System.out.println("Height: " + (pokemon.getHeight() / 10.0) + " m");
378         System.out.println("Weight: " + (pokemon.getWeight() / 10.0) + " kg");
379         
380         // Display types
381         System.out.println("\nTypes:");
382         List<TypeInfo> types = pokemon.getTypes();
383         if (types != null) {
384             for (TypeInfo typeInfo : types) {
385                 if (typeInfo.getType() != null) {
386                     System.out.println("- " + capitalize(typeInfo.getType().getName()));
387                 }
388             }
389         }
390         
391         // Display abilities
392         System.out.println("\nAbilities:");
393         List<AbilityInfo> abilities = pokemon.getAbilities();
394         if (abilities != null) {
395             for (AbilityInfo abilityInfo : abilities) {
396                 if (abilityInfo.getAbility() != null) {
397                     String hiddenTag = abilityInfo.isHidden() ? " (Hidden)" : "";
398                     System.out.println("- " + capitalize(abilityInfo.getAbility().getName()) + hiddenTag);
399                 }
400             }
401         }
402         
403         // Display base stats
404         System.out.println("\nBase Stats:");
405         List<StatInfo> stats = pokemon.getStats();
406         if (stats != null) {
407             for (StatInfo statInfo : stats) {
408                 if (statInfo.getStat() != null) {
409                     String statName = statInfo.getStat().getName().replace("-", " ");
410                     System.out.printf("- %-20s: %d%n", capitalize(statName), statInfo.getBase_stat());
411                 }
412             }
413         } else {
414             System.out.println("No stats available");
415         }
416         
417         // Display moves (just a few)
418         List<MoveInfo> moves = pokemon.getMoves();
419         if (moves != null && !moves.isEmpty()) {
420             System.out.println("\nSample Moves (first 5):");
421             for (int i = 0; i < Math.min(5, moves.size()); i++) {
422                 MoveInfo moveInfo = moves.get(i);
423                 if (moveInfo.getMove() != null) {
424                     System.out.println("- " + capitalize(moveInfo.getMove().getName().replace("-", " ")));
425                 }
426             }
427             System.out.println("... and " + (moves.size() - Math.min(5, moves.size())) + " more moves");
428         }
429         
430         // Wait for user to press Enter before continuing
431         System.out.print("\nPress Enter to continue...");
432         scanner.nextLine();
433     }
434     
435     /**
436      * Display the list of Pokémon types
437      */
438     private void displayTypesList(TypeListResponse response) {
439         System.out.println("\n--- Pokémon Types ---");
440         List<NamedResource> results = response.getResults();
441         
442         if (results == null || results.isEmpty()) {
443             System.out.println("No types found.");
444             return;
445         }
446         
447         for (int i = 0; i < results.size(); i++) {
448             System.out.printf("%2d. %s%n", (i + 1), capitalize(results.get(i).getName()));
449         }
450         
451         // Option to view type details
452         System.out.print("\nEnter a type number for details or press Enter to continue: ");
453         String input = scanner.nextLine().trim();
454         
455         if (!input.isEmpty()) {
456             try {
457                 int index = Integer.parseInt(input) - 1;
458                 if (index >= 0 && index < results.size()) {
459                     NamedResource type = results.get(index);
460                     System.out.println("\nFetching details for " + type.getName() + " type...");
461                     
462                     // Fetch and display type details
463                     fetchTypeDetails(type.getName());
464                 } else {
465                     System.out.println("Invalid selection.");
466                 }
467             } catch (NumberFormatException e) {
468                 System.out.println("Invalid input.");
469             }
470         }
471     }
472     
473     /**
474      * Display the list of Pokémon abilities
475      */
476     private void displayAbilitiesList(AbilityListResponse response) {
477         System.out.println("\n--- Pokémon Abilities ---");
478         List<NamedResource> results = response.getResults();
479         
480         if (results == null || results.isEmpty()) {
481             System.out.println("No abilities found.");
482             return;
483         }
484         
485         for (int i = 0; i < results.size(); i++) {
486             System.out.printf("%2d. %s%n", (i + 1), capitalize(results.get(i).getName().replace("-", " ")));
487         }
488         
489         // Option to view ability details
490         System.out.print("\nEnter an ability number for details or press Enter to continue: ");
491         String input = scanner.nextLine().trim();
492         
493         if (!input.isEmpty()) {
494             try {
495                 int index = Integer.parseInt(input) - 1;
496                 if (index >= 0 && index < results.size()) {
497                     NamedResource ability = results.get(index);
498                     System.out.println("\nFetching details for " + ability.getName() + " ability...");
499                     
500                     // Future enhancement: Fetch and display ability details
501                     System.out.println("Ability details feature coming soon!");
502                 } else {
503                     System.out.println("Invalid selection.");
504                 }
505             } catch (NumberFormatException e) {
506                 System.out.println("Invalid input.");
507             }
508         }
509     }
510     
511     /**
512      * Helper method to capitalize a string
513      */
514     private String capitalize(String str) {
515         if (str == null || str.isEmpty()) {
516             return str;
517         }
518         return str.substring(0, 1).toUpperCase() + str.substring(1);
519     }
520     
521     /**
522      * Fetch and display details for a specific Pokémon type
523      */
524     private void fetchTypeDetails(String typeName) {
525         Result<TypeDetailResponse, ApiError> result = client.fluent()
526                 .get("/type/" + typeName, TypeDetailResponse.class);
527         
528         result.ifSuccess(this::displayTypeDetails)
529               .ifFailure(error -> System.out.println("❌ Error: " + error.getMessage()));
530     }
531     
532     /**
533      * Display detailed information about a Pokémon type
534      */
535     private void displayTypeDetails(TypeDetailResponse type) {
536         System.out.println("\n=== Type Details: " + capitalize(type.getName()) + " ===");
537         System.out.println("ID: " + type.getId());
538         
539         DamageRelations damageRelations = type.getDamageRelations();
540         if (damageRelations != null) {
541             System.out.println("\n--- Damage Relations ---");
542             
543             System.out.println("\nWeak against (2x damage from):");
544             displayNamedResources(damageRelations.getDoubleDamageFrom());
545             
546             System.out.println("\nStrong against (2x damage to):");
547             displayNamedResources(damageRelations.getDoubleDamageTo());
548             
549             System.out.println("\nResistant to (½ damage from):");
550             displayNamedResources(damageRelations.getHalfDamageFrom());
551             
552             System.out.println("\nNot very effective against (½ damage to):");
553             displayNamedResources(damageRelations.getHalfDamageTo());
554             
555             System.out.println("\nImmune to (no damage from):");
556             displayNamedResources(damageRelations.getNoDamageFrom());
557             
558             System.out.println("\nDoesn't affect (no damage to):");
559             displayNamedResources(damageRelations.getNoDamageTo());
560         }
561         
562         // Display some moves of this type
563         List<NamedResource> moves = type.getMoves();
564         if (moves != null && !moves.isEmpty()) {
565             System.out.println("\n--- Sample Moves ---");
566             int displayCount = Math.min(10, moves.size());
567             for (int i = 0; i < displayCount; i++) {
568                 System.out.println("- " + capitalize(moves.get(i).getName().replace("-", " ")));
569             }
570             
571             if (moves.size() > displayCount) {
572                 System.out.println("... and " + (moves.size() - displayCount) + " more moves");
573             }
574         }
575         
576         // Wait for user to press Enter before continuing
577         System.out.print("\nPress Enter to continue...");
578         scanner.nextLine();
579     }
580     
581     /**
582      * Helper method to display a list of named resources
583      */
584     private void displayNamedResources(List<NamedResource> resources) {
585         if (resources == null || resources.isEmpty()) {
586             System.out.println("None");
587             return;
588         }
589         
590         for (NamedResource resource : resources) {
591             System.out.println("- " + capitalize(resource.getName()));
592         }
593     }
594     
595     //
596     // Model classes for PokéAPI responses
597     //
598     
599     /**
600      * Base response for paginated results
601      */
602     public static class PaginatedResponse<T> {
603         private int count;
604         private String next;
605         private String previous;
606         private List<T> results;
607         
608         public int getCount() {
609             return count;
610         }
611         
612         public void setCount(int count) {
613             this.count = count;
614         }
615         
616         public String getNext() {
617             return next;
618         }
619         
620         public void setNext(String next) {
621             this.next = next;
622         }
623         
624         public String getPrevious() {
625             return previous;
626         }
627         
628         public void setPrevious(String previous) {
629             this.previous = previous;
630         }
631         
632         public List<T> getResults() {
633             return results;
634         }
635         
636         public void setResults(List<T> results) {
637             this.results = results;
638         }
639     }
640     
641     /**
642      * Response for the Pokémon list endpoint
643      */
644     public static class PokemonListResponse extends PaginatedResponse<NamedResource> {
645     }
646     
647     /**
648      * Response for the Types list endpoint
649      */
650     public static class TypeListResponse extends PaginatedResponse<NamedResource> {
651     }
652     
653     /**
654      * Response for the Abilities list endpoint
655      */
656     public static class AbilityListResponse extends PaginatedResponse<NamedResource> {
657     }
658     
659     /**
660      * Named resource (name and URL) used throughout the API
661      */
662     public static class NamedResource {
663         private String name;
664         private String url;
665         
666         public String getName() {
667             return name;
668         }
669         
670         public void setName(String name) {
671             this.name = name;
672         }
673         
674         public String getUrl() {
675             return url;
676         }
677         
678         public void setUrl(String url) {
679             this.url = url;
680         }
681     }
682     
683     /**
684      * Detailed Pokémon response
685      */
686     public static class PokemonDetailResponse {
687         private int id;
688         private String name;
689         private int height;
690         private int weight;
691         private List<TypeInfo> types;
692         private List<AbilityInfo> abilities;
693         private List<StatInfo> stats;
694         private List<MoveInfo> moves;
695         
696         public int getId() {
697             return id;
698         }
699         
700         public void setId(int id) {
701             this.id = id;
702         }
703         
704         public String getName() {
705             return name;
706         }
707         
708         public void setName(String name) {
709             this.name = name;
710         }
711         
712         public int getHeight() {
713             return height;
714         }
715         
716         public void setHeight(int height) {
717             this.height = height;
718         }
719         
720         public int getWeight() {
721             return weight;
722         }
723         
724         public void setWeight(int weight) {
725             this.weight = weight;
726         }
727         
728         public List<TypeInfo> getTypes() {
729             return types;
730         }
731         
732         public void setTypes(List<TypeInfo> types) {
733             this.types = types;
734         }
735         
736         public List<AbilityInfo> getAbilities() {
737             return abilities;
738         }
739         
740         public void setAbilities(List<AbilityInfo> abilities) {
741             this.abilities = abilities;
742         }
743         
744         public List<StatInfo> getStats() {
745             return stats;
746         }
747         
748         public void setStats(List<StatInfo> stats) {
749             this.stats = stats;
750         }
751         
752         public List<MoveInfo> getMoves() {
753             return moves;
754         }
755         
756         public void setMoves(List<MoveInfo> moves) {
757             this.moves = moves;
758         }
759     }
760     
761     /**
762      * Type information for a Pokémon
763      */
764     public static class TypeInfo {
765         private int slot;
766         private NamedResource type;
767         
768         public int getSlot() {
769             return slot;
770         }
771         
772         public void setSlot(int slot) {
773             this.slot = slot;
774         }
775         
776         public NamedResource getType() {
777             return type;
778         }
779         
780         public void setType(NamedResource type) {
781             this.type = type;
782         }
783     }
784     
785     /**
786      * Ability information for a Pokémon
787      */
788     public static class AbilityInfo {
789         private boolean is_hidden;
790         private int slot;
791         private NamedResource ability;
792         
793         public boolean isHidden() {
794             return is_hidden;
795         }
796         
797         public void setHidden(boolean hidden) {
798             is_hidden = hidden;
799         }
800         
801         public int getSlot() {
802             return slot;
803         }
804         
805         public void setSlot(int slot) {
806             this.slot = slot;
807         }
808         
809         public NamedResource getAbility() {
810             return ability;
811         }
812         
813         public void setAbility(NamedResource ability) {
814             this.ability = ability;
815         }
816     }
817     
818     /**
819      * Stat information for a Pokémon
820      */
821     public static class StatInfo {
822         // Using exact field names from the API for proper JSON mapping
823         private int base_stat; // The base value of the stat
824         private int effort;
825         private NamedResource stat;
826         
827         /**
828          * Gets the base stat value.
829          * The field is named base_stat in JSON but we use camelCase for Java methods.
830          * 
831          * @return The base stat value
832          */
833         public int getBaseStat() {
834             return base_stat;
835         }
836         
837         /**
838          * Gets the base stat value directly.
839          * This method is added for direct access to the field.
840          * 
841          * @return The base stat value
842          */
843         public int getBase_stat() {
844             return base_stat;
845         }
846         
847         /**
848          * Sets the base stat value.
849          * 
850          * @param baseStat The base stat value
851          */
852         public void setBaseStat(int baseStat) {
853             this.base_stat = baseStat;
854         }
855         
856         /**
857          * Sets the base stat value directly.
858          * 
859          * @param base_stat The base stat value
860          */
861         public void setBase_stat(int base_stat) {
862             this.base_stat = base_stat;
863         }
864         
865         public int getEffort() {
866             return effort;
867         }
868         
869         public void setEffort(int effort) {
870             this.effort = effort;
871         }
872         
873         public NamedResource getStat() {
874             return stat;
875         }
876         
877         public void setStat(NamedResource stat) {
878             this.stat = stat;
879         }
880         
881         @Override
882         public String toString() {
883             return "StatInfo{" +
884                     "base_stat=" + base_stat +
885                     ", effort=" + effort +
886                     ", stat=" + (stat != null ? stat.getName() : "null") +
887                     '}';
888         }
889     }
890     
891     /**
892      * Move information for a Pokémon
893      */
894     public static class MoveInfo {
895         private NamedResource move;
896         
897         public NamedResource getMove() {
898             return move;
899         }
900         
901         public void setMove(NamedResource move) {
902             this.move = move;
903         }
904     }
905     
906     /**
907      * Response for the Type details endpoint
908      */
909     public static class TypeDetailResponse {
910         private int id;
911         private String name;
912         private DamageRelations damage_relations;
913         private List<NamedResource> moves;
914         
915         public int getId() {
916             return id;
917         }
918         
919         public void setId(int id) {
920             this.id = id;
921         }
922         
923         public String getName() {
924             return name;
925         }
926         
927         public void setName(String name) {
928             this.name = name;
929         }
930         
931         public DamageRelations getDamageRelations() {
932             return damage_relations;
933         }
934         
935         public void setDamageRelations(DamageRelations damageRelations) {
936             this.damage_relations = damageRelations;
937         }
938         
939         public List<NamedResource> getMoves() {
940             return moves;
941         }
942         
943         public void setMoves(List<NamedResource> moves) {
944             this.moves = moves;
945         }
946     }
947     
948     /**
949      * Damage relations information for a type
950      */
951     public static class DamageRelations {
952         private List<NamedResource> double_damage_from;
953         private List<NamedResource> double_damage_to;
954         private List<NamedResource> half_damage_from;
955         private List<NamedResource> half_damage_to;
956         private List<NamedResource> no_damage_from;
957         private List<NamedResource> no_damage_to;
958         
959         public List<NamedResource> getDoubleDamageFrom() {
960             return double_damage_from;
961         }
962         
963         public void setDoubleDamageFrom(List<NamedResource> doubleDamageFrom) {
964             this.double_damage_from = doubleDamageFrom;
965         }
966         
967         public List<NamedResource> getDoubleDamageTo() {
968             return double_damage_to;
969         }
970         
971         public void setDoubleDamageTo(List<NamedResource> doubleDamageTo) {
972             this.double_damage_to = doubleDamageTo;
973         }
974         
975         public List<NamedResource> getHalfDamageFrom() {
976             return half_damage_from;
977         }
978         
979         public void setHalfDamageFrom(List<NamedResource> halfDamageFrom) {
980             this.half_damage_from = halfDamageFrom;
981         }
982         
983         public List<NamedResource> getHalfDamageTo() {
984             return half_damage_to;
985         }
986         
987         public void setHalfDamageTo(List<NamedResource> halfDamageTo) {
988             this.half_damage_to = halfDamageTo;
989         }
990         
991         public List<NamedResource> getNoDamageFrom() {
992             return no_damage_from;
993         }
994         
995         public void setNoDamageFrom(List<NamedResource> noDamageFrom) {
996             this.no_damage_from = noDamageFrom;
997         }
998         
999         public List<NamedResource> getNoDamageTo() {
1000             return no_damage_to;
1001         }
1002         
1003         public void setNoDamageTo(List<NamedResource> noDamageTo) {
1004             this.no_damage_to = noDamageTo;
1005         }
1006     }
1007 }