This commit is contained in:
Maxopoly 2021-11-04 17:02:56 +01:00
commit f664f9a93a
19 changed files with 1710 additions and 0 deletions

265
.gitignore vendored Normal file
View File

@ -0,0 +1,265 @@
# Created by https://www.gitignore.io/api/java,batch,linux,macos,maven,windows,eclipse,intellij+all
# Edit at https://www.gitignore.io/?templates=java,batch,linux,macos,maven,windows,eclipse,intellij+all
### Batch ###
# BatchFiles
*.bat
*.cmd
*.btm
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
### Eclipse Patch ###
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Annotation Processing
.apt_generated
.sts4-cache/
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
### Java ###
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk

9
LICENSE.txt Normal file
View File

@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright © 2021 Maxopoly
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

0
README.md Normal file
View File

64
pom.xml Normal file
View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.maxopoly</groupId>
<artifactId>tcgdex</artifactId>
<name>tcgdex</name>
<version>1.0.0</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,77 @@
package com.github.maxopoly.tcgdex;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* Describes a single ability of a pokemon
*
*/
public class Ability {
static List<Ability> parse(JSONArray array) {
if (array == null) {
return Collections.emptyList();
}
List<Ability> result = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
result.add(new Ability(array.getJSONObject(i)));
}
return result;
}
private final String type;
private final String name;
private final String effect;
Ability(String type, String name, String effect) {
this.type = type;
this.name = name;
this.effect = effect;
}
Ability(JSONObject json) {
this(json.getString("type"), json.getString("name"), json.getString("effect"));
}
public boolean equals(Object o) {
if (!(o instanceof Ability)) {
return false;
}
Ability other = (Ability) o;
return Objects.deepEquals(new Object[] { this.type, this.name, this.effect },
new Object[] { other.type, other.name, other.effect });
}
public int hashCode() {
return Objects.hash(this.type, this.name, this.effect);
}
/**
* @return Type of the ability, for example 'Poke-POWER'
*/
public String getType() {
return type;
}
/**
* @return Name of the ability
*/
public String getName() {
return name;
}
/**
*
* @return Description/Effect of the ability
*/
public String getEffect() {
return effect;
}
}

View File

@ -0,0 +1,88 @@
package com.github.maxopoly.tcgdex;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* Describes a single attack of a pokemon, for example 'Confuse Ray'
*
*/
public class Attack {
static List<Attack> parse(JSONArray array) {
if (array == null) {
return Collections.emptyList();
}
List<Attack> result = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
result.add(new Attack(array.getJSONObject(i)));
}
return result;
}
private final List<Types> cost;
private final String name;
private final String effect;
private final String damage;
Attack(List<Types> cost, String name, String effect, String damage) {
super();
this.cost = cost;
this.name = name;
this.effect = effect;
this.damage = damage;
}
Attack(JSONObject json) {
this(Types.parse(json.getJSONArray("cost")), json.getString("name"), json.optString("effect"),
json.optString("damage"));
}
public boolean equals(Object o) {
if (!(o instanceof Attack)) {
return false;
}
Attack other = (Attack) o;
return Objects.deepEquals(new Object[] { this.cost, this.name, this.effect, this.damage },
new Object[] { other.cost, other.name, other.effect, other.damage });
}
public int hashCode() {
return Objects.hash(this.cost, this.name, this.effect, this.damage);
}
/**
* @return Cost of the attack in the same order as listed on the card
*/
public List<Types> getCost() {
return cost;
}
/**
* @return Name of the attack
*/
public String getName() {
return name;
}
/**
* @return Effect/Description of the attack, may be null for attacks without text
*/
public String getEffect() {
return effect;
}
/**
* @return Damage the attack deals. May just be a number like '30', but can also
* be a multiplier like 'x20'
*/
public String getDamage() {
return damage;
}
}

View File

@ -0,0 +1,302 @@
package com.github.maxopoly.tcgdex;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* Full description of a card, including all information available about it
*
*/
public class CardInfo {
private final String id;
private final String localId;
private final String name;
private final String image;
private final String illustrator;
private final Rarities rarity;
private final Categories category;
private final boolean hasNormalVariant;
private final boolean hasReverseVariant;
private final boolean hasHolo;
private final boolean hasFirstEditionPic;
private final SetResume set;
private final List<Integer> dexIDs;
private final Integer hp;
private final List<Types> types;
private final String evolveFrom;
private final String description;
private final String level;
private final String stage;
private final String suffix;
private final List<Attack> attacks;
private final List<Weakness> weakness;
private final List<Ability> abilities;
private final Integer retreat;
private final String regulationMark;
public CardInfo(String id, String localId, String name, String image, String illustrator, Rarities rarity,
Categories category, boolean hasNormalVariant, boolean hasReverseVariant, boolean hasHolo,
boolean hasFirstEditionPic, SetResume set, List<Integer> dexIDs, Integer hp, List<Types> types,
String evolveFrom, String description, String level, String stage, String suffix, List<Attack> attacks,
List<Weakness> weakness, List<Ability> abilities, Integer retreat, String regulationMark) {
super();
this.id = id;
this.localId = localId;
this.name = name;
this.image = image;
this.illustrator = illustrator;
this.rarity = rarity;
this.category = category;
this.hasNormalVariant = hasNormalVariant;
this.hasReverseVariant = hasReverseVariant;
this.hasHolo = hasHolo;
this.hasFirstEditionPic = hasFirstEditionPic;
this.set = set;
this.dexIDs = dexIDs;
this.hp = hp;
this.types = types;
this.evolveFrom = evolveFrom;
this.description = description;
this.level = level;
this.stage = stage;
this.suffix = suffix;
this.attacks = attacks;
this.weakness = weakness;
this.abilities = abilities;
this.retreat = retreat;
this.regulationMark = regulationMark;
}
CardInfo(JSONObject json) {
this.id = json.getString("id");
this.localId = json.getString("localId");
this.illustrator = json.getString("illustrator");
this.name = json.getString("name");
this.image = json.getString("name");
this.rarity = Rarities.parse(json.getString("rarity"));
this.category = Categories.parse(json.getString("category"));
JSONObject variantSection = json.getJSONObject("variants");
this.hasNormalVariant = variantSection.getBoolean("normal");
this.hasReverseVariant = variantSection.getBoolean("reverse");
this.hasHolo = variantSection.getBoolean("holo");
this.hasFirstEditionPic = variantSection.getBoolean("firstEdition");
this.set = new SetResume(json.getJSONObject("set"));
this.dexIDs = new ArrayList<>(1);
JSONArray dexArray = json.optJSONArray("dexId");
if (dexArray != null) {
for (int i = 0; i < dexArray.length(); i++) {
this.dexIDs.add(dexArray.getInt(i));
}
}
this.hp = json.optInt("hp", -1) > 0 ? json.getInt("hp") : null;
this.types = Types.parse(json.optJSONArray("types"));
this.stage = json.optString("stage");
this.suffix = json.optString("suffix");
this.attacks = Attack.parse(json.optJSONArray("attacks"));
this.weakness = Weakness.parse(json.optJSONArray("weaknesses"));
this.retreat = json.optInt("retreat", -1) > 0 ? json.getInt("retreat") : null;
this.regulationMark = json.optString("regulationMark");
this.level = json.optString("level");
this.evolveFrom = json.optString("evolveFrom");
this.description = json.optString("effect");
this.abilities = Ability.parse(json.optJSONArray("abilities"));
}
/**
* @return Pokemon's abilities. May be empty if it doesn't have any, but never
* null
*/
public List<Ability> getAbilities() {
return abilities;
}
/**
*
* @return Card's regulation mark. May be null if unknown or doesn't exist
*/
public String getRegulationMark() {
return regulationMark;
}
/**
* @return Attacks the pokemon has. Empty for cards without attacks
*/
public List<Attack> getAttacks() {
return attacks;
}
/**
* @return Weaknesses the pokemon has. Empty for cards without attacks
*/
public List<Weakness> getWeakness() {
return weakness;
}
/**
* @return Card's retreat. Will be null for cards without retreat
*/
public Integer getRetreat() {
return retreat;
}
/**
*
* @return Card unique ID
*/
public String getId() {
return id;
}
/**
*
* @return Card set ID
*/
public String getLocalId() {
return localId;
}
/**
*
* @return Card name
*/
public String getName() {
return name;
}
/**
*
* @return Image URL, may be null
*/
public String getImage() {
return image;
}
/**
*
* @return Card illustrator
*/
public String getIllustrator() {
return illustrator;
}
/**
*
* @return Card rarity
*/
public Rarities getRarity() {
return rarity;
}
/**
*
* @return Card category
*/
public Categories getCategory() {
return category;
}
/**
*
* @return Does the card have a normal variant without any shines
*/
public boolean hasNormalVariant() {
return hasNormalVariant;
}
/**
*
* @return Does the card have a reverse variant (colored background is shining)
*/
public boolean hasReverseVariant() {
return hasReverseVariant;
}
/**
*
* @return Does the card have a holo variant (picture is shining)
*/
public boolean hasHoloVariant() {
return hasHolo;
}
/**
*
* @return Does the card have a small first edition in the middle left
*/
public boolean hasFirstEditionPic() {
return hasFirstEditionPic;
}
/**
* @return Resume of the set the card belongs to
*/
public SetResume getSet() {
return set;
}
/**
*
* @return List of the national pokedex IDs of the pokemon on the card (may be
* multiple)
*/
public List<Integer> getDexIDs() {
return dexIDs;
}
/**
* @return HP of the pokemon, will be null if the card is not a pokemon
*/
public Integer getHp() {
return hp;
}
/**
* @return Types of the pokemon
*/
public List<Types> getTypes() {
return types;
}
/**
*
* @return Name of the pokemon this one evolves from
*/
public String getEvolveFrom() {
return evolveFrom;
}
/**
*
* @return Card effect/description, may be null
*/
public String getEffect() {
return description;
}
/**
*
* @return Pokemon level, may be 'X', hence not an integer
*/
public String getLevel() {
return level;
}
/**
* @return Pokemon's stage, like 'Basic'
*/
public String getStage() {
return stage;
}
/**
* @return Suffix, like 'V', may be null
*/
public String getSuffix() {
return suffix;
}
}

View File

@ -0,0 +1,72 @@
package com.github.maxopoly.tcgdex;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* Core information to describe a single card
*
*/
public class CardResume {
static List<CardResume> parse(JSONArray array) {
if (array == null) {
return Collections.emptyList();
}
List<CardResume> result = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
result.add(new CardResume(array.getJSONObject(i)));
}
return result;
}
private final String id;
private final String localId;
private final String name;
private final String image;
CardResume(String id, String localId, String name, String image) {
this.id = id;
this.localId = localId;
this.name = name;
this.image = image;
}
CardResume(JSONObject json) {
this(json.getString("id"), json.getString("localId"), json.getString("name"), json.optString("image"));
}
/**
* @return Globally unique card ID based on the set ID and the cards ID within the set
*/
public String getId() {
return id;
}
/**
* @return ID indexing this card within its set, usually just its number
*/
public String getLocalId() {
return localId;
}
/**
*
* @return Card name
*/
public String getName() {
return name;
}
/**
*
* @return Card image, can be null
*/
public String getImage() {
return image;
}
}

View File

@ -0,0 +1,15 @@
package com.github.maxopoly.tcgdex;
public enum Categories {
POKEMON, ENERGY, TRAINER;
static Categories parse(String raw) {
return Categories.valueOf(raw.toUpperCase().replace(" ", "_"));
}
public String toPrettyString() {
return Utils.prettifyEnumName(this);
}
}

View File

