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.
This commit is contained in:
Dylan K. Taylor 2019-03-13 15:10:31 +00:00
parent 7f4b76aa86
commit 01904adf49
6 changed files with 320 additions and 317 deletions

View File

@ -24,147 +24,63 @@ declare(strict_types=1);
namespace pocketmine\block\utils; namespace pocketmine\block\utils;
use pocketmine\utils\Color; 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{ final class DyeColor{
use EnumTrait {
/** @var DyeColor */ register as Enum_register;
private static $WHITE; __construct as Enum___construct;
/** @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;
} }
/** @var DyeColor[] */ /** @var DyeColor[] */
private static $numericIdMap = []; private static $numericIdMap = [];
/** @var DyeColor[] separate mapping that doesn't depend on magic numbers */
private static $all = [];
/** protected static function setup() : array{
* @internal return [
*/ new DyeColor("white", "White", 0, new Color(0xf0, 0xf0, 0xf0)),
public static function _init() : void{ new DyeColor("orange", "Orange", 1, new Color(0xf9, 0x80, 0x1d)),
self::register(self::$WHITE = new DyeColor("White", 0, new Color(0xf0, 0xf0, 0xf0))); new DyeColor("magenta", "Magenta", 2, new Color(0xc7, 0x4e, 0xbd)),
self::register(self::$ORANGE = new DyeColor("Orange", 1, new Color(0xf9, 0x80, 0x1d))); new DyeColor("light_blue", "Light Blue", 3, new Color(0x3a, 0xb3, 0xda)),
self::register(self::$MAGENTA = new DyeColor("Magenta", 2, new Color(0xc7, 0x4e, 0xbd))); new DyeColor("yellow", "Yellow", 4, new Color(0xfe, 0xd8, 0x3d)),
self::register(self::$LIGHT_BLUE = new DyeColor("Light Blue", 3, new Color(0x3a, 0xb3, 0xda))); new DyeColor("lime", "Lime", 5, new Color(0x80, 0xc7, 0x1f)),
self::register(self::$YELLOW = new DyeColor("Yellow", 4, new Color(0xfe, 0xd8, 0x3d))); new DyeColor("pink", "Pink", 6, new Color(0xf3, 0x8b, 0xaa)),
self::register(self::$LIME = new DyeColor("Lime", 5, new Color(0x80, 0xc7, 0x1f))); new DyeColor("gray", "Gray", 7, new Color(0x47, 0x4f, 0x52)),
self::register(self::$PINK = new DyeColor("Pink", 6, new Color(0xf3, 0x8b, 0xaa))); new DyeColor("light_gray", "Light Gray", 8, new Color(0x9d, 0x9d, 0x97)),
self::register(self::$GRAY = new DyeColor("Gray", 7, new Color(0x47, 0x4f, 0x52))); new DyeColor("cyan", "Cyan", 9, new Color(0x16, 0x9c, 0x9c)),
self::register(self::$LIGHT_GRAY = new DyeColor("Light Gray", 8, new Color(0x9d, 0x9d, 0x97))); new DyeColor("purple", "Purple", 10, new Color(0x89, 0x32, 0xb8)),
self::register(self::$CYAN = new DyeColor("Cyan", 9, new Color(0x16, 0x9c, 0x9c))); new DyeColor("blue", "Blue", 11, new Color(0x3c, 0x44, 0xaa)),
self::register(self::$PURPLE = new DyeColor("Purple", 10, new Color(0x89, 0x32, 0xb8))); new DyeColor("brown", "Brown", 12, new Color(0x83, 0x54, 0x32)),
self::register(self::$BLUE = new DyeColor("Blue", 11, new Color(0x3c, 0x44, 0xaa))); new DyeColor("green", "Green", 13, new Color(0x5e, 0x7c, 0x16)),
self::register(self::$BROWN = new DyeColor("Brown", 12, new Color(0x83, 0x54, 0x32))); new DyeColor("red", "Red", 14, new Color(0xb0, 0x2e, 0x26)),
self::register(self::$GREEN = new DyeColor("Green", 13, new Color(0x5e, 0x7c, 0x16))); new DyeColor("black", "Black", 15, new Color(0x1d, 0x1d, 0x21)),
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)));
} }
private static function register(DyeColor $color) : void{ protected static function register(DyeColor $color) : void{
self::Enum_register($color);
self::$numericIdMap[$color->getMagicNumber()] = $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 * @throws \InvalidArgumentException
*/ */
public static function fromMagicNumber(int $magicNumber, bool $inverted = false) : DyeColor{ public static function fromMagicNumber(int $magicNumber, bool $inverted = false) : DyeColor{
self::checkInit();
$real = $inverted ? ~$magicNumber & 0xf : $magicNumber; $real = $inverted ? ~$magicNumber & 0xf : $magicNumber;
if(!isset(self::$numericIdMap[$real])){ if(!isset(self::$numericIdMap[$real])){
throw new \InvalidArgumentException("Unknown dye colour magic number $magicNumber"); throw new \InvalidArgumentException("Unknown dye colour magic number $magicNumber");
@ -192,7 +109,8 @@ final class DyeColor{
/** @var Color */ /** @var Color */
private $rgbValue; 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->displayName = $displayName;
$this->magicNumber = $magicNumber; $this->magicNumber = $magicNumber;
$this->rgbValue = $rgbValue; $this->rgbValue = $rgbValue;
@ -226,4 +144,3 @@ final class DyeColor{
return ~$this->magicNumber & 0xf; return ~$this->magicNumber & 0xf;
} }
} }
DyeColor::_init();

View File

@ -23,71 +23,43 @@ declare(strict_types=1);
namespace pocketmine\block\utils; 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{ final class SkullType{
use EnumTrait {
/** @var SkullType */ register as Enum_register;
private static $SKELETON; __construct as Enum___construct;
/** @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;
} }
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[] */ /** @var SkullType[] */
private static $numericIdMap = []; private static $numericIdMap = [];
public static function _init() : void{ protected static function setup() : array{
self::register(self::$SKELETON = new SkullType("Skeleton Skull", 0)); return [
self::register(self::$WITHER_SKELETON = new SkullType("Wither Skeleton Skull", 1)); new SkullType("skeleton", "Skeleton Skull", 0),
self::register(self::$ZOMBIE = new SkullType("Zombie Head", 2)); new SkullType("wither_skeleton", "Wither Skeleton Skull", 1),
self::register(self::$HUMAN = new SkullType("Player Head", 3)); new SkullType("zombie", "Zombie Head", 2),
self::register(self::$CREEPER = new SkullType("Creeper Head", 4)); new SkullType("player", "Player Head", 3),
self::register(self::$DRAGON = new SkullType("Dragon Head", 5)); 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::$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 */ /** @var int */
private $magicNumber; 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->displayName = $displayName;
$this->magicNumber = $magicNumber; $this->magicNumber = $magicNumber;
} }
@ -128,4 +101,3 @@ final class SkullType{
return $this->magicNumber; return $this->magicNumber;
} }
} }
SkullType::_init();

View File

@ -23,36 +23,25 @@ declare(strict_types=1);
namespace pocketmine\block\utils; 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{ final class SlabType{
use EnumTrait;
/** protected static function setup() : array{
* @var string return [
*/ new self("bottom"),
private $name; new self("top"),
new self("double")
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;
} }
} }

View File

@ -23,74 +23,43 @@ declare(strict_types=1);
namespace pocketmine\block\utils; 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{ final class TreeType{
use EnumTrait {
/** @var TreeType */ register as Enum_register;
private static $OAK; __construct as Enum___construct;
/** @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;
} }
/** @var TreeType[] */ /** @var TreeType[] */
private static $numericIdMap = []; private static $numericIdMap = [];
/** @var TreeType[] */
private static $all = [];
/** protected static function setup() : array{
* @internal return [
*/ new TreeType("oak", "Oak", 0),
public static function _init() : void{ new TreeType("spruce", "Spruce", 1),
self::register(self::$OAK = new TreeType("Oak", 0)); new TreeType("birch", "Birch", 2),
self::register(self::$SPRUCE = new TreeType("Spruce", 1)); new TreeType("jungle", "Jungle", 3),
self::register(self::$BIRCH = new TreeType("Birch", 2)); new TreeType("acacia", "Acacia", 4),
self::register(self::$JUNGLE = new TreeType("Jungle", 3)); new TreeType("dark_oak","Dark Oak", 5)
self::register(self::$ACACIA = new TreeType("Acacia", 4)); ];
self::register(self::$DARK_OAK = new TreeType("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::$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 * @throws \InvalidArgumentException
*/ */
public static function fromMagicNumber(int $magicNumber) : TreeType{ public static function fromMagicNumber(int $magicNumber) : TreeType{
self::checkInit();
if(!isset(self::$numericIdMap[$magicNumber])){ if(!isset(self::$numericIdMap[$magicNumber])){
throw new \InvalidArgumentException("Unknown tree type magic number $magicNumber"); throw new \InvalidArgumentException("Unknown tree type magic number $magicNumber");
} }
@ -113,10 +83,12 @@ final class TreeType{
private $magicNumber; private $magicNumber;
/** /**
* @param string $enumName
* @param string $displayName * @param string $displayName
* @param int $magicNumber * @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->displayName = $displayName;
$this->magicNumber = $magicNumber; $this->magicNumber = $magicNumber;
} }
@ -135,4 +107,3 @@ final class TreeType{
return $this->magicNumber; return $this->magicNumber;
} }
} }
TreeType::_init();

View File

@ -23,43 +23,25 @@ declare(strict_types=1);
namespace pocketmine\item; 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{ final class ItemUseResult{
/** @var ItemUseResult */ use EnumTrait;
private static $NONE;
/** @var ItemUseResult */
private static $FAILED;
/** @var ItemUseResult */
private static $SUCCEEDED;
/** protected static function setup() : array{
* No action attempted to take place. Does nothing. return [
* new self("none"),
* @return ItemUseResult new self("fail"),
*/ new self("success")
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
} }
} }

View File

@ -0,0 +1,172 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\utils;
use function count;
use function implode;
use function preg_match;
use function sprintf;
use function strtoupper;
trait EnumTrait{
/** @var self[] */
private static $members = null;
/**
* Registers the given object as an enum member.
*
* @param self $member
*
* @throws \InvalidArgumentException
*/
protected static function register(self $member) : void{
$name = strtoupper($member->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;
}
}