From 01904adf49dc95f89ccdb583f007405f5aef910d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 13 Mar 2019 15:10:31 +0000 Subject: [PATCH] Improve enum implementations, move some components to traits The reason this uses a trait is because `self` refers to the including class in a trait, which offers a small amount of automatic type safety. If we had templates or generics, this would be a generic class instead. --- src/pocketmine/block/utils/DyeColor.php | 183 +++++++---------------- src/pocketmine/block/utils/SkullType.php | 88 ++++------- src/pocketmine/block/utils/SlabType.php | 47 +++--- src/pocketmine/block/utils/TreeType.php | 93 ++++-------- src/pocketmine/item/ItemUseResult.php | 54 +++---- src/pocketmine/utils/EnumTrait.php | 172 +++++++++++++++++++++ 6 files changed, 320 insertions(+), 317 deletions(-) create mode 100644 src/pocketmine/utils/EnumTrait.php diff --git a/src/pocketmine/block/utils/DyeColor.php b/src/pocketmine/block/utils/DyeColor.php index 35a5bb9d0..122403e95 100644 --- a/src/pocketmine/block/utils/DyeColor.php +++ b/src/pocketmine/block/utils/DyeColor.php @@ -24,147 +24,63 @@ declare(strict_types=1); namespace pocketmine\block\utils; use pocketmine\utils\Color; +use pocketmine\utils\EnumTrait; +/** + * This doc-block is generated automatically, do not modify it manually. + * This must be regenerated whenever enum members are added, removed or changed. + * @see EnumTrait::_generateMethodAnnotations() + * + * @method static self WHITE() + * @method static self ORANGE() + * @method static self MAGENTA() + * @method static self LIGHT_BLUE() + * @method static self YELLOW() + * @method static self LIME() + * @method static self PINK() + * @method static self GRAY() + * @method static self LIGHT_GRAY() + * @method static self CYAN() + * @method static self PURPLE() + * @method static self BLUE() + * @method static self BROWN() + * @method static self GREEN() + * @method static self RED() + * @method static self BLACK() + */ final class DyeColor{ - - /** @var DyeColor */ - private static $WHITE; - /** @var DyeColor */ - private static $ORANGE; - /** @var DyeColor */ - private static $MAGENTA; - /** @var DyeColor */ - private static $LIGHT_BLUE; - /** @var DyeColor */ - private static $YELLOW; - /** @var DyeColor */ - private static $LIME; - /** @var DyeColor */ - private static $PINK; - /** @var DyeColor */ - private static $GRAY; - /** @var DyeColor */ - private static $LIGHT_GRAY; - /** @var DyeColor */ - private static $CYAN; - /** @var DyeColor */ - private static $PURPLE; - /** @var DyeColor */ - private static $BLUE; - /** @var DyeColor */ - private static $BROWN; - /** @var DyeColor */ - private static $GREEN; - /** @var DyeColor */ - private static $RED; - /** @var DyeColor */ - private static $BLACK; - - /* auto-generated code */ - - public static function WHITE() : DyeColor{ - return self::$WHITE; - } - - public static function ORANGE() : DyeColor{ - return self::$ORANGE; - } - - public static function MAGENTA() : DyeColor{ - return self::$MAGENTA; - } - - public static function LIGHT_BLUE() : DyeColor{ - return self::$LIGHT_BLUE; - } - - public static function YELLOW() : DyeColor{ - return self::$YELLOW; - } - - public static function LIME() : DyeColor{ - return self::$LIME; - } - - public static function PINK() : DyeColor{ - return self::$PINK; - } - - public static function GRAY() : DyeColor{ - return self::$GRAY; - } - - public static function LIGHT_GRAY() : DyeColor{ - return self::$LIGHT_GRAY; - } - - public static function CYAN() : DyeColor{ - return self::$CYAN; - } - - public static function PURPLE() : DyeColor{ - return self::$PURPLE; - } - - public static function BLUE() : DyeColor{ - return self::$BLUE; - } - - public static function BROWN() : DyeColor{ - return self::$BROWN; - } - - public static function GREEN() : DyeColor{ - return self::$GREEN; - } - - public static function RED() : DyeColor{ - return self::$RED; - } - - public static function BLACK() : DyeColor{ - return self::$BLACK; + use EnumTrait { + register as Enum_register; + __construct as Enum___construct; } /** @var DyeColor[] */ private static $numericIdMap = []; - /** @var DyeColor[] separate mapping that doesn't depend on magic numbers */ - private static $all = []; - /** - * @internal - */ - public static function _init() : void{ - self::register(self::$WHITE = new DyeColor("White", 0, new Color(0xf0, 0xf0, 0xf0))); - self::register(self::$ORANGE = new DyeColor("Orange", 1, new Color(0xf9, 0x80, 0x1d))); - self::register(self::$MAGENTA = new DyeColor("Magenta", 2, new Color(0xc7, 0x4e, 0xbd))); - self::register(self::$LIGHT_BLUE = new DyeColor("Light Blue", 3, new Color(0x3a, 0xb3, 0xda))); - self::register(self::$YELLOW = new DyeColor("Yellow", 4, new Color(0xfe, 0xd8, 0x3d))); - self::register(self::$LIME = new DyeColor("Lime", 5, new Color(0x80, 0xc7, 0x1f))); - self::register(self::$PINK = new DyeColor("Pink", 6, new Color(0xf3, 0x8b, 0xaa))); - self::register(self::$GRAY = new DyeColor("Gray", 7, new Color(0x47, 0x4f, 0x52))); - self::register(self::$LIGHT_GRAY = new DyeColor("Light Gray", 8, new Color(0x9d, 0x9d, 0x97))); - self::register(self::$CYAN = new DyeColor("Cyan", 9, new Color(0x16, 0x9c, 0x9c))); - self::register(self::$PURPLE = new DyeColor("Purple", 10, new Color(0x89, 0x32, 0xb8))); - self::register(self::$BLUE = new DyeColor("Blue", 11, new Color(0x3c, 0x44, 0xaa))); - self::register(self::$BROWN = new DyeColor("Brown", 12, new Color(0x83, 0x54, 0x32))); - self::register(self::$GREEN = new DyeColor("Green", 13, new Color(0x5e, 0x7c, 0x16))); - self::register(self::$RED = new DyeColor("Red", 14, new Color(0xb0, 0x2e, 0x26))); - self::register(self::$BLACK = new DyeColor("Black", 15, new Color(0x1d, 0x1d, 0x21))); + protected static function setup() : array{ + return [ + new DyeColor("white", "White", 0, new Color(0xf0, 0xf0, 0xf0)), + new DyeColor("orange", "Orange", 1, new Color(0xf9, 0x80, 0x1d)), + new DyeColor("magenta", "Magenta", 2, new Color(0xc7, 0x4e, 0xbd)), + new DyeColor("light_blue", "Light Blue", 3, new Color(0x3a, 0xb3, 0xda)), + new DyeColor("yellow", "Yellow", 4, new Color(0xfe, 0xd8, 0x3d)), + new DyeColor("lime", "Lime", 5, new Color(0x80, 0xc7, 0x1f)), + new DyeColor("pink", "Pink", 6, new Color(0xf3, 0x8b, 0xaa)), + new DyeColor("gray", "Gray", 7, new Color(0x47, 0x4f, 0x52)), + new DyeColor("light_gray", "Light Gray", 8, new Color(0x9d, 0x9d, 0x97)), + new DyeColor("cyan", "Cyan", 9, new Color(0x16, 0x9c, 0x9c)), + new DyeColor("purple", "Purple", 10, new Color(0x89, 0x32, 0xb8)), + new DyeColor("blue", "Blue", 11, new Color(0x3c, 0x44, 0xaa)), + new DyeColor("brown", "Brown", 12, new Color(0x83, 0x54, 0x32)), + new DyeColor("green", "Green", 13, new Color(0x5e, 0x7c, 0x16)), + new DyeColor("red", "Red", 14, new Color(0xb0, 0x2e, 0x26)), + new DyeColor("black", "Black", 15, new Color(0x1d, 0x1d, 0x21)), + ]; } - private static function register(DyeColor $color) : void{ + protected static function register(DyeColor $color) : void{ + self::Enum_register($color); self::$numericIdMap[$color->getMagicNumber()] = $color; - self::$all[] = $color; - } - - /** - * Returns a set of all known dye colours. - * - * @return DyeColor[] - */ - public static function getAll() : array{ - return self::$all; } /** @@ -178,6 +94,7 @@ final class DyeColor{ * @throws \InvalidArgumentException */ public static function fromMagicNumber(int $magicNumber, bool $inverted = false) : DyeColor{ + self::checkInit(); $real = $inverted ? ~$magicNumber & 0xf : $magicNumber; if(!isset(self::$numericIdMap[$real])){ throw new \InvalidArgumentException("Unknown dye colour magic number $magicNumber"); @@ -192,7 +109,8 @@ final class DyeColor{ /** @var Color */ private $rgbValue; - private function __construct(string $displayName, int $magicNumber, Color $rgbValue){ + private function __construct(string $enumName, string $displayName, int $magicNumber, Color $rgbValue){ + $this->Enum___construct($enumName); $this->displayName = $displayName; $this->magicNumber = $magicNumber; $this->rgbValue = $rgbValue; @@ -226,4 +144,3 @@ final class DyeColor{ return ~$this->magicNumber & 0xf; } } -DyeColor::_init(); diff --git a/src/pocketmine/block/utils/SkullType.php b/src/pocketmine/block/utils/SkullType.php index ecbdd9302..305573788 100644 --- a/src/pocketmine/block/utils/SkullType.php +++ b/src/pocketmine/block/utils/SkullType.php @@ -23,71 +23,43 @@ declare(strict_types=1); namespace pocketmine\block\utils; +use pocketmine\utils\EnumTrait; + +/** + * This doc-block is generated automatically, do not modify it manually. + * This must be regenerated whenever enum members are added, removed or changed. + * @see EnumTrait::_generateMethodAnnotations() + * + * @method static self SKELETON() + * @method static self WITHER_SKELETON() + * @method static self ZOMBIE() + * @method static self PLAYER() + * @method static self CREEPER() + * @method static self DRAGON() + */ final class SkullType{ - - /** @var SkullType */ - private static $SKELETON; - /** @var SkullType */ - private static $WITHER_SKELETON; - /** @var SkullType */ - private static $ZOMBIE; - /** @var SkullType */ - private static $HUMAN; - /** @var SkullType */ - private static $CREEPER; - /** @var SkullType */ - private static $DRAGON; - - /* auto-generated code */ - - public static function SKELETON() : SkullType{ - return self::$SKELETON; + use EnumTrait { + register as Enum_register; + __construct as Enum___construct; } - public static function WITHER_SKELETON() : SkullType{ - return self::$WITHER_SKELETON; - } - - public static function ZOMBIE() : SkullType{ - return self::$ZOMBIE; - } - - public static function HUMAN() : SkullType{ - return self::$HUMAN; - } - - public static function CREEPER() : SkullType{ - return self::$CREEPER; - } - - public static function DRAGON() : SkullType{ - return self::$DRAGON; - } - - /** @var SkullType[] */ - private static $all = []; /** @var SkullType[] */ private static $numericIdMap = []; - public static function _init() : void{ - self::register(self::$SKELETON = new SkullType("Skeleton Skull", 0)); - self::register(self::$WITHER_SKELETON = new SkullType("Wither Skeleton Skull", 1)); - self::register(self::$ZOMBIE = new SkullType("Zombie Head", 2)); - self::register(self::$HUMAN = new SkullType("Player Head", 3)); - self::register(self::$CREEPER = new SkullType("Creeper Head", 4)); - self::register(self::$DRAGON = new SkullType("Dragon Head", 5)); + protected static function setup() : array{ + return [ + new SkullType("skeleton", "Skeleton Skull", 0), + new SkullType("wither_skeleton", "Wither Skeleton Skull", 1), + new SkullType("zombie", "Zombie Head", 2), + new SkullType("player", "Player Head", 3), + new SkullType("creeper", "Creeper Head", 4), + new SkullType("dragon", "Dragon Head", 5) + ]; } - private static function register(SkullType $type) : void{ + protected static function register(SkullType $type) : void{ + self::Enum_register($type); self::$numericIdMap[$type->getMagicNumber()] = $type; - self::$all[] = $type; - } - - /** - * @return SkullType[] - */ - public static function getAll() : array{ - return self::$all; } /** @@ -109,7 +81,8 @@ final class SkullType{ /** @var int */ private $magicNumber; - public function __construct(string $displayName, int $magicNumber){ + public function __construct(string $enumName, string $displayName, int $magicNumber){ + $this->Enum___construct($enumName); $this->displayName = $displayName; $this->magicNumber = $magicNumber; } @@ -128,4 +101,3 @@ final class SkullType{ return $this->magicNumber; } } -SkullType::_init(); diff --git a/src/pocketmine/block/utils/SlabType.php b/src/pocketmine/block/utils/SlabType.php index 57fa535ca..53550520f 100644 --- a/src/pocketmine/block/utils/SlabType.php +++ b/src/pocketmine/block/utils/SlabType.php @@ -23,36 +23,25 @@ declare(strict_types=1); namespace pocketmine\block\utils; +use pocketmine\utils\EnumTrait; + +/** + * This doc-block is generated automatically, do not modify it manually. + * This must be regenerated whenever enum members are added, removed or changed. + * @see EnumTrait::_generateMethodAnnotations() + * + * @method static self BOTTOM() + * @method static self TOP() + * @method static self DOUBLE() + */ final class SlabType{ + use EnumTrait; - /** - * @var string - */ - private $name; - - public static function BOTTOM() : self{ - /** @var SlabType $ret */ - static $ret = null; - return $ret ?? ($ret = new self("bottom")); - } - - public static function TOP() : self{ - /** @var SlabType $ret */ - static $ret = null; - return $ret ?? ($ret = new self("top")); - } - - public static function DOUBLE() : self{ - /** @var SlabType $ret */ - static $ret = null; - return $ret ?? ($ret = new self("double")); - } - - private function __construct(string $name){ - $this->name = $name; - } - - public function getName() : string{ - return $this->name; + protected static function setup() : array{ + return [ + new self("bottom"), + new self("top"), + new self("double") + ]; } } diff --git a/src/pocketmine/block/utils/TreeType.php b/src/pocketmine/block/utils/TreeType.php index c6bce83f8..a2c894f5d 100644 --- a/src/pocketmine/block/utils/TreeType.php +++ b/src/pocketmine/block/utils/TreeType.php @@ -23,74 +23,43 @@ declare(strict_types=1); namespace pocketmine\block\utils; +use pocketmine\utils\EnumTrait; + +/** + * This doc-block is generated automatically, do not modify it manually. + * This must be regenerated whenever enum members are added, removed or changed. + * @see EnumTrait::_generateMethodAnnotations() + * + * @method static self OAK() + * @method static self SPRUCE() + * @method static self BIRCH() + * @method static self JUNGLE() + * @method static self ACACIA() + * @method static self DARK_OAK() + */ final class TreeType{ - - /** @var TreeType */ - private static $OAK; - /** @var TreeType */ - private static $SPRUCE; - /** @var TreeType */ - private static $BIRCH; - /** @var TreeType */ - private static $JUNGLE; - /** @var TreeType */ - private static $ACACIA; - /** @var TreeType */ - private static $DARK_OAK; - - /* auto-generated code */ - - public static function OAK() : TreeType{ - return self::$OAK; - } - - public static function SPRUCE() : TreeType{ - return self::$SPRUCE; - } - - public static function BIRCH() : TreeType{ - return self::$BIRCH; - } - - public static function JUNGLE() : TreeType{ - return self::$JUNGLE; - } - - public static function ACACIA() : TreeType{ - return self::$ACACIA; - } - - public static function DARK_OAK() : TreeType{ - return self::$DARK_OAK; + use EnumTrait { + register as Enum_register; + __construct as Enum___construct; } /** @var TreeType[] */ private static $numericIdMap = []; - /** @var TreeType[] */ - private static $all = []; - /** - * @internal - */ - public static function _init() : void{ - self::register(self::$OAK = new TreeType("Oak", 0)); - self::register(self::$SPRUCE = new TreeType("Spruce", 1)); - self::register(self::$BIRCH = new TreeType("Birch", 2)); - self::register(self::$JUNGLE = new TreeType("Jungle", 3)); - self::register(self::$ACACIA = new TreeType("Acacia", 4)); - self::register(self::$DARK_OAK = new TreeType("Dark Oak", 5)); + protected static function setup() : array{ + return [ + new TreeType("oak", "Oak", 0), + new TreeType("spruce", "Spruce", 1), + new TreeType("birch", "Birch", 2), + new TreeType("jungle", "Jungle", 3), + new TreeType("acacia", "Acacia", 4), + new TreeType("dark_oak","Dark Oak", 5) + ]; } - private static function register(TreeType $type) : void{ + protected static function register(TreeType $type) : void{ + self::Enum_register($type); self::$numericIdMap[$type->getMagicNumber()] = $type; - self::$all[] = $type; - } - - /** - * @return TreeType[] - */ - public static function getAll() : array{ - return self::$all; } /** @@ -101,6 +70,7 @@ final class TreeType{ * @throws \InvalidArgumentException */ public static function fromMagicNumber(int $magicNumber) : TreeType{ + self::checkInit(); if(!isset(self::$numericIdMap[$magicNumber])){ throw new \InvalidArgumentException("Unknown tree type magic number $magicNumber"); } @@ -113,10 +83,12 @@ final class TreeType{ private $magicNumber; /** + * @param string $enumName * @param string $displayName * @param int $magicNumber */ - private function __construct(string $displayName, int $magicNumber){ + private function __construct(string $enumName, string $displayName, int $magicNumber){ + $this->Enum___construct($enumName); $this->displayName = $displayName; $this->magicNumber = $magicNumber; } @@ -135,4 +107,3 @@ final class TreeType{ return $this->magicNumber; } } -TreeType::_init(); diff --git a/src/pocketmine/item/ItemUseResult.php b/src/pocketmine/item/ItemUseResult.php index 90e8b4724..3341d9161 100644 --- a/src/pocketmine/item/ItemUseResult.php +++ b/src/pocketmine/item/ItemUseResult.php @@ -23,43 +23,25 @@ declare(strict_types=1); namespace pocketmine\item; +use pocketmine\utils\EnumTrait; + +/** + * This doc-block is generated automatically, do not modify it manually. + * This must be regenerated whenever enum members are added, removed or changed. + * @see EnumTrait::_generateMethodAnnotations() + * + * @method static self NONE() + * @method static self FAIL() + * @method static self SUCCESS() + */ final class ItemUseResult{ - /** @var ItemUseResult */ - private static $NONE; - /** @var ItemUseResult */ - private static $FAILED; - /** @var ItemUseResult */ - private static $SUCCEEDED; + use EnumTrait; - /** - * No action attempted to take place. Does nothing. - * - * @return ItemUseResult - */ - public static function NONE() : ItemUseResult{ - return self::$NONE ?? (self::$NONE = new self()); - } - - /** - * An action attempted to take place, but failed due to cancellation. This triggers rollback behaviour for a remote - * player. - * - * @return ItemUseResult - */ - public static function FAIL() : ItemUseResult{ - return self::$FAILED ?? (self::$FAILED = new self()); - } - - /** - * An action took place successfully. Changes inventory contents if appropriate. - * - * @return ItemUseResult - */ - public static function SUCCESS() : ItemUseResult{ - return self::$SUCCEEDED ?? (self::$SUCCEEDED = new self()); - } - - private function __construct(){ - //NOOP + protected static function setup() : array{ + return [ + new self("none"), + new self("fail"), + new self("success") + ]; } } diff --git a/src/pocketmine/utils/EnumTrait.php b/src/pocketmine/utils/EnumTrait.php new file mode 100644 index 000000000..263996400 --- /dev/null +++ b/src/pocketmine/utils/EnumTrait.php @@ -0,0 +1,172 @@ +getEnumName()); + if(isset(self::$members[$name])){ + throw new \InvalidArgumentException("Enum member name \"$name\" is already reserved"); + } + self::$members[strtoupper($member->getEnumName())] = $member; + } + + /** + * Returns an array of enum members to be registered. + * + * (This ought to be private, but traits suck too much for that.) + * + * @return self[] + */ + abstract protected static function setup() : array; + + /** + * @internal Lazy-inits the enum if necessary. + * + * @throws \InvalidArgumentException + */ + private static function checkInit() : void{ + if(self::$members === null){ + self::$members = []; + foreach(self::setup() as $item){ + self::register($item); + } + } + } + + /** + * @param string $name + * + * @return self + */ + public static function fromString(string $name) : self{ + self::checkInit(); + $name = strtoupper($name); + if(!isset(self::$members[$name])){ + throw new \Error("Undefined enum member: " . self::class . "::" . $name); + } + return self::$members[$name]; + } + + /** + * @param string $name + * @param array $arguments + * + * @return self + */ + public static function __callStatic($name, $arguments){ + if(!empty($arguments)){ + throw new \ArgumentCountError("Expected exactly 0 arguments, " . count($arguments) . " passed"); + } + return self::fromString($name); + } + + /** + * @return self[] + */ + public static function getAll() : array{ + self::checkInit(); + return self::$members; + } + + /** + * Generates code for static methods for all known enum members. + * + * @return string + */ + public static function _generateGetters() : string{ + $lines = []; + + static $fnTmpl = ' +public static function %1$s() : self{ + return self::fromString("%1$s"); +}'; + + foreach(self::getAll() as $name => $member){ + $lines[] = sprintf($fnTmpl, $name); + } + return "/* --- auto-generated code start --- */\n" . implode("\n", $lines) . "\n\n/* --- auto-generated code end --- */\n"; + } + + /** + * Generates a block of @ method annotations for accessors for this enum's known members. + * + * @return string + */ + public static function _generateMethodAnnotations() : string{ + $traitName = (new \ReflectionClass(__TRAIT__))->getShortName(); + $fnName = (new \ReflectionMethod(__METHOD__))->getShortName(); + $lines = ["/**"]; + $lines[] = " * This doc-block is generated automatically, do not modify it manually."; + $lines[] = " * This must be regenerated whenever enum members are added, removed or changed."; + $lines[] = " * @see $traitName::$fnName()"; + $lines[] = " *"; + static $lineTmpl = " * @method static self %s()"; + + foreach(self::getAll() as $name => $member){ + $lines[] = sprintf($lineTmpl, $name); + } + $lines[] = " */\n"; + return implode("\n", $lines); + } + + /** @var string */ + private $enumName; + + /** + * @param string $enumName + * @throws \InvalidArgumentException + */ + private function __construct(string $enumName){ + static $pattern = '/^\D[A-Za-z\d_]+$/u'; + if(preg_match($pattern, $enumName, $matches) === 0){ + throw new \InvalidArgumentException("Invalid enum member name \"$enumName\", should only contain letters, numbers and underscores, and must not start with a number"); + } + $this->enumName = $enumName; + } + + /** + * @return string + */ + public function getEnumName() : string{ + return $this->enumName; + } +}