@ -0,0 +1,16 @@
package com.github.maxopoly.tcgdex;
public enum Rarities {
RARE, COMMON, NONE, ULTRA_RARE, UNCOMMON, AMAZING, SECRET_RARE;
static Rarities parse(String raw) {
return Rarities.valueOf(raw.toUpperCase().replace(" ", "_"));
}
public String toPrettyString() {
return Utils.prettifyEnumName(this);
}
}

View File

@ -0,0 +1,31 @@
package com.github.maxopoly.tcgdex;
import java.util.List;
import org.json.JSONObject;
/**
* Detailed info regarding a series, including which sets it includes
*
*/
public class SeriesInfo extends SeriesResume {
private final List<SetResume> sets;
SeriesInfo(String id, String name, List<SetResume> sets) {
super(id, name);
this.sets = sets;
}
SeriesInfo(JSONObject json) {
super(json);
this.sets = SetResume.parse(json.optJSONArray("sets"));
}
/**
* @return Resumes of the sets part of this series
*/
public List<SetResume> getSets() {
return sets;
}
}

View File

@ -0,0 +1,37 @@
package com.github.maxopoly.tcgdex;
import org.json.JSONObject;
/**
* Contains all information describing a series, an overarching group of sets, for example XY
*
*/
public class SeriesResume {
private final String id;
private final String name;
SeriesResume(String id, String name) {
this.id = id;
this.name = name;
}
SeriesResume(JSONObject json) {
this(json.getString("id"), json.getString("name"));
}
/**
* @return Serie unique ID
*/
public String getId() {
return id;
}
/**
* @return Serie name
*/
public String getName() {
return name;
}
}

View File

@ -0,0 +1,131 @@
package com.github.maxopoly.tcgdex;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import org.json.JSONObject;
/**
* Detailed information regarding a set
*
*/
public class SetInfo extends SetResume {
private static final DateTimeFormatter timeParser = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private final int holo;
private final int normal;
private final int firstEd;
private final int reverse;
private final SeriesResume series;
private final String tcgOnlineCode;
private final LocalDate releaseDate;
private final boolean legalInStandard;
private final boolean legalInExpanded;
private final List<CardResume> cards;
SetInfo(JSONObject json) {
super(json);
JSONObject count = json.getJSONObject("cardCount");
this.holo = count.getInt("holo");
this.firstEd = count.getInt("firstEd");
this.reverse = count.getInt("reverse");
this.normal = count.getInt("normal");
this.series = new SeriesResume(json.getJSONObject("serie"));
this.tcgOnlineCode = json.getString("tcgOnline");
this.releaseDate = LocalDate.parse(json.getString("releaseDate"), timeParser);
JSONObject legality = json.getJSONObject("legal");
this.legalInExpanded = legality.getBoolean("expanded");
this.legalInStandard = legality.getBoolean("standard");
this.cards = CardResume.parse(json.getJSONArray("cards"));
}
SetInfo(String id, String name, String logo, String symbol, int officialCardCount, int totalCardCount, int holo,
int normal, int firstEd, int reverse, SeriesResume series, String tcgOnlineCode, LocalDate releaseDate,
boolean legalInStandard, boolean legalInExpanded, List<CardResume> cards) {
super(id, name, logo, symbol, officialCardCount, totalCardCount);
this.holo = holo;
this.normal = normal;
this.firstEd = firstEd;
this.reverse = reverse;
this.series = series;
this.tcgOnlineCode = tcgOnlineCode;
this.releaseDate = releaseDate;
this.legalInStandard = legalInStandard;
this.legalInExpanded = legalInExpanded;
this.cards = cards;
}
/**
* @return Amount of holo cards the set has
*/
public int getHoloCardCount() {
return holo;
}
/**
* @return Amount of normal cards the set has
*/
public int getNormalCardCount() {
return normal;
}
/**
* @return Amount of first edition cards the set has
*/
public int getFirstEdCardCount() {
return firstEd;
}
/**
* @return Amount of reverse cards the set has
*/
public int getReverseCardCount() {
return reverse;
}
/**
* @return Series the set is part of
*/
public SeriesResume getSeries() {
return series;
}
/**
* @return Pokémon TCG Online Set code
*/
public String getTcgOnlineCode() {
return tcgOnlineCode;
}
/**
* @return When the set was released
*/
public LocalDate getReleaseDate() {
return releaseDate;
}
/**
* @return Ability to use this set in standard competitions
*/
public boolean isLegalInStandard() {
return legalInStandard;
}
/**
* @return Ability to use this set in Expanded competitions
*/
public boolean isLegalInExpanded() {
return legalInExpanded;
}
/**
* @return All cards part of the set
*/
public List<CardResume> getCards() {
return cards;
}
}

