From f4b8ebd7a9572449bf794bc54d8f34830fb085f9 Mon Sep 17 00:00:00 2001 From: Avior Date: Thu, 14 Apr 2022 13:02:53 +0200 Subject: [PATCH 01/11] feat: Started full rework to make it work like the Other SDKs Signed-off-by: Avior --- .editorconfig | 34 +++ .gitattributes | 6 + .github/ISSUE_TEMPLATE/bug.yml | 32 +++ .github/ISSUE_TEMPLATE/enhancement.yml | 16 ++ .github/PULL_REQUEST_TEMPLATE.md | 5 + .github/dependabot.yml | 21 ++ .github/workflows/build.yml | 34 +++ .github/workflows/publish.yml | 45 +++ .gitignore | 138 +++------- LICENSE.txt => LICENSE.md | 4 +- README.md | 168 ++++++++++-- build.gradle.kts | 80 ++++++ gradle.properties | 1 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 234 ++++++++++++++++ pom.xml | 64 ----- settings.gradle.kts | 2 + src/main/java/net/tcgdex/Ability.java | 79 ------ src/main/java/net/tcgdex/Attack.java | 90 ------ src/main/java/net/tcgdex/CardInfo.java | 259 ------------------ src/main/java/net/tcgdex/CardResume.java | 95 ------- src/main/java/net/tcgdex/Categories.java | 15 - src/main/java/net/tcgdex/Rarities.java | 16 -- src/main/java/net/tcgdex/SeriesInfo.java | 31 --- src/main/java/net/tcgdex/SeriesResume.java | 37 --- src/main/java/net/tcgdex/SetInfo.java | 130 --------- src/main/java/net/tcgdex/SetResume.java | 89 ------ src/main/java/net/tcgdex/TCGDexAPI.java | 243 ---------------- src/main/java/net/tcgdex/Types.java | 39 --- src/main/java/net/tcgdex/Utils.java | 64 ----- src/main/java/net/tcgdex/Weakness.java | 77 ------ src/main/kotlin/net/tcgdex/sdk/TCGdex.kt | 134 +++++++++ src/main/kotlin/net/tcgdex/sdk/Utils.kt | 57 ++++ .../net/tcgdex/sdk/internal/CacheEntry.kt | 8 + .../kotlin/net/tcgdex/sdk/internal/Model.kt | 10 + src/main/kotlin/net/tcgdex/sdk/models/Card.kt | 133 +++++++++ .../net/tcgdex/sdk/models/CardResume.kt | 38 +++ .../kotlin/net/tcgdex/sdk/models/Extension.kt | 7 + .../kotlin/net/tcgdex/sdk/models/Quality.kt | 6 + .../kotlin/net/tcgdex/sdk/models/Serie.kt | 38 +++ .../net/tcgdex/sdk/models/SerieResume.kt | 17 ++ src/main/kotlin/net/tcgdex/sdk/models/Set.kt | 55 ++++ .../kotlin/net/tcgdex/sdk/models/SetResume.kt | 54 ++++ .../net/tcgdex/sdk/models/StringEndpoint.kt | 6 + .../net/tcgdex/sdk/models/subs/CardAbility.kt | 21 ++ .../net/tcgdex/sdk/models/subs/CardAttack.kt | 25 ++ .../net/tcgdex/sdk/models/subs/CardWeakRes.kt | 17 ++ .../tcgdex/sdk/models/subs/SetCardCount.kt | 28 ++ .../sdk/models/subs/SetCardCountResume.kt | 6 + src/test/java/net/tcgdex/TestAPI.java | 169 ------------ src/test/kotlin/net/tcgdex/sdk/APITest.kt | 51 ++++ 52 files changed, 1418 insertions(+), 1615 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/enhancement.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/publish.yml rename LICENSE.txt => LICENSE.md (94%) create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew delete mode 100644 pom.xml create mode 100644 settings.gradle.kts delete mode 100644 src/main/java/net/tcgdex/Ability.java delete mode 100644 src/main/java/net/tcgdex/Attack.java delete mode 100644 src/main/java/net/tcgdex/CardInfo.java delete mode 100644 src/main/java/net/tcgdex/CardResume.java delete mode 100644 src/main/java/net/tcgdex/Categories.java delete mode 100644 src/main/java/net/tcgdex/Rarities.java delete mode 100644 src/main/java/net/tcgdex/SeriesInfo.java delete mode 100644 src/main/java/net/tcgdex/SeriesResume.java delete mode 100644 src/main/java/net/tcgdex/SetInfo.java delete mode 100644 src/main/java/net/tcgdex/SetResume.java delete mode 100644 src/main/java/net/tcgdex/TCGDexAPI.java delete mode 100644 src/main/java/net/tcgdex/Types.java delete mode 100644 src/main/java/net/tcgdex/Utils.java delete mode 100644 src/main/java/net/tcgdex/Weakness.java create mode 100644 src/main/kotlin/net/tcgdex/sdk/TCGdex.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/Utils.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/internal/CacheEntry.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/internal/Model.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/Card.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/Extension.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/Quality.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/Serie.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/Set.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt delete mode 100644 src/test/java/net/tcgdex/TestAPI.java create mode 100644 src/test/kotlin/net/tcgdex/sdk/APITest.kt diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f3d1f59 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,34 @@ +root = true + +# Base Configuration +[*] +indent_style = tab +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 80 +end_of_line = lf + +# Yaml Standard +[*.{yaml,yml}] +indent_style = space +indent_size = 2 + +# Markdown Standards +[*.md] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false + +# C++ Standards +[*.{cpp,h,ino}] +indent_style = space + +# PHP Standards +[*.php] +indent_style = space + +# Java/Kotlin Standards +[*.{java,kt,kts}] +indent_style = space diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0e203ee --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf +* text=auto eol=lf diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..cc91a46 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,32 @@ +name: Bug +description: Use this template if you have found a bug in the SDK +title: "bug: " +labels: [bug] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this Form! + - type: textarea + id: happening + attributes: + label: What is Hapening + placeholder: "Ex: When using this function the SDK crash..." + validations: + required: true + - type: textarea + id: happen + attributes: + label: Please explain what should happen + description: If you selected Other or new Data Type please put it as the first line + placeholder: "Ex: The SDK should not crash" + validations: + required: true + - type: textarea + id: reproducre + attributes: + label: Please give us a way to reproduce + description: You can list a way or give us access to a minimal Github repo to reproduce it + placeholder: "Ex: https://github.com/you/your-repository" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml new file mode 100644 index 0000000..6aaaf57 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -0,0 +1,16 @@ +name: Enhancement +description: Use this template if you have an Idea that will enhance the SDK +title: "enhancement: " +labels: [enhancement] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this Form! + - type: textarea + id: explain + attributes: + label: Please explain in more details what idea you have + placeholder: "Ex: I would love to see this or this..." + validations: + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..ffce708 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3f89993 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,21 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "daily" + commit-message: + prefix: build + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + commit-message: + prefix: build diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..7674218 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,34 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + - name: Build with Gradle + uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee + with: + arguments: build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..9aeb2b8 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,45 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle + +name: Gradle Package + +on: + release: + types: [created] + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + server-id: github # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }} # location for the settings.xml file + + - name: Build with Gradle + uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee + with: + arguments: build + + # The USERNAME and TOKEN need to correspond to the credentials environment variables used in + # the publishing section of your build.gradle + - name: Publish to GitHub Packages + uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee + with: + arguments: publish + env: + USERNAME: ${{ github.actor }} + TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 859d54a..5c84812 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -# 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 +# Created by https://www.toptal.com/developers/gitignore/api/java,batch,linux,macos,gradle,windows,eclipse,intellij+all +# Edit at https://www.toptal.com/developers/gitignore?templates=java,batch,linux,macos,gradle,windows,eclipse,intellij+all ### Batch ### # BatchFiles @@ -9,7 +9,6 @@ *.btm ### Eclipse ### - .metadata bin/ tmp/ @@ -60,104 +59,35 @@ local.properties # Annotation Processing .apt_generated/ +.apt_generated_test/ # 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 +# Uncomment this line if you wish to ignore the project description file. +# Typically, this file would be tracked if it contains build/dependency configurations: +#.project +### Eclipse Patch ### +# Spring Boot Tooling .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/ +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. -# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 +.idea/* -*.iml -modules.xml -.idea/misc.xml -*.ipr +!.idea/codeStyles +!.idea/runConfigurations ### Java ### # Compiled class file @@ -183,6 +113,7 @@ modules.xml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +replay_pid* ### Linux ### *~ @@ -208,6 +139,7 @@ hs_err_pid* # Icon must end with two \r Icon + # Thumbnails ._* @@ -227,21 +159,14 @@ 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 +### macOS Patch ### +# iCloud generated files +*.icloud ### Windows ### # Windows thumbnail cache files Thumbs.db +Thumbs.db:encryptable ehthumbs.db ehthumbs_vista.db @@ -263,3 +188,28 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk + +### Gradle ### +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# End of https://www.toptal.com/developers/gitignore/api/java,batch,linux,macos,gradle,windows,eclipse,intellij+all diff --git a/LICENSE.txt b/LICENSE.md similarity index 94% rename from LICENSE.txt rename to LICENSE.md index 45f2a9c..1dda0c2 100644 --- a/LICENSE.txt +++ b/LICENSE.md @@ -1,6 +1,6 @@ - The MIT License (MIT) +The MIT License (MIT) -Copyright © 2021 Maxopoly +Copyright © 2022 Maxopoly and Avior 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: diff --git a/README.md b/README.md index 9729f7e..9c14272 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,155 @@ -# Tcgdex-java +

+ + TCGdex Main Image + +

+

+ + Package Version + + + + Github stars + + + the TCGdex JAvascript SDK is released under the MIT license. + + + Discord Link + +

-This is a Java API for querying the Pokémon TCG database of [TCGdex](https://github.com/tcgdex/). +# TCGdex Kotlin/Java SDK -To use it, first initialize an API instance with the language you want results to be in -```java -TCGDexAPI api = new TCGDexAPI(TCGDexAPI.Language.EN); +The Kotlin/Java SDK provides a convenient access with the Open Source TCGdex API. + +The SDK is available both for Kotlin use and Java. + +## Documentation + +_The full API/SDK documentation in progress at [API Documentation - TCGdex](https://www.tcgdex.dev)_ + +### Getting Started + +#### How To install + +**Gradle** + +1. add the repository in you `build.gradle` + +```gradle +repositories { + ... + maven { url 'https://jitpack.io' } +} ``` -Use it to obtain POJOs representing cards, sets and series. For all of these, there is a function listing all available including short info and a function to get detailed information regarding one. +2. Add the dependency in your `build.gradle` -```java -//all cards available -List allCards = api.getAllCards(); -//detailed card info -CardInfo card = api.getCardInfo(allCards.get(23)); +```gradle +dependencies { + implementation 'com.github.tcgdex:java-sdk:{Version}' +} +``` + +**Maven** + +1. add the repository in your `pom.xml` -//all sets available -List allSets = getAllSets(); -//Obtained set info either based on the listing of all sets -SetInfo set = getSetInfo(allSets.get(1)); -//or based on some card, also works for CardInfo -SetInfo set2 = getSetInfo(allCards.get(10)); +```xml + + + jitpack.io + https://jitpack.io + + +``` -//all series available -List allSeries = getAllSeries(); -//same possibilties for series -SeriesInfo series = getSeriesInfo(allSeries.get(2)); -SeriesInfo series2 = getSeriesInfo(set); +2. Add the depedency in your `pom.xml` +```xml + + com.github.tcgdex + java-sdk + {Version} + ``` + +#### Usage + +_Note: a complete documentation is available at [TCGdex.dev](https://www.tcgdex.dev)_ + +##### Kotlin + +**Example: Fetch a Card** + +```kotlin +// Import the SDK +import net.tcgdex.sdk.TCGdex + +// Init the library with the language code (see the API REST documentation for the list) +val api = TCGdex("en") + +// returns you a Card Class with every informations filled! +val card = api.fetchCard("swsh1-1") +``` + +**Other Functions** + +```kotlin +api.fetchCard("swsh3-136") +api.fetchCard("swsh3", "136") +api.fetchSet("swsh3") +api.fetchSets() +api.fetchSerie("swsh") +api.fetchSeries() +api.fetchTypes() +api.fetchType("Colorless") +api.fetchRetreats() +api.fetchRetreat("1") +api.fetchRarities() +api.fetchRarity("Uncommon") +api.fetchIllustrators() +api.fetchIllustrator("tetsuya koizumi") +api.fetchHPs() +api.fetchHP("110") +api.fetchCategories() +api.fetchCategory("Pokemon") +api.fetchDexIds() +api.fetchDexId("162") +api.fetchEnergyTypes() +api.fetchEnergyType("Special") +api.fetchRegulationMarks() +api.fetchRegulationMark("D") +api.fetchStages() +api.fetchStage("Basic") +api.fetchSuffixes() +api.fetchSuffix("EX") +api.fetchTrainerTypes() +api.fetchTrainerType("Tool") +api.fetchVariants() +api.fetchVariant("holo") +``` + +## Contributing + +See [CONTRIBUTING.md](https://github.com/tcgdex/javascript-sdk/blob/master/CONTRIBUTING.md) + +TL::DR + +- Fork + +- Commit your changes + +- Pull Request on this Repository + +## License + +This project is licensed under the MIT License. A copy of the license is available at [LICENSE.md](https://github.com/tcgdex/javascript-sdk/blob/master/LICENSE.md) + +This is based on the [Maxopoly](https://github.com/Maxopoly) TCGdex Java SDK diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..31b2178 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,80 @@ +plugins { + // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. + kotlin("jvm") version "1.6.20" + + // Apply the java-library plugin for API and implementation separation. + `java-library` + `maven-publish` +} + +repositories { + mavenCentral() +} + +dependencies { + + // Use the Kotlin JDK 8 standard library. + implementation(kotlin("stdlib-jdk8")) + // Gson + implementation("com.google.code.gson:gson:2.9.0") + + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + } +} + + +tasks { + compileKotlin { + kotlinOptions.jvmTarget = "1.8" + } + compileTestKotlin { + kotlinOptions.jvmTarget = "1.8" + } + +} + +java { + withJavadocJar() + withSourcesJar() +} + +publishing { + publications { + create("maven") { + groupId = "net.tcgdex" + artifactId = "sdk" + version = "2.0.0" + + from(components["java"]) + + pom { + name.set("TCGdex SDK") + description.set("Communicate with the Open Source TCGdex API in Kotlin/Java using the SDK") + url.set("https://github.com/tcgdex/java-sdk") + licenses { + license { + name.set("MIT License") + url.set("https://github.com/tcgdex/java-sdk/blob/master/LICENSE.txt") + } + } + developers { + developer { + id.set("avior") + name.set("Avior") + email.set("contact@tcgdex.net") + } + } + scm { + connection.set("scm:git@github.com:tcgdex/java-sdk.git") + url.set("https://github.com/tcgdex/java-sdk") + } + } + } + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..7fc6f1f --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7454180f2ae8848c63b8b4dea2cb829da983f2fa GIT binary patch literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..fe753c9 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..1b6c787 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 4502080..0000000 --- a/pom.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - 4.0.0 - - net.tcgdex - - tcgdex - - tcgdex - 1.0.0 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - 11 - 11 - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.3 - - - package - - shade - - - false - - - - - - - - - - org.json - json - 20160810 - - - junit - junit - 4.13.2 - test - - - org.apache.httpcomponents - httpclient - 4.5.13 - - - - diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..6c007c1 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,2 @@ + +rootProject.name = "tcgdex-sdk" diff --git a/src/main/java/net/tcgdex/Ability.java b/src/main/java/net/tcgdex/Ability.java deleted file mode 100644 index efa5f6d..0000000 --- a/src/main/java/net/tcgdex/Ability.java +++ /dev/null @@ -1,79 +0,0 @@ -package net.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 parse(JSONArray array) { - if (array == null) { - return Collections.emptyList(); - } - List 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(JSONObject json) { - this(json.getString("type"), json.getString("name"), json.getString("effect")); - } - - Ability(String type, String name, String effect) { - this.type = type; - this.name = name; - this.effect = effect; - } - - @Override - 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 }); - } - - /** - * - * @return Description/Effect of the ability - */ - public String getEffect() { - return effect; - } - - /** - * @return Name of the ability - */ - public String getName() { - return name; - } - - /** - * @return Type of the ability, for example 'Poke-POWER' - */ - public String getType() { - return type; - } - - @Override - public int hashCode() { - return Objects.hash(this.type, this.name, this.effect); - } - -} diff --git a/src/main/java/net/tcgdex/Attack.java b/src/main/java/net/tcgdex/Attack.java deleted file mode 100644 index 8b0a656..0000000 --- a/src/main/java/net/tcgdex/Attack.java +++ /dev/null @@ -1,90 +0,0 @@ -package net.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 parse(JSONArray array) { - if (array == null) { - return Collections.emptyList(); - } - List result = new ArrayList<>(); - for (int i = 0; i < array.length(); i++) { - result.add(new Attack(array.getJSONObject(i))); - } - return result; - } - - private final List cost; - private final String name; - private final String effect; - private final String damage; - - Attack(JSONObject json) { - this(Types.parse(json.optJSONArray("cost")), json.getString("name"), json.optString("effect"), - json.optString("damage")); - } - - Attack(List cost, String name, String effect, String damage) { - super(); - this.cost = cost; - this.name = name; - this.effect = effect; - this.damage = damage; - } - - @Override - 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 }); - } - - /** - * @return Cost of the attack in the same order as listed on the card - */ - public List getCost() { - return cost; - } - - /** - * @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; - } - - /** - * @return Effect/Description of the attack, may be null for attacks without text - */ - public String getEffect() { - return effect; - } - - /** - * @return Name of the attack - */ - public String getName() { - return name; - } - - @Override - public int hashCode() { - return Objects.hash(this.cost, this.name, this.effect, this.damage); - } - -} diff --git a/src/main/java/net/tcgdex/CardInfo.java b/src/main/java/net/tcgdex/CardInfo.java deleted file mode 100644 index 6b71bbf..0000000 --- a/src/main/java/net/tcgdex/CardInfo.java +++ /dev/null @@ -1,259 +0,0 @@ -package net.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 extends CardResume { - - 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 dexIDs; - private final Integer hp; - private final List types; - private final String evolveFrom; - private final String description; - private final String level; - private final String stage; - private final String suffix; - private final List attacks; - private final List weakness; - private final List abilities; - private final Integer retreat; - private final String regulationMark; - - CardInfo(JSONObject json) { - super(json); - this.illustrator = json.optString("illustrator"); - 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")); - } - - 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 dexIDs, Integer hp, List types, - String evolveFrom, String description, String level, String stage, String suffix, List attacks, - List weakness, List abilities, Integer retreat, String regulationMark) { - super(id, localId, name, 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; - } - - /** - * @return Pokemon's abilities. May be empty if it doesn't have any, but never - * null - */ - public List getAbilities() { - return abilities; - } - - /** - * @return Attacks the pokemon has. Empty for cards without attacks - */ - public List getAttacks() { - return attacks; - } - - /** - * - * @return Card category - */ - public Categories getCategory() { - return category; - } - - /** - * - * @return List of the national pokedex IDs of the pokemon on the card (may be - * multiple) - */ - public List getDexIDs() { - return dexIDs; - } - - /** - * - * @return Card effect/description, may be null - */ - public String getEffect() { - return description; - } - - /** - * - * @return Name of the pokemon this one evolves from - */ - public String getEvolveFrom() { - return evolveFrom; - } - - /** - * @return HP of the pokemon, will be null if the card is not a pokemon - */ - public Integer getHp() { - return hp; - } - - /** - * - * @return Card illustrator, may be null - */ - public String getIllustrator() { - return illustrator; - } - - /** - * - * @return Pokemon level, may be 'X', hence not an integer - */ - public String getLevel() { - return level; - } - - /** - * - * @return Card rarity - */ - public Rarities getRarity() { - return rarity; - } - - /** - * - * @return Card's regulation mark. May be null if unknown or doesn't exist - */ - public String getRegulationMark() { - return regulationMark; - } - - /** - * @return Card's retreat. Will be null for cards without retreat - */ - public Integer getRetreat() { - return retreat; - } - - /** - * @return Resume of the set the card belongs to - */ - public SetResume getSet() { - return set; - } - - /** - * @return Pokemon's stage, like 'Basic' - */ - public String getStage() { - return stage; - } - - /** - * @return Suffix, like 'V', may be null - */ - public String getSuffix() { - return suffix; - } - - /** - * @return Types of the pokemon - */ - public List getTypes() { - return types; - } - - /** - * @return Weaknesses the pokemon has. Empty for cards without attacks - */ - public List getWeakness() { - return weakness; - } - - /** - * - * @return Does the card have a small first edition in the middle left - */ - public boolean hasFirstEditionPic() { - return hasFirstEditionPic; - } - - /** - * - * @return Does the card have a holo variant (picture is shining) - */ - public boolean hasHoloVariant() { - return hasHolo; - } - - /** - * - * @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; - } - -} diff --git a/src/main/java/net/tcgdex/CardResume.java b/src/main/java/net/tcgdex/CardResume.java deleted file mode 100644 index a0be5e3..0000000 --- a/src/main/java/net/tcgdex/CardResume.java +++ /dev/null @@ -1,95 +0,0 @@ -package net.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 implements Comparable { - - static List parse(JSONArray array) { - if (array == null) { - return Collections.emptyList(); - } - List 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(JSONObject json) { - this(json.getString("id"), json.getString("localId"), json.getString("name"), json.optString("image")); - } - - CardResume(String id, String localId, String name, String image) { - this.id = id; - this.localId = localId; - this.name = name; - this.image = image; - } - - public String toString() { - return String.format("%s (%s): %s,%s", name, id, localId, image); - } - - /** - * @return Globally unique card ID based on the set ID and the cards ID within the set - */ - public String getId() { - return id; - } - - /** - * - * @return Card image, can be null - */ - public String getImage() { - return image; - } - - /** - * @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; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof CardResume)) { - return false; - } - CardResume other = (CardResume) o; - return this.id.equals(other.id); - } - - @Override - public int hashCode() { - return this.id.hashCode(); - } - - @Override - public int compareTo(CardResume arg0) { - return this.id.compareTo(arg0.id); - } -} diff --git a/src/main/java/net/tcgdex/Categories.java b/src/main/java/net/tcgdex/Categories.java deleted file mode 100644 index 7bea6d7..0000000 --- a/src/main/java/net/tcgdex/Categories.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.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); - } - -} diff --git a/src/main/java/net/tcgdex/Rarities.java b/src/main/java/net/tcgdex/Rarities.java deleted file mode 100644 index aea6aa9..0000000 --- a/src/main/java/net/tcgdex/Rarities.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.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); - } - - -} diff --git a/src/main/java/net/tcgdex/SeriesInfo.java b/src/main/java/net/tcgdex/SeriesInfo.java deleted file mode 100644 index 5d21128..0000000 --- a/src/main/java/net/tcgdex/SeriesInfo.java +++ /dev/null @@ -1,31 +0,0 @@ -package net.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 sets; - - SeriesInfo(JSONObject json) { - super(json); - this.sets = SetResume.parse(json.optJSONArray("sets")); - } - - SeriesInfo(String id, String name, List sets) { - super(id, name); - this.sets = sets; - } - - /** - * @return Resumes of the sets part of this series - */ - public List getSets() { - return sets; - } -} diff --git a/src/main/java/net/tcgdex/SeriesResume.java b/src/main/java/net/tcgdex/SeriesResume.java deleted file mode 100644 index 3063a26..0000000 --- a/src/main/java/net/tcgdex/SeriesResume.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.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(JSONObject json) { - this(json.getString("id"), json.getString("name")); - } - - SeriesResume(String id, String name) { - this.id = id; - this.name = name; - } - - /** - * @return Serie unique ID - */ - public String getId() { - return id; - } - - /** - * @return Serie name - */ - public String getName() { - return name; - } - -} diff --git a/src/main/java/net/tcgdex/SetInfo.java b/src/main/java/net/tcgdex/SetInfo.java deleted file mode 100644 index b40e45f..0000000 --- a/src/main/java/net/tcgdex/SetInfo.java +++ /dev/null @@ -1,130 +0,0 @@ -package net.tcgdex; - -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -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 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 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 All cards part of the set - */ - public List getCards() { - return cards; - } - - /** - * @return Amount of first edition cards the set has - */ - public int getFirstEdCardCount() { - return firstEd; - } - - /** - * @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 When the set was released - */ - public LocalDate getReleaseDate() { - return releaseDate; - } - - /** - * @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 Ability to use this set in Expanded competitions - */ - public boolean isLegalInExpanded() { - return legalInExpanded; - } - - /** - * @return Ability to use this set in standard competitions - */ - public boolean isLegalInStandard() { - return legalInStandard; - } -} diff --git a/src/main/java/net/tcgdex/SetResume.java b/src/main/java/net/tcgdex/SetResume.java deleted file mode 100644 index 2e1964a..0000000 --- a/src/main/java/net/tcgdex/SetResume.java +++ /dev/null @@ -1,89 +0,0 @@ -package net.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 parse(JSONArray array) { - if (array == null) { - return Collections.emptyList(); - } - List 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; - - 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")); - } - - 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; - } - - /** - * @return Set unique ID - */ - public String getId() { - return id; - } - - /** - * @return Set logo URL, may be null - */ - public String getLogo() { - return logo; - } - - /** - * @return Set name - */ - public String getName() { - return name; - } - - /** - * @return Official amount of cards in this set - */ - public int getOfficialCardCount() { - return officialCardCount; - } - - /** - * @return Set symbol URL, may be null - */ - public String getSymbol() { - return symbol; - } - - /** - * @return Total amount of cards in this set - */ - public int getTotalCardCount() { - return totalCardCount; - } -} diff --git a/src/main/java/net/tcgdex/TCGDexAPI.java b/src/main/java/net/tcgdex/TCGDexAPI.java deleted file mode 100644 index 4bde224..0000000 --- a/src/main/java/net/tcgdex/TCGDexAPI.java +++ /dev/null @@ -1,243 +0,0 @@ -package net.tcgdex; - -import java.awt.image.BufferedImage; -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(); - } - } - - public enum ImageResolution { - HIGH, LOW; - - public String forUseInURL() { - return this.name().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; - } - - 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; - } - - /** - * Loads the image of a card - * @param card Card to get image for - * @param resolution High is recommended if the card is supposed to be readable and more than a thumbnail - * @return BufferedImage loaded, possibly null if no image of this card exists for this language - * @throws IOException Thrown in response to any kind of networking error - */ - public BufferedImage getImage(CardResume card, ImageResolution resolution) throws IOException { - String url = card.getImage() + "/" + resolution.forUseInURL() + ".png"; - return Utils.getImage(url); - } - - /** - * 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 getAllCards() throws IOException { - String data = Utils.doGet(buildURL("cards")); - return CardResume.parse(new JSONArray(data)); - } - - /** - * 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 getAllIllustrators() throws IOException { - return loadStringArrayFrom("illustrators"); - } - - public List getAllPossibleHPValues() throws IOException { - String data = Utils.doGet(buildURL("hp")); - JSONArray json = new JSONArray(data); - List result = new ArrayList<>(); - for (int i = 0; i < json.length(); i++) { - result.add(json.getInt(i)); - } - return result; - } - - /** - * 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 getAllSeries() throws IOException { - String data = Utils.doGet(buildURL("series")); - JSONArray json = new JSONArray(data); - List 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 getAllSets() throws IOException { - String data = Utils.doGet(buildURL("sets")); - return SetResume.parse(new JSONArray(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 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)); - JSONObject json = new JSONObject(data); - if (json.has("error")) { - return null; - } - return new CardInfo(json); - } - - /** - * 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)); - JSONObject json = new JSONObject(data); - if (json.has("error")) { - return null; - } - return new CardInfo(json); - } - - /** - * Gets detailed information of a series based on a set belonging to it - * - * @param set Set the series belongs to - * @return Detailed information of the series - * @throws IOException Thrown in response to any kind of networking error - */ - public SeriesInfo getSeriesInfo(SetInfo set) throws IOException { - String data = Utils.doGet(buildURL("series", set.getSeries().getId())); - return new SeriesInfo(new JSONObject(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 a card belonging to it - * - * @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(CardResume card) throws IOException { - String id = card.getId(); - String data = Utils.doGet(buildURL("sets", id.substring(0, id.lastIndexOf("-")))); - return new SetInfo(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 loadCategories() throws IOException { - return loadStringArrayFrom("categories"); - } - - List loadRarities() throws IOException { - return loadStringArrayFrom("rarities"); - } - - private List loadStringArrayFrom(String path) throws IOException { - String data = Utils.doGet(buildURL(path)); - JSONArray json = new JSONArray(data); - List result = new ArrayList<>(); - for (int i = 0; i < json.length(); i++) { - result.add(json.getString(i)); - } - return result; - } - - List loadTypes() throws IOException { - return loadStringArrayFrom("types"); - } - -} diff --git a/src/main/java/net/tcgdex/Types.java b/src/main/java/net/tcgdex/Types.java deleted file mode 100644 index f82021a..0000000 --- a/src/main/java/net/tcgdex/Types.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.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 parse(JSONArray array) { - if (array == null) { - return Collections.emptyList(); - } - List 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); - } - -} diff --git a/src/main/java/net/tcgdex/Utils.java b/src/main/java/net/tcgdex/Utils.java deleted file mode 100644 index 320228e..0000000 --- a/src/main/java/net/tcgdex/Utils.java +++ /dev/null @@ -1,64 +0,0 @@ -package net.tcgdex; - -import java.awt.image.BufferedImage; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; - -import javax.imageio.ImageIO; - -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 BufferedImage getImage(String imageUrl) throws IOException { - return ImageIO.read(new URL(imageUrl)); - } - - 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(); - } - -} diff --git a/src/main/java/net/tcgdex/Weakness.java b/src/main/java/net/tcgdex/Weakness.java deleted file mode 100644 index e1f1bd6..0000000 --- a/src/main/java/net/tcgdex/Weakness.java +++ /dev/null @@ -1,77 +0,0 @@ -package net.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 parse(JSONArray array) { - if (array == null) { - return Collections.emptyList(); - } - List 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(JSONObject json) { - this(Types.parse(json.getString("type")), json.optString("value")); - } - - Weakness(Types type, String value) { - this.type = type; - this.value = value; - } - - @Override - 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}); - } - - /** - * @return Type the weakness is to - */ - public Types getType() { - return type; - } - - /** - * @return Descriptor of the weakness multiplier, including a leading x, for example 'x2'. May be null - */ - public String getValue() { - return value; - } - - @Override - public int hashCode() { - return Objects.hash(this.type, this.value); - } - - @Override - public String toString() { - return String.format("%s %s", this.type, this.value); - } - - - -} diff --git a/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt b/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt new file mode 100644 index 0000000..44cfa1e --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt @@ -0,0 +1,134 @@ +package net.tcgdex.sdk + +import net.tcgdex.sdk.models.* +import net.tcgdex.sdk.models.Set + +class TCGdex( + var language: String +) { + + private val URI = "https://api.tcgdex.net/v2" + + fun fetchCards(): Array? { + return this.fetch(Array::class.java, "cards") + } + + fun fetchCard(cardId: String): Card? { + return this.fetch(Card::class.java, "cards", cardId) + } + + fun fetchCard(setId: String, cardId: String): Card? { + return this.fetch(Card::class.java, "sets", setId, cardId) + } + + fun fetchSets(): Array? { + return this.fetch(Array::class.java, "sets") + } + + fun fetchSet(set: String): Set? { + return this.fetch(Set::class.java, "sets", set) + } + + fun fetchSeries(): Array? { + return this.fetch(Array::class.java, "series") + } + + fun fetchSerie(serie: String): Serie? { + return this.fetch(Serie::class.java, "series", serie) + } + + fun fetchVariants(): Array? { + return this.fetch(Array::class.java, "variants") + } + fun fetchVariant(variant: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "variants", variant) + } + + fun fetchTrainerTypes(): Array? { + return this.fetch(Array::class.java, "trainer-types") + } + fun fetchTrainerType(type: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "trainer-types", type) + } + + fun fetchSuffixes(): Array? { + return this.fetch(Array::class.java, "suffixes") + } + fun fetchSuffix(suffix: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "suffixes", suffix) + } + + fun fetchStages(): Array? { + return this.fetch(Array::class.java, "stages") + } + fun fetchStage(stage: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "stages", stage) + } + + fun fetchRegulationMarks(): Array? { + return this.fetch(Array::class.java, "regulation-marks") + } + fun fetchRegulationMark(regulationMark: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "regulation-marks", regulationMark) + } + + fun fetchEnergyTypes(): Array? { + return this.fetch(Array::class.java, "energy-types") + } + fun fetchEnergyType(energyType: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "energy-types", energyType) + } + + fun fetchDexIds(): Array? { + return this.fetch(Array::class.java, "dex-ids") + } + fun fetchDexId(dexId: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "dex-ids", dexId) + } + + fun fetchTypes(): Array? { + return this.fetch(Array::class.java, "types") + } + fun fetchType(type: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "types", type) + } + + fun fetchCategories(): Array? { + return this.fetch(Array::class.java, "categories") + } + fun fetchCategory(category: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "categories", category) + } + + fun fetchRetreats(): Array? { + return this.fetch(Array::class.java, "categories") + } + fun fetchRetreat(retreat: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "retreats", retreat) + } + + fun fetchRarities(): Array? { + return this.fetch(Array::class.java, "rarities") + } + fun fetchRarity(rarity: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "rarities", rarity) + } + + fun fetchIllustrators(): Array? { + return this.fetch(Array::class.java, "illustrators") + } + fun fetchIllustrator(illustrator: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "illustrators", illustrator) + } + + fun fetchHPs(): Array? { + return this.fetch(Array::class.java, "hp") + } + fun fetchHP(hp: String): StringEndpoint? { + return this.fetch(StringEndpoint::class.java, "hp", hp) + } + + private fun fetch(cls: Class, vararg path: String): T? { + return Utils.fetch("$URI/$language/${path.map { it.replace(" ", "%20") }.joinToString("/")}", cls) + } +} diff --git a/src/main/kotlin/net/tcgdex/sdk/Utils.kt b/src/main/kotlin/net/tcgdex/sdk/Utils.kt new file mode 100644 index 0000000..6071010 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/Utils.kt @@ -0,0 +1,57 @@ +package net.tcgdex.sdk + +import com.google.gson.Gson +import net.tcgdex.sdk.internal.CacheEntry +import java.awt.image.BufferedImage +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import java.net.URL +import java.time.LocalDateTime +import javax.imageio.ImageIO + +object Utils { + /** + * Request Time to Live + * in minutes + */ + var ttl: Long = 60 + + private val cache: HashMap> = HashMap() + + private val gson = Gson() + + fun fetch(url: String, cls: Class): T? { + var entry = this.cache[url] + val now = LocalDateTime.now().minusMinutes(ttl) + if (entry == null || entry.time.isBefore(now)) { + val req = URL(url).openConnection() + req.setRequestProperty("user-agent", "@tcgdex/java-sdk") + + val br = BufferedReader(InputStreamReader(req.getInputStream())); + var txt = "" + var line = br.readLine() + while (line != null) + { + txt += line + line = br.readLine() + } + + entry = CacheEntry(txt) + + this.cache[url] = entry + } + + try { + return gson.fromJson( + entry.value, cls + ) + } catch (e: IOException) { + return null + } + } + + fun downloadImage(path: String): BufferedImage { + return ImageIO.read(URL(path)) + } +} diff --git a/src/main/kotlin/net/tcgdex/sdk/internal/CacheEntry.kt b/src/main/kotlin/net/tcgdex/sdk/internal/CacheEntry.kt new file mode 100644 index 0000000..8242059 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/internal/CacheEntry.kt @@ -0,0 +1,8 @@ +package net.tcgdex.sdk.internal + +import java.time.LocalDateTime + +data class CacheEntry ( + val value: T, + val time: LocalDateTime = LocalDateTime.now() +) diff --git a/src/main/kotlin/net/tcgdex/sdk/internal/Model.kt b/src/main/kotlin/net/tcgdex/sdk/internal/Model.kt new file mode 100644 index 0000000..d6b9259 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/internal/Model.kt @@ -0,0 +1,10 @@ +package net.tcgdex.sdk.internal + +import com.google.gson.Gson + +abstract class Model { + override fun toString(): String { + val gson = Gson() + return gson.toJson(this) + } +} diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Card.kt b/src/main/kotlin/net/tcgdex/sdk/models/Card.kt new file mode 100644 index 0000000..7038e58 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/Card.kt @@ -0,0 +1,133 @@ +package net.tcgdex.sdk.models + +import net.tcgdex.sdk.Utils +import net.tcgdex.sdk.internal.Model +import net.tcgdex.sdk.models.subs.CardAbility +import net.tcgdex.sdk.models.subs.CardAttack +import net.tcgdex.sdk.models.subs.CardWeakRes +import java.awt.image.BufferedImage + +/** + * Full description of a card, including all information available about it + * + */ +data class Card internal constructor( + /** + * @return Globally unique card ID based on the set ID and the cards ID within the set + */ + val id: String, + /** + * @return ID indexing this card within its set, usually just its number + */ + val localId: String?, + /** + * + * @return Card name + */ + val name: String?, + /** + * + * @return Card image, can be null + */ + val image: String?, + /** + * + * @return Card illustrator, may be null + */ + val illustrator: String, + + /** + * + * @return Card rarity + */ + val rarity: String, + + /** + * + * @return Card category + */ + val category: String, + private val hasNormalVariant: Boolean, + private val hasReverseVariant: Boolean, + private val hasHolo: Boolean, + private val hasFirstEditionPic: Boolean, + + /** + * @return Resume of the set the card belongs to + */ + val set: SetResume, + private val dexIDs: MutableList, + + /** + * @return HP of the pokemon, will be null if the card is not a pokemon + */ + val hp: Int?, + + /** + * @return Types of the pokemon + */ + val types: List?, + + /** + * + * @return Name of the pokemon this one evolves from + */ + val evolveFrom: String, + + /** + * + * @return Card effect/description, may be null + */ + val effect: String, + + /** + * + * @return Pokemon level, may be 'X', hence not an integer + */ + val level: String, + + /** + * @return Pokemon's stage, like 'Basic' + */ + val stage: String, + + /** + * @return Suffix, like 'V', may be null + */ + val suffix: String, + + /** + * @return Attacks the pokemon has. Empty for cards without attacks + */ + val attacks: List, + + /** + * @return Weaknesses the pokemon has. Empty for cards without attacks + */ + val cardWeakRes: List, + + /** + * @return Pokemon's abilities. May be empty if it doesn't have any, but never + * null + */ + val abilities: List, + + /** + * @return Card's retreat. Will be null for cards without retreat + */ + val retreat: Int?, + + /** + * + * @return Card's regulation mark. May be null if unknown or doesn't exist + */ + val regulationMark: String? +) : Model() { + fun getImageUrl(quality: Quality, extension: Extension): String { + return "${this.image}/${quality}.${extension}" + } + + fun getImage(quality: Quality, extension: Extension): BufferedImage { + return Utils.downloadImage(this.getImageUrl(quality, extension)) + } +} diff --git a/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt new file mode 100644 index 0000000..fc0f735 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt @@ -0,0 +1,38 @@ +package net.tcgdex.sdk.models + +import net.tcgdex.sdk.Utils +import net.tcgdex.sdk.internal.Model +import java.awt.image.BufferedImage + +/** + * Core information to describe a single card + * + */ +data class CardResume internal constructor( + /** + * @return Globally unique card ID based on the set ID and the cards ID within the set + */ + val id: String, + /** + * @return ID indexing this card within its set, usually just its number + */ + val localId: String?, + /** + * + * @return Card name + */ + val name: String?, + /** + * + * @return Card image, can be null + */ + val image: String? +) : Model() { + fun getImageUrl(quality: Quality, extension: Extension): String { + return "${this.image}/${quality}.${extension}" + } + + fun getImage(quality: Quality, extension: Extension): BufferedImage { + return Utils.downloadImage(this.getImageUrl(quality, extension)) + } +} diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Extension.kt b/src/main/kotlin/net/tcgdex/sdk/models/Extension.kt new file mode 100644 index 0000000..6105b6b --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/Extension.kt @@ -0,0 +1,7 @@ +package net.tcgdex.sdk.models + +enum class Extension(val value: String) { + PNG("png"), + JPG("jpg"), + WEBP("webp") +} \ No newline at end of file diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Quality.kt b/src/main/kotlin/net/tcgdex/sdk/models/Quality.kt new file mode 100644 index 0000000..e2c0f1a --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/Quality.kt @@ -0,0 +1,6 @@ +package net.tcgdex.sdk.models + +enum class Quality(val value: String) { + HIGH("high"), + LOW("low") +} \ No newline at end of file diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt b/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt new file mode 100644 index 0000000..c0259fb --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt @@ -0,0 +1,38 @@ +package net.tcgdex.sdk.models + +import net.tcgdex.sdk.Utils +import net.tcgdex.sdk.internal.Model +import java.awt.image.BufferedImage + +/** + * Detailed info regarding a series, including which sets it includes + * + */ +class Serie ( + /** + * @return Resumes of the sets part of this series + */ + val sets: List, + /** + * @return Serie unique ID + */ + val id: String, + /** + * @return Serie name + */ + val name: String, + + val logo: String? +) : Model() { + fun getLogoUrl(extension: Extension): String? { + if (this.logo == null) { + return null + } + return "${this.logo}.${extension}" + } + + fun getLogo(extension: Extension): BufferedImage? { + val logo = this.getLogoUrl(extension) ?: return null + return Utils.downloadImage(logo) + } +} diff --git a/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt new file mode 100644 index 0000000..66a886c --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt @@ -0,0 +1,17 @@ +package net.tcgdex.sdk.models + + +/** + * Contains all information describing a series, an overarching group of sets, for example XY + * + */ +open class SerieResume internal constructor( + /** + * @return Serie unique ID + */ + val id: String?, + /** + * @return Serie name + */ + val name: String? +) \ No newline at end of file diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Set.kt b/src/main/kotlin/net/tcgdex/sdk/models/Set.kt new file mode 100644 index 0000000..17fa168 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/Set.kt @@ -0,0 +1,55 @@ +package net.tcgdex.sdk.models + +import net.tcgdex.sdk.Utils +import net.tcgdex.sdk.internal.Model +import net.tcgdex.sdk.models.subs.SetCardCount +import java.awt.image.BufferedImage + +/** + * Contains all information describing a set of cards + * + */ +data class Set internal constructor( + /** + * @return Set unique ID + */ + val id: String?, + /** + * @return Set name + */ + val name: String?, + /** + * @return Set logo URL, may be null + */ + val logo: String?, + /** + * @return Set symbol URL, may be null + */ + val symbol: String?, + + val cardCount: SetCardCount +) : Model() { + fun getLogoUrl(extension: Extension): String? { + if (this.logo == null) { + return null + } + return "${this.logo}.${extension}" + } + + fun getLogo(extension: Extension): BufferedImage? { + val logo = this.getLogoUrl(extension) ?: return null + return Utils.downloadImage(logo) + } + + fun getSymbolUrl(extension: Extension): String? { + if (this.symbol == null) { + return null + } + return "${this.symbol}.${extension}" + } + + fun getSymbol(extension: Extension): BufferedImage? { + val symbol = this.getSymbolUrl(extension) ?: return null + return Utils.downloadImage(symbol) + } +} diff --git a/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt new file mode 100644 index 0000000..ad4271f --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt @@ -0,0 +1,54 @@ +package net.tcgdex.sdk.models + +import net.tcgdex.sdk.Utils +import net.tcgdex.sdk.models.subs.SetCardCountResume +import java.awt.image.BufferedImage + +/** + * Contains all information describing a set of cards + * + */ +data class SetResume internal constructor( + /** + * @return Set unique ID + */ + val id: String?, + /** + * @return Set name + */ + val name: String?, + /** + * @return Set logo URL, may be null + */ + val logo: String?, + /** + * @return Set symbol URL, may be null + */ + val symbol: String?, + + val cardCount: SetCardCountResume +) { + fun getLogoUrl(extension: Extension): String? { + if (this.logo == null) { + return null + } + return "${this.logo}.${extension}" + } + + fun getLogo(extension: Extension): BufferedImage? { + val logo = this.getLogoUrl(extension) ?: return null + return Utils.downloadImage(logo) + } + + fun getSymbolUrl(extension: Extension): String? { + if (this.symbol == null) { + return null + } + return "${this.symbol}.${extension}" + } + + fun getSymbol(extension: Extension): BufferedImage? { + val symbol = this.getSymbolUrl(extension) ?: return null + return Utils.downloadImage(symbol) + } +} diff --git a/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt b/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt new file mode 100644 index 0000000..53bf209 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt @@ -0,0 +1,6 @@ +package net.tcgdex.sdk.models + +data class StringEndpoint( + val name: String, + val cards: Array +) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt new file mode 100644 index 0000000..36e5aa8 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt @@ -0,0 +1,21 @@ +package net.tcgdex.sdk.models.subs + +/** + * Describes a single ability of a pokemon + * + */ +data class CardAbility internal constructor( + /** + * @return Type of the ability, for example 'Poke-POWER' + */ + val type: String, + /** + * @return Name of the ability + */ + val name: String, + /** + * + * @return Description/Effect of the ability + */ + val effect: String +) \ No newline at end of file diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt new file mode 100644 index 0000000..aae1690 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt @@ -0,0 +1,25 @@ +package net.tcgdex.sdk.models.subs + +/** + * Describes a single attack of a pokemon, for example 'Confuse Ray' + * + */ +data class CardAttack internal constructor( + /** + * @return Cost of the attack in the same order as listed on the card + */ + val cost: List, + /** + * @return Name of the attack + */ + val name: String, + /** + * @return Effect/Description of the attack, may be null for attacks without text + */ + val effect: String, + /** + * @return Damage the attack deals. May just be a number like '30', but can also + * be a multiplier like 'x20' + */ + val damage: String +) \ No newline at end of file diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt new file mode 100644 index 0000000..6554c8b --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt @@ -0,0 +1,17 @@ +package net.tcgdex.sdk.models.subs + + +/** + * Describes the weakness of a single pokemon, for example: 2x to Fire + * + */ +data class CardWeakRes internal constructor( + /** + * @return Type the weakness is to + */ + val type: String, + /** + * @return Descriptor of the weakness multiplier, including a leading x, for example 'x2'. May be null + */ + val value: String +) \ No newline at end of file diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt new file mode 100644 index 0000000..4cdb2bd --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt @@ -0,0 +1,28 @@ +package net.tcgdex.sdk.models.subs + +data class SetCardCount ( + /** + * total of number of cards + */ + val total: Int, + /** + * number of cards officialy (on the bottom of each cards) + */ + val official: Int, + /** + * number of cards having a normal version + */ + val normal: Int, + /** + * number of cards having an reverse version + */ + val reverse: Int, + /** + * number of cards having an holo version + */ + val holo: Int, + /** + * Number of possible cards + */ + val firstEd: Int? +) \ No newline at end of file diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt new file mode 100644 index 0000000..e764894 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt @@ -0,0 +1,6 @@ +package net.tcgdex.sdk.models.subs + +data class SetCardCountResume ( + val total: Int, + val official: Int +) \ No newline at end of file diff --git a/src/test/java/net/tcgdex/TestAPI.java b/src/test/java/net/tcgdex/TestAPI.java deleted file mode 100644 index 6e2ec82..0000000 --- a/src/test/java/net/tcgdex/TestAPI.java +++ /dev/null @@ -1,169 +0,0 @@ -package net.tcgdex; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.time.Month; -import java.util.Arrays; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -import net.tcgdex.TCGDexAPI.ImageResolution; -import net.tcgdex.TCGDexAPI.Language; - -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 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 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 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 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")); - assertEquals("ex13", api.getSetInfo(api.getCardInfo("ex13-96")).getId()); - } - - @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() throws IOException { - CardInfo info = api.getCardInfo("base4", "1"); - BufferedImage high = api.getImage(info, ImageResolution.HIGH); - assertNotNull(high); - assertEquals(825, high.getHeight()); - assertEquals(600, high.getWidth()); - BufferedImage low = api.getImage(info, ImageResolution.LOW); - assertNotNull(low); - assertEquals(337, low.getHeight()); - assertEquals(245, low.getWidth()); - } - -} diff --git a/src/test/kotlin/net/tcgdex/sdk/APITest.kt b/src/test/kotlin/net/tcgdex/sdk/APITest.kt new file mode 100644 index 0000000..ed1d565 --- /dev/null +++ b/src/test/kotlin/net/tcgdex/sdk/APITest.kt @@ -0,0 +1,51 @@ +package net.tcgdex.sdk + +import java.io.IOException +import kotlin.test.Test +import kotlin.test.BeforeTest +import kotlin.test.assertNotNull + +class APITest { + private lateinit var api: TCGdex + @BeforeTest + fun initAPI() { + api = TCGdex("en") + } + + @Test + @Throws(IOException::class) + fun testFullCardInfo() { + assertNotNull(api.fetchCard("swsh3-136")) + assertNotNull(api.fetchCard("swsh3", "136")) + assertNotNull(api.fetchSet("swsh3")) + assertNotNull(api.fetchSets()) + assertNotNull(api.fetchSerie("swsh")) + assertNotNull(api.fetchSeries()) + assertNotNull(api.fetchTypes()) + assertNotNull(api.fetchType("Colorless")) + assertNotNull(api.fetchRetreats()) + assertNotNull(api.fetchRetreat("1")) + assertNotNull(api.fetchRarities()) + assertNotNull(api.fetchRarity("Uncommon")) + assertNotNull(api.fetchIllustrators()) + assertNotNull(api.fetchIllustrator("tetsuya koizumi")) + assertNotNull(api.fetchHPs()) + assertNotNull(api.fetchHP("110")) + assertNotNull(api.fetchCategories()) + assertNotNull(api.fetchCategory("Pokemon")) + assertNotNull(api.fetchDexIds()) + assertNotNull(api.fetchDexId("162")) + assertNotNull(api.fetchEnergyTypes()) + assertNotNull(api.fetchEnergyType("Special")) + assertNotNull(api.fetchRegulationMarks()) + assertNotNull(api.fetchRegulationMark("D")) + assertNotNull(api.fetchStages()) + assertNotNull(api.fetchStage("Basic")) + assertNotNull(api.fetchSuffixes()) + assertNotNull(api.fetchSuffix("EX")) + assertNotNull(api.fetchTrainerTypes()) + assertNotNull(api.fetchTrainerType("Tool")) + assertNotNull(api.fetchVariants()) + assertNotNull(api.fetchVariant("holo")) + } +} From 7998307d116181930bd62a6c4c30c7f42975e9ae Mon Sep 17 00:00:00 2001 From: Avior Date: Tue, 19 Apr 2022 10:35:36 +0200 Subject: [PATCH 02/11] fix: gradlew permission Signed-off-by: Avior --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From 592d97c4e9a58ce9c2711ff29ebb6bf12ba5be3f Mon Sep 17 00:00:00 2001 From: Avior Date: Wed, 20 Apr 2022 12:47:42 +0200 Subject: [PATCH 03/11] fix: use the correct groupId --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9c14272..9bfdceb 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ repositories { ```gradle dependencies { - implementation 'com.github.tcgdex:java-sdk:{Version}' + implementation 'net.tcgdex:java-sdk:{Version}' } ``` @@ -74,7 +74,7 @@ dependencies { ```xml - com.github.tcgdex + net.tcgdex java-sdk {Version} From f47a388dd34c2cf7f820c1838192f0f9121d64cf Mon Sep 17 00:00:00 2001 From: Avior Date: Mon, 2 May 2022 15:33:56 +0200 Subject: [PATCH 04/11] docs: Add documentation Signed-off-by: Avior --- src/main/kotlin/net/tcgdex/sdk/TCGdex.kt | 235 +++++++++++++++++- src/main/kotlin/net/tcgdex/sdk/Utils.kt | 76 ++++-- .../kotlin/net/tcgdex/sdk/internal/Model.kt | 8 + src/main/kotlin/net/tcgdex/sdk/models/Card.kt | 169 +++++-------- .../net/tcgdex/sdk/models/CardResume.kt | 51 ++-- .../kotlin/net/tcgdex/sdk/models/Extension.kt | 19 +- .../kotlin/net/tcgdex/sdk/models/Quality.kt | 16 +- .../kotlin/net/tcgdex/sdk/models/Serie.kt | 33 ++- .../net/tcgdex/sdk/models/SerieResume.kt | 48 +++- src/main/kotlin/net/tcgdex/sdk/models/Set.kt | 57 +++-- .../kotlin/net/tcgdex/sdk/models/SetResume.kt | 64 +++-- .../net/tcgdex/sdk/models/StringEndpoint.kt | 13 +- .../net/tcgdex/sdk/models/subs/CardAbility.kt | 5 +- .../net/tcgdex/sdk/models/subs/CardAttack.kt | 26 +- .../net/tcgdex/sdk/models/subs/CardItem.kt | 12 + .../tcgdex/sdk/models/subs/CardVariants.kt | 18 ++ .../net/tcgdex/sdk/models/subs/CardWeakRes.kt | 14 +- .../net/tcgdex/sdk/models/subs/Legal.kt | 14 ++ .../tcgdex/sdk/models/subs/SetCardCount.kt | 30 +-- .../sdk/models/subs/SetCardCountResume.kt | 8 +- src/test/kotlin/net/tcgdex/sdk/APITest.kt | 1 + 21 files changed, 662 insertions(+), 255 deletions(-) create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/subs/CardItem.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/subs/CardVariants.kt create mode 100644 src/main/kotlin/net/tcgdex/sdk/models/subs/Legal.kt diff --git a/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt b/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt index 44cfa1e..cc6ca79 100644 --- a/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt +++ b/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt @@ -1,134 +1,353 @@ package net.tcgdex.sdk -import net.tcgdex.sdk.models.* +import net.tcgdex.sdk.models.Card +import net.tcgdex.sdk.models.CardResume +import net.tcgdex.sdk.models.Serie +import net.tcgdex.sdk.models.SerieResume import net.tcgdex.sdk.models.Set +import net.tcgdex.sdk.models.SetResume +import net.tcgdex.sdk.models.StringEndpoint +/** + * TCGdex + * + * @property language the language you want to use, values: [en,fr,es,de,pt,it] + * @constructor Create empty TCGdex in the specific language + */ class TCGdex( var language: String ) { - private val URI = "https://api.tcgdex.net/v2" + /** + * The API Endpoint you want to use + */ + var URI = "https://api.tcgdex.net/v2" + /** + * Fetch every Pokémon cards + * + * _note: take some time as there is around 13k-15k cards depending on the language_ + * + * @return the list of Pokémon Cards + */ fun fetchCards(): Array? { return this.fetch(Array::class.java, "cards") } + /** + * Fetch a specific card + * + * @param card the card ID + * @return The card + */ fun fetchCard(cardId: String): Card? { return this.fetch(Card::class.java, "cards", cardId) } + /** + * Fetch a specific card by it's set and local IDs + * + * @param setId the set ID/name + * @param cardId the card local ID + * @return the card you want + */ fun fetchCard(setId: String, cardId: String): Card? { return this.fetch(Card::class.java, "sets", setId, cardId) } - fun fetchSets(): Array? { + /** + * Fetch every pokémon TCG Sets + * + * @return the list of Pokémon TCG sets + */ + fun fetchSets(): Array? { return this.fetch(Array::class.java, "sets") - } + } - fun fetchSet(set: String): Set? { + /** + * Fetch a specific set + * + * @param set the set you want to fetch (you can use the Set ID or name) + * @return The set you searched + */ + fun fetchSet(set: String): Set? { return this.fetch(Set::class.java, "sets", set) - } + } + /** + * Fetch every pokémon TCG Series + * + * @return the list of Pokémon TCG Series + */ fun fetchSeries(): Array? { return this.fetch(Array::class.java, "series") } + /** + * Fetch a specific serie + * + * @param serie the serie you want to fetch (you can use the Serie ID or name) + * @return The serie you searched + */ fun fetchSerie(serie: String): Serie? { return this.fetch(Serie::class.java, "series", serie) } + /** + * Fetch evey variants it is possible to have + * + * @return the list of evey variants it is possible to have + */ fun fetchVariants(): Array? { return this.fetch(Array::class.java, "variants") } + + /** + * Fetch cards by variant + * + * @param variant the variant you want to filter by + * @return a StringEndpoint containing the cards with the specified variant + */ fun fetchVariant(variant: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "variants", variant) } + /** + * Fetch evey Trainer Types it is possible to have + * + * @return the list of evey Trainer Types it is possible to have + */ fun fetchTrainerTypes(): Array? { return this.fetch(Array::class.java, "trainer-types") } + + /** + * Fetch cards by trainer type + * + * @param type the trainer type you want to filter by + * @return a StringEndpoint containing the cards with the specified trainer type + */ fun fetchTrainerType(type: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "trainer-types", type) } + /** + * Fetch evey suffixes it is possible to have + * + * @return the list of evey suffixes it is possible to have + */ fun fetchSuffixes(): Array? { return this.fetch(Array::class.java, "suffixes") } + + /** + * Fetch cards by suffix + * + * @param suffix the suffix you want to filter by + * @return a StringEndpoint containing the cards with the specified suffix + */ fun fetchSuffix(suffix: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "suffixes", suffix) } + /** + * Fetch evey stages it is possible to have + * + * @return the list of evey stages it is possible to have + */ fun fetchStages(): Array? { return this.fetch(Array::class.java, "stages") } + + /** + * Fetch cards by stage + * + * @param stage the stage you want to filter by + * @return a StringEndpoint containing the cards with the specified stage + */ fun fetchStage(stage: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "stages", stage) } + /** + * Fetch evey Regulation Marks it is possible to have + * + * @return the list of evey Regulation Marks it is possible to have + */ fun fetchRegulationMarks(): Array? { return this.fetch(Array::class.java, "regulation-marks") } + + /** + * Fetch cards by regulation mark + * + * @param regulationMark the regulation mark you want to filter by + * @return a StringEndpoint containing the cards with the specified regulation mark + */ fun fetchRegulationMark(regulationMark: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "regulation-marks", regulationMark) } + /** + * Fetch evey Energy Types it is possible to have + * + * @return the list of evey Energy Types it is possible to have + */ fun fetchEnergyTypes(): Array? { return this.fetch(Array::class.java, "energy-types") } + + /** + * Fetch cards by Energy type + * + * @param energyType the Energy type you want to filter by + * @return a StringEndpoint containing the cards with the specified Energy type + */ fun fetchEnergyType(energyType: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "energy-types", energyType) } + /** + * Fetch evey Pokédex IDS it is possible to have + * + * @return the list of evey Pokédex IDS it is possible to have + */ fun fetchDexIds(): Array? { return this.fetch(Array::class.java, "dex-ids") } + + /** + * Fetch cards by pokédex ID + * + * @param dexId the pokédex ID you want to filter by + * @return a StringEndpoint containing the cards with the specified pokédex ID + */ fun fetchDexId(dexId: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "dex-ids", dexId) } + /** + * Fetch evey types it is possible to have + * + * @return the list of evey types it is possible to have + */ fun fetchTypes(): Array? { return this.fetch(Array::class.java, "types") } + + /** + * Fetch cards by type + * + * @param type the type you want to filter by + * @return a StringEndpoint containing the cards with the specified type + */ fun fetchType(type: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "types", type) } + /** + * Fetch evey categories it is possible to have + * + * @return the list of evey categories it is possible to have + */ fun fetchCategories(): Array? { return this.fetch(Array::class.java, "categories") } + + /** + * Fetch cards by category + * + * @param category the category you want to filter by + * @return a StringEndpoint containing the cards with the specified category + */ fun fetchCategory(category: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "categories", category) } + /** + * Fetch evey retreat count it is possible to have + * + * @return the list of evey retreat count it is possible to have + */ fun fetchRetreats(): Array? { - return this.fetch(Array::class.java, "categories") + return this.fetch(Array::class.java, "retreats") } + + /** + * Fetch cards by retreat + * + * @param retreat the retreat count you want to filter by + * @return a StringEndpoint containing the cards with the specified retreat count + */ fun fetchRetreat(retreat: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "retreats", retreat) } + /** + * Fetch every Card rarities you can have (in your language) + * + * @return the list of Card rarities you can have (in your language) + */ fun fetchRarities(): Array? { return this.fetch(Array::class.java, "rarities") } + + /** + * Fetch cards by rarity + * + * @param rarity the rarity you want to filter by (language specific) + * @return a StringEndpoint containing the cards with the specified rarity + */ fun fetchRarity(rarity: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "rarities", rarity) } + /** + * Fetch every cards illustrators + * + * @return the list of ilustrators + */ fun fetchIllustrators(): Array? { return this.fetch(Array::class.java, "illustrators") } + + /** + * Fetch cards by illustrator + * + * @param illustrator the illustrator you want to filter by + * @return a StringEndpoint containing the cards with the specified illustrator + */ fun fetchIllustrator(illustrator: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "illustrators", illustrator) } + /** + * Fetch the list of possible value the HP field can take + * + * @return the list of possible value the HP field can take + */ fun fetchHPs(): Array? { return this.fetch(Array::class.java, "hp") } + + /** + * Fetch cards by hp + * + * @param hp the hp count you want to filter by + * @return a StringEndpoint containing the cards with the specified hp count + */ fun fetchHP(hp: String): StringEndpoint? { return this.fetch(StringEndpoint::class.java, "hp", hp) } private fun fetch(cls: Class, vararg path: String): T? { - return Utils.fetch("$URI/$language/${path.map { it.replace(" ", "%20") }.joinToString("/")}", cls) + return Utils.fetch(this, "$URI/$language/${ + path.joinToString("/") { + it.replace( + " ", + "%20" + ) + } + }", cls) } } diff --git a/src/main/kotlin/net/tcgdex/sdk/Utils.kt b/src/main/kotlin/net/tcgdex/sdk/Utils.kt index 6071010..8e89515 100644 --- a/src/main/kotlin/net/tcgdex/sdk/Utils.kt +++ b/src/main/kotlin/net/tcgdex/sdk/Utils.kt @@ -2,6 +2,7 @@ package net.tcgdex.sdk import com.google.gson.Gson import net.tcgdex.sdk.internal.CacheEntry +import net.tcgdex.sdk.internal.Model import java.awt.image.BufferedImage import java.io.BufferedReader import java.io.IOException @@ -11,46 +12,69 @@ import java.time.LocalDateTime import javax.imageio.ImageIO object Utils { - /** - * Request Time to Live - * in minutes - */ - var ttl: Long = 60 + /** + * Request Time to Live + * in minutes + */ + var ttl: Long = 60 - private val cache: HashMap> = HashMap() + /** + * requests cache + */ + private val cache: HashMap> = HashMap() - private val gson = Gson() + private val gson = Gson() - fun fetch(url: String, cls: Class): T? { - var entry = this.cache[url] - val now = LocalDateTime.now().minusMinutes(ttl) - if (entry == null || entry.time.isBefore(now)) { - val req = URL(url).openConnection() - req.setRequestProperty("user-agent", "@tcgdex/java-sdk") + /** + * fetch from the API + * + * @param tcgdex the TCGdex instance to link with it + * @param url the url to fetch from + * @param cls the Class to build the response into + * + * @return an initialized cls or null + */ + fun fetch(tcgdex: TCGdex, url: String, cls: Class): T? { + var entry = this.cache[url] + val now = LocalDateTime.now().minusMinutes(ttl) + if (entry == null || entry.time.isBefore(now)) { + val req = URL(url).openConnection() + req.setRequestProperty("user-agent", "@tcgdex/java-sdk") - val br = BufferedReader(InputStreamReader(req.getInputStream())); - var txt = "" - var line = br.readLine() - while (line != null) - { - txt += line - line = br.readLine() - } + val br = BufferedReader(InputStreamReader(req.getInputStream())); + var txt = "" + var line = br.readLine() + while (line != null) + { + txt += line + line = br.readLine() + } - entry = CacheEntry(txt) + entry = CacheEntry(txt) - this.cache[url] = entry - } + this.cache[url] = entry + } try { - return gson.fromJson( + val model = gson.fromJson( entry.value, cls ) + if (model is Model) { + model.tcgdex = tcgdex + } + return model } catch (e: IOException) { return null } - } + } + /** + * download an image from the internet + * + * @param path the url to the image + * + * @return the download image buffer + */ fun downloadImage(path: String): BufferedImage { return ImageIO.read(URL(path)) } diff --git a/src/main/kotlin/net/tcgdex/sdk/internal/Model.kt b/src/main/kotlin/net/tcgdex/sdk/internal/Model.kt index d6b9259..f84992d 100644 --- a/src/main/kotlin/net/tcgdex/sdk/internal/Model.kt +++ b/src/main/kotlin/net/tcgdex/sdk/internal/Model.kt @@ -1,10 +1,18 @@ package net.tcgdex.sdk.internal import com.google.gson.Gson +import net.tcgdex.sdk.TCGdex abstract class Model { + /** + * Give you a string representation of the Model + * + * @return the model data as JSON + */ override fun toString(): String { val gson = Gson() return gson.toJson(this) } + + public lateinit var tcgdex: TCGdex } diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Card.kt b/src/main/kotlin/net/tcgdex/sdk/models/Card.kt index 7038e58..68e2e2f 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Card.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Card.kt @@ -2,132 +2,93 @@ package net.tcgdex.sdk.models import net.tcgdex.sdk.Utils import net.tcgdex.sdk.internal.Model -import net.tcgdex.sdk.models.subs.CardAbility -import net.tcgdex.sdk.models.subs.CardAttack -import net.tcgdex.sdk.models.subs.CardWeakRes +import net.tcgdex.sdk.models.subs.* import java.awt.image.BufferedImage - /** - * Full description of a card, including all information available about it + * Pokémon TCG Card, It contains every informations about a specific card * + * @property id Globally unique card ID based on the set ID and the cards ID within the set + * @property localId ID indexing this card within its set, usually just its number + * @property name Card name + * @property image Card image url without the extension and quality + * @property illustrator Card illustrator + * @property rarity Card rarity + * @property category Card category + * @property variants The card possible variants + * @property set Resume of the set the card belongs to + * @property dexIDs the Pokémon Pokédex IDs (multiple if multiple pokémon appears on the card) + * @property hp HP of the pokemon + * @property types Types of the pokemon (multiple because some have multiple in the older sets) + * @property evolveFrom Name of the pokemon this one evolves from + * @property description the Pokémon Pokédex like description + * @property level the Pokémon Level (can be "X" if the card is of level X) + * @property stage the Pokémon Stage (changes depending on the API language) + * @property suffix the Pokémon Suffix (changes depending on the API language) + * @property item the Item the Pokémon have + * @property abilities the Card abilities (some cards have multiple abilities) + * @property attacks the Card Attacks + * @property weaknesses the Pokémon Weaknesses + * @property resistances the Pokémon Resistances + * @property retreat the Pokémon retreat Cost + * @property effect the Card Effect (Trainer/Energy only) + * @property trainerType the trainer sub type (changes depending on the API language) + * @property energyType the energy sub type (changes depending on the API language) + * @property regulationMark the Card Regulation mark + * @property legal the card ability to be played in tournaments */ data class Card internal constructor( - /** - * @return Globally unique card ID based on the set ID and the cards ID within the set - */ val id: String, - /** - * @return ID indexing this card within its set, usually just its number - */ - val localId: String?, - /** - * - * @return Card name - */ - val name: String?, - /** - * - * @return Card image, can be null - */ + val localId: String, + val name: String, val image: String?, - /** - * - * @return Card illustrator, may be null - */ - val illustrator: String, - /** - * - * @return Card rarity - */ + val illustrator: String?, val rarity: String, - - /** - * - * @return Card category - */ val category: String, - private val hasNormalVariant: Boolean, - private val hasReverseVariant: Boolean, - private val hasHolo: Boolean, - private val hasFirstEditionPic: Boolean, - - /** - * @return Resume of the set the card belongs to - */ + val variants: CardVariants?, val set: SetResume, - private val dexIDs: MutableList, - /** - * @return HP of the pokemon, will be null if the card is not a pokemon - */ + private val dexIDs: List?, val hp: Int?, - - /** - * @return Types of the pokemon - */ - val types: List?, - - /** - * - * @return Name of the pokemon this one evolves from - */ - val evolveFrom: String, - - /** - * - * @return Card effect/description, may be null - */ - val effect: String, - - /** - * - * @return Pokemon level, may be 'X', hence not an integer - */ - val level: String, - - /** - * @return Pokemon's stage, like 'Basic' - */ - val stage: String, - - /** - * @return Suffix, like 'V', may be null - */ - val suffix: String, - - /** - * @return Attacks the pokemon has. Empty for cards without attacks - */ - val attacks: List, - - /** - * @return Weaknesses the pokemon has. Empty for cards without attacks - */ - val cardWeakRes: List, - - /** - * @return Pokemon's abilities. May be empty if it doesn't have any, but never - * null - */ + val types: List?, + val evolveFrom: String?, + val description: String?, + val level: String?, + val stage: String?, + val suffix: String?, + val item: CardItem?, val abilities: List, - - /** - * @return Card's retreat. Will be null for cards without retreat - */ + val attacks: List, + val weaknesses: List, + val resistances: List, val retreat: Int?, + val effect: String?, + val trainerType: String?, + val energyType: String?, + val regulationMark: String?, + val legal: Legal +) : Model() { + /** + * the Card Image full URL * - * @return Card's regulation mark. May be null if unknown or doesn't exist + * @param quality the quality you want your image to be in + * @param extension extension you want you image to be + * @return the full card URL with the extension and quality */ - val regulationMark: String? -) : Model() { fun getImageUrl(quality: Quality, extension: Extension): String { return "${this.image}/${quality}.${extension}" } - fun getImage(quality: Quality, extension: Extension): BufferedImage { - return Utils.downloadImage(this.getImageUrl(quality, extension)) + /** + * Get image buffer + * + * @param quality the quality you want your image to be in + * @param format extension you want you image to be + * @return the full card Buffer in the format you want + */ + fun getImage(quality: Quality, format: Extension): BufferedImage { + return Utils.downloadImage(this.getImageUrl(quality, format)) } } diff --git a/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt index fc0f735..286ea71 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt @@ -5,34 +5,51 @@ import net.tcgdex.sdk.internal.Model import java.awt.image.BufferedImage /** - * Core information to describe a single card + * Card Resume class, contains basic informations about a specific card * + * to get the full card you can use the `getFullCard()` function + * + * @property id Globally unique card ID based on the set ID and the cards ID within the set + * @property localId ID indexing this card within its set, usually just its number + * @property name Card name + * @property image Card image url without the extension and quality */ data class CardResume internal constructor( - /** - * @return Globally unique card ID based on the set ID and the cards ID within the set - */ + val id: String, + val localId: String, + val name: String, + val image: String? +) : Model() { + /** - * @return ID indexing this card within its set, usually just its number - */ - val localId: String?, - /** + * the the Card Image full URL * - * @return Card name + * @param quality the quality you want your image to be in + * @param extension extension you want you image to be + * @return the full card URL with the extension and quality */ - val name: String?, + fun getImageUrl(quality: Quality, extension: Extension): String { + return "${this.image}/${quality}.${extension}" + } + /** + * Get image buffer * - * @return Card image, can be null + * @param quality the quality you want your image to be in + * @param format extension you want you image to be + * @return the full card Buffer in the format you want */ - val image: String? -) : Model() { - fun getImageUrl(quality: Quality, extension: Extension): String { - return "${this.image}/${quality}.${extension}" + fun getImage(quality: Quality, format: Extension): BufferedImage { + return Utils.downloadImage(this.getImageUrl(quality, format)) } - fun getImage(quality: Quality, extension: Extension): BufferedImage { - return Utils.downloadImage(this.getImageUrl(quality, extension)) + /** + * Get the full Card + * + * @return the full card if available + */ + fun getFullCard(): Card? { + return this.tcgdex.fetchCard(this.id) } } diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Extension.kt b/src/main/kotlin/net/tcgdex/sdk/models/Extension.kt index 6105b6b..af35270 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Extension.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Extension.kt @@ -1,7 +1,24 @@ package net.tcgdex.sdk.models +/** + * The different extension an image is available in + * + * @property value the string representation of the set + */ enum class Extension(val value: String) { + + /** + * .png image, with transparent background + */ PNG("png"), + + /** + * .jpg image, with white background + */ JPG("jpg"), + + /** + * .webp image, with transparent background + */ WEBP("webp") -} \ No newline at end of file +} diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Quality.kt b/src/main/kotlin/net/tcgdex/sdk/models/Quality.kt index e2c0f1a..56d86a9 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Quality.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Quality.kt @@ -1,6 +1,20 @@ package net.tcgdex.sdk.models +/** + * Image quality if applicable + * (only cards does have quality selector) + * + * @property value the string representation of the quality + */ enum class Quality(val value: String) { + + /** + * A High quality image + */ HIGH("high"), + + /** + * A Low quality image + */ LOW("low") -} \ No newline at end of file +} diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt b/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt index c0259fb..de11dc4 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt @@ -5,25 +5,26 @@ import net.tcgdex.sdk.internal.Model import java.awt.image.BufferedImage /** - * Detailed info regarding a series, including which sets it includes + * Pokémon TCG Serie * + * @property sets the list of sets the Serie contains + * @property id the Serie unique ID + * @property name the Serie name + * @property logo the Serie Logo (basically also the first set logo) */ class Serie ( - /** - * @return Resumes of the sets part of this series - */ val sets: List, - /** - * @return Serie unique ID - */ val id: String, - /** - * @return Serie name - */ val name: String, - val logo: String? ) : Model() { + + /** + * Get the logo full url + * + * @param extension the file extension you want to use + * @return the full URL of the logo + */ fun getLogoUrl(extension: Extension): String? { if (this.logo == null) { return null @@ -31,8 +32,14 @@ class Serie ( return "${this.logo}.${extension}" } - fun getLogo(extension: Extension): BufferedImage? { - val logo = this.getLogoUrl(extension) ?: return null + /** + * Get the logo buffer + * + * @param format the image format + * @return a buffer containing the image + */ + fun getLogo(format: Extension): BufferedImage? { + val logo = this.getLogoUrl(format) ?: return null return Utils.downloadImage(logo) } } diff --git a/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt index 66a886c..dcae242 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt @@ -1,17 +1,53 @@ package net.tcgdex.sdk.models +import net.tcgdex.sdk.Utils +import net.tcgdex.sdk.internal.Model +import java.awt.image.BufferedImage + /** - * Contains all information describing a series, an overarching group of sets, for example XY + * Serie Resume * + * @property id the Serie unique ID + * @property name the Serie name + * @property logo the Serie Logo (basically also the first set logo) */ open class SerieResume internal constructor( + val id: String, + val name: String, + val logo: String? +) : Model() { + + /** + * Get the logo full url + * + * @param extension + * @return + */ + fun getLogoUrl(extension: Extension): String? { + if (this.logo == null) { + return null + } + return "${this.logo}.${extension}" + } + /** - * @return Serie unique ID + * Get the logo buffer + * + * @param format + * @return */ - val id: String?, + fun getLogo(format: Extension): BufferedImage? { + val logo = this.getLogoUrl(format) ?: return null + return Utils.downloadImage(logo) + } + /** - * @return Serie name + * Get the full Serie + * + * @return the full serie if available */ - val name: String? -) \ No newline at end of file + fun getFullSerie(): Serie? { + return this.tcgdex.fetchSerie(this.id) + } +} diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Set.kt b/src/main/kotlin/net/tcgdex/sdk/models/Set.kt index 17fa168..3f5c92e 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Set.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Set.kt @@ -2,33 +2,44 @@ package net.tcgdex.sdk.models import net.tcgdex.sdk.Utils import net.tcgdex.sdk.internal.Model +import net.tcgdex.sdk.models.subs.Legal import net.tcgdex.sdk.models.subs.SetCardCount import java.awt.image.BufferedImage /** - * Contains all information describing a set of cards + * Pokémon TCG Set class * + * @property id Globally unique set ID + * @property name the Set mame + * @property logo the Set Logo incomplete URL (use getLogoUrl/getLogo) + * @property symbol the Set Symbol imcomplete URL (use getSymbolUrl/getSymbol) + * @property serie the serie this set is a part of + * @property tcgOnline the TCG Online Code + * @property releaseDate the Set release date as yyyy-mm-dd + * @property legal the set legality (won't indicate if a card is banned) + * @property cardCount the number of card in the set + * @property cards the cards contained in this set */ data class Set internal constructor( - /** - * @return Set unique ID - */ val id: String?, - /** - * @return Set name - */ val name: String?, - /** - * @return Set logo URL, may be null - */ val logo: String?, - /** - * @return Set symbol URL, may be null - */ val symbol: String?, - val cardCount: SetCardCount + val serie: SerieResume, + val tcgOnline: String?, + val releaseDate: String?, + val legal: Legal, + val cardCount: SetCardCount, + val cards: List ) : Model() { + + /** + * Get logo url + * + * @param extension + * @return + */ fun getLogoUrl(extension: Extension): String? { if (this.logo == null) { return null @@ -36,11 +47,23 @@ data class Set internal constructor( return "${this.logo}.${extension}" } + /** + * Get logo + * + * @param extension + * @return + */ fun getLogo(extension: Extension): BufferedImage? { val logo = this.getLogoUrl(extension) ?: return null return Utils.downloadImage(logo) } + /** + * Get symbol url + * + * @param extension + * @return + */ fun getSymbolUrl(extension: Extension): String? { if (this.symbol == null) { return null @@ -48,6 +71,12 @@ data class Set internal constructor( return "${this.symbol}.${extension}" } + /** + * Get symbol + * + * @param extension + * @return + */ fun getSymbol(extension: Extension): BufferedImage? { val symbol = this.getSymbolUrl(extension) ?: return null return Utils.downloadImage(symbol) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt index ad4271f..8aa02bc 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt @@ -1,33 +1,38 @@ package net.tcgdex.sdk.models import net.tcgdex.sdk.Utils +import net.tcgdex.sdk.internal.Model import net.tcgdex.sdk.models.subs.SetCardCountResume import java.awt.image.BufferedImage /** - * Contains all information describing a set of cards + * Set resume * + * @property id Globally unique set ID + * @property name the Set mame + * @property logo the Set Logo incomplete URL (use getLogoUrl/getLogo) + * @property symbol the Set Symbol imcomplete URL (use getSymbolUrl/getSymbol) + * @property cardCount the number of card in the set */ data class SetResume internal constructor( - /** - * @return Set unique ID - */ - val id: String?, - /** - * @return Set name - */ - val name: String?, - /** - * @return Set logo URL, may be null - */ + + val id: String, + + val name: String, + val logo: String?, - /** - * @return Set symbol URL, may be null - */ + val symbol: String?, val cardCount: SetCardCountResume -) { +) : Model() { + + /** + * Get logo url + * + * @param extension + * @return + */ fun getLogoUrl(extension: Extension): String? { if (this.logo == null) { return null @@ -35,11 +40,23 @@ data class SetResume internal constructor( return "${this.logo}.${extension}" } + /** + * Get logo + * + * @param extension + * @return + */ fun getLogo(extension: Extension): BufferedImage? { val logo = this.getLogoUrl(extension) ?: return null return Utils.downloadImage(logo) } + /** + * Get symbol url + * + * @param extension + * @return + */ fun getSymbolUrl(extension: Extension): String? { if (this.symbol == null) { return null @@ -47,8 +64,23 @@ data class SetResume internal constructor( return "${this.symbol}.${extension}" } + /** + * Get symbol + * + * @param extension + * @return + */ fun getSymbol(extension: Extension): BufferedImage? { val symbol = this.getSymbolUrl(extension) ?: return null return Utils.downloadImage(symbol) } + + /** + * Get the full set + * + * @return the full set if available + */ + fun getFullSet(): Set? { + return this.tcgdex.fetchSet(this.id) + } } diff --git a/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt b/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt index 53bf209..4e67512 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt @@ -1,6 +1,15 @@ package net.tcgdex.sdk.models +import net.tcgdex.sdk.internal.Model + +/** + * Class that handle a lot of Endpoints + * + * @property name the endpoint value + * @property cards the cards that contains `name` in them + * @constructor Create empty String endpoint + */ data class StringEndpoint( val name: String, - val cards: Array -) + val cards: List +) : Model() diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt index 36e5aa8..02b8108 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt @@ -3,6 +3,9 @@ package net.tcgdex.sdk.models.subs /** * Describes a single ability of a pokemon * + * @property type The Ability type (language dependant) + * @property name The ability name + * @property effect The ability effect */ data class CardAbility internal constructor( /** @@ -18,4 +21,4 @@ data class CardAbility internal constructor( * @return Description/Effect of the ability */ val effect: String -) \ No newline at end of file +) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt index aae1690..fa196c4 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt @@ -3,23 +3,15 @@ package net.tcgdex.sdk.models.subs /** * Describes a single attack of a pokemon, for example 'Confuse Ray' * + * @property name Name of the attack + * @property cost Cost of the attack in the same order as listed on the card + * @property effect Effect/Description of the attack, may be null for attacks without text + * @property damage Damage the attack deals. May just be a number like '30', but can also be a multiplier like 'x20' */ data class CardAttack internal constructor( - /** - * @return Cost of the attack in the same order as listed on the card - */ - val cost: List, - /** - * @return Name of the attack - */ + val name: String, - /** - * @return Effect/Description of the attack, may be null for attacks without text - */ - val effect: String, - /** - * @return Damage the attack deals. May just be a number like '30', but can also - * be a multiplier like 'x20' - */ - val damage: String -) \ No newline at end of file + val cost: List? = null, + val effect: String? = null, + val damage: String? = null +) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardItem.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardItem.kt new file mode 100644 index 0000000..95cabd8 --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardItem.kt @@ -0,0 +1,12 @@ +package net.tcgdex.sdk.models.subs + +/** + * Card Item + * + * @property name the Item name + * @property effect the item effect + */ +data class CardItem( + val name: String, + val effect: String +) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardVariants.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardVariants.kt new file mode 100644 index 0000000..b4753aa --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardVariants.kt @@ -0,0 +1,18 @@ +package net.tcgdex.sdk.models.subs + +/** + * Card variants + * + * @property normal basic variant no special effects + * @property reverse the card have some shine behind colored content + * @property holo the card picture have some shine to it + * @property firstEdition the card contains a First Edition Stamp (only Base serie) + * @property wPromo the card has a wPromo stamp on it + */ +data class CardVariants( + val normal: Boolean?, + val reverse: Boolean?, + val holo: Boolean?, + val firstEdition: Boolean?, + val wPromo: Boolean? +) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt index 6554c8b..86bafff 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt @@ -2,16 +2,12 @@ package net.tcgdex.sdk.models.subs /** - * Describes the weakness of a single pokemon, for example: 2x to Fire + * Describes the weakness/resistance of a single pokemon, for example: 2x to Fire * + * @property type the affecting type + * @property value the multiplier mostly `x2` but can also be `-30`, `+30` depending on the card */ data class CardWeakRes internal constructor( - /** - * @return Type the weakness is to - */ val type: String, - /** - * @return Descriptor of the weakness multiplier, including a leading x, for example 'x2'. May be null - */ - val value: String -) \ No newline at end of file + val value: String? = null +) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/Legal.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/Legal.kt new file mode 100644 index 0000000..369f10a --- /dev/null +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/Legal.kt @@ -0,0 +1,14 @@ +package net.tcgdex.sdk.models.subs + +/** + * Card Legality + * + * _note: cards are always legal in the ulimited tournament_ + * + * @property standard card is legal in standard tournaments + * @property expanded card is legal in expanded tournaments + */ +data class Legal( + val standard: Boolean, + val expanded: Boolean +) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt index 4cdb2bd..5effd3c 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt @@ -1,28 +1,20 @@ package net.tcgdex.sdk.models.subs +/** + * Set card count + * + * @property total total of number of cards + * @property official number of cards officialy (on the bottom of each cards) + * @property normal number of cards having a normal version + * @property reverse number of cards having an reverse version + * @property holo number of cards having an holo version + * @property firstEd Number of possible cards + */ data class SetCardCount ( - /** - * total of number of cards - */ val total: Int, - /** - * number of cards officialy (on the bottom of each cards) - */ val official: Int, - /** - * number of cards having a normal version - */ val normal: Int, - /** - * number of cards having an reverse version - */ val reverse: Int, - /** - * number of cards having an holo version - */ val holo: Int, - /** - * Number of possible cards - */ val firstEd: Int? -) \ No newline at end of file +) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt index e764894..733ca95 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt @@ -1,6 +1,12 @@ package net.tcgdex.sdk.models.subs +/** + * Set card count resume + * + * @property total total of number of cards + * @property official number of cards officialy (on the bottom of each cards) + */ data class SetCardCountResume ( val total: Int, val official: Int -) \ No newline at end of file +) diff --git a/src/test/kotlin/net/tcgdex/sdk/APITest.kt b/src/test/kotlin/net/tcgdex/sdk/APITest.kt index ed1d565..aadb47d 100644 --- a/src/test/kotlin/net/tcgdex/sdk/APITest.kt +++ b/src/test/kotlin/net/tcgdex/sdk/APITest.kt @@ -15,6 +15,7 @@ class APITest { @Test @Throws(IOException::class) fun testFullCardInfo() { + assertNotNull(api.fetchCards()) assertNotNull(api.fetchCard("swsh3-136")) assertNotNull(api.fetchCard("swsh3", "136")) assertNotNull(api.fetchSet("swsh3")) From 3f843ba3ab966d361f43661727e4bb38f34e4e64 Mon Sep 17 00:00:00 2001 From: Avior Date: Tue, 3 May 2022 17:29:27 +0200 Subject: [PATCH 05/11] fix: Url not returning the correct item --- build.gradle.kts | 10 +++++++--- src/main/kotlin/net/tcgdex/sdk/models/Card.kt | 2 +- src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt | 2 +- src/main/kotlin/net/tcgdex/sdk/models/Serie.kt | 2 +- src/main/kotlin/net/tcgdex/sdk/models/Set.kt | 4 ++-- .../kotlin/net/tcgdex/sdk/models/StringEndpoint.kt | 2 +- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 31b2178..bde8ee2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,10 @@ plugins { `maven-publish` } +val artifact = "sdk" +group = "net.tcgdex" +version = "2.0.1" + repositories { mavenCentral() } @@ -47,9 +51,9 @@ java { publishing { publications { create("maven") { - groupId = "net.tcgdex" - artifactId = "sdk" - version = "2.0.0" + // groupId = group + artifactId = artifact + // version = ver from(components["java"]) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Card.kt b/src/main/kotlin/net/tcgdex/sdk/models/Card.kt index 68e2e2f..100f910 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Card.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Card.kt @@ -78,7 +78,7 @@ data class Card internal constructor( * @return the full card URL with the extension and quality */ fun getImageUrl(quality: Quality, extension: Extension): String { - return "${this.image}/${quality}.${extension}" + return "${this.image}/${quality.value}.${extension}" } /** diff --git a/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt index 286ea71..62197c2 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt @@ -30,7 +30,7 @@ data class CardResume internal constructor( * @return the full card URL with the extension and quality */ fun getImageUrl(quality: Quality, extension: Extension): String { - return "${this.image}/${quality}.${extension}" + return "${this.image}/${quality}.${extension.value}" } /** diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt b/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt index de11dc4..7662996 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt @@ -29,7 +29,7 @@ class Serie ( if (this.logo == null) { return null } - return "${this.logo}.${extension}" + return "${this.logo}.${extension.value}" } /** diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Set.kt b/src/main/kotlin/net/tcgdex/sdk/models/Set.kt index 3f5c92e..2c1683e 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Set.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Set.kt @@ -44,7 +44,7 @@ data class Set internal constructor( if (this.logo == null) { return null } - return "${this.logo}.${extension}" + return "${this.logo}.${extension.value}" } /** @@ -68,7 +68,7 @@ data class Set internal constructor( if (this.symbol == null) { return null } - return "${this.symbol}.${extension}" + return "${this.symbol}.${extension.value}" } /** diff --git a/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt b/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt index 4e67512..b312aa6 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt @@ -6,7 +6,7 @@ import net.tcgdex.sdk.internal.Model * Class that handle a lot of Endpoints * * @property name the endpoint value - * @property cards the cards that contains `name` in them + * @property cards the cards that contain `name` in them * @constructor Create empty String endpoint */ data class StringEndpoint( From 89bb809d6497960b7d1f3e7b9c516cdfac287fe2 Mon Sep 17 00:00:00 2001 From: Avior Date: Wed, 4 May 2022 11:34:50 +0200 Subject: [PATCH 06/11] fix: make javadocs --- build.gradle.kts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index bde8ee2..c4fc5f9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,7 @@ plugins { // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. kotlin("jvm") version "1.6.20" + id("org.jetbrains.dokka") version "1.6.21" // Apply the java-library plugin for API and implementation separation. `java-library` @@ -40,7 +41,6 @@ tasks { compileTestKotlin { kotlinOptions.jvmTarget = "1.8" } - } java { @@ -48,6 +48,11 @@ java { withSourcesJar() } +// Javadocs +val javadocJar = tasks.named("javadocJar") { + from(tasks.named("dokkaJavadoc")) +} + publishing { publications { create("maven") { From 57fe9dd5acfcf8041df7b608592a7c94071d3928 Mon Sep 17 00:00:00 2001 From: Avior Date: Wed, 4 May 2022 15:00:48 +0200 Subject: [PATCH 07/11] feat: Add possibility to change group/artifactId/version from env --- build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c4fc5f9..ca6e88c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,9 +8,9 @@ plugins { `maven-publish` } -val artifact = "sdk" -group = "net.tcgdex" -version = "2.0.1" +val artifact = System.getenv("artifact") ?: "sdk" +group = System.getenv("group") ?: "net.tcgdex" +version = System.getenv("version") ?: "2.0.0" repositories { mavenCentral() From 3d442731ab5408addac918bfbba1c7a3e5a7ef6e Mon Sep 17 00:00:00 2001 From: Avior Date: Wed, 4 May 2022 15:01:32 +0200 Subject: [PATCH 08/11] feat: Moved Extension/Quality files to root --- .../kotlin/net/tcgdex/sdk/{models => }/Extension.kt | 12 ++++++++---- .../kotlin/net/tcgdex/sdk/{models => }/Quality.kt | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) rename src/main/kotlin/net/tcgdex/sdk/{models => }/Extension.kt (68%) rename src/main/kotlin/net/tcgdex/sdk/{models => }/Quality.kt (60%) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Extension.kt b/src/main/kotlin/net/tcgdex/sdk/Extension.kt similarity index 68% rename from src/main/kotlin/net/tcgdex/sdk/models/Extension.kt rename to src/main/kotlin/net/tcgdex/sdk/Extension.kt index af35270..07f9950 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Extension.kt +++ b/src/main/kotlin/net/tcgdex/sdk/Extension.kt @@ -1,11 +1,15 @@ -package net.tcgdex.sdk.models +package net.tcgdex.sdk /** * The different extension an image is available in - * - * @property value the string representation of the set */ -enum class Extension(val value: String) { +enum class Extension( + + /** + * the string representation of the extension + */ + val value: String +) { /** * .png image, with transparent background diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Quality.kt b/src/main/kotlin/net/tcgdex/sdk/Quality.kt similarity index 60% rename from src/main/kotlin/net/tcgdex/sdk/models/Quality.kt rename to src/main/kotlin/net/tcgdex/sdk/Quality.kt index 56d86a9..3c6ee0a 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Quality.kt +++ b/src/main/kotlin/net/tcgdex/sdk/Quality.kt @@ -1,12 +1,16 @@ -package net.tcgdex.sdk.models +package net.tcgdex.sdk /** * Image quality if applicable * (only cards does have quality selector) - * - * @property value the string representation of the quality */ -enum class Quality(val value: String) { +enum class Quality( + + /** + * the string representation of the quality + */ + val value: String +) { /** * A High quality image From 9daabee521385f8bef95e9d635102adc4bf76598 Mon Sep 17 00:00:00 2001 From: Avior Date: Wed, 4 May 2022 15:02:07 +0200 Subject: [PATCH 09/11] fix: Most Javadoc were not found by the generator --- src/main/kotlin/net/tcgdex/sdk/TCGdex.kt | 10 +- src/main/kotlin/net/tcgdex/sdk/models/Card.kt | 215 +++++++++++++----- .../net/tcgdex/sdk/models/CardResume.kt | 22 +- .../kotlin/net/tcgdex/sdk/models/Serie.kt | 22 +- .../net/tcgdex/sdk/models/SerieResume.kt | 17 +- src/main/kotlin/net/tcgdex/sdk/models/Set.kt | 51 ++++- .../kotlin/net/tcgdex/sdk/models/SetResume.kt | 22 +- .../net/tcgdex/sdk/models/StringEndpoint.kt | 11 +- .../net/tcgdex/sdk/models/subs/CardAbility.kt | 13 +- .../net/tcgdex/sdk/models/subs/CardAttack.kt | 29 ++- .../net/tcgdex/sdk/models/subs/CardItem.kt | 15 +- .../tcgdex/sdk/models/subs/CardVariants.kt | 36 ++- .../net/tcgdex/sdk/models/subs/CardWeakRes.kt | 15 +- .../net/tcgdex/sdk/models/subs/Legal.kt | 17 +- .../tcgdex/sdk/models/subs/SetCardCount.kt | 43 ++-- .../sdk/models/subs/SetCardCountResume.kt | 15 +- 16 files changed, 393 insertions(+), 160 deletions(-) diff --git a/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt b/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt index cc6ca79..7510342 100644 --- a/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt +++ b/src/main/kotlin/net/tcgdex/sdk/TCGdex.kt @@ -9,12 +9,14 @@ import net.tcgdex.sdk.models.SetResume import net.tcgdex.sdk.models.StringEndpoint /** - * TCGdex + * The main TCGdex SDK instance * - * @property language the language you want to use, values: [en,fr,es,de,pt,it] - * @constructor Create empty TCGdex in the specific language + * @constructor Create the TCGdex Instance in the specific language */ class TCGdex( + /** + * the language you want to use, values: [en,fr,es,de,pt,it] + */ var language: String ) { @@ -45,7 +47,7 @@ class TCGdex( } /** - * Fetch a specific card by it's set and local IDs + * Fetch a specific card by its set and local IDs * * @param setId the set ID/name * @param cardId the card local ID diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Card.kt b/src/main/kotlin/net/tcgdex/sdk/models/Card.kt index 100f910..e209b65 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Card.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Card.kt @@ -1,73 +1,166 @@ package net.tcgdex.sdk.models +import net.tcgdex.sdk.Extension +import net.tcgdex.sdk.Quality import net.tcgdex.sdk.Utils import net.tcgdex.sdk.internal.Model -import net.tcgdex.sdk.models.subs.* +import net.tcgdex.sdk.models.subs.CardAbility +import net.tcgdex.sdk.models.subs.CardAttack +import net.tcgdex.sdk.models.subs.CardItem +import net.tcgdex.sdk.models.subs.CardVariants +import net.tcgdex.sdk.models.subs.CardWeakRes +import net.tcgdex.sdk.models.subs.Legal import java.awt.image.BufferedImage /** * Pokémon TCG Card, It contains every informations about a specific card - * - * @property id Globally unique card ID based on the set ID and the cards ID within the set - * @property localId ID indexing this card within its set, usually just its number - * @property name Card name - * @property image Card image url without the extension and quality - * @property illustrator Card illustrator - * @property rarity Card rarity - * @property category Card category - * @property variants The card possible variants - * @property set Resume of the set the card belongs to - * @property dexIDs the Pokémon Pokédex IDs (multiple if multiple pokémon appears on the card) - * @property hp HP of the pokemon - * @property types Types of the pokemon (multiple because some have multiple in the older sets) - * @property evolveFrom Name of the pokemon this one evolves from - * @property description the Pokémon Pokédex like description - * @property level the Pokémon Level (can be "X" if the card is of level X) - * @property stage the Pokémon Stage (changes depending on the API language) - * @property suffix the Pokémon Suffix (changes depending on the API language) - * @property item the Item the Pokémon have - * @property abilities the Card abilities (some cards have multiple abilities) - * @property attacks the Card Attacks - * @property weaknesses the Pokémon Weaknesses - * @property resistances the Pokémon Resistances - * @property retreat the Pokémon retreat Cost - * @property effect the Card Effect (Trainer/Energy only) - * @property trainerType the trainer sub type (changes depending on the API language) - * @property energyType the energy sub type (changes depending on the API language) - * @property regulationMark the Card Regulation mark - * @property legal the card ability to be played in tournaments */ data class Card internal constructor( - val id: String, - val localId: String, - val name: String, - val image: String?, - - val illustrator: String?, - val rarity: String, - val category: String, - val variants: CardVariants?, - val set: SetResume, - - private val dexIDs: List?, - val hp: Int?, - val types: List?, - val evolveFrom: String?, - val description: String?, - val level: String?, - val stage: String?, - val suffix: String?, - val item: CardItem?, - val abilities: List, - val attacks: List, - val weaknesses: List, - val resistances: List, - val retreat: Int?, - - val effect: String?, - val trainerType: String?, - val energyType: String?, - val regulationMark: String?, - val legal: Legal + + /** + * Globally unique card ID based on the set ID and the cards ID within the set + */ + val id: String, + + /** + * ID indexing this card within its set, usually just its number + */ + val localId: String, + + /** + * Card name + */ + val name: String, + + /** + * Card image url without the extension and quality + */ + val image: String?, + + + /** + * Card illustrator + */ + val illustrator: String?, + + /** + * Card rarity + */ + val rarity: String, + + /** + * Card category + */ + val category: String, + + /** + * The card possible variants + */ + val variants: CardVariants?, + + /** + * Resume of the set the card belongs to + */ + val set: SetResume, + + + /** + * the Pokémon Pokédex IDs (multiple if multiple pokémon appears on the card) + */ + val dexIDs: List?, + + /** + * HP of the pokemon + */ + val hp: Int?, + + /** + * Types of the pokemon (multiple because some have multiple in the older sets) + */ + val types: List?, + + /** + * Name of the pokemon this one evolves from + */ + val evolveFrom: String?, + + /** + * the Pokémon Pokédex like description + */ + val description: String?, + + /** + *the Pokémon Level (can be "X" if the card is of level X) + */ + val level: String?, + + /** + * the Pokémon Stage (changes depending on the API language) + */ + val stage: String?, + + /** + * the Pokémon Suffix (changes depending on the API language) + */ + val suffix: String?, + + /** + * the Item the Pokémon have + */ + val item: CardItem?, + + /** + * the Card abilities (some cards have multiple abilities) + */ + val abilities: List, + + /** + * the Card Attacks + */ + val attacks: List, + + /** + * + * the Pokémon Weaknesses + */ + val weaknesses: List, + + /** + * + * the Pokémon Resistances + */ + val resistances: List, + + /** + * + * the Pokémon retreat Cost + */ + val retreat: Int?, + + + /** + * effect the Card Effect (Trainer/Energy only) + */ + val effect: String?, + + /** + * the trainer sub type (changes depending on the API language) + */ + val trainerType: String?, + + /** + * the energy sub type (changes depending on the API language) + */ + val energyType: String?, + + /** + * the Card Regulation mark + */ + val regulationMark: String?, + + /** + * the card ability to be played in tournaments + */ + val legal: Legal ) : Model() { /** diff --git a/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt index 62197c2..f936dba 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/CardResume.kt @@ -1,5 +1,7 @@ package net.tcgdex.sdk.models +import net.tcgdex.sdk.Extension +import net.tcgdex.sdk.Quality import net.tcgdex.sdk.Utils import net.tcgdex.sdk.internal.Model import java.awt.image.BufferedImage @@ -8,17 +10,27 @@ import java.awt.image.BufferedImage * Card Resume class, contains basic informations about a specific card * * to get the full card you can use the `getFullCard()` function - * - * @property id Globally unique card ID based on the set ID and the cards ID within the set - * @property localId ID indexing this card within its set, usually just its number - * @property name Card name - * @property image Card image url without the extension and quality */ data class CardResume internal constructor( + /** + * Globally unique card ID based on the set ID and the cards ID within the set + */ val id: String, + + /** + * ID indexing this card within its set, usually just its number + */ val localId: String, + + /** + * Card name + */ val name: String, + + /** + * Card image url without the extension and quality + */ val image: String? ) : Model() { diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt b/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt index 7662996..3b6560c 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Serie.kt @@ -1,21 +1,33 @@ package net.tcgdex.sdk.models +import net.tcgdex.sdk.Extension import net.tcgdex.sdk.Utils import net.tcgdex.sdk.internal.Model import java.awt.image.BufferedImage /** * Pokémon TCG Serie - * - * @property sets the list of sets the Serie contains - * @property id the Serie unique ID - * @property name the Serie name - * @property logo the Serie Logo (basically also the first set logo) */ class Serie ( + + /** + * the list of sets the Serie contains + */ val sets: List, + + /** + * the Serie unique ID + */ val id: String, + + /** + * the Serie name + */ val name: String, + + /** + * the Serie Logo (basically also the first set logo) + */ val logo: String? ) : Model() { diff --git a/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt index dcae242..e0f5324 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/SerieResume.kt @@ -1,5 +1,6 @@ package net.tcgdex.sdk.models +import net.tcgdex.sdk.Extension import net.tcgdex.sdk.Utils import net.tcgdex.sdk.internal.Model import java.awt.image.BufferedImage @@ -7,14 +8,22 @@ import java.awt.image.BufferedImage /** * Serie Resume - * - * @property id the Serie unique ID - * @property name the Serie name - * @property logo the Serie Logo (basically also the first set logo) */ open class SerieResume internal constructor( + + /** + * the Serie unique ID + */ val id: String, + + /** + * the Serie name + */ val name: String, + + /** + * the Serie Logo (basically also the first set logo) + */ val logo: String? ) : Model() { diff --git a/src/main/kotlin/net/tcgdex/sdk/models/Set.kt b/src/main/kotlin/net/tcgdex/sdk/models/Set.kt index 2c1683e..d9079e4 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/Set.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/Set.kt @@ -1,5 +1,6 @@ package net.tcgdex.sdk.models +import net.tcgdex.sdk.Extension import net.tcgdex.sdk.Utils import net.tcgdex.sdk.internal.Model import net.tcgdex.sdk.models.subs.Legal @@ -8,29 +9,57 @@ import java.awt.image.BufferedImage /** * Pokémon TCG Set class - * - * @property id Globally unique set ID - * @property name the Set mame - * @property logo the Set Logo incomplete URL (use getLogoUrl/getLogo) - * @property symbol the Set Symbol imcomplete URL (use getSymbolUrl/getSymbol) - * @property serie the serie this set is a part of - * @property tcgOnline the TCG Online Code - * @property releaseDate the Set release date as yyyy-mm-dd - * @property legal the set legality (won't indicate if a card is banned) - * @property cardCount the number of card in the set - * @property cards the cards contained in this set */ data class Set internal constructor( + + /** + * Globally unique set ID + */ val id: String?, + + /** + * the Set mame + */ val name: String?, + + /** + * the Set Logo incomplete URL (use getLogoUrl/getLogo) + */ val logo: String?, + + /** + * the Set Symbol imcomplete URL (use getSymbolUrl/getSymbol) + */ val symbol: String?, + /** + * the serie this set is a part of + */ val serie: SerieResume, + + /** + * the TCG Online Code + */ val tcgOnline: String?, + + /** + * the Set release date as yyyy-mm-dd + */ val releaseDate: String?, + + /** + * the set legality (won't indicate if a card is banned) + */ val legal: Legal, + + /** + * the number of card in the set + */ val cardCount: SetCardCount, + + /** + * the cards contained in this set + */ val cards: List ) : Model() { diff --git a/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt index 8aa02bc..f0db5ed 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/SetResume.kt @@ -1,5 +1,6 @@ package net.tcgdex.sdk.models +import net.tcgdex.sdk.Extension import net.tcgdex.sdk.Utils import net.tcgdex.sdk.internal.Model import net.tcgdex.sdk.models.subs.SetCardCountResume @@ -7,23 +8,32 @@ import java.awt.image.BufferedImage /** * Set resume - * - * @property id Globally unique set ID - * @property name the Set mame - * @property logo the Set Logo incomplete URL (use getLogoUrl/getLogo) - * @property symbol the Set Symbol imcomplete URL (use getSymbolUrl/getSymbol) - * @property cardCount the number of card in the set */ data class SetResume internal constructor( + /** + * Globally unique set ID + */ val id: String, + /** + * the Set mame + */ val name: String, + /** + * the Set Logo incomplete URL (use getLogoUrl/getLogo) + */ val logo: String?, + /** + * the Set Symbol incomplete URL (use getSymbolUrl/getSymbol) + */ val symbol: String?, + /** + * the number of card in the set + */ val cardCount: SetCardCountResume ) : Model() { diff --git a/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt b/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt index b312aa6..943b68f 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/StringEndpoint.kt @@ -3,13 +3,18 @@ package net.tcgdex.sdk.models import net.tcgdex.sdk.internal.Model /** - * Class that handle a lot of Endpoints + * Generix class that handle a lot of Endpoints * - * @property name the endpoint value - * @property cards the cards that contain `name` in them * @constructor Create empty String endpoint */ data class StringEndpoint( + /** + * the endpoint value + */ val name: String, + + /** + * the cards that contain `name` in them + */ val cards: List ) : Model() diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt index 02b8108..6eb400b 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAbility.kt @@ -2,23 +2,22 @@ package net.tcgdex.sdk.models.subs /** * Describes a single ability of a pokemon - * - * @property type The Ability type (language dependant) - * @property name The ability name - * @property effect The ability effect */ data class CardAbility internal constructor( + /** - * @return Type of the ability, for example 'Poke-POWER' + * The Ability type (language dependant) */ val type: String, + /** - * @return Name of the ability + * Name of the ability */ val name: String, + /** * - * @return Description/Effect of the ability + * Description/Effect of the ability */ val effect: String ) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt index fa196c4..e7536da 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardAttack.kt @@ -2,16 +2,27 @@ package net.tcgdex.sdk.models.subs /** * Describes a single attack of a pokemon, for example 'Confuse Ray' - * - * @property name Name of the attack - * @property cost Cost of the attack in the same order as listed on the card - * @property effect Effect/Description of the attack, may be null for attacks without text - * @property damage Damage the attack deals. May just be a number like '30', but can also be a multiplier like 'x20' */ data class CardAttack internal constructor( - val name: String, - val cost: List? = null, - val effect: String? = null, - val damage: String? = null + + /** + * Name of the attack + */ + val name: String, + + /** + * Cost of the attack in the same order as listed on the card + */ + val cost: List? = null, + + /** + * Effect/Description of the attack, may be null for attacks without text + */ + val effect: String? = null, + + /** + * Damage the attack deals. May just be a number like '30', but can also be a multiplier like 'x20' + */ + val damage: String? = null ) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardItem.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardItem.kt index 95cabd8..294cad6 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardItem.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardItem.kt @@ -2,11 +2,16 @@ package net.tcgdex.sdk.models.subs /** * Card Item - * - * @property name the Item name - * @property effect the item effect */ data class CardItem( - val name: String, - val effect: String + + /** + * the Item name + */ + val name: String, + + /** + * the item effect + */ + val effect: String ) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardVariants.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardVariants.kt index b4753aa..890025e 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardVariants.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardVariants.kt @@ -2,17 +2,31 @@ package net.tcgdex.sdk.models.subs /** * Card variants - * - * @property normal basic variant no special effects - * @property reverse the card have some shine behind colored content - * @property holo the card picture have some shine to it - * @property firstEdition the card contains a First Edition Stamp (only Base serie) - * @property wPromo the card has a wPromo stamp on it */ data class CardVariants( - val normal: Boolean?, - val reverse: Boolean?, - val holo: Boolean?, - val firstEdition: Boolean?, - val wPromo: Boolean? + + /** + * basic variant no special effects + */ + val normal: Boolean?, + + /** + * the card have some shine behind colored content + */ + val reverse: Boolean?, + + /** + * the card picture have some shine to it + */ + val holo: Boolean?, + + /** + * the card contains a First Edition Stamp (only Base serie) + */ + val firstEdition: Boolean?, + + /** + * the card has a wPromo stamp on it + */ + val wPromo: Boolean? ) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt index 86bafff..1b10ce2 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/CardWeakRes.kt @@ -3,11 +3,16 @@ package net.tcgdex.sdk.models.subs /** * Describes the weakness/resistance of a single pokemon, for example: 2x to Fire - * - * @property type the affecting type - * @property value the multiplier mostly `x2` but can also be `-30`, `+30` depending on the card */ data class CardWeakRes internal constructor( - val type: String, - val value: String? = null + + /** + * the affecting type + */ + val type: String, + + /** + * the multiplier mostly `x2` but can also be `-30`, `+30` depending on the card + */ + val value: String? ) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/Legal.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/Legal.kt index 369f10a..5f10d3a 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/Legal.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/Legal.kt @@ -3,12 +3,17 @@ package net.tcgdex.sdk.models.subs /** * Card Legality * - * _note: cards are always legal in the ulimited tournament_ - * - * @property standard card is legal in standard tournaments - * @property expanded card is legal in expanded tournaments + * _note: cards are always usable in the unlimited tournaments_ */ data class Legal( - val standard: Boolean, - val expanded: Boolean + + /** + * card is usable in standard tournaments + */ + val standard: Boolean, + + /** + * card is usable in expanded tournaments + */ + val expanded: Boolean ) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt index 5effd3c..d90ab35 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCount.kt @@ -2,19 +2,36 @@ package net.tcgdex.sdk.models.subs /** * Set card count - * - * @property total total of number of cards - * @property official number of cards officialy (on the bottom of each cards) - * @property normal number of cards having a normal version - * @property reverse number of cards having an reverse version - * @property holo number of cards having an holo version - * @property firstEd Number of possible cards */ data class SetCardCount ( - val total: Int, - val official: Int, - val normal: Int, - val reverse: Int, - val holo: Int, - val firstEd: Int? + + /** + * total of number of cards + */ + val total: Int, + + /** + * number of cards officialy (on the bottom of each cards) + */ + val official: Int, + + /** + * number of cards having a normal version + */ + val normal: Int, + + /** + * number of cards having an reverse version + */ + val reverse: Int, + + /** + * number of cards having an holo version + */ + val holo: Int, + + /** + * Number of possible cards + */ + val firstEd: Int? ) diff --git a/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt index 733ca95..adc1ca1 100644 --- a/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt +++ b/src/main/kotlin/net/tcgdex/sdk/models/subs/SetCardCountResume.kt @@ -2,11 +2,16 @@ package net.tcgdex.sdk.models.subs /** * Set card count resume - * - * @property total total of number of cards - * @property official number of cards officialy (on the bottom of each cards) */ data class SetCardCountResume ( - val total: Int, - val official: Int + + /** + * total of number of cards + */ + val total: Int, + + /** + * number of cards officialy (on the bottom of each cards) + */ + val official: Int ) From e3a3d270ace3cfff1030a3ba2f3bc14205790d86 Mon Sep 17 00:00:00 2001 From: Avior Date: Wed, 4 May 2022 15:03:28 +0200 Subject: [PATCH 10/11] fix: simplified .gitignore --- .gitignore | 216 +--------------------------------------------------- gradlew.bat | 89 ++++++++++++++++++++++ 2 files changed, 91 insertions(+), 214 deletions(-) create mode 100644 gradlew.bat diff --git a/.gitignore b/.gitignore index 5c84812..e0d53d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,215 +1,3 @@ - -# Created by https://www.toptal.com/developers/gitignore/api/java,batch,linux,macos,gradle,windows,eclipse,intellij+all -# Edit at https://www.toptal.com/developers/gitignore?templates=java,batch,linux,macos,gradle,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/ -.apt_generated_test/ - -# Scala IDE specific (Scala & Java development for Eclipse) -.cache-main -.scala_dependencies -.worksheet - -# Uncomment this line if you wish to ignore the project description file. -# Typically, this file would be tracked if it contains build/dependency configurations: -#.project - -### Eclipse Patch ### -# Spring Boot Tooling -.sts4-cache/ - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -### Intellij+all Patch ### -# Ignore everything but code style settings and run configurations -# that are supposed to be shared within teams. - -.idea/* - -!.idea/codeStyles -!.idea/runConfigurations - -### 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* -replay_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 - -### macOS Patch ### -# iCloud generated files -*.icloud - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -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 - -### Gradle ### .gradle -**/build/ -!src/**/build/ - -# Ignore Gradle GUI config -gradle-app.setting - -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) -!gradle-wrapper.jar - -# Avoid ignore Gradle wrappper properties -!gradle-wrapper.properties - -# Cache of project -.gradletasknamecache - -# Eclipse Gradle plugin generated files -# Eclipse Core -.project -# JDT-specific (Eclipse Java Development Tools) -.classpath - -# End of https://www.toptal.com/developers/gitignore/api/java,batch,linux,macos,gradle,windows,eclipse,intellij+all +.idea +build diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 3e6d605f04f8fb0b2583cd4925ef6a13a608c01a Mon Sep 17 00:00:00 2001 From: Avior Date: Wed, 4 May 2022 15:23:49 +0200 Subject: [PATCH 11/11] fix: incorrect package --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9bfdceb..2e14e6e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

- + Package Version