Skip to content

Instantly share code, notes, and snippets.

@baba-s
Created August 28, 2017 09:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baba-s/3b2ab75a92548156f69fd6958082a55a to your computer and use it in GitHub Desktop.
Save baba-s/3b2ab75a92548156f69fd6958082a55a to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using UnityEditor;
/// <summary>
/// 列挙型を管理するスクリプトを作成するエディタ拡張
/// </summary>
public static class EnumCreator
{
//====================================================================================
// 定数(const)
//====================================================================================
private const string MENU_ITEM_NAME = "Tools/Create Enum";
private const string XML_PATH = "Assets/Editor/enum.xml";
private const string DST_PATH = "Assets/Scripts/Enum.cs";
//====================================================================================
// クラス
//====================================================================================
/// <summary>
/// 列挙型の要素の情報を管理するクラス
/// </summary>
private sealed class EnumValueData
{
public readonly string m_name ; // 名前
public readonly string m_comment ; // コメント
public readonly string m_upperName ; // アッパーケースの名前
public readonly string m_upperCamelName ; // アッパーキャメルケースの名前
public readonly string m_lowerName ; // ローワーケースの名前
public readonly string m_lowerCamelName ; // ローワーキャメルケースの名前
/// <summary>
/// コンストラクタ
/// </summary>
public EnumValueData
(
string name ,
string comment
)
{
m_name = name ;
m_comment = comment ;
m_upperName = name.ToUpper();
m_upperCamelName = name.SnakeToUpperCamel();
m_lowerName = name.ToLower();
m_lowerCamelName = name.SnakeToLowerCamel();
}
/// <summary>
/// コンストラクタ
/// </summary>
public EnumValueData( XmlNode node ) : this
(
name : node.Attributes[ "name" ].GetValueOrDefault(),
comment : node.Attributes[ "comment" ].GetValueOrDefault()
)
{
}
}
/// <summary>
/// 列挙型の情報を管理するクラス
/// </summary>
private sealed class EnumData
{
public readonly string m_name ; // 名前
public readonly string m_comment ; // コメント
public readonly string m_upperName ; // アッパーケースの名前
public readonly string m_upperCamelName ; // アッパーキャメルケースの名前
public readonly EnumValueData[] m_list ; // 要素の配列
public readonly int m_count ; // 要素数
public readonly int m_countWithoutNone ; // NONE を除く要素数
/// <summary>
/// コンストラクタ
/// </summary>
public EnumData
(
string name ,
string comment ,
EnumValueData[] list
)
{
m_name = name ;
m_comment = comment ;
m_list = list ;
m_upperName = name.ToUpper();
m_upperCamelName = name.SnakeToUpperCamel();
m_count = list.Length;
m_countWithoutNone = list.Count( c => c.m_name != "none" );
}
/// <summary>
/// コンストラクタ
/// </summary>
public EnumData( XmlNode node ) : this
(
name : node.Attributes[ "name" ].GetValueOrDefault(),
comment : node.Attributes[ "comment" ].GetValueOrDefault(),
list : node.ChildNodes.Select( c => new EnumValueData( c ) ).ToArray()
)
{
}
}
//====================================================================================
// 関数
//====================================================================================
/// <summary>
/// 列挙型を管理するスクリプトを作成します
/// </summary>
[MenuItem( MENU_ITEM_NAME )]
private static void Create()
{
var document = new XmlDocument();
document.Load( XML_PATH );
var root = document.FirstChild;
var enumList = root.ChildNodes
.Where( c => c.Name == "enum" )
.Select( c => new EnumData( c ) )
.ToArray()
;
var builder = new StringBuilder();
builder.AppendLine( "using System.Collections.Generic;" );
builder.AppendLine();
//====================================================================================
// 列挙型
//====================================================================================
foreach ( var enumData in enumList )
{
var enumValueList = enumData.m_list;
builder.AppendLine( "/// <summary>" );
foreach ( var enumValue in enumValueList )
{
builder.AppendFormat( "/// <para>{0}:{1}</para>", enumValue.m_upperName, enumValue.m_comment ).AppendLine();
}
builder.AppendLine( "/// </summary>" );
builder.AppendFormat( "public enum {0}", enumData.m_upperName ).AppendLine();
builder.AppendLine( "{" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t" ).AppendFormat( "/// <para>{0}</para>", enumValue.m_comment ).AppendLine();
builder.Append( "\t" ).AppendFormat( "{0}, ", enumValue.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine();
}
builder.Append( "\t" ).AppendLine( "/// <para>番兵</para>" );
builder.Append( "\t" ).AppendLine( "SIZEOF, " );
builder.AppendLine( "}" );
builder.AppendLine();
}
//====================================================================================
// Comparer クラス
//====================================================================================
foreach ( var enumData in enumList )
{
builder.AppendLine( "/// <summary>" );
builder.AppendFormat( "/// {0} 型の等価比較をサポートするクラス", enumData.m_upperName ).AppendLine();
builder.AppendLine( "/// </summary>" );
builder.AppendFormat( "public sealed class {0}Comparer : IEqualityComparer<{1}>", enumData.m_upperCamelName, enumData.m_upperName ).AppendLine();
builder.AppendLine( "{" );
builder.Append( "\t" ).AppendFormat( "public bool Equals( {0} x, {0} y )", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine( "{" );
builder.Append( "\t\t" ).AppendLine( "return x == y;" );
builder.Append( "\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine();
builder.Append( "\t" ).AppendFormat( "public int GetHashCode( {0} obj )", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine( "{" );
builder.Append( "\t\t" ).AppendLine( "return ( int )obj;" );
builder.Append( "\t" ).AppendLine( "}" );
builder.AppendLine( "}" );
builder.AppendLine();
}
//====================================================================================
// Utils クラス
//====================================================================================
foreach ( var enumData in enumList )
{
var enumValueList = enumData.m_list;
builder.AppendLine( "/// <summary>" );
builder.AppendFormat( "/// {0} 型の汎用機能を管理するクラス", enumData.m_upperName ).AppendLine();
builder.AppendLine( "/// </summary>" );
builder.AppendFormat( "public static partial class {0}Utils", enumData.m_upperCamelName ).AppendLine();
builder.AppendLine( "{" );
//--------------------------------------------------------------------------------
// 変数
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendFormat( "private static {0}[] m_list = null;", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendFormat( "private static {0}[] m_listWithoutNone = null;", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine();
//--------------------------------------------------------------------------------
// Count プロパティ
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendLine( "/// <summary>" );
builder.Append( "\t" ).AppendFormat( "/// {0} 型の要素数 {1} を返します", enumData.m_upperName, enumData.m_count ).AppendLine();
builder.Append( "\t" ).AppendLine( "/// </summary>" );
builder.Append( "\t" ).AppendFormat( @"public static int Count {{ get {{ return {0}; }} }}", enumData.m_count ).AppendLine();
builder.Append( "\t" ).AppendLine();
//--------------------------------------------------------------------------------
// CountWithoutNone プロパティ
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendLine( "/// <summary>" );
builder.Append( "\t" ).AppendFormat( "/// {0}.NONE を除く {0} 型の要素数 {1} を返します", enumData.m_upperName, enumData.m_countWithoutNone ).AppendLine();
builder.Append( "\t" ).AppendLine( "/// </summary>" );
builder.Append( "\t" ).AppendFormat( @"public static int CountWithoutNone {{ get {{ return {0}; }} }}", enumData.m_countWithoutNone ).AppendLine();
builder.Append( "\t" ).AppendLine();
//--------------------------------------------------------------------------------
// List プロパティ
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendLine( "/// <summary>" );
builder.Append( "\t" ).AppendFormat( @"/// <para>{0} の要素の一覧を返します</para>", enumData.m_upperName ).AppendLine();
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t" ).AppendFormat( "/// <para>{0}.{1}</para>", enumData.m_upperName, enumValue.m_upperName ).AppendLine();
}
builder.Append( "\t" ).AppendLine( "/// </summary>" );
builder.Append( "\t" ).AppendFormat( "public static {0}[] List", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine( "{" );
builder.Append( "\t\t" ).AppendLine( "get" );
builder.Append( "\t\t" ).AppendLine( "{" );
builder.Append( "\t\t\t" ).AppendLine( "if ( m_list == null )" );
builder.Append( "\t\t\t" ).AppendLine( "{" );
builder.Append( "\t\t\t\t" ).AppendFormat( "m_list = new {0}[]", enumData.m_upperName ).AppendLine();
builder.Append( "\t\t\t\t" ).AppendLine( "{" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t\t\t\t\t" ).AppendFormat( "{0}.{1}, ", enumData.m_upperName, enumValue.m_upperName ).AppendLine();
}
builder.Append( "\t\t\t\t" ).AppendLine( "};" );
builder.Append( "\t\t\t" ).AppendLine( "}" );
builder.Append( "\t\t\t" ).AppendLine( "return m_list;" );
builder.Append( "\t\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine();
//--------------------------------------------------------------------------------
// ListWithoutNone プロパティ
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendLine( "/// <summary>" );
builder.Append( "\t" ).AppendFormat( @"/// <para>{0}.NONE を除く {0} の要素の一覧を返します</para>", enumData.m_upperName ).AppendLine();
foreach ( var enumValue in enumValueList.Where( c => c.m_name != "none" ) )
{
builder.Append( "\t" ).AppendFormat( "/// <para>{0}.{1}</para>", enumData.m_upperName, enumValue.m_upperName ).AppendLine();
}
builder.Append( "\t" ).AppendLine( "/// </summary>" );
builder.Append( "\t" ).AppendFormat( "public static {0}[] ListWithoutNone", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine( "{" );
builder.Append( "\t\t" ).AppendLine( "get" );
builder.Append( "\t\t" ).AppendLine( "{" );
builder.Append( "\t\t\t" ).AppendLine( "if ( m_listWithoutNone == null )" );
builder.Append( "\t\t\t" ).AppendLine( "{" );
builder.Append( "\t\t\t\t" ).AppendFormat( "m_listWithoutNone = new {0}[]", enumData.m_upperName ).AppendLine();
builder.Append( "\t\t\t\t" ).AppendLine( "{" );
foreach ( var enumValue in enumValueList.Where( c => c.m_name != "none" ) )
{
builder.Append( "\t\t\t\t\t" ).AppendFormat( "{0}.{1}, ", enumData.m_upperName, enumValue.m_upperName ).AppendLine();
}
builder.Append( "\t\t\t\t" ).AppendLine( "};" );
builder.Append( "\t\t\t" ).AppendLine( "}" );
builder.Append( "\t\t\t" ).AppendLine( "return m_listWithoutNone;" );
builder.Append( "\t\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine();
//--------------------------------------------------------------------------------
// アッパーケースの文字列から変換
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendLine( "/// <summary>" );
builder.Append( "\t" ).AppendFormat( @"/// <para>アッパーケースの文字列を {0} の要素に変換します</para>", enumData.m_upperName ).AppendLine();
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t" ).AppendFormat( @"/// <para>""{1}"": {0}.{1}</para>", enumData.m_upperName, enumValue.m_upperName ).AppendLine();
}
builder.Append( "\t" ).AppendLine( "/// </summary>" );
builder.Append( "\t" ).AppendFormat( "public static {0} FromString( string str )", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine( "{" );
builder.Append( "\t\t" ).AppendLine( "switch ( str )" );
builder.Append( "\t\t" ).AppendLine( "{" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t\t\t" ).AppendFormat( @"case ""{1}"": return {0}.{1};", enumData.m_upperName, enumValue.m_upperName ).AppendLine();
}
builder.Append( "\t\t" ).AppendLine( "}" );
builder.Append( "\t\t" ).AppendLine( "return 0;" );
builder.Append( "\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine();
//--------------------------------------------------------------------------------
// アッパーケースの文字列に変換
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendLine( "/// <summary>" );
builder.Append( "\t" ).AppendLine( "/// <para>アッパーケースの文字列に変換します</para>" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t" ).AppendFormat( @"/// <para>{0}.{1}: ""{1}""</para>", enumData.m_upperName, enumValue.m_upperName ).AppendLine();
}
builder.Append( "\t" ).AppendLine( "/// </summary>" );
builder.Append( "\t" ).AppendFormat( "public static string ToUpper( this {0} self )", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine( "{" );
builder.Append( "\t\t" ).AppendLine( "switch ( self )" );
builder.Append( "\t\t" ).AppendLine( "{" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t\t\t" ).AppendFormat( @"case {0}.{1}: return ""{1}"";", enumData.m_upperName, enumValue.m_upperName ).AppendLine();
}
builder.Append( "\t\t" ).AppendLine( "}" );
builder.Append( "\t\t" ).AppendLine( "return string.Empty;" );
builder.Append( "\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine();
//--------------------------------------------------------------------------------
// ローワーケースの文字列に変換
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendLine( "/// <summary>" );
builder.Append( "\t" ).AppendLine( "/// <para>アッパーケースの文字列に変換します</para>" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t" ).AppendFormat( @"/// <para>{0}.{1}: ""{2}""</para>", enumData.m_upperName, enumValue.m_upperName, enumValue.m_lowerName ).AppendLine();
}
builder.Append( "\t" ).AppendLine( "/// </summary>" );
builder.Append( "\t" ).AppendFormat( "public static string ToLower( this {0} self )", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine( "{" );
builder.Append( "\t\t" ).AppendLine( "switch ( self )" );
builder.Append( "\t\t" ).AppendLine( "{" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t\t\t" ).AppendFormat( @"case {0}.{1}: return ""{2}"";", enumData.m_upperName, enumValue.m_upperName, enumValue.m_lowerName ).AppendLine();
}
builder.Append( "\t\t" ).AppendLine( "}" );
builder.Append( "\t\t" ).AppendLine( "return string.Empty;" );
builder.Append( "\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine();
//--------------------------------------------------------------------------------
// アッパーキャメルケースの文字列に変換
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendLine( "/// <summary>" );
builder.Append( "\t" ).AppendLine( "/// <para>アッパーケースの文字列に変換します</para>" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t" ).AppendFormat( @"/// <para>{0}.{1}: ""{2}""</para>", enumData.m_upperName, enumValue.m_upperName, enumValue.m_upperCamelName ).AppendLine();
}
builder.Append( "\t" ).AppendLine( "/// </summary>" );
builder.Append( "\t" ).AppendFormat( "public static string ToUpperCamel( this {0} self )", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine( "{" );
builder.Append( "\t\t" ).AppendLine( "switch ( self )" );
builder.Append( "\t\t" ).AppendLine( "{" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t\t\t" ).AppendFormat( @"case {0}.{1}: return ""{2}"";", enumData.m_upperName, enumValue.m_upperName, enumValue.m_upperCamelName ).AppendLine();
}
builder.Append( "\t\t" ).AppendLine( "}" );
builder.Append( "\t\t" ).AppendLine( "return string.Empty;" );
builder.Append( "\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine();
//--------------------------------------------------------------------------------
// ローワーキャメルケースの文字列に変換
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendLine( "/// <summary>" );
builder.Append( "\t" ).AppendLine( "/// <para>アッパーケースの文字列に変換します</para>" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t" ).AppendFormat( @"/// <para>{0}.{1}: ""{2}""</para>", enumData.m_upperName, enumValue.m_upperName, enumValue.m_lowerCamelName ).AppendLine();
}
builder.Append( "\t" ).AppendLine( "/// </summary>" );
builder.Append( "\t" ).AppendFormat( "public static string ToLowerCamel( this {0} self )", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine( "{" );
builder.Append( "\t\t" ).AppendLine( "switch ( self )" );
builder.Append( "\t\t" ).AppendLine( "{" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t\t\t" ).AppendFormat( @"case {0}.{1}: return ""{2}"";", enumData.m_upperName, enumValue.m_upperName, enumValue.m_lowerCamelName ).AppendLine();
}
builder.Append( "\t\t" ).AppendLine( "}" );
builder.Append( "\t\t" ).AppendLine( "return string.Empty;" );
builder.Append( "\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine();
//--------------------------------------------------------------------------------
// コメントの文字列に変換
//--------------------------------------------------------------------------------
builder.Append( "\t" ).AppendLine( "/// <summary>" );
builder.Append( "\t" ).AppendLine( "/// <para>コメントの文字列に変換します</para>" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t" ).AppendFormat( @"/// <para>{0}.{1}: ""{2}""</para>", enumData.m_upperName, enumValue.m_upperName, enumValue.m_comment ).AppendLine();
}
builder.Append( "\t" ).AppendLine( "/// </summary>" );
builder.Append( "\t" ).AppendFormat( "public static string ToComment( this {0} self )", enumData.m_upperName ).AppendLine();
builder.Append( "\t" ).AppendLine( "{" );
builder.Append( "\t\t" ).AppendLine( "switch ( self )" );
builder.Append( "\t\t" ).AppendLine( "{" );
foreach ( var enumValue in enumValueList )
{
builder.Append( "\t\t\t" ).AppendFormat( @"case {0}.{1}: return ""{2}"";", enumData.m_upperName, enumValue.m_upperName, enumValue.m_comment ).AppendLine();
}
builder.Append( "\t\t" ).AppendLine( "}" );
builder.Append( "\t\t" ).AppendLine( "return string.Empty;" );
builder.Append( "\t" ).AppendLine( "}" );
builder.Append( "\t" ).AppendLine();
builder.Append( "}" );
}
var directoryName = Path.GetDirectoryName( DST_PATH );
if ( !Directory.Exists( directoryName ) )
{
Directory.CreateDirectory( directoryName );
}
File.WriteAllText( DST_PATH, builder.ToString(), Encoding.UTF8 );
AssetDatabase.Refresh( ImportAssetOptions.ImportRecursive );
var filename = Path.GetFileName( DST_PATH );
EditorUtility.DisplayDialog( filename, "作成が完了しました", "OK" );
var result = AssetDatabase.LoadAssetAtPath<MonoScript>( DST_PATH );
EditorGUIUtility.PingObject( result );
}
/// <summary>
/// 指定された文字列をスネークケースからアッパーキャメルケースに変換して返します
/// </summary>
private static string SnakeToUpperCamel( this string self )
{
if ( string.IsNullOrEmpty( self ) ) return self;
return self
.Split( new[] { '_' }, StringSplitOptions.RemoveEmptyEntries )
.Select( s => char.ToUpperInvariant( s[ 0 ] ) + s.Substring( 1, s.Length - 1 ) )
.Aggregate( string.Empty, ( s1, s2 ) => s1 + s2 )
;
}
/// <summary>
/// 指定された文字列をスネークケースからローワーキャメルケースに変換して返します
/// </summary>
private static string SnakeToLowerCamel( this string self )
{
if ( string.IsNullOrEmpty( self ) ) return self;
return self
.SnakeToUpperCamel()
.Insert( 0, char.ToLowerInvariant( self[ 0 ] ).ToString() ).Remove( 1, 1 )
;
}
/// <summary>
/// XmlAttribute.Value を返します。null の場合はデフォルト値を返します
/// </summary>
private static string GetValueOrDefault( this XmlAttribute self, string defaultValue = "" )
{
return self != null ? self.Value : defaultValue;
}
/// <summary>
/// 指定された XmlNodeList の要素を新しいフォームに射影して返します
/// </summary>
private static IEnumerable<T> Select<T>( this XmlNodeList self, Func<XmlNode, T> selector )
{
foreach ( XmlNode n in self )
{
yield return selector( n );
}
}
/// <summary>
/// 指定された XmlNodeList の要素をフィルター処理して返します
/// </summary>
private static IEnumerable<XmlNode> Where( this XmlNodeList self, Func<XmlNode, bool> predicate )
{
foreach ( XmlNode n in self )
{
if ( predicate( n ) )
{
yield return n;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment