From a815bbb79d2a914fe85eae456ac8c46f83860e4b Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 22 Jan 2024 20:25:35 +0800 Subject: [PATCH] :necktie: up: update the java class parser and some commands --- app/Console/SubCmd/JavaCmd/ClassToJsonCmd.php | 54 +++++++- app/Console/SubCmd/JavaCmd/MetadataCmd.php | 17 ++- app/Lib/Defines/ClassMeta.php | 87 ++++++++++++ app/Lib/Defines/FieldMeta.php | 58 +++----- app/Lib/Generate/Java/JavaType.php | 14 -- app/Lib/Generate/LangName.php | 15 -- app/Lib/Parser/AbstractDTOParser.php | 27 +++- app/Lib/Parser/DTOParser.php | 91 ++++++++++++ app/Lib/Parser/Java/JavaDTOMeta.php | 80 +---------- app/Lib/Parser/Java/JavaDTOParser.php | 129 +++++++----------- app/Lib/Parser/Java/JavaField.php | 24 +++- app/Lib/Parser/MySQL/TableField.php | 21 +-- test/unittest/Lib/Parser/DBTableTest.php | 6 +- 13 files changed, 364 insertions(+), 259 deletions(-) create mode 100644 app/Lib/Defines/ClassMeta.php delete mode 100644 app/Lib/Generate/Java/JavaType.php delete mode 100644 app/Lib/Generate/LangName.php create mode 100644 app/Lib/Parser/DTOParser.php diff --git a/app/Console/SubCmd/JavaCmd/ClassToJsonCmd.php b/app/Console/SubCmd/JavaCmd/ClassToJsonCmd.php index cf776f6..5dc1546 100644 --- a/app/Console/SubCmd/JavaCmd/ClassToJsonCmd.php +++ b/app/Console/SubCmd/JavaCmd/ClassToJsonCmd.php @@ -5,6 +5,11 @@ use Inhere\Console\Command; use Inhere\Console\IO\Input; use Inhere\Console\IO\Output; +use Inhere\Kite\Console\Component\ContentsAutoReader; +use Inhere\Kite\Console\Component\ContentsAutoWriter; +use Inhere\Kite\Lib\Parser\Java\JavaDTOParser; +use Toolkit\Stdlib\Json; +use Toolkit\Stdlib\Str\StrBuilder; /** * Class ClassToJsonCmd @@ -12,20 +17,24 @@ class ClassToJsonCmd extends Command { protected static string $name = 'to-json'; - protected static string $desc = 'parse java DTO class, convert fields to json'; + protected static string $desc = 'parse java DTO class, convert fields to json or json5'; + + public static function aliases(): array + { + return ['json']; + } protected function configure(): void { - // $this->flags->addOptByRule($name, $rule); $this->flags->addOptsByRules([ - 'class' => 'string;The class code or path to parse;required', - 'json5,5' => 'bool;set output json5 format data', + 'source,s' => 'string;The class code or filepath to parse. allow use @c for clipboard;required', + 'output,o' => 'string;The output target for result;;stdout', + 'json5,5' => 'bool;set output json5 format data, will with comment', + 'start,pos' => 'int;The start position for parse. 0=header, 1=class, 2=body;;0', ]); } /** - * Do execute command - * * @param Input $input * @param Output $output * @@ -33,7 +42,38 @@ protected function configure(): void */ protected function execute(Input $input, Output $output): int { - $output->println(__METHOD__); + $fs = $this->flags; + + $source = $fs->getOpt('source'); + $source = ContentsAutoReader::readFrom($source); + + $m = JavaDTOParser::parse($source, [ + 'startPos' => $fs->getOpt('start'), + ]); + + // output json + if (!$fs->getOpt('json5')) { + $map = []; + foreach ($m->fields as $field) { + $map[$field->name] = $field->exampleValue(); + } + + $str = Json::pretty($map); + } else { + // output json5 + $sb = StrBuilder::new("{\n"); + $sb->append("\n"); + foreach ($m->fields as $field) { + $sb->writef(" // %s\n", $field->comment); + $sb->writef(" \"%s\": %s,\n", $field->name, $field->exampleValue(true)); + } + + $sb->append('}'); + + $str = $sb->getAndClear(); + } + + ContentsAutoWriter::writeTo($fs->getOpt('output'), $str); return 0; } } diff --git a/app/Console/SubCmd/JavaCmd/MetadataCmd.php b/app/Console/SubCmd/JavaCmd/MetadataCmd.php index 63c3460..ebb136c 100644 --- a/app/Console/SubCmd/JavaCmd/MetadataCmd.php +++ b/app/Console/SubCmd/JavaCmd/MetadataCmd.php @@ -6,7 +6,9 @@ use Inhere\Console\IO\Input; use Inhere\Console\IO\Output; use Inhere\Kite\Console\Component\ContentsAutoReader; -use Inhere\Kite\Lib\Parser\Java\JavaDtoParser; +use Inhere\Kite\Console\Component\ContentsAutoWriter; +use Inhere\Kite\Lib\Parser\Java\JavaDTOParser; +use Toolkit\Stdlib\Json; /** * Class MetadataCmd @@ -14,7 +16,7 @@ class MetadataCmd extends Command { protected static string $name = 'metadata'; - protected static string $desc = 'parse java DTO class, collect field metadata information'; + protected static string $desc = 'parse java DTO class, collect metadata information'; public static function aliases(): array { @@ -45,9 +47,16 @@ protected function execute(Input $input, Output $output): int $source = $fs->getOpt('source'); $source = ContentsAutoReader::readFrom($source); - $p = JavaDtoParser::parse($source); + $m = JavaDTOParser::parse($source); + + $distOut = $fs->getOpt('output'); + if ($distOut === 'stdout') { + $output->prettyJSON($m->toArray()); + } else { + $str = Json::pretty($m->toArray()); + ContentsAutoWriter::writeTo($distOut, $str); + } - $output->prettyJSON($p->toArray()); return 0; } } diff --git a/app/Lib/Defines/ClassMeta.php b/app/Lib/Defines/ClassMeta.php new file mode 100644 index 0000000..9f4624d --- /dev/null +++ b/app/Lib/Defines/ClassMeta.php @@ -0,0 +1,87 @@ +description && $s[0] !== '@') { + $this->description = $s; + } + + $this->comments[] = $comment; + } + +} \ No newline at end of file diff --git a/app/Lib/Defines/FieldMeta.php b/app/Lib/Defines/FieldMeta.php index f412cb8..959d4a1 100644 --- a/app/Lib/Defines/FieldMeta.php +++ b/app/Lib/Defines/FieldMeta.php @@ -31,7 +31,7 @@ class FieldMeta extends BaseObject public string $type = UniformType::STRING; /** - * sub-elem type on type is array + * sub-elem type on type is array, map, object. * * @var string */ @@ -57,6 +57,7 @@ class FieldMeta extends BaseObject */ public ?FieldMeta $elem = null; + /** @var string uniform type */ private string $_uniformType = ''; /** @@ -90,22 +91,15 @@ protected function toUniformType(): string */ public function langType(string $lang, string $name = ''): string { - $name = $name ?: $this->name; $type = $this->type; + $name = $name ?: $this->name; - if ($lang === ProgramLang::PHP) { - return $this->toPhpType($type, $name); - } - - if ($lang === ProgramLang::JAVA) { - return $this->toJavaType($type, $name); - } - - if ($lang === ProgramLang::GO) { - return $this->toGoType($type, $name); - } - - throw new InvalidArgumentException('unknown language: ' . $lang); + return match ($lang) { + ProgramLang::PHP => $this->toPhpType($type, $name), + ProgramLang::JAVA => $this->toJavaType($type, $name), + ProgramLang::GO => $this->toGoType($type, $name), + default => throw new InvalidArgumentException('unknown language: ' . $lang), + }; } private function toGoType(string $type, string $name = ''): string @@ -131,21 +125,10 @@ public function toJavaType(string $type, string $name = ''): string } if ($type === UniformType::ARRAY) { - $elemType = $this->subType ?: $name; - if ($elemType === 'List') { - $elemType .= '_KW'; - } - + $elemType = $this->subType ?: $name . '_KW'; return sprintf('%s<%s>', JavaType::LIST, Str::upFirst($elemType)); } - if (Str::hasSuffixIC($name, 'ids')) { - return sprintf('%s<%s>', JavaType::LIST, JavaType::LONG); - } - - if ($type === UniformType::OBJECT) { - return Str::upFirst($name); - } return Str::upFirst($type); } @@ -181,7 +164,7 @@ public function exampleValue(bool $quote = false): string|int|float { if ($this->example !== '') { $value = $this->example; - } elseif ($this->isString()) { + } elseif ($this->isUniType(UniformType::STRING)) { $value = $this->type; } else { $value = $this->zeroValue(); @@ -228,7 +211,6 @@ public function isMultiWords(): bool if (preg_match('/[A-Z]/', $this->name)) { return true; } - return false; } @@ -270,14 +252,6 @@ public function isInt64X(): bool return in_array($this->uniformType(), [UniformType::INT64, UniformType::UINT64], true); } - /** - * @return bool - */ - public function isString(): bool - { - return $this->isUniType(UniformType::STRING); - } - /** * @param string $type * @@ -314,6 +288,16 @@ public function isNeedQuote(): bool return false; } + /** + * @param string $type + * + * @return void + */ + public function setType(string $type): void + { + $this->type = $type; + } + /** * @param string $comment * diff --git a/app/Lib/Generate/Java/JavaType.php b/app/Lib/Generate/Java/JavaType.php deleted file mode 100644 index b146635..0000000 --- a/app/Lib/Generate/Java/JavaType.php +++ /dev/null @@ -1,14 +0,0 @@ -content = $content; + } +} diff --git a/app/Lib/Parser/DTOParser.php b/app/Lib/Parser/DTOParser.php new file mode 100644 index 0000000..d41c1e2 --- /dev/null +++ b/app/Lib/Parser/DTOParser.php @@ -0,0 +1,91 @@ + + */ + private static array $parsers = [ + ProgramLang::JAVA => JavaDTOParser::class, + ]; + + /** + * @param string $lang + * + * @return AbstractDTOParser + */ + public static function create(string $lang): AbstractDTOParser + { + $class = self::$parsers[$lang] ?? null; + if ($class) { + return new $class(); + } + + throw new InvalidArgumentException("unsupported parser for language: $lang"); + } + + /** + * @param string $lang + * @param string $content + * + * @return ClassMeta or subclass + */ + public static function parse(string $lang, string $content): ClassMeta + { + $p = self::create($lang); + $p->setContent($content); + return $p->doParse(); + } + + /** + * @param string $lang + * @param string $filePath + * + * @return ClassMeta or subclass + */ + public static function parseFile(string $lang, string $filePath): ClassMeta + { + $content = file_get_contents($filePath); + return self::parse($lang, $content); + } + + /** + * @param string $s + * + * @return bool + */ + public static function isClassType(string $s): bool + { + return preg_match('#^(class|interface|enum)$#', $s) === 1; + } + + /** + * @param string $s + * + * @return bool + */ + public static function isAccessModifier(string $s): bool + { + return preg_match('#^(public|private|protected)$#i', $s) === 1; + } + + public static function isOtherModifier(string $s): bool + { + return preg_match('#^(final|static|abstract)$#i', $s) === 1; + } + +} \ No newline at end of file diff --git a/app/Lib/Parser/Java/JavaDTOMeta.php b/app/Lib/Parser/Java/JavaDTOMeta.php index 78420e2..7695e7a 100644 --- a/app/Lib/Parser/Java/JavaDTOMeta.php +++ b/app/Lib/Parser/Java/JavaDTOMeta.php @@ -2,88 +2,12 @@ namespace Inhere\Kite\Lib\Parser\Java; -use Inhere\Kite\Lib\Defines\AccessModifier; -use Inhere\Kite\Lib\Defines\ElementType; -use Toolkit\Stdlib\Obj\BaseObject; +use Inhere\Kite\Lib\Defines\ClassMeta; /** * @author inhere */ -class JavaDTOMeta extends BaseObject +class JavaDTOMeta extends ClassMeta { - /** - * @var string package path - */ - public string $package = ''; - - /** - * @var array imports classes - */ - public array $imports = []; - - /** - * @var string access modifier - */ - public string $accessModifier = AccessModifier::DEFAULT; - - /** - * @var string[] other modifiers: final|static|abstract - */ - public array $otherModifiers = []; - - /** - * @var string class type - */ - public string $type = ElementType::TYPE_CLASS; - - /** - * @var string class name - */ - public string $name = ''; - - /** - * @var string class description - */ - public string $description = ''; - - /** - * @var string[] class comment - */ - public array $comments = []; - - /** - * @var array annotations on class - */ - public array $annotations = []; - - /** - * @var string extends class - */ - public string $extends = ''; - - /** - * @var string[] interface classes - */ - public array $interfaces = []; - - /** - * @var JavaField[] fields in class - */ - public array $fields = []; - - /** - * @param string $comment - * - * @return void - */ - public function addComment(string $comment): void - { - $s = trim($comment, "/* \t"); - if ($s && !$this->description && $s[0] !== '@') { - $this->description = $s; - } - - $this->comments[] = $comment; - } } \ No newline at end of file diff --git a/app/Lib/Parser/Java/JavaDTOParser.php b/app/Lib/Parser/Java/JavaDTOParser.php index 0537436..b1e87de 100644 --- a/app/Lib/Parser/Java/JavaDTOParser.php +++ b/app/Lib/Parser/Java/JavaDTOParser.php @@ -2,67 +2,60 @@ namespace Inhere\Kite\Lib\Parser\Java; +use Inhere\Kite\Lib\Defines\ClassMeta; +use Inhere\Kite\Lib\Defines\FieldMeta; +use Inhere\Kite\Lib\Parser\AbstractDTOParser; +use Inhere\Kite\Lib\Parser\DTOParser; use Toolkit\Stdlib\Helper\Assert; +use Toolkit\Stdlib\Obj; use Toolkit\Stdlib\Str; -use function file_get_contents; -use function preg_match; use function rtrim; /** * @author inhere */ -class JavaDTOParser extends JavaDTOMeta +class JavaDTOParser extends AbstractDTOParser { - /** - * @var string source code - */ - private string $content = ''; - - private array $tokens = []; - /** * Quick parse from source string * * @param string $content + * @param array $options for parser. * - * @return static + * @return JavaDTOMeta */ - public static function parse(string $content): static + public static function parse(string $content, array $options = []): JavaDTOMeta { $obj = new static(); $obj->setContent($content); - return $obj->do(); - } - - /** - * Quick parse from source file - * - * @param string $filePath - * - * @return static - */ - public static function parseFromFile(string $filePath): static - { - $content = file_get_contents($filePath); - return self::parse($content); + Obj::init($obj, $options); + return $obj->doParse(); } + /** @var int contain: package, imports statement */ public const POS_HEADER = 0; - public const POS_CLASS = 1; - public const POS_BODY = 2; + + /** @var int contain: class comment and class define */ + public const POS_CLASS = 1; + + /** @var int contain: fields and methods */ + public const POS_BODY = 2; + + private int $startPos = self::POS_HEADER; /** * Do parse the source code * - * @return $this + * @return JavaDTOMeta */ - public function do(): static + public function doParse(): JavaDTOMeta { + $pos = $this->startPos; $src = trim($this->content); Assert::notEmpty($src, 'The content can not be empty'); $field = null; - $pos = self::POS_HEADER; + $meta = new JavaDTOMeta(); // split to array by \n $inComment = false; @@ -73,7 +66,7 @@ public function do(): static if ($inComment) { if ($pos < self::POS_BODY) { - $this->addComment($line); + $meta->addComment($line); } else { $field->addComment($line); } @@ -85,14 +78,14 @@ public function do(): static } if (Str::startWith($line, 'package ')) { - $this->package = substr($line, 8, -1); + $meta->package = substr($line, 8, -1); continue; } if (Str::startWith($line, 'import ')) { $pos = self::POS_CLASS; - $this->imports[] = substr($line, 7, -1); + $meta->imports[] = substr($line, 7, -1); continue; } @@ -101,10 +94,10 @@ public function do(): static $inComment = true; if ($pos < self::POS_BODY) { $pos = self::POS_CLASS; - $this->addComment($line); + $meta->addComment($line); } else { // in body if ($field) { - $this->fields[] = $field; + $meta->fields[] = $field; } // start new field @@ -118,7 +111,7 @@ public function do(): static // annotations if (Str::startWith($line, '@')) { if ($pos < self::POS_BODY) { - $this->annotations[] = substr($line, 1); + $meta->annotations[] = substr($line, 1); } else { if (!$field) { $field = new JavaField(); @@ -131,7 +124,7 @@ public function do(): static // class or interface or enum if ($pos === self::POS_CLASS && Str::contains($line, [' class ', ' interface ', ' enum '])) { $pos = self::POS_BODY; - $this->parseClassLine($line); + $this->parseClassLine($line, $meta); continue; } @@ -142,13 +135,13 @@ public function do(): static } if ($field) { - $this->fields[] = $field; + $meta->fields[] = $field; } - return $this; + return $meta; } - private function parseClassLine(string $line): void + protected function parseClassLine(string $line, ClassMeta $meta): void { $nodes = Str::splitTrimmed($line, ' '); @@ -163,29 +156,29 @@ private function parseClassLine(string $line): void } if ($isIFace) { - $this->interfaces[] = $node; + $meta->interfaces[] = $node; continue; } // is access modifier - if ($this->isAccessModifier($node)) { - $this->accessModifier = $node; - } elseif ($this->isClassType($node)) { - $this->type = $node; - $this->name = $nodes[$i + 1]; // class name + if (DTOParser::isAccessModifier($node)) { + $meta->accessModifier = $node; + } elseif (DTOParser::isClassType($node)) { + $meta->type = $node; + $meta->name = $nodes[$i + 1]; // class name $skipNext = true; } elseif ($node === 'extends') { - $this->extends = $nodes[$i + 1]; + $meta->extends = $nodes[$i + 1]; $skipNext = true; } elseif ($node === 'implements') { $isIFace = true; } else { // final|static - $this->otherModifiers[] = $node; + $meta->otherModifiers[] = $node; } } } - private function parseFieldLine(string $line, ?JavaField $field): JavaField + protected function parseFieldLine(string $line, ?FieldMeta $field): FieldMeta { if (!$field) { $field = new JavaField(); @@ -193,12 +186,12 @@ private function parseFieldLine(string $line, ?JavaField $field): JavaField $nodes = Str::splitTrimmed($line, ' '); foreach ($nodes as $i => $node) { - if ($this->isAccessModifier($node)) { + if (DTOParser::isAccessModifier($node)) { $field->accessModifier = $node; - } elseif ($this->isOtherModifier($node)) { + } elseif (DTOParser::isOtherModifier($node)) { $field->otherModifiers[] = $node; } else { - $field->type = $node; + $field->setType($node); $field->name = rtrim($nodes[$i + 1], ' ;'); break; } @@ -207,35 +200,9 @@ private function parseFieldLine(string $line, ?JavaField $field): JavaField return $field; } - /** - * @param string $s - * - * @return bool - */ - private function isClassType(string $s): bool - { - return preg_match('#^(class|interface|enum)$#', $s) === 1; - } - - /** - * @param string $s - * - * @return bool - */ - private function isAccessModifier(string $s): bool + public function setStartPos(int $startPos): void { - return preg_match('#^(public|private|protected)$#i', $s) === 1; + $this->startPos = $startPos; } - private function isOtherModifier(string $s): bool - { - return preg_match('#^(final|static)$#i', $s) === 1; - } - - public function setContent(string $content): void - { - $this->content = $content; - } - - } diff --git a/app/Lib/Parser/Java/JavaField.php b/app/Lib/Parser/Java/JavaField.php index 49fa816..1c658ae 100644 --- a/app/Lib/Parser/Java/JavaField.php +++ b/app/Lib/Parser/Java/JavaField.php @@ -4,6 +4,7 @@ use Inhere\Kite\Lib\Defines\DataType\JavaType; use Inhere\Kite\Lib\Defines\FieldMeta; +use function substr; /** * @author inhere @@ -16,7 +17,7 @@ class JavaField extends FieldMeta public array $annotations = []; /** - * @var string + * @var string access modifier. eg: public */ public string $accessModifier = ''; @@ -25,6 +26,27 @@ class JavaField extends FieldMeta */ public array $otherModifiers = []; + /** + * @param string $type + * + * @return void + */ + public function setType(string $type): void + { + if (str_contains($type, '<')) { + $str = $type; + $pos1 = strpos($str, '<'); + $type = substr($type, 0, $pos1); + + // sub type + if ($type === JavaType::LIST) { + $this->subType = substr($str, $pos1+1, -1); + } + } + + $this->type = $type; + } + /** * get uniform type * diff --git a/app/Lib/Parser/MySQL/TableField.php b/app/Lib/Parser/MySQL/TableField.php index 52ee3dc..fce2295 100644 --- a/app/Lib/Parser/MySQL/TableField.php +++ b/app/Lib/Parser/MySQL/TableField.php @@ -4,7 +4,6 @@ use Inhere\Kite\Lib\Defines\DataType\DBType; use Inhere\Kite\Lib\Defines\FieldMeta; -use Toolkit\Stdlib\Str; /** * class TableField @@ -40,27 +39,13 @@ class TableField extends FieldMeta public string $default = ''; /** - * @return string - */ - public function phpType(): string - { - return DBType::toPhpType($this->type); - } - - /** - * @param string $type DB type - * @param string $name + * get uniform type * * @return string */ - public function toJavaType(string $type, string $name): string + protected function toUniformType(): string { - if ($type === DBType::JSON) { - return Str::upFirst($name); - } - - $phpType = DBType::toPhpType($type); - return parent::toJavaType($phpType, $name); + return DBType::toUniformType($this->type); } /** diff --git a/test/unittest/Lib/Parser/DBTableTest.php b/test/unittest/Lib/Parser/DBTableTest.php index 245441e..b0713b6 100644 --- a/test/unittest/Lib/Parser/DBTableTest.php +++ b/test/unittest/Lib/Parser/DBTableTest.php @@ -2,9 +2,9 @@ namespace Inhere\KiteTest\Lib\Parser; +use Inhere\Kite\Lib\Defines\DataType\DBType; use Inhere\Kite\Lib\Parser\DBMdTable; use Inhere\Kite\Lib\Parser\DBTable; -use Inhere\Kite\Lib\Parser\MySQL\DBType; use Inhere\KiteTest\BaseKiteTestCase; /** @@ -12,7 +12,7 @@ */ class DBTableTest extends BaseKiteTestCase { - private $mdTable1 = << INDEXES: PRIMARY KEY (`id`), UNIQUE KEY `uni_uid_orderno` (`uid`, `orderno`) MD; - private $createSql1 = <<