View File

@ -0,0 +1,89 @@
package com.github.maxopoly.tcgdex;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* Contains all information describing a set of cards
*
*/
public class SetResume {
static List<SetResume> parse(JSONArray array) {
if (array == null) {
return Collections.emptyList();
}
List<SetResume> result = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
result.add(new SetResume(array.getJSONObject(i)));
}
return result;
}
private final String id;
private final String name;
private final String logo;
private final String symbol;
private final int officialCardCount;
private final int totalCardCount;
SetResume(String id, String name, String logo, String symbol, int officialCardCount, int totalCardCount) {
this.id = id;
this.name = name;
this.logo = logo;
this.symbol = symbol;
this.officialCardCount = officialCardCount;
this.totalCardCount = totalCardCount;
}
public SetResume(JSONObject json) {
this(json.getString("id"), json.getString("name"), json.optString("logo"), json.optString("symbol"),
json.getJSONObject("cardCount").getInt("total"), json.getJSONObject("cardCount").getInt("official"));
}
/**
* @return Set unique ID
*/
public String getId() {
return id;
}
/**
* @return Set name
*/
public String getName() {
return name;
}
/**
* @return Set logo URL, may be null
*/
public String getLogo() {
return logo;
}
/**
* @return Set symbol URL, may be null
*/
public String getSymbol() {
return symbol;
}
/**
* @return Official amount of cards in this set
*/
public int getOfficialCardCount() {
return officialCardCount;
}
/**
* @return Total amount of cards in this set
*/
public int getTotalCardCount() {
return totalCardCount;
}
}

View File

@ -0,0 +1,189 @@
package com.github.maxopoly.tcgdex;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
public class TCGDexAPI {
public enum Language {
DE, EN, ES, FR, IT, PT;
public String getAPIID() {
return toString().toLowerCase();
}
}
private static final String API_URL = "https://api.tcgdex.net/v2/%s/%s";
private Language language;
/**
* Construct a new API instance. Does not do any networking, merely initialized
* the local state. Multiple instances of this class may be held and used at the
* same time without issue
*
* @param language Language to use for all queries
*/
public TCGDexAPI(Language language) {
this.language = language;
}
/**
* Gets a list containing the core information for every core
*
* @return Resume of every card available
* @throws IOException Thrown in response to any kind of networking error
*/
public List<CardResume> getAllCards() throws IOException {
String data = Utils.doGet(buildURL("cards"));
return CardResume.parse(new JSONArray(data));
}
/**
* Gets detailed information of a card based on a sets identifier and the
* identifier/index of a card within this set. Note that for example 'base4-1'
* is already a combined identifier, here the correct setID would be 'base4' and
* the cardID '1'
*
* @param setID Unique ID of the set
* @param cardID ID/index describing the card within the set
* @return Card info obtained
* @throws IOException Thrown in response to any kind of networking error
*/
public CardInfo getCardInfo(String setID, String cardID) throws IOException {
String data = Utils.doGet(buildURL("sets", setID, cardID));
return new CardInfo(new JSONObject(data));
}
/**
* Gets detailed information of a card based on its globally unique identifier,
* for example 'base4-1'
*
* @param globalCardID Globally unique ID of the card
* @return Card info obtained
* @throws IOException Thrown in response to any kind of networking error
*/
public CardInfo getCardInfo(String globalCardID) throws IOException {
String data = Utils.doGet(buildURL("cards", globalCardID));
return new CardInfo(new JSONObject(data));
}
/**
* Gets detailed information of a card based on its resume
*
* @param card Card to get info for
* @return Card info obtained
* @throws IOException Thrown in response to any kind of networking error
*/
public CardInfo getCardInfo(CardResume card) throws IOException {
return getCardInfo(card.getId());
}
/**
* Gets a list containing the core information regarding all series
*
* @return Resume of each series
* @throws IOException Thrown in response to any kind of networking error
*/
public List<SeriesResume> getAllSeries() throws IOException {
String data = Utils.doGet(buildURL("series"));
JSONArray json = new JSONArray(data);
List<SeriesResume> result = new ArrayList<>();
for (int i = 0; i < json.length(); i++) {
result.add(new SeriesResume(json.getJSONObject(i)));
}
return result;
}
/**
* Gets a list containing the core information regarding all sets
*
* @return Resume of each set
* @throws IOException Thrown in response to any kind of networking error
*/
public List<SetResume> getAllSets() throws IOException {
String data = Utils.doGet(buildURL("sets"));
return SetResume.parse(new JSONArray(data));
}
/**
* Gets detailed information of a series based on its ID
*
* @param seriesID ID of the series
* @return Detailed information of the series
* @throws IOException Thrown in response to any kind of networking error
*/
public SeriesInfo getSeriesInfo(String seriesID) throws IOException {
String data = Utils.doGet(buildURL("series", seriesID));
return new SeriesInfo(new JSONObject(data));
}
/**
* Gets detailed information of a set based on its ID
*
* @param setID ID of the set
* @return Detailed information of the set
* @throws IOException Thrown in response to any kind of networking error
*/
public SetInfo getSetInfo(String setID) throws IOException {
String data = Utils.doGet(buildURL("sets", setID));
return new SetInfo(new JSONObject(data));
}
List<String> loadRarities() throws IOException {
return loadStringArrayFrom("rarities");
}
List<String> loadCategories() throws IOException {
return loadStringArrayFrom("categories");
}
List<String> loadTypes() throws IOException {
return loadStringArrayFrom("types");
}
/**
* Gets a list of all known card illustrators
*
* @return List of all illustrators
* @throws IOException Thrown in response to any kind of networking error
*/
public List<String> getAllIllustrators() throws IOException {
return loadStringArrayFrom("illustrators");
}
public List<Integer> getAllPossibleHPValues() throws IOException {
String data = Utils.doGet(buildURL("hp"));
JSONArray json = new JSONArray(data);
List<Integer> result = new ArrayList<>();
for (int i = 0; i < json.length(); i++) {
result.add(json.getInt(i));
}
return result;
}
private List<String> loadStringArrayFrom(String path) throws IOException {
String data = Utils.doGet(buildURL(path));
JSONArray json = new JSONArray(data);
List<String> result = new ArrayList<>();
for (int i = 0; i < json.length(); i++) {
result.add(json.getString(i));
}
return result;
}
private String buildURL(String path, String... optional) {
String result = String.format(API_URL, this.language.getAPIID(), path);
// not gonna do a string builder here, because we intend this array to be of
// length 1 in almost all cases
for (String opt : optional) {
result += "/" + opt;
}
return result;
}
}

View File

@ -0,0 +1,39 @@
package com.github.maxopoly.tcgdex;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.json.JSONArray;
public enum Types {
PSYCHIC, FIGHTING, COLORLESS, LIGHTNING, GRASS, DRAGON, METAL, FIRE, WATER, DARKNESS, FAIRY;
static List<Types> parse(JSONArray array) {
if (array == null) {
return Collections.emptyList();
}
List<Types> result = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
Types type = parse(array.getString(i));
if (type != null) {
result.add(type);
}
}
return result;
}
static Types parse(String raw) {
try {
return Types.valueOf(raw.toUpperCase().replace(" ", "_"));
} catch (IllegalArgumentException e) {
return null;
}
}
public String toPrettyString() {
return Utils.prettifyEnumName(this);
}
}

View File

@ -0,0 +1,57 @@
package com.github.maxopoly.tcgdex;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
class Utils {
static String doGet(String url) throws IOException {
CloseableHttpClient client = null;
CloseableHttpResponse response = null;
client = HttpClients.createDefault();
HttpGet httpPost = new HttpGet(url);
response = client.execute(httpPost);
String output = null;
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = null;
StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null) {
builder.append(line);
}
output = builder.toString();
return output;
}
public static String prettifyEnumName(Enum<?> enumInstance) {
boolean space = true;
String rawRepresentation = enumInstance.name();
StringBuilder output = new StringBuilder(rawRepresentation.length());
for(int i = 0; i < rawRepresentation.length(); i++) {
String character = rawRepresentation.substring(i, i+1);
if (character.equals("_")) {
output.append(" ");
space = true;
continue;
}
if (space) {
//keep upper case
space = false;
output.append(character);
}
else {
output.append(character.toLowerCase());
}
}
return output.toString();
}
}

View File

@ -0,0 +1,74 @@
package com.github.maxopoly.tcgdex;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* Describes the weakness of a single pokemon, for example: 2x to Fire
*
*/
public class Weakness {
static List<Weakness> parse(JSONArray array) {
if (array == null) {
return Collections.emptyList();
}
List<Weakness> result = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
result.add(new Weakness(array.getJSONObject(i)));
}
return result;
}
private final Types type;
private final String value;
Weakness(Types type, String value) {
this.type = type;
this.value = value;
}
Weakness(JSONObject json) {
this(Types.parse(json.getString("type")), json.getString("value"));
}
public boolean equals(Object o) {
if (!(o instanceof Weakness)) {
return false;
}
Weakness other = (Weakness) o;
return Objects.deepEquals(new Object[] { this.type, this.value},
new Object[] { other.type, other.value});
}
public int hashCode() {
return Objects.hash(this.type, this.value);
}
public String toString() {
return String.format("%s %s", this.type, this.value);
}
/**
* @return Type the weakness is to
*/
public Types getType() {
return type;
}
/**
* @return Descriptor of the weakness multiplier, including a leading x, for example 'x2'
*/
public String getValue() {
return value;
}
}

View File

