妄想プログラマのらくがき帳 : 2015

2015年5月21日木曜日

[C#][Roslyn]SyntaxWalkerでメソッドコメントを取得する

リバースエンジニアリングをするときやコーディングが先でドキュメントを後に書くような開発のときとか、C#のソースコードからメソッドコメントをツールで抽出したいなあーと思うことがたまにあります。

既存のツールで抽出できるものがあるかもしれませんが、有料だったり、細かいところに手が届かなかったりで、結局手作業でやるはめになりがちです。そんなとき、Roslynを使えば簡単にメソッドコメントを抽出できますよーっていうのが今回の内容です。

XMLドキュメントコメントを格納するクラスを作る

まずメソッドコメントであるXMLを格納するクラスを作ります。
class DocumentComment
{
    public string Summary { get; set; }
    public List<ParamDocumentComment> Params { get; set; }

    public DocumentComment()
    {
        Params = new List<ParamDocumentComment>();
    }
}

class ParamDocumentComment
{
    public string Name { get; set; }
    public string Comment { get; set; }
}
今回はsummaryとparamsを取ってくるだけにするので、それぞれのプロパティのみの構成にしています。

メソッドコメントのXMLをパースするクラスを作る

次にメソッドコメントのXMLをパースするクラスを作ります。
class DocumentCommentParser
{
    public static DocumentComment Parse(string documentComment)
    {
        // XDocumentクラスを用いてsammary要素とparam要素を取得する
        XDocument doc = XDocument.Parse(documentComment);
        var summary = doc.Descendants("summary").FirstOrDefault();
        var paramList = doc.Descendants("param");

        var docComment = new DocumentComment();
        // summary要素が取得できた場合、不要な改行や空白を取り除く
        docComment.Summary = (summary != null) ? RemoveAnySpaceChar(summary.Value) : string.Empty;

        foreach (var param in paramList)
        {
            // param要素はname属性が取得できたもののみ有効な要素とする
            var name = param.Attribute("name");
            if (name != null)
            {
                var pdc = new ParamDocumentComment();
                pdc.Name = RemoveAnySpaceChar(name.Value);
                pdc.Comment = RemoveAnySpaceChar(param.Value);
                docComment.Params.Add(pdc);
            }
        }
        return docComment;
    }

    // 引数の文字列から正規表現のパターン「\s」に該当する文字を削除した文字列を返す
    private static string RemoveAnySpaceChar(string str)
    {
        return Regex.Replace(str, "\\s", "");

    }
}
引数のXML文字列をパースし、DocumentCommentクラスを返すParse()メソッドを定義します。XDocumentクラスを使えばXMLのパースも簡単です。

CSharpSyntaxWalkerを継承したメソッドコメント収集クラスを作る

次にCSharpSymtaxWalkerを継承してメソッドコメントを収集するクラスを作ります。
class MethodDocumentCommentWalker : CSharpSyntaxWalker
{
    private SemanticModel m_semanticModel; // ドキュメントコメントを取得するにはセマンティックモデルが必要
    private Dictionary<MethodDeclarationSyntax, DocumentComment> m_documentComments = new Dictionary<MethodDeclarationSyntax, DocumentComment>();

    // 取得したドキュメントコメントのリストを返すプロパティ
    public IReadOnlyDictionary<MethodDeclarationSyntax, DocumentComment> DocumentComments
    {
        get { return m_documentComments; }
    }

    public MethodDocumentCommentWalker(SyntaxTree tree)
    {
        // コンストラクタでセマンティックモデルを作成しておく
        var compilation = CSharpCompilation.Create("tmpcompilation", syntaxTrees: new[] { tree });
        m_semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees[0], true);
    }

    // VisitMethodDeclaration()をオーバーライドし、各メソッド宣言からドキュメントコメントを取得する
    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        base.VisitMethodDeclaration(node);

        // IMethodSymbol.GetDocumentationCommentXml()でメソッドコメントのXML文字列を取得し、
        // パーサークラスを用いてパースする
        IMethodSymbol symbol = m_semanticModel.GetDeclaredSymbol(node);
        m_documentComments[node] = DocumentCommentParser.Parse(symbol.GetDocumentationCommentXml());
    }
}

