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;
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();

View File

@ -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();

View File

@ -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")
];
}
}

View File

@ -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();

View File

@ -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")
];
}
}

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;
}
}