@ -0,0 +1,155 @@
package com.github.maxopoly.tcgdex;
import org.junit.Test;
import com.github.maxopoly.tcgdex.TCGDexAPI.Language;
import static org.junit.Assert.*;
import java.io.IOException;
import java.time.Month;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
public class TestAPI {
private TCGDexAPI api;
@Before
public void initAPI() {
this.api = new TCGDexAPI(Language.EN);
}
@Test
public void testRarities() {
assertEquals("Rare", Rarities.RARE.toPrettyString());
assertEquals("Ultra Rare", Rarities.ULTRA_RARE.toPrettyString());
assertEquals("Secret Rare", Rarities.SECRET_RARE.toPrettyString());
assertEquals("Common", Rarities.COMMON.toPrettyString());
}
@Test
public void compareRarityEnumToAPI() throws IOException {
List<String> rarities = api.loadRarities();
assertNotNull(rarities);
assertEquals(rarities.size(), Rarities.values().length);
for (String rawRarity : rarities) {
Rarities rarity = Rarities.parse(rawRarity);
assertNotNull(rarity);
assert (rarity.toPrettyString().equals(rawRarity));
}
}
@Test
public void compareCategoryEnumToAPI() throws IOException {
List<String> categories = api.loadCategories();
assertNotNull(categories);
assertEquals(categories.size(), Categories.values().length);
for (String rawCategory : categories) {
Categories category = Categories.parse(rawCategory);
assertNotNull(category);
assert (category.toPrettyString().equals(rawCategory));
}
}
@Test
public void compareTypeEnumToAPI() throws IOException {
List<String> types = api.loadTypes();
assertNotNull(types);
assertEquals(types.size(), Types.values().length);
for (String rawType : types) {
Types type = Types.parse(rawType);
assertNotNull(type);
assert (type.toPrettyString().equals(rawType));
}
}
@Test
public void testHPAPI() throws IOException {
List<Integer> hp = api.getAllPossibleHPValues();
assertNotNull(hp);
assert (hp.size() > 30);
assert (hp.contains(60));
assert (hp.contains(140));
assert (hp.contains(320));
}
@Test
public void testFullCardInfo() throws IOException {
CardInfo info = api.getCardInfo("base4", "1");
assertNotNull(info);
assertEquals(Categories.POKEMON, info.getCategory());
assertEquals("base4-1", info.getId());
assertEquals("Ken Sugimori", info.getIllustrator());
assertEquals("1", info.getLocalId());
assertEquals("Alakazam", info.getName());
assertEquals(Rarities.RARE, info.getRarity());
SetResume resume = info.getSet();
assertNotNull(resume);
assertEquals(130, resume.getOfficialCardCount());
assertEquals(130, resume.getTotalCardCount());
assertEquals("base4", resume.getId());
assertEquals("Base Set 2", resume.getName());
assertFalse(info.hasFirstEditionPic());
assertTrue(info.hasHoloVariant());
assertTrue(info.hasNormalVariant());
assertTrue(info.hasReverseVariant());
assertArrayEquals(new Integer[] { 65 }, info.getDexIDs().toArray());
Attack attack = new Attack(Arrays.asList(Types.PSYCHIC, Types.PSYCHIC, Types.PSYCHIC), "Confuse Ray",
"Flip a coin. If heads, the Defending Pokémon is now Confused.", "30");
assertArrayEquals(new Attack[] { attack }, info.getAttacks().toArray());
assertEquals(80, (int) info.getHp());
assertArrayEquals(new Types[] { Types.PSYCHIC }, info.getTypes().toArray());
Ability ability = new Ability("Poke-POWER", "Damage Swap",
"As often as you like during your turn (before your attack), you may move 1 damage counter from 1 of your "
+ "Pokémon to another as long as you don't Knock Out that Pokémon. This power can't be used if Alakazam is Asleep, "
+ "Confused, or Paralyzed.");
assertArrayEquals(new Ability[] { ability }, info.getAbilities().toArray());
assertEquals("Stage2", info.getStage());
assertEquals("Kadabra", info.getEvolveFrom());
assertArrayEquals(new Weakness[] { new Weakness(Types.PSYCHIC, "×2") }, info.getWeakness().toArray());
// fetch a few more cards of different types to make sure there are no
// exceptions due to weird combinations of fields missing
assertNotNull(api.getCardInfo("sm10-183"));
assertNotNull(api.getCardInfo("ex8-90"));
assertNotNull(api.getCardInfo("swsh3-84"));
assertNotNull(api.getCardInfo("swsh4-98"));
assertNotNull(api.getCardInfo("ex13-96"));
}
@Test
public void testSets() throws IOException {
SetInfo set = api.getSetInfo("sm10");
assertNotNull(set);
assertEquals(234, set.getCards().size());
assertEquals(234, set.getOfficialCardCount());
assertEquals(0, set.getReverseCardCount());
assertEquals(0, set.getHoloCardCount());
assertEquals(0, set.getFirstEdCardCount());
assertEquals("Unbroken Bonds", set.getName());
assertEquals(3, set.getReleaseDate().getDayOfMonth());
assertEquals(Month.MAY, set.getReleaseDate().getMonth());
assertEquals("UNB", set.getTcgOnlineCode());
assertEquals("Sun & Moon", set.getSeries().getName());
assertTrue(set.isLegalInExpanded());
assertFalse(set.isLegalInStandard());
}
@Test
public void testSeries() throws IOException {
SeriesInfo info = api.getSeriesInfo("sm");
assertEquals("sm", info.getId());
assertEquals("Sun & Moon", info.getName());
assertEquals(18, info.getSets().size());
assertEquals("Shining Legends", info.getSets().get(4).getName());
}
@Test
public void testImage() {
}
}