メソッドコメント収集クラスを用いてメソッドコメントを収集する

最後にメソッドコメント収集クラスでメソッドコメントを収集します。
class Program
{
    static void Main(string[] args)
    {
        SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText("sample.cs"));
        
        // MethodDocumentCommentWalker.Visit()を呼び出せば、
        // MethodDocumentCommentWalker.DocumentCommentsに収集した結果が格納される
        var walker = new MethodDocumentCommentWalker(syntaxTree);
        walker.Visit(syntaxTree.GetRoot());

        foreach (var docComment in walker.DocumentComments)
        {
            MethodDeclarationSyntax method = docComment.Key;
            DocumentComment comment = docComment.Value;

            // ここで取得したメソッドコメントを整形してファイルに出力したりする
            Console.WriteLine("#" + method.Identifier);
            Console.WriteLine("##Summary");
            Console.WriteLine(comment.Summary);
            Console.WriteLine("##parameters");
            foreach (var param in comment.Params)
            {
                Console.WriteLine(param.Name + "\t" + param.Comment);
            }
            Console.WriteLine();
        }
    }
}

上記処理では、以下のサンプルクラスに対してメソッドコメント収集を行って、メソッド名とsummary、paramsのセットでコンソールに出力しています。
class sample
{
    /// <summary>
    /// サンプルクラスのコンストラクタコメント。
    /// </summary>
    public sample()
    {
    }

    /// <summary>
    /// Method1のメソッドコメント。
    /// </summary>
    /// <param name="arg">Method1の引数1。</param>
    /// <returns>Method1の戻り値。</returns>
    public int Method1(int arg)
    {
        return arg * 2;
    }

    /// <summary>
    /// Method2のメソッドコメント。
    /// </summary>
    /// <param name="arg1">Method2の引数1。</param>
    /// <param name="arg2">Method2の引数2。</param>
    /// <returns>Method2の戻り値。</returns>
    public int Method2(int arg1, int arg2)
    {
        return arg1 * arg2;
    }
}
出力結果はこんな↓感じになります。

#Method1
##Summary
Method1のメソッドコメント。
##parameters
arg Method1の引数1。

#Method2
##Summary
Method2のメソッドコメント。
##parameters
arg1 Method2の引数1。
arg2 Method2の引数2。

収集する部分と出力する部分が独立しているので、出力形式や出力先の変更も簡単です。

2015年5月20日水曜日

[Android]ボタンの背景を変える(設定する)

  • ボタンの背景を静的に設定する
ボタンの背景を静的に設定するには、xmlファイルのボタン要素にbackground属性を追加します。
以下がボタンに背景色を設定する場合のxmlファイル例です。
<Button
    android:id="@+id/button1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="64dp"
    android:text="Button" 
    android:background="#ff0000" /> <!-- 背景色をRGBで指定 -->
ボタンに背景画像を設定する場合は、[res]-[drawable]フォルダに画像を置き、
次のようにbackground属性を設定します(以下の例は、drawableフォルダにbutton_background.pngを置いた場合の例です)。
<Button
    android:id="@+id/button1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="64dp"
    android:text="Button" 
    android:background="@drawable/button_background" /> <!-- 背景画像を設定 -->

  • ボタンの背景を動的に設定する
ボタンの背景色を動的に設定するには、Button.setBackgroundColor()を使います。
Button button = (Button) findViewById(R.id.button1);
button.setBackgroundColor(Color.rgb(0, 100, 200));

背景画像を動的に設定するには、Button.setBackgroundResource()を使います。
Button button = (Button) findViewById(R.id.button1);
// drawableフォルダに置いたbutton_background.pngを背景に設定
button.setBackgroundResource(R.drawable.button_background);

  • ボタンの状態に応じて背景が変わるようにする
