From 13d238614080adb952e7d83fc6992c40d19c819a Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sun, 23 Nov 2025 11:40:06 +0530 Subject: [PATCH 1/2] Fix Java examples using Kotlin-specific syntax Java examples were incorrectly using Kotlin-specific syntax like listOf() and mapOf(). This commit fixes the issue by: - Added language-specific parameter support to getParamExample() method in Kotlin.php - Created getKotlinMapExample() and getJavaMapExample() helper methods for proper syntax generation - Updated getPermissionExample() to support both Java (Arrays.asList) and Kotlin (listOf) syntax - Added new javaParamExample Twig filter that generates Java-compatible syntax - Updated Java example templates to use javaParamExample filter - Fixed import ordering in Java templates to follow Java conventions (java.* imports first, then project imports) Now Java examples correctly use: - Arrays.asList() instead of listOf() - new HashMap() {{ put() }} instead of mapOf() - Collections.emptyList() instead of listOf() --- .github/workflows/tests.yml | 2 +- composer.lock | 150 ++++++------ src/SDK/Language/Kotlin.php | 254 +++++++++++++++++--- templates/android/docs/java/example.md.twig | 10 +- templates/kotlin/docs/java/example.md.twig | 10 +- 5 files changed, 316 insertions(+), 110 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1d2bc5166..5a95e2f5c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -94,7 +94,7 @@ jobs: - name: Setup PHP with PECL extension uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-version }} + php-version: '8.3' extensions: curl - name: Install diff --git a/composer.lock b/composer.lock index 54b0f3840..59427e00c 100644 --- a/composer.lock +++ b/composer.lock @@ -801,16 +801,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.1", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { @@ -853,9 +853,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2025-08-13T20:13:15+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "phar-io/manifest", @@ -977,16 +977,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "11.0.10", + "version": "11.0.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "1a800a7446add2d79cc6b3c01c45381810367d76" + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1a800a7446add2d79cc6b3c01c45381810367d76", - "reference": "1a800a7446add2d79cc6b3c01c45381810367d76", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", "shasum": "" }, "require": { @@ -1043,7 +1043,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/show" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" }, "funding": [ { @@ -1063,7 +1063,7 @@ "type": "tidelift" } ], - "time": "2025-06-18T08:56:18+00:00" + "time": "2025-08-27T14:37:49+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1312,16 +1312,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.34", + "version": "11.5.44", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3e4c6ef395f7cb61a6206c23e0e04b31724174f2" + "reference": "c346885c95423eda3f65d85a194aaa24873cda82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e4c6ef395f7cb61a6206c23e0e04b31724174f2", - "reference": "3e4c6ef395f7cb61a6206c23e0e04b31724174f2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c346885c95423eda3f65d85a194aaa24873cda82", + "reference": "c346885c95423eda3f65d85a194aaa24873cda82", "shasum": "" }, "require": { @@ -1335,7 +1335,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.10", + "phpunit/php-code-coverage": "^11.0.11", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", @@ -1345,7 +1345,7 @@ "sebastian/comparator": "^6.3.2", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.1", - "sebastian/exporter": "^6.3.0", + "sebastian/exporter": "^6.3.2", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", "sebastian/type": "^5.1.3", @@ -1393,7 +1393,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.34" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.44" }, "funding": [ { @@ -1417,7 +1417,7 @@ "type": "tidelift" } ], - "time": "2025-08-20T14:41:45+00:00" + "time": "2025-11-13T07:17:35+00:00" }, { "name": "psr/container", @@ -1937,16 +1937,16 @@ }, { "name": "sebastian/exporter", - "version": "6.3.0", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", "shasum": "" }, "require": { @@ -1960,7 +1960,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -2003,15 +2003,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-12-05T09:17:50+00:00" + "time": "2025-09-24T06:12:51+00:00" }, { "name": "sebastian/global-state", @@ -2448,16 +2460,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.13.2", + "version": "3.13.5", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "5b5e3821314f947dd040c70f7992a64eac89025c" + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5b5e3821314f947dd040c70f7992a64eac89025c", - "reference": "5b5e3821314f947dd040c70f7992a64eac89025c", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", "shasum": "" }, "require": { @@ -2474,11 +2486,6 @@ "bin/phpcs" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -2528,7 +2535,7 @@ "type": "thanks_dev" } ], - "time": "2025-06-17T22:17:01+00:00" + "time": "2025-11-04T16:30:35+00:00" }, { "name": "staabm/side-effects-detector", @@ -2584,16 +2591,16 @@ }, { "name": "symfony/console", - "version": "v7.3.2", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" + "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", - "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", + "url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", + "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", "shasum": "" }, "require": { @@ -2658,7 +2665,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.2" + "source": "https://github.com/symfony/console/tree/v7.3.6" }, "funding": [ { @@ -2678,7 +2685,7 @@ "type": "tidelift" } ], - "time": "2025-07-30T17:13:41+00:00" + "time": "2025-11-04T01:21:42+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -2849,16 +2856,16 @@ }, { "name": "symfony/process", - "version": "v7.3.0", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -2890,7 +2897,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -2901,25 +2908,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-17T09:11:12+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -2973,7 +2984,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -2984,25 +2995,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/string", - "version": "v7.3.2", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", - "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -3017,7 +3032,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -3060,7 +3074,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.2" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -3080,20 +3094,20 @@ "type": "tidelift" } ], - "time": "2025-07-10T08:47:49+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -3122,7 +3136,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -3130,7 +3144,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], diff --git a/src/SDK/Language/Kotlin.php b/src/SDK/Language/Kotlin.php index c072e9d3a..5d18e06bc 100644 --- a/src/SDK/Language/Kotlin.php +++ b/src/SDK/Language/Kotlin.php @@ -205,9 +205,10 @@ public function getParamDefault(array $param): string /** * @param array $param + * @param string $lang Language variant: 'kotlin' (default) or 'java' * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = 'kotlin'): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; @@ -230,10 +231,12 @@ public function getParamExample(array $param): string $output .= "\"\""; break; case self::TYPE_OBJECT: - $output .= 'mapOf( "a" to "b" )'; + $output .= $lang === 'java' + ? 'Map.of("a", "b")' + : 'mapOf( "a" to "b" )'; break; case self::TYPE_ARRAY: - $output .= 'listOf()'; + $output .= $lang === 'java' ? 'List.of()' : 'listOf()'; break; } } else { @@ -241,29 +244,15 @@ public function getParamExample(array $param): string case self::TYPE_OBJECT: $decoded = json_decode($example, true); if ($decoded && is_array($decoded)) { - $mapEntries = []; - foreach ($decoded as $key => $value) { - $formattedKey = '"' . $key . '"'; - if (is_string($value)) { - $formattedValue = '"' . $value . '"'; - } elseif (is_bool($value)) { - $formattedValue = $value ? 'true' : 'false'; - } elseif (is_null($value)) { - $formattedValue = 'null'; - } elseif (is_array($value)) { - $formattedValue = 'listOf()'; // Simplified for nested arrays - } else { - $formattedValue = (string)$value; - } - $mapEntries[] = ' ' . $formattedKey . ' to ' . $formattedValue; - } - if (count($mapEntries) > 0) { - $output .= "mapOf(\n" . implode(",\n", $mapEntries) . "\n )"; + if ($lang === 'java') { + $output .= $this->getJavaMapExample($decoded); } else { - $output .= 'mapOf( "a" to "b" )'; + $output .= $this->getKotlinMapExample($decoded); } } else { - $output .= 'mapOf( "a" to "b" )'; + $output .= $lang === 'java' + ? 'Map.of("a", "b")' + : 'mapOf( "a" to "b" )'; } break; case self::TYPE_FILE: @@ -273,15 +262,9 @@ public function getParamExample(array $param): string break; case self::TYPE_ARRAY: if ($this->isPermissionString($example)) { - $output .= $this->getPermissionExample($example); + $output .= $this->getPermissionExample($example, $lang); } else { - if (\str_starts_with($example, '[')) { - $example = \substr($example, 1); - } - if (\str_ends_with($example, ']')) { - $example = \substr($example, 0, -1); - } - $output .= 'listOf(' . $example . ')'; + $output .= $this->getArrayExample($example, $lang); } break; case self::TYPE_BOOLEAN: @@ -296,6 +279,212 @@ public function getParamExample(array $param): string return $output; } + /** + * Generate Kotlin-style map initialization + * + * @param array $data + * @return string + */ + protected function getKotlinMapExample(array $data): string + { + return $this->getKotlinMapExampleRecursive($data, 0); + } + + /** + * Recursive helper for generating Kotlin mapOf() with proper indentation + * + * @param array $data + * @param int $indentLevel Indentation level for nested maps + * @return string + */ + private function getKotlinMapExampleRecursive(array $data, int $indentLevel): string + { + $mapEntries = []; + $baseIndent = str_repeat(' ', $indentLevel + 2); + + foreach ($data as $key => $value) { + $formattedKey = '"' . $key . '"'; + if (is_string($value)) { + $formattedValue = '"' . $value . '"'; + } elseif (is_bool($value)) { + $formattedValue = $value ? 'true' : 'false'; + } elseif (is_null($value)) { + $formattedValue = 'null'; + } elseif (is_array($value)) { + // Check if it's an associative array (object) or indexed array + $isObject = array_keys($value) !== range(0, count($value) - 1); + if ($isObject) { + $formattedValue = $this->getKotlinMapExampleRecursive($value, $indentLevel + 1); + } else { + $formattedValue = $this->getArrayExample(json_encode($value), 'kotlin'); + } + } else { + $formattedValue = (string)$value; + } + $mapEntries[] = $baseIndent . $formattedKey . ' to ' . $formattedValue; + } + + if (count($mapEntries) > 0) { + $closeIndent = str_repeat(' ', $indentLevel + 1); + return "mapOf(\n" . implode(",\n", $mapEntries) . "\n" . $closeIndent . ")"; + } else { + return 'mapOf( "a" to "b" )'; + } + } + + /** + * Generate Java-style map initialization using Map.of() + * + * @param array $data + * @return string + */ + protected function getJavaMapExample(array $data): string + { + return $this->getJavaMapExampleRecursive($data, 0); + } + + /** + * Recursive helper for generating Java Map.of() with proper indentation + * + * @param array $data + * @param int $indentLevel Indentation level for nested maps + * @return string + */ + private function getJavaMapExampleRecursive(array $data, int $indentLevel): string + { + $mapEntries = []; + $baseIndent = str_repeat(' ', $indentLevel + 2); + + foreach ($data as $key => $value) { + $formattedKey = '"' . $key . '"'; + if (is_string($value)) { + $formattedValue = '"' . $value . '"'; + } elseif (is_bool($value)) { + $formattedValue = $value ? 'true' : 'false'; + } elseif (is_null($value)) { + $formattedValue = 'null'; + } elseif (is_array($value)) { + // Check if it's an associative array (object) or indexed array + $isObject = array_keys($value) !== range(0, count($value) - 1); + if ($isObject) { + $formattedValue = $this->getJavaMapExampleRecursive($value, $indentLevel + 1); + } else { + $formattedValue = $this->getArrayExample(json_encode($value), 'java'); + } + } else { + $formattedValue = (string)$value; + } + $mapEntries[] = $baseIndent . $formattedKey . ', ' . $formattedValue; + } + + if (count($mapEntries) > 0) { + $closeIndent = str_repeat(' ', $indentLevel + 1); + return "Map.of(\n" . implode(",\n", $mapEntries) . "\n" . $closeIndent . ")"; + } else { + return 'Map.of("a", "b")'; + } + } + + /** + * Generate array example for the given language + * + * @param string $example Array example like '[1, 2, 3]' or '[{"key": "value"}]' + * @param string $lang Language variant: 'kotlin' or 'java' + * @return string + */ + protected function getArrayExample(string $example, string $lang = 'kotlin'): string + { + // Try to decode as JSON to handle arrays of objects + $decoded = json_decode($example, true); + if ($decoded && is_array($decoded)) { + $arrayItems = []; + foreach ($decoded as $item) { + if (is_array($item)) { + // Check if it's an associative array (object) or indexed array (nested array) + $isObject = array_keys($item) !== range(0, count($item) - 1); + + if ($isObject) { + // It's an object/map, convert it + if ($lang === 'java') { + $arrayItems[] = $this->getJavaMapExample($item); + } else { + $arrayItems[] = $this->getKotlinMapExample($item); + } + } else { + // It's a nested array, recursively convert it + $arrayItems[] = $this->getArrayExample(json_encode($item), $lang); + } + } else { + // Primitive value + if (is_string($item)) { + $arrayItems[] = '"' . $item . '"'; + } elseif (is_bool($item)) { + $arrayItems[] = $item ? 'true' : 'false'; + } elseif (is_null($item)) { + $arrayItems[] = 'null'; + } else { + $arrayItems[] = (string)$item; + } + } + } + return $lang === 'java' + ? 'List.of(' . implode(', ', $arrayItems) . ')' + : 'listOf(' . implode(', ', $arrayItems) . ')'; + } + + // Fallback to old behavior for non-JSON arrays + if (\str_starts_with($example, '[')) { + $example = \substr($example, 1); + } + if (\str_ends_with($example, ']')) { + $example = \substr($example, 0, -1); + } + return $lang === 'java' + ? 'List.of(' . $example . ')' + : 'listOf(' . $example . ')'; + } + + /** + * Generate permission example for the given language + * + * @param string $example Permission string like '["read(\"any\")"]' + * @param string $lang Language variant: 'kotlin' or 'java' + * @return string + */ + public function getPermissionExample(string $example, string $lang = 'kotlin'): string + { + $permissions = []; + $staticOp = $this->getStaticAccessOperator(); + $quote = $this->getStringQuote(); + $prefix = $this->getPermissionPrefix(); + + foreach ($this->extractPermissionParts($example) as $permission) { + $args = []; + if ($permission['id'] !== null) { + $args[] = $quote . $permission['id'] . $quote; + } + if ($permission['innerRole'] !== null) { + $args[] = $quote . $permission['innerRole'] . $quote; + } + $argsString = implode(', ', $args); + + $action = $permission['action']; + $role = $permission['role']; + $action = $this->transformPermissionAction($action); + $role = $this->transformPermissionRole($role); + + $permissions[] = $prefix . 'Permission' . $staticOp . $action . '(' . $prefix . 'Role' . $staticOp . $role . '(' . $argsString . '))'; + } + + $permissionsString = implode(', ', $permissions); + + // For Java, use List.of() instead of listOf() + if ($lang === 'java') { + return 'List.of(' . $permissionsString . ')'; + } + return 'listOf(' . $permissionsString . ')'; + } + /** * @return array */ @@ -496,6 +685,9 @@ public function getFilters(): array new TwigFilter('propertyAssignment', function (array $property, array $spec) { return $this->getPropertyAssignment($property, $spec); }), + new TwigFilter('javaParamExample', function (array $param) { + return $this->getParamExample($param, 'java'); + }, ['is_safe' => ['html']]), ]; } diff --git a/templates/android/docs/java/example.md.twig b/templates/android/docs/java/example.md.twig index 591acb28f..663e7c257 100644 --- a/templates/android/docs/java/example.md.twig +++ b/templates/android/docs/java/example.md.twig @@ -3,6 +3,10 @@ import {{ sdk.namespace | caseDot }}.coroutines.CoroutineCallback; {% if method.parameters.all | filter((param) => param.type == 'file') | length > 0 %} import {{ sdk.namespace | caseDot }}.models.InputFile; {% endif %} +{% if method.parameters.all | hasPermissionParam %} +import {{ sdk.namespace | caseDot }}.Permission; +import {{ sdk.namespace | caseDot }}.Role; +{% endif %} import {{ sdk.namespace | caseDot }}.services.{{ service.name | caseUcfirst }}; {% set added = [] %} {% for parameter in method.parameters.all %} @@ -13,10 +17,6 @@ import {{ sdk.namespace | caseDot }}.enums.{{ parameter.enumName | caseUcfirst } {% endif %} {% endif %} {% endfor %} -{% if method.parameters.all | hasPermissionParam %} -import {{ sdk.namespace | caseDot }}.Permission; -import {{ sdk.namespace | caseDot }}.Role; -{% endif %} Client client = new Client(context) {%~ if method.auth|length > 0 %} @@ -41,7 +41,7 @@ Client client = new Client(context) {% for parameter in method.parameters.all %} {% if parameter.enumValues is not empty %}{{ parameter.enumName | caseUcfirst }}.{{ (parameter.enumKeys[0] ?? parameter.enumValues[0]) | caseEnumKey }}, // {{ parameter.name }} {% if not parameter.required %}(optional){% endif %} -{% else %}{{ parameter | paramExample }}, // {{ parameter.name }} {% if not parameter.required %}(optional){% endif %} +{% else %}{{ parameter | javaParamExample }}, // {{ parameter.name }} {% if not parameter.required %}(optional){% endif %} {% endif %} {%~ if loop.last %} diff --git a/templates/kotlin/docs/java/example.md.twig b/templates/kotlin/docs/java/example.md.twig index 2b0db8978..9ae6a3a83 100644 --- a/templates/kotlin/docs/java/example.md.twig +++ b/templates/kotlin/docs/java/example.md.twig @@ -3,6 +3,10 @@ import {{ sdk.namespace | caseDot }}.coroutines.CoroutineCallback; {% if method.parameters.all | filter((param) => param.type == 'file') | length > 0 %} import {{ sdk.namespace | caseDot }}.models.InputFile; {% endif %} +{% if method.parameters.all | hasPermissionParam %} +import {{ sdk.namespace | caseDot }}.Permission; +import {{ sdk.namespace | caseDot }}.Role; +{% endif %} import {{ sdk.namespace | caseDot }}.services.{{ service.name | caseUcfirst }}; {% set added = [] %} {% for parameter in method.parameters.all %} @@ -13,10 +17,6 @@ import {{ sdk.namespace | caseDot }}.enums.{{ parameter.enumName | caseUcfirst } {% endif %} {% endif %} {% endfor %} -{% if method.parameters.all | hasPermissionParam %} -import {{ sdk.namespace | caseDot }}.Permission; -import {{ sdk.namespace | caseDot }}.Role; -{% endif %} Client client = new Client() {% if method.auth|length > 0 %} @@ -38,7 +38,7 @@ Client client = new Client() }));{% endif %} {%~ for parameter in method.parameters.all %} - {% if parameter.enumValues | length > 0%}{{ parameter.enumName | caseUcfirst }}.{{ (parameter.enumKeys[0] ?? parameter.enumValues[0]) | caseEnumKey }}{% else %}{{ parameter | paramExample }}{% endif %}, // {{ parameter.name }}{% if not parameter.required %} (optional){% endif %} + {% if parameter.enumValues | length > 0%}{{ parameter.enumName | caseUcfirst }}.{{ (parameter.enumKeys[0] ?? parameter.enumValues[0]) | caseEnumKey }}{% else %}{{ parameter | javaParamExample }}{% endif %}, // {{ parameter.name }}{% if not parameter.required %} (optional){% endif %} {%~ if loop.last %} new CoroutineCallback<>((result, error) -> { From 9902d299e6a945d4d9b2ee3e9571af7c0594124c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 24 Nov 2025 19:02:00 +0530 Subject: [PATCH 2/2] review comments --- src/SDK/Language.php | 3 ++- src/SDK/Language/CLI.php | 3 ++- src/SDK/Language/Dart.php | 3 ++- src/SDK/Language/Deno.php | 3 ++- src/SDK/Language/DotNet.php | 3 ++- src/SDK/Language/Go.php | 3 ++- src/SDK/Language/GraphQL.php | 3 ++- src/SDK/Language/Kotlin.php | 36 +++++++------------------------- src/SDK/Language/Node.php | 3 ++- src/SDK/Language/PHP.php | 3 ++- src/SDK/Language/Python.php | 3 ++- src/SDK/Language/REST.php | 3 ++- src/SDK/Language/ReactNative.php | 3 ++- src/SDK/Language/Ruby.php | 3 ++- src/SDK/Language/Swift.php | 3 ++- src/SDK/Language/Web.php | 3 ++- 16 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/SDK/Language.php b/src/SDK/Language.php index 083b12be4..099a53066 100644 --- a/src/SDK/Language.php +++ b/src/SDK/Language.php @@ -70,9 +70,10 @@ abstract public function getParamDefault(array $param): string; /** * @param array $param + * @param string $lang Optional language variant (for multi-language SDKs) * @return string */ - abstract public function getParamExample(array $param): string; + abstract public function getParamExample(array $param, string $lang = ''): string; /** * @param string $key diff --git a/src/SDK/Language/CLI.php b/src/SDK/Language/CLI.php index 5cd135dad..db72e8778 100644 --- a/src/SDK/Language/CLI.php +++ b/src/SDK/Language/CLI.php @@ -377,9 +377,10 @@ public function getTypeName(array $parameter, array $spec = []): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/Dart.php b/src/SDK/Language/Dart.php index aa402024e..6ad8efbaa 100644 --- a/src/SDK/Language/Dart.php +++ b/src/SDK/Language/Dart.php @@ -234,9 +234,10 @@ public function getParamDefault(array $param): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/Deno.php b/src/SDK/Language/Deno.php index 842298d03..c3145c60d 100644 --- a/src/SDK/Language/Deno.php +++ b/src/SDK/Language/Deno.php @@ -181,9 +181,10 @@ public function getTypeName(array $parameter, array $spec = []): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/DotNet.php b/src/SDK/Language/DotNet.php index 49e36b3ab..d2d89ce5d 100644 --- a/src/SDK/Language/DotNet.php +++ b/src/SDK/Language/DotNet.php @@ -261,9 +261,10 @@ public function getParamDefault(array $param): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/Go.php b/src/SDK/Language/Go.php index 83bf3a00b..f32a118f8 100644 --- a/src/SDK/Language/Go.php +++ b/src/SDK/Language/Go.php @@ -238,9 +238,10 @@ public function getParamDefault(array $param): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/GraphQL.php b/src/SDK/Language/GraphQL.php index f6e3a9311..19b581e9b 100644 --- a/src/SDK/Language/GraphQL.php +++ b/src/SDK/Language/GraphQL.php @@ -128,9 +128,10 @@ public function getParamDefault(array $param): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/Kotlin.php b/src/SDK/Language/Kotlin.php index 5d18e06bc..0f0abe609 100644 --- a/src/SDK/Language/Kotlin.php +++ b/src/SDK/Language/Kotlin.php @@ -283,21 +283,10 @@ public function getParamExample(array $param, string $lang = 'kotlin'): string * Generate Kotlin-style map initialization * * @param array $data - * @return string - */ - protected function getKotlinMapExample(array $data): string - { - return $this->getKotlinMapExampleRecursive($data, 0); - } - - /** - * Recursive helper for generating Kotlin mapOf() with proper indentation - * - * @param array $data * @param int $indentLevel Indentation level for nested maps * @return string */ - private function getKotlinMapExampleRecursive(array $data, int $indentLevel): string + protected function getKotlinMapExample(array $data, int $indentLevel = 0): string { $mapEntries = []; $baseIndent = str_repeat(' ', $indentLevel + 2); @@ -312,9 +301,9 @@ private function getKotlinMapExampleRecursive(array $data, int $indentLevel): st $formattedValue = 'null'; } elseif (is_array($value)) { // Check if it's an associative array (object) or indexed array - $isObject = array_keys($value) !== range(0, count($value) - 1); + $isObject = !array_is_list($value); if ($isObject) { - $formattedValue = $this->getKotlinMapExampleRecursive($value, $indentLevel + 1); + $formattedValue = $this->getKotlinMapExample($value, $indentLevel + 1); } else { $formattedValue = $this->getArrayExample(json_encode($value), 'kotlin'); } @@ -336,21 +325,10 @@ private function getKotlinMapExampleRecursive(array $data, int $indentLevel): st * Generate Java-style map initialization using Map.of() * * @param array $data - * @return string - */ - protected function getJavaMapExample(array $data): string - { - return $this->getJavaMapExampleRecursive($data, 0); - } - - /** - * Recursive helper for generating Java Map.of() with proper indentation - * - * @param array $data * @param int $indentLevel Indentation level for nested maps * @return string */ - private function getJavaMapExampleRecursive(array $data, int $indentLevel): string + protected function getJavaMapExample(array $data, int $indentLevel = 0): string { $mapEntries = []; $baseIndent = str_repeat(' ', $indentLevel + 2); @@ -365,9 +343,9 @@ private function getJavaMapExampleRecursive(array $data, int $indentLevel): stri $formattedValue = 'null'; } elseif (is_array($value)) { // Check if it's an associative array (object) or indexed array - $isObject = array_keys($value) !== range(0, count($value) - 1); + $isObject = !array_is_list($value); if ($isObject) { - $formattedValue = $this->getJavaMapExampleRecursive($value, $indentLevel + 1); + $formattedValue = $this->getJavaMapExample($value, $indentLevel + 1); } else { $formattedValue = $this->getArrayExample(json_encode($value), 'java'); } @@ -401,7 +379,7 @@ protected function getArrayExample(string $example, string $lang = 'kotlin'): st foreach ($decoded as $item) { if (is_array($item)) { // Check if it's an associative array (object) or indexed array (nested array) - $isObject = array_keys($item) !== range(0, count($item) - 1); + $isObject = !array_is_list($item); if ($isObject) { // It's an object/map, convert it diff --git a/src/SDK/Language/Node.php b/src/SDK/Language/Node.php index 304b76380..31d86b8e0 100644 --- a/src/SDK/Language/Node.php +++ b/src/SDK/Language/Node.php @@ -140,9 +140,10 @@ public function getReturn(array $method, array $spec): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/PHP.php b/src/SDK/Language/PHP.php index c9e628467..47e28932c 100644 --- a/src/SDK/Language/PHP.php +++ b/src/SDK/Language/PHP.php @@ -355,9 +355,10 @@ public function getParamDefault(array $param): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/Python.php b/src/SDK/Language/Python.php index c664d570b..dd2107c07 100644 --- a/src/SDK/Language/Python.php +++ b/src/SDK/Language/Python.php @@ -358,9 +358,10 @@ public function getParamDefault(array $param): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/REST.php b/src/SDK/Language/REST.php index f950f8118..c83e1ec73 100644 --- a/src/SDK/Language/REST.php +++ b/src/SDK/Language/REST.php @@ -84,9 +84,10 @@ public function getParamDefault(array $param): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/ReactNative.php b/src/SDK/Language/ReactNative.php index 8018ac2a6..81748709c 100644 --- a/src/SDK/Language/ReactNative.php +++ b/src/SDK/Language/ReactNative.php @@ -188,9 +188,10 @@ public function getTypeName(array $parameter, array $method = []): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/Ruby.php b/src/SDK/Language/Ruby.php index 565b8373a..011d831b0 100644 --- a/src/SDK/Language/Ruby.php +++ b/src/SDK/Language/Ruby.php @@ -289,9 +289,10 @@ public function getParamDefault(array $param): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/Swift.php b/src/SDK/Language/Swift.php index 5603e3f11..6e36e4251 100644 --- a/src/SDK/Language/Swift.php +++ b/src/SDK/Language/Swift.php @@ -409,9 +409,10 @@ public function getParamDefault(array $param): string /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? ''; diff --git a/src/SDK/Language/Web.php b/src/SDK/Language/Web.php index 509203d12..2490f833f 100644 --- a/src/SDK/Language/Web.php +++ b/src/SDK/Language/Web.php @@ -150,9 +150,10 @@ public function getFiles(): array /** * @param array $param + * @param string $lang * @return string */ - public function getParamExample(array $param): string + public function getParamExample(array $param, string $lang = ''): string { $type = $param['type'] ?? ''; $example = $param['example'] ?? '';