ボタンがフォーカスされたときや押されたときに背景が変化するようにするには、
State List Drawableを作成し、xmlでbackground属性に指定します。

1. State List Drawableを作成する。
まず、以下のようなxmlファイルを作成し、[res]-[drawable]フォルダに配置します。
xmlファイル名は何でも良いんですが、とりあえず"custom_button.xml"としました。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/button_pressed"
          android:state_pressed="true" />
    <item android:drawable="@drawable/button_focused"
          android:state_focused="true" />
    <item android:drawable="@drawable/button_default" />
</selector>
item要素でボタンに設定する画像をリストアップします。
android:drawableに設定する値は、drawableフォルダ内の画像ファイル名です
(button_default.pngの場合、"@drawable/button_default"を設定します)。

android:state_pressed="true"の要素には、ボタンが押下されたときに設定する画像、
android:state_focused="true"の要素には、ボタンがフォーカスされたときに設定する画像を指定します。

2. ボタン要素のbackground属性に、作成したxmlファイルを設定する。
xmlファイルのボタン要素のbackground属性に、以下のように先ほど作成したxmlファイルの名前を指定します。
<Button
    android:id="@+id/btnCustom"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true"
    android:layout_marginBottom="172dp"
    android:background="@drawable/custom_button"
    android:text="Custom button" />
以上で、ボタンの状態に応じてボタン背景が設定した画像に変わるようになりました。

@通常時


@フォーカス時


@ボタン押下時



2015年1月25日日曜日

[Android]AndroidStudioでエラーが出てエミュレータが起動しない場合の対処法

AndroidStudio でコードを書いていざデバッグ!というときに「emulator: ERROR: x86 emulation currently requires hardware acceleration!」というエラーが出てエミュレータが起動しない場合があります。私の場合、AndroidStudioをインストールして一番最初のデバッグ実行時にこのエラーが出ました。


ふむふむ、エラーメッセージによると「Inter HAXM をちゃんとインストールして使えるようにしてくれ」とのこと。。
あれえ?おかしいなあ、HAXM はインストールしたのになあ…と思ったんですが、調べてみたらインストールできてませんでした。おかしいのはどうやら私の頭の方だったようで(;^_^A  改めて HAXM をインストールし直したらエミュレータが起動しました\(^o^)/

つまり、上記のようなエラーが出てエミュレータが起動しないのは、エラーメッセージにあるように HAXM がインストールできていない、または使えるようになっていないからです。ということで、HAXM のインストール方法を以下に記します。

HAXMのインストール条件

HAXM をインストールするには、ハードウェアと OS が HAXM のシステム要件を満たしている必要があります。具体的には、CPU が
  • Intel® VT-x
  • Intel® EM64T (Intel® 64)
  • Execute Disable (XD) Bit
をサポートしている必要があり、OS は Windows Vista 以降でなければなりません。詳細は Installation Instructions for Intel® Hardware Accelerated Execution Manager の「System Requirements」を参照してください。

ちなみに CPU がサポートしている機能は Intel® Product Information で確認できます。

HAXMのインストール方法

HAXM のインストール条件が整っていることが確認できたら、以下の手順で HAXM をインストールします。
  1. BIOS で VT-x と Execute Disable (XD) Bit を有効にする
    まず最初に BIOS で VT-x と Execute Disable (XD) Bit を有効にしておく必要があります。BIOS の設定項目で VT-x と Execute Disable (XD) Bit を探し出し、Enabled にして設定を保存、パソコンを再起動してください。
    ※BIOS での設定項目名は環境によって異なるので、注意深く探してみてください。
  2. SDK Manager で Intel x86 Emulator Accelerator (HAXM installer) をインストール
    次に SDK Manager で Intel x86 Emulator Accelerator (HAXM installer) をインストールします。

  3. HAXM のインストールフォルダ※を開き、「intelhaxm-android.exe」を管理者権限で実行します。
    ※インストールフォルダは環境によって違うかもしれません。私の場合「C:\Android\sdk\extras\intel\Hardware_Accelerated_Execution_Manager」でした。
3の手順が問題なく完了すればインストール完了!これでエミュレータが起動するようになるはずです。

メモリ上限サイズの設定

インストールの途中でメモリ上限サイズの設定があります。


ここで設定する上限サイズですが、1つ注意しないといけない点があります。ここでの設定値が AVD のメモリサイズより小さい場合、「HAX is not working and emulator runs in emulation mode」「emulator: The memory needed by this VM exceeds the driver limit.」というエラーが出て HAX が有効になりません。


HAX を有効にするには、再度「intelhaxm-android.exe」を実行して上限サイズを変更するか、AVDのメモリサイズを上限サイズより小さくする必要があります。

2015年1月12日月曜日

[Android]AndroidStudioをインストールしてみた。

去る2014/12/09に AndroidStudio 1.0 がリリースされました。
ということで、ちょっと遅くなりましたが AndroidStudio をインストールしてみました。
※インストール手順は2014/12/30時点での手順です。

インストーラーをダウンロードする

まずインストーラーをダウンロードします。下記場所からインストーラーをダウンロードしてください。

Download Android Studio and SDK Tools
  1. 緑色の「Download Android Studio for Windows」ボタンをクリック。
  2. 利用規約が表示されるので、よく読み、同意なら、「I have read and agree with the above terms and conditions」チェックボックスをチェックし、青色の「Download Android Studio for Windows」ボタンをクリック。

AndroidStudioをインストールする

次に AndroidStudio をインストールします。
  1. ダウンロードした .exe をクリックしてインストーラーを起動します。
    1. JDK がインストールされていない場合、JDK要求画面が表示されるので、JDK ダウンロードページへのリンクをクリックし、JDK インストーラーをダウンロードしてください。
      JDK要求画面

    2. JDK ダウンロードページですが、JDK は CPU と PSD がある場合は CPU を選択し、開発環境のプラットフォームに合った .exe をダウンロードします。
    3. ダウンロードした .exe をクリックし、インストーラーに従って JDK をインストールします。
    4. Android Studio のインストーラーに戻り、JDK のインストール先パスをテキストボックスに設定します。
      ※Windows の JDK デフォルトインストール先「C:\Program Files\Java\jdk1.7.0_71\bin」を入力する場合、パスにスペースが含まれるため、パスをダブルクォーテーションで囲む必要があるので注意。まあパスにスペースがあると駄目なケースは結構あるので、3で JDK をインストールするときにインストール先パスをスペース無しの場所にした方が無難です。

  2. コンポーネント選択画面が表示されます。特に理由が無ければデフォルト設定で「Next」を押し次へ進みます。
    コンポーネント選択画面

  3. ライセンス条項が表示されるので、同意するなら「I Agree」ボタンを押し次へ進みます。
    ※当然ですが、同意しないと先へ進めません
  4. Android Studio と Android SDK のインストール先設定画面が表示されます。デフォルトで問題ないならそのままで、変更が必要ならパスを変更し、「Next」ボタンを押し次へ進みます。
    インストール先設定画面

  5. エミュレータ設定画面が表示されます。ここでエミュレータが使用するメモリサイズを設定しますが、このサイズが小さすぎるとエミュレータの高速化が有効にならないケースが出てくるので、Recommended の512MB とするより Custom で 1024MB くらいにしといた方がいいかもしれません。
    サイズを設定したら「Next」ボタンを押し次へ進みます。
    エミュレータ設定画面

  6. スタートメニューフォルダの作成画面が表示されます。デフォルトから変更したい場合は変更してください。
    最後に「Install」ボタンを押してインストールを開始します。あとはインストールが終わるまで待つだけです。
 
以上で、AndroidStudio のインストールは完了です。