暇になったので小ネタ、Expressionから値が欲しいとき(´д`)

System.Linq.ExpressionをVisitしたりコネコネしたりする時のお話(・ω・)


Expressionを使ってなにをするかといえば、.NETの構文からクエリ言語を生成するだとか、IQueryableもどきみたいなことをするケースが多いと思いますが(`・ω・´) *1
っで、ExpressionをVisitしながらターゲットを構築していく途中で、Expressionを評価した値が欲しくなるケースがあると思います。


要は、

Where(Expression<Func<TEntity, bool>> filter)

みたいなメソッドを作って、その中でクエリを構築するようなケースで、

Query<Data>.Where(_ => _.No >= param.Min && _.No <= param.Max)

っと書いた時に、param.Minやparam.Maxの値をクエリ構築中に評価したい、っというような話なわけですが(・ω・) *2


っで、値が欲しい場合、Expression.Lambda(expression).Compile().DynamicInvoke()すれば取得できるんですが、これは死ぬほど遅いので多用するのはちょっと考えないといけないわけで(´д`;)
でも、いくつかのケースではCompile()せずに、値を高速に取得ができるので、その方法について以下ソース。

public static object EvalExpression(Expression expression)
{
    // Constant
    if (expression.NodeType == ExpressionType.Constant)
    {
        return ((ConstantExpression)expression).Value;
    }

    // MemberAccess
    if (expression.NodeType == ExpressionType.MemberAccess)
    {
        var member = (MemberExpression)expression;

        var target = member.Expression != null ? EvalExpression(member.Expression) : null;
        var pi = member.Member as PropertyInfo;
        if (pi != null)
        {
            return pi.GetValue(target, null);
        }
        var fi = member.Member as FieldInfo;
        if (fi != null)
        {
            return fi.GetValue(target);
        }
    }

    // Call
    if (expression.NodeType == ExpressionType.Call)
    {
        var call = (MethodCallExpression)expression;

        var target = call.Object != null ? EvalExpression(call.Object) : null;
        return call.Method.Invoke(target, call.Arguments.Select(EvalExpression).ToArray());
    }

    // 遅いので未サポートにしておく
    //return Expression.Lambda(expression).Compile().DynamicInvoke();
    throw new NotSupportedException("Unsupported expression.");
}

まず、定数値についてはConstantExpressionのValueから値を取得できるのでそれを使用。
フィールド及びプロパティからの取得については、MemberExpression.Memberで取得できるMemberInfoを使ってリフレクションを使って処理という感じ。


DSLを構築するようなケースだと、値を評価したいケースなんて、定数かメンバからの値取得が大半なので、上記だけ高速になれば十分な気はしますが(・ω・)
複雑な式の評価値が欲しいようなケースでは事前に一時変数に代入してしまえばMemberExpressionで処理できるわけで、ここまでにしても良いんですが(・∀・;)


でもまあ、単純なメソッド呼び出しくらいもサポートしておこうかしら、っというわけで、MethodCallExpressionの処理も追加しておきました。
ただし、宣言として、メソッド呼び出しの引数として上記の処理内で解決できないような式を書くことは未サポートということで。


使用イメージは以下。

// Expressionからなにかしらを構築していく処理の例
static void EvalExpression(Expression<Func<object>> expression)
{
    Debug.WriteLine(expression.Body);
}

とかにたいして、

// Constant
Test(() => 1);

// MemberAccess
var a = 1;
Test(() => a);

// MemberAccess
var hoge = new Hoge()
Test(() => hoge.Value);

// Call
Test(() => hoge.GetValue());

みたいなものがExpression.Lambda(expression).Compile()に比べて高速に処理できるようになりますた(`・ω・´)


なんか1年近く日記を書いていませんでしたが、中途半端に暇になったので小ネタを書いてみますた(・ω・;)

*1:要はDSLの構築。

*2:式木全体の評価はExpressionVisitorで処理。

AU取ってたど〜(/・ω・)/

受験票無くして合否がわからなかったんですが、システム監査の合格書届きました。
基本、開発の人間としてはSAを最後にモチベーションも下がってきてますが、情報処理技術者試験も残すは系統的にSTだけなので、ここまできたらコンプを目指しまてみましょうか(・ω・;)


過去ログはこんな感じ。

SM取ったど〜(・ω・)
http://d.hatena.ne.jp/machi_pon/20111217/1324120241
SA取ったど〜(´・ω・`)
http://d.hatena.ne.jp/machi_pon/20101220/1292846778
PM取ったど〜(#゚д゚)、ペッ
http://d.hatena.ne.jp/machi_pon/20100627/1277595650
ES取ったど〜、発表早くなっているのね(´д`)
http://d.hatena.ne.jp/machi_pon/20090630/1246355918
はいはいNW取ったど〜(・ω・)ノ
http://d.hatena.ne.jp/machi_pon/20081215/1229313497
DB取ったど〜
http://d.hatena.ne.jp/machi_pon/20080616/1213614589
SV取ったど〜
http://d.hatena.ne.jp/machi_pon/20070611/1181533946


正直、ここまでくると情処もテクニークで受かっているだけの気がしないでも無いので、これをネタにお小遣い稼ぎができないものかと。
PMの論文とか、ネタ的に使い回しがききやすいし*1、なんだかんだ言いつつSI屋ではPM資格が一番だと未だに考えていると思うので、元ネタ作成サービスとか(・∀・;)


っというわけで、今回の臨時収入はとりあえず貯金します(`・ω・´)
Ivyにケチがつかなければ、仕事PCのリプレースでも良かったんですが。
とりあえず4.1タブレットは一つ欲しいけど(・∀・)

*1:この点で、論文ものではPMが一番受かりやすいと思う。ちなみに個人的な意見としては、一番難しいのはNW(範囲が広い的な意味で)、資格の本来の意味に立ち返って勉強することが一番役に立つのはSC(自分の時はSV)です(`・ω・´)

AndroidとJ2SEの両方で処理を共有するためのログラッパー

小ネタ。
ComponentとかModelとか、別にAndroidに依存しない部分はJ2SE用と共通で作りたいわけですが(・ω・)
テストとかも、エミュレータでやると遅いので、PC上で出来るところはしちゃいたいですし。*1


っで、そこでちょっと面倒なのが、ログ出力。
AndroidのLogはstaticなメソッドで実装されていたりするし(´д`;)


っということで、ログラッパークラスを作って、PCでもAndroidでも下記の様にログ出力を書けるようにする方法について。

public class MyComponent {

    private static final Logger log = LoggerFactory.getLogger(MyComponent.class);

    public void hoge() {
        log.trace("うさ☆うさだよもん");
    }
}

なお、内部的には、Android用の実装はLogクラスを使い、PC用実装はSLF4Jを使う形にします。

共通

まず、ログを抽象化するインターフェースとそのファクトリーインターフェースを下記のような感じで用意します(・ω・)

public interface Logger {

    boolean isTraceEnabled();
...
    boolean isErrorEnabled();
    void trace(String msg);
    void trace(String msg, Throwable t);
...
    void error(String msg);
    void error(String msg, Throwable t);
}
public interface LoggerFactoryAdapter {

    Logger getLogger(final Class<?> clazz);
}

っで、ファクトリーを取得するクラスを下記の様な感じで作成。
ANDROID_LOGGER_FACTORY_ADAPTER、STANDARD_LOGGER_FACTORY_ADAPTERはそれぞれAndroidとSLF4用のLoggerFactoryAdapter実装クラスのクラス名になります。

public final class LoggerFactory {

    private static final String ANDROID_LOGGER_FACTORY_ADAPTER = "os.android.log.AndroidLoggerFactoryAdapter";

    private static final String STANDARD_LOGGER_FACTORY_ADAPTER = "os.standard.log.SLF4JLoggerFactoryAdapter";

    private static final Map<String, LoggerFactoryAdapter> adopters = new ConcurrentHashMap<String, LoggerFactoryAdapter>();

    public static Logger getLogger(final Class<?> clazz) {
        if (Environment.isAndroid()) {
            return getAdapter(ANDROID_LOGGER_FACTORY_ADAPTER).getLogger(clazz);
        }
        return getAdapter(STANDARD_LOGGER_FACTORY_ADAPTER).getLogger(clazz);
    }

    private static LoggerFactoryAdapter getAdapter(final String loggerName) {
        LoggerFactoryAdapter adopter = adopters.get(loggerName);
        if (adopter == null) {
            try {
                adopter = (LoggerFactoryAdapter)Class.forName(loggerName).newInstance();
            } catch (InstantiationException e) {
                throw new WrapRuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new WrapRuntimeException(e);
            } catch (ClassNotFoundException e) {
                throw new WrapRuntimeException(e);
            }
            adopters.put(loggerName, adopter);
        }
        return adopter;
    }

    private LoggerFactory() {
    }
}

ちなみに、Androidか否かの実行環境の判定は下記の様にしてみましたが、どんなもんでしょう(・ω・)?

public final class Environment {

    public static boolean isAndroid() {
        String vmName = System.getProperty("java.vm.name");
        return vmName.toLowerCase().contains("dalvik");
    }
}

Android用ログ実装

そしてAndroid用のログ実装としては、下記の様なクラスを用意。
内容としては、クラス名をTAGとして扱いLogの処理をラップするだけ(・ω・)

public class AndroidLogger implements Logger {

    private final String tag;

    public AndroidLogger(final String tag) {
        this.tag = tag;
    }

    @Override
    public boolean isTraceEnabled() {
        return Log.isLoggable(tag, Log.VERBOSE);
    }
...
    @Override
    public boolean isErrorEnabled() {
        return Log.isLoggable(tag, Log.ERROR);
    }

    @Override
    public void trace(final String msg) {
        Log.v(tag, msg);
    }
...
    @Override
    public void error(final String msg, final Throwable t) {
        Log.e(tag, msg, t);
    }
}
public class AndroidLoggerFactoryAdapter implements LoggerFactoryAdapter {

    @Override
    public Logger getLogger(final Class<?> clazz) {
        return new AndroidLogger(clazz.getSimpleName());
    }
}

Android用(SLF4J版)ログ実装

次はPC用のログ実装について、SLF4J版を使ってこんな感じで。
内容的には、SLF4Jが他のログライブラリ(Log4j、commons-loggingとか)のブリッジでやっているのと同じような事ですだ(・ω・)

public class SLF4JLogger implements Logger {

    private final org.slf4j.Logger logger;

    public SLF4JLogger(final org.slf4j.Logger logger) {
        this.logger = logger;
    }

    @Override
    public boolean isTraceEnabled() {
        return logger.isTraceEnabled();
    }
...
    @Override
    public boolean isErrorEnabled() {
        return logger.isErrorEnabled();
    }

    @Override
    public void trace(final String msg) {
        logger.trace(msg);
    }
...
    @Override
    public void error(final String msg, final Throwable t) {
        logger.error(msg, t);
    }
}
public class SLF4JLocationAwareLogger implements Logger {

    private static final String FQCN = SLF4JLocationAwareLogger.class.getName();

    private final LocationAwareLogger logger;

    public SLF4JLocationAwareLogger(final LocationAwareLogger logger) {
        this.logger = logger;
    }

    @Override
    public boolean isTraceEnabled() {
        return logger.isTraceEnabled();
    }
...
    @Override
    public boolean isErrorEnabled() {
        return logger.isErrorEnabled();
    }

    @Override
    public void trace(final String msg) {
        logger.log(null, FQCN, LocationAwareLogger.TRACE_INT, msg, null, null);
    }
...
    @Override
    public void error(final String msg, final Throwable t) {
        logger.log(null, FQCN, LocationAwareLogger.ERROR_INT, msg, null, t);
    }
}
public class SLF4JLoggerFactoryAdapter implements LoggerFactoryAdapter {

    @Override
    public Logger getLogger(final Class<?> clazz) {
        org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(clazz);
        if (logger instanceof LocationAwareLogger) {
            return new SLF4JLocationAwareLogger((LocationAwareLogger)logger);
        } else {
            return new SLF4JLogger(logger);
        }
    }
}

ビルド環境

上記のライブラリをビルドするプロジェクトではandroid.jarの参照が必要ですが、PC用の実行環境ではandroid.jarは不要です。
同様に、Android環境でもSLF4Jのjarは不要です。
LoggerFactoryはリフレクションでログファクトリーを作成することで、実行環境では不要なライブラリを参照しなくても済むようにしているという話です。


っで、これでPCでもAndroidでも同じ書き方で使えるログ処理が用意できたので、共有できる部分はこの方法を使って、テストもPCベースで走らせられるものはそうして楽ちんぽん(・∀・)、っと行きたいところです。

*1:みんなJenkinsでAndroidエミュレータプラグイン使って、複数バージョンのテストとかまでしているのかしら(・ω・)? 自分は遅いのが嫌なので、PCベースでテストできるものはPCベースで or 2.3エミュオンリーとかですが。

Entity Frameworkでもバルク更新したいよね(・∀・)

ORMも、Entityした方向に向かうとバルク更新とかを軽視しがちで(´д`;)
あげくの果ては、そういう用途にORMを使うなとか、そこだけ別の仕組みを使えとか、ORMを作っている人自らがそんな事を言い出したりもしますが(・∀・#)


でも、やっぱりORMの他の機能と違和感無い形でのバルク更新もしたいよね、っと思うのが人情というもの(・ω・)
自分もLINQ to SQLの時はこんな風に拡張メソッドを使って対処していたわけですが。
当然、Entity Frameworkでも同様の事はやりたくなるわけで。
UserVoiceのEFに対するFeature SuggestionsでもBatch CUD supportの要望は2位だしね〜(・ω・)


っと思っていたら、EntityFramework.Extendedなるものがあるそうで(・∀・)

EntityFramework.Extended
http://weblogs.asp.net/pwelter34/archive/2011/11/29/entity-framework-batch-update-and-future-queries.aspx

NuGetからもインスコできるので、実際に試してみました。


試してみる

更新と削除を以下の様な感じで(・ω・)

// 更新
context.Employee.Update(t => t.Id == 4054, t => new Employee { Name = "うさ☆うさ" });

// 削除
context.Employee.Delete(t => t.Department.Name == "デ部");

ちなみに、SQLを覗いてみるとこんな感じのものが生成されていて。

UPDATE [Employee] SET 
Name = @p__update__0 
FROM [Employee] AS j0 INNER JOIN (
SELECT 
[Extent1].[Id] AS [Id]
FROM [dbo].[Employee] AS [Extent1]
WHERE 4054 = [Extent1].[Id]
) AS j1 ON (j0.Id = j1.Id)
DELETE [Employee]
FROM [Employee] AS j0 INNER JOIN (
SELECT 
[Extent1].[Id] AS [Id]
FROM  [dbo].[Employee] AS [Extent1]
INNER JOIN [dbo].[Department] AS [Extent2] ON [Extent1].[DepartmentId] = [Extent2].[Id]
WHERE N'デ部' = [Extent2].[Name]
) AS j1 ON (j0.Id = j1.Id)

ちゃんとバルク更新を確認できたのでしたヽ(・∀・)ノ

EntityFramework.Extendedのソース

EntityFramework.Extendedのソースは下記にあって。
https://github.com/loresoft/EntityFramework.Extended

バッチ更新は、EntityFramework.Extended/Extensions/BatchExtensions.csのあたりの処理がそれ。
DynamicQueryableを使ったりしながらSQLを加工し、ExecuteNonQuery()で処理実行、っという流れ(・ω・)


っというわけで、EntityFramework.ExtendedがあればEntity Frameworkを使うときの痛みも減るかも(・∀・)?


…っといいつつ、結局Micro ORMを使っているかもしれないけどね(・∀・;)
Micro ORMに関する考えも、Twitterの内容を日記にまとめておこうかな〜。

Smart Object Query(Java用LINQ to Objectsモドキ私家版)公開

昨年末の気分は既に長期休暇モードな時期に、手慰みで作っていたライブラリを公開してみました(・∀・)
https://github.com/usausa/Java-Smart-ObjectQuery

なんぞこれ(・ω・)?

.NETもJavaも仕事でやっている人間なら一度は実装を試みる、LINQ(to Objects)っぽい感じのコレクション操作ライブラリの私家版です。
実用性はおいといて、LINQ(to Objects)ってどういうものよ?、っというのをJavaなりの解釈で実装するとどうなるか、っということをやってみた感じです(・ω・) *1

そもそも

そもそもLINQ to Objectsって使うの(・ω・)?、っという話がありますが。
こういう意見が出る背景としては、

  • 実行効率を考えればクエリはエッジ(データストア、もっというとRDB内)でやるべきものじゃないの(・ω・)?

っというものがあると思います。
勿論、それは当然とした上で、

  • データストア上のデータ構造と、プレゼンテーション層用のデータ構造は一致しない

みたいな話もあるわけで。


例えばRDBのようなRowセットに対して、画面も単純な表形式であれば単純に処理を記述できますが。
オサレなデザインや分析系の画面などで、動的な縦横変換やデータ間の関係を考慮した描画が必要になったり、そこに権限とかその他要素諸々が絡んできたりすると、データストア上のデータ構造そのままを使って描画処理を記述しようとすると、処理の記述が煩雑になってきたりします(・ω・;)
そこで、データストア上のデータ構造をプレゼンテーション層用のデータ構造に変換して、描画処理の記述を簡潔にしようとしたりするわけで、自分は、この手の処理にLINQ(to Object)をよく使用します(・∀・)*2


あと、データストア上でのクエリをこねくり回して、プレゼンテーション層用のデータ構造でデータを取得することも出来るんじゃないの(・ω・)?、っという話もありますが。
それは責務が違うし、プレゼンテーションが複雑になればなるほど破綻するので、自分としては、

  • データストアからはデータストア上のデータ構造のまま、最適なプランでデータを取得する
  • アプリケーション内でデータストア上のデータ構造をプレゼンテーション層用のデータ構造に変換
  • プレゼンテーションではプレゼンテーション層用のデータ構造を用いて簡潔に処理を記述する

っというパターン押しです(`・ω・´)

本題/使い方

っで、Smart Object Queryについてですが、テストコードもちゃんと用意していないので、ここに少し使い方を書いておきます。

処理一覧
メソッド 分類 種別 Iterator
fromArray クエリ化 始点 (ArrayList)
from 始点 (元のIterator)
fromEnumeration 始点 EnumerationIterator
fromEmpty 始点 EmptyIterator
fromSingle 始点 SingleIterator
repeat 集合生成 始点 RepeatIterator
range 始点 RangeIterator
rownum インデックス付加 中途 IndexedIterator
defaultIfEmpty 空集合 中途 DefaultIfEmptyIterator
conact 集合演算 結合 ConactIterator
distinct 中途 DistinctIterator
union 結合 UnionIterator
expect 結合 ExpectIterator
intersect 結合 IntersectIterator
zip 平行アクセス 結合 ZipIterator
skip ページング 中途 SkipIterator
skipWhile 中途 SkipWhileIterator
take 中途 TakeIterator
takeWhile 中途 TakeWhileIterator
order ソート 再評価 (ArrayList)
reverse 再評価 (ArrayList)
all 限量詞演算 終端 -
any 終端 -
contains 終端 -
first 取得 終端 -
last 終端 -
elementAt 終端 -
count 集約演算 終端 -
aggregate 終端 -
sum 終端 -
max 終端 -
min 終端 -
average 終端 -
where 選択 中途 WhereIterator
groupBy グループ化 中途 GroupedIterator
select 射影 中途 SelectIterator
selectMany 中途 SelectManyIterator
join 結合 結合 JoinIterator
groupJoin 結合 GroupJoinIterator
toList 変換 終端 -
toSet 終端 -
toMap 終端 -
toLookup 終端 -
each 評価 副作用 -

メソッド」はObjectQueryクラスのメソッドです。
引数のSelector等の違いによる、複数定義があるものもあります。


「分類」はざっくりとした分類です(・∀・;)


「種別」は、そのメソッドIteratorのチェインの始点/途中/終端のどれかについての分類です。


Iterator」はその処理を実装するIteratorです。
(ArrayList)のような記述は、そのクラスを利用して処理を実装しているものです。

基本

基本ということで、Listに入った0〜9の数値のうち、偶数だけを取得するような処理の記述

List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 10; i++) {
    list.add(i);
}

for (Integer value : ObjectQuery.from(list).where(new Predicate<Integer>() {
    @Override
    public boolean test(final Integer param) {
        return param % 2 == 0;
    }})) {
    System.out.println(value);
}
[実行結果]
0
2
4
6
8

ラムダが無いとPredicateの記述が冗長ですよね。
っということで、よく使用する関数やセレクタは、専用のクラスへ定義を集約したり、Beanのstaticメンバなんかに定義しておくのもありだと思います。

public class Functions {

    public static final Predicate<Integer> EVEN = new Predicate<Integer>() {
        @Override
        public boolean test(final Integer param) {
            return param % 2 == 0;
        }
    };
}

を用意しておいて、

for (Integer value : ObjectQuery.from(list).where(Functions.EVEN)) {
    System.out.println(value);
}

とか。

ページング

次のサンプルはページングということで、まずデータ用のクラスとして下記のようなものを用意して。

public class Data {

    private int id;

    private String data;

    public int getId() {
        return id;
    }

    public String getData() {
        return data;
    }

    public Data(final int id, final String data) {
        this.id = id;
        this.data = data;
    }

    @Override
    public String toString() {
        return "{ id = [" + id + "], data = [" + data + "] }";
    }
}

ついでにデータの作成もObjectQueryを使ってみたりして。

Iterable<Data> datas = ObjectQuery.range(1, 10).select(new Func1<Integer, Data>() {
    @Override
    public Data eval(final Integer id) {
        return new Data(id, "Data-" + id);
    }
});

for (Data data : datas) {
    System.out.println(data);
}
[実行結果]
{ id = [1], data = [Data-1] }
{ id = [2], data = [Data-2] }
{ id = [3], data = [Data-3] }
{ id = [4], data = [Data-4] }
{ id = [5], data = [Data-5] }
{ id = [6], data = [Data-6] }
{ id = [7], data = [Data-7] }
{ id = [8], data = [Data-8] }
{ id = [9], data = [Data-9] }
{ id = [10], data = [Data-10] }

あと、表示についてはeach()メソッドを使用してこんな風にも書けたりとか。

ObjectQuery.from(datas).each(new Action<Data>() {
    @Override
    public void run(final Data data) {
        System.out.println(data);
    }
});

っで、本題のページングですが、

ObjectQuery
    .from(datas)                            // datasから
    .order(new Func1<Data, Integer>() {     // id逆順でソートして
        @Override
        public Integer eval(final Data data) {
            return - data.getId();
        }
    })
    .skip(5)                               // 5件飛ばしから
    .take(2)                               // 2件取得したものを
    .each(new Action<Data>() {             // 表示
        @Override
        public void run(final Data data) {
            System.out.println(data);
        }
    });
[実行結果]
{ id = [5], data = [Data-5] }
{ id = [4], data = [Data-4] }

みたいな感じで。

取得

条件にマッチする最初のデータを取得。

Data data = ObjectQuery.from(datas).first(new Predicate<Data>() {
    @Override
    public boolean test(final Data data) {
        return data.getId() % 2 == 1;
    }
});
System.out.println(data);
[実行結果]
{ id = [1], data = [Data-1] }

条件にマッチする最後のデータを取得。

Data data = ObjectQuery.from(datas).last(new Predicate<Data>() {
    @Override
    public boolean test(final Data data) {
        return data.getId() % 2 == 1;
    }
});
System.out.println(data);
[実行結果]
{ id = [9], data = [Data-9] }

first()、last()メソッドは、本家におけるFirstOrDefault()、LastOrDefault()に対応するもので、データが無い時に例外を投げるバージョンは用意していません。
なぜなら、自分がそのパターンをあまり使わないから(・∀・;)
関連して、single()も用意していなかったり。

判定処理

コレクション中の値について、全ての値が条件を満たすかチェックするall()と、条件を満たすものが一つでもあるかチェックするany()について。
便宜上下記のようなメソッドを用意しておいて。

public static class Functions {

    public static final Predicate<Integer> lessThan(final int compare) {
        return new Predicate<Integer>() {
            @Override
            public boolean test(final Integer value) {
                return value < compare;
            }
        };
    }

    public static final Predicate<Integer> greaterThan(final int compare) {
        return new Predicate<Integer>() {
            @Override
            public boolean test(final Integer value) {
                return value < compare;
            }
        };
    }
}

要素全てが条件を満たす判定処理all()と、要素の何れかが条件を満たす判定処理any()の使用方法は下記のような感じで。

// 1..10は全て11未満
assertTrue(ObjectQuery.range(1, 10).all(Functions.lessThan(11)));
// 1..10は全て10未満では無い
assertFalse(ObjectQuery.range(1, 10).all(Functions.lessThan(10)));

// 1..10は何れかが9より大きい
assertTrue(ObjectQuery.range(1, 10).any(Functions.greaterThan(9)));
// 1..10は何れかが10より大きくない
assertFalse(ObjectQuery.range(1, 10).any(Functions.greaterThan(10)));
集計関数

件数を取得するcount()、最大値max()、最小値min()、平均値average()、汎用/内部的に使用しているaggregate()とか。
Javaの都合上、戻り値の型毎にmaxLong()、maxDouble()等のバリエーションを用意(・ω・;)


っで、使い方の前に、Selectorの関数を以下のように用意しておいて。

public class Data {

    private int id;
...
    public static Func1<Data, Integer> ID_SELECTOR = new Func1<Data, Integer>() {
        @Override
        public Integer eval(final Data data) {
            return data.getId();
        }
    };
}

以下、使用方法。

int count = ObjectQuery.from(datas).count(new Predicate<Data>() {
    @Override
    public boolean test(final Data data) {
        return data.getId() > 5;
    }
});
System.out.println(count);
int max = ObjectQuery.from(datas).max(Data.ID_SELECTOR);
System.out.println(max);
int min = ObjectQuery.from(datas).min(Data.ID_SELECTOR);
System.out.println(min);
[実行結果]
5
10
1

なお、int配列に対する処理とかは、ObjectQueryではなくIntegersクラスとかに実装するような方針なのです(・ω・)

集合演算

集合演算(和、積、差)について。
使用するデータは下記だとして。

List<Integer> list = Arrays.asList(1, 3, 2, 3);

List<Integer> list1 = Arrays.asList(1, 3, 2);
List<Integer> list2 = Arrays.asList(3, 4);

重複削除。

for (Integer value : ObjectQuery.from(list).distinct()) {
    System.out.println(value);
}
[実行結果]
1
3
2

和。

for (Integer value : ObjectQuery.from(list1).union(list2)) {
    System.out.println(value);
}
[実行結果]
1
3
2
4

積。

for (Integer value : ObjectQuery.from(list1).intersect(list2)) {
    System.out.println(value);
}
[実行結果]
3

差。

for (Integer value : ObjectQuery.from(list1).expect(list2)) {
    System.out.println(value);
}
[実行結果]
1
2
結合

次は結合処理について(・ω・)
まず、以下のようなデータクラス(トランザクション系およびマスタ系だと思いねえ)を用意。

public class TxData {

    private String id;

    private int masterId;

    public String getId() {
        return id;
    }

    public int getMasterId() {
        return masterId;
    }

    public TxData(String id, int masterId) {
        this.id = id;
        this.masterId = masterId;
    }

    public static final Func1<TxData, Integer> MASTER_ID_SELECTOR = new Func1<TxData, Integer>() {
        @Override
        public Integer eval(final TxData data) {
            return data.getMasterId();
        }
    };
}
public class MasterData {

    private int id;

    private String name;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public MasterData(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public static final Func1<MasterData, Integer> ID_SELECTOR = new Func1<MasterData, Integer>() {
        @Override
        public Integer eval(final MasterData data) {
            return data.getId();
        }
    };
}

あと、ヘルパーとして下記の様なTuple作成処理も用意しておいて。

public static class Functions {

    public static <T1, T2> Func2<T1, T2, Tuple2<T1, T2>> makeTuple() {
        return new Func2<T1, T2, Tuple2<T1,T2>>() {
            @Override
            public Tuple2<T1, T2> eval(final T1 value1, final T2 value2) {
                return Tuple2.pair(value1, value2);
            }
        };
    }
}

っで、テストデータとしては以下のようなものを用意するとして。

List<TxData> txs = new ArrayList<TxData>();
txs.add(new TxData("A", 1));
txs.add(new TxData("B", 2));
txs.add(new TxData("C", 2));
txs.add(new TxData("D", 4));
txs.add(new TxData("E", 4));
txs.add(new TxData("F", 4));

List<MasterData> masters = new ArrayList<MasterData>();
masters.add(new MasterData(1, "Data-1"));
masters.add(new MasterData(2, "Data-2"));
masters.add(new MasterData(3, "Data-3"));
masters.add(new MasterData(4, "Data-4"));
masters.add(new MasterData(5, "Data-5"));

まずjoin()。

for (Tuple2<TxData, MasterData> tuple : ObjectQuery.from(txs).join(masters, TxData.MASTER_ID_SELECTOR, MasterData.ID_SELECTOR, Functions.<TxData, MasterData>makeTuple())) {
    System.out.println(tuple.getValue1().getId() + ":" + tuple.getValue2().getName() );
}
[実行結果]
A:Data-1
B:Data-2
C:Data-2
D:Data-4
E:Data-4
F:Data-4

次にgroupJoin()。

for (Tuple2<MasterData, Iterable<TxData>> tuple : ObjectQuery.from(masters).groupJoin(txs, MasterData.ID_SELECTOR, TxData.MASTER_ID_SELECTOR, Functions.<MasterData, Iterable<TxData>>makeTuple())) {
    System.out.println(tuple.getValue1().getId() + ":" + ObjectQuery.from(tuple.getValue2()).count());
}
[実行結果]
1:1
2:2
3:0
4:3
5:0
変換

っで、最後はデータストア上のデータ構造とプレゼンテーション層用のデータ構造の変換するようなパターンについて。
ここではLookup(Java版はLinkedHashMapを使った簡易実装)とObjectQuery独自のrownum()を使用してchop()処理を作成するサンプル。
chopっていうのは、Listを指定件数ごとに分割したList>みたいな形に変換する処理のこと。
rownum()は、本家LINQにおいて、いくつかのメソッドでFuncを引数にした定義があるものの代わりです。
データ構造Tを、Tと連番をタプルにしたデータ構造Indexedに変換する処理(Iterator)ですだ(・ω・)


まず、chop()の実装は以下のような感じで。

public static <T> Lookup<Integer, T> chop(final Iterable<T> collection, final int size) {
    return ObjectQuery.from(collection).rownum().toLookup(new Func1<Indexed<T>, Integer>() {
        @Override
        public Integer eval(final Indexed<T> param) {
            return param.getIndex() / size;
        }
    }, new Func1<Indexed<T>, T>() {
        @Override
        public T eval(final Indexed<T> param) {
            return param.getElement();
        }
    });
}

これを使って、12件のデータを5件毎に分割する処理を記述すると、以下のような感じになります。

Iterable<String> list = ObjectQuery.range(1, 12).select(new Func1<Integer, String>() {
    @Override
    public String eval(final Integer value) {
        return "Data-" + value;
    }
});

for (Grouping<Integer, String> grouping : chop(list, 5)) {
    System.out.println(grouping.getKey());
    for (String data : grouping) {
        System.out.println("  " + data);
    }
}
[実行結果]
0
  Data-1
  Data-2
  Data-3
  Data-4
  Data-5
1
  Data-6
  Data-7
  Data-8
  Data-9
  Data-10
2
  Data-11
  Data-12

RDBから取得したRowセットのデータ構造を、この種の変換でプレゼンテーション用の構造に変換すれば、jspでの記述は単純になりますよね、っという話ですだ(`・ω・´)

中身についても少し、Iterator!、Iterator!!、Iterator!!!

ObjectQueryは、LINQ(to Objects)モドキのライブラリの中でも、シンプルでベタな実装だと思います(・∀・;)
っというか、本家LINQ(to Objects)が何者か*3について、Javaプログラマにも理解してもらうことを意識して実装したものです。


ただ、Javaの言語仕様的に苦しい点もいくつかあるわけですが(・ω・;)

拡張メソッドが無い点

本家LINQ(to Objects)はIEnumerableに対する拡張メソッドとして実装されているわけで、拡張メソッドがあれば、staticなメソッドを用意するだけでFluentな記述ができますが。
LINQモドキの中には、Fluentな記述ではなくstatic importを使用して下記のような記述にしているものもあったりして。

select(where(list, predicate), selector);

一方のObjectQueryでは、IterableをラッピングしたしたObjectQueryインスタンスに変換することで、メソッドチェーンを使用した記述ができるようにしています。
そして、ObjectQuery自体がIterableをimplementsすることで、そのまま拡張forループに渡せるようにしています。
まあ、この辺は趣味の問題かしら(・ω・)?

yieldが無い点、currentとnextの違い

各種Iteratorについては、yieldが無いので状態を保存するためのメンバを用意したりとわかりにくい実装になっているものもあります。


そして以外と厄介なのが、本家におけるIEnumeratorがCurrentを処理するものなのに対して、JavaIteratorはnextを処理するものだという点。
どこら辺が厄介かと言えば、Iteratorのチェインに対して単純に次のIteratorのnextを処理すれば良いというわけではなく、Predicateでの判定結果も考慮しないといけないという所。
その為、一部IteratorのhasNext()ではプリフェッチに関連してループがより分かりにくいものになっていたり、hasNext()の複数回呼び出しを考慮して次の値をキャッシュするようになっていたりします。
この辺、自分がよく考えないで実装しているだけで、もっと綺麗に書けるかも(´・ω・`)

ラムダが無い点、Genericsの扱い、functorについて

ラムダが無い点について、利用者から見た場合のfunctorを匿名クラスで記述するので冗長になる点については、サンプルにあるようなFunctionsクラスを用意したり、selectorをstaticメンバとして保持することで若干の緩和は出来ますが(・∀・;)


ObjectQuery実装内の話については、Genericsの扱いの違いから、functorクラスの使用方法が本家とは違う考えになっているものがあります。
例えば、本家ではPredicateではなくFuncな点については、JavaだとBooleanにしかできないので、nullが返されないようにFuncではなく別途Predicateを用意していたりとか(・ω・)
Funcを引数に取るメソッドの代わりにrownum()を用意しているのも似たような理由だったりして。



っということで、自作ライブラリ(実用というより学習用)の紹介日記でした(・ω・)

*1:ちゃんとしたのをお探しなら、Quaereとかlambdajとかをドゾ(・∀・)

*2:逆に、それ以外の用途ではあまり使用しないかも(・ω・)?

*3:Iterator!、Iterator!!、Iterator!!!のチェーン(・∀・)

SM取ったど〜(・ω・)

女の子の泣き顔とかすごく可愛くて、それだけで○◇×が△っちゃう自分はどちらかというとSだと思います(`・ω・´)
…っとお約束のボケをかましつつ、恒例の情報処理技術者試験の取ったどシリーズ、ITサービスマネージャ(SM)編を書こうと思ったわけですが。


ただ、基本開発サイドの人としては、サービスマネージャとか、正直あまり思い入れが無い領域だったりして(・∀・;) *1


まあ、DevOpsの考え方とか好きだし、今時のWebサービスを作るときとか、運用システムひっくるめて設計なりアーキテクチャを決定なりする必要はあるわけで(`・ω・´) *2
こういうこととか、データセンター向けの問題管理システムなんかも作ったりしているので、ITILとかまったく知識が無いわけでもなかったりもして。*3


あとは、運用関連というと、最近特に必要だと感じるのは、オンプレミスだけでなく、AWSとかクラウドの使用を含めての運用管理とか。
もはや、ITサービス運用者にとってはクラウドの知識は必須の時代だと思うわけですよ(`・ω・´)


っというわけで、あまりサービスマネージャに絡むネタも思いつかないので、今回はこんなもんで(・ω・)

*1:もはや単なる報奨金ゲッターになってるし。勝率7割なので分の良いお小遣い稼ぎ(・∀・)

*2:っというか、本来どんなシステムでも運用までひっくるめて設計すべきだけど、規模が小さいシステムだとサボりがちになるだけよね。後は、システムを発注する場合、機能要件として明確に盛り込んでおかなければ、運用の考慮とか、確実に手を抜かれるであろう部分であったりもして(・ω・;)

*3:世の中には業種特化型のグループウエアとかもあるけど、これは業務特化型のBTSみたいなものだったり。監視システムのアラートから自動でインシデントを登録する機能があるとか、そういう部分が固有なだけで。…っというか、そういう設計にしてみた。

最近Jenkinsネタも流行っているし、静的テストの設定を晒してみたり

Jenkins本が発売されたり、記事が出たりして、盛り上がっている今日この頃ですが(・∀・)

Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)

Jenkins実践入門 ?ビルド・テスト・デプロイを自動化する技術 (WEB+DB PRESS plus)

それらの解説の中では、CheckStyleFindBugsによる静的テスト、Coberturaによるカバレージ取得あたりが取り上げられていて、Javaの開発環境ではこのあたりの仕様が定番というところでしょうか(・ω・)?
かくいううちでもCheckStyleFindBugs、Coberturaは使っていて、加えるならPMDの併用とCPDでの重複コードチェックもやっているっという感じです。


っで、CheckStyleなんかのルール設定に関して、他の人達はどんな風にしているのかしら(・ω・)?、っというのがちょっと気になったので、まずは自分の所の設定を晒してみます。
ルール設定を下記に示しますが、多少ひねた言い方をすれば、「SI屋が」「土方を使って」「業務システムを作る」際のルールなので、設定自体は甘めになっています(その代わり警告0にすることは必須)
これらのルールを使ったJenkinsさんによるチェックと、人の目による責務の正しさのチェックとかが、自分んとこのコードの品質確認のルールです(`・ω・´)

CheckStyle

CheckStyleについてはこんな感じ(・ω・)
一部の数値が非常に甘い(・∀・;)ですが、一部Utilでのべた書きとかも許容するためのもので、実際にControllerやServiceのメソッドが160行もあったら、それは人の目によるチェックの方で指摘が行くことになります(´д`;)
あと、ParameterNumberの数値が甘いのは、一部Facadeを考慮してとか。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
  <property name="severity" value="warning"/>
  <property name="charset" value="UTF-8"/>
  <module name="TreeWalker">
    <property name="severity" value="warning"/>
    <property name="tabWidth" value="4"/>
    <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
    <module name="JavadocMethod">
      <property name="severity" value="ignore"/>
      <property name="suppressLoadErrors" value="true"/>
      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
    </module>
    <module name="JavadocType">
      <property name="severity" value="ignore"/>
      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
    </module>
    <module name="JavadocVariable">
      <property name="severity" value="ignore"/>
      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
    </module>
    <module name="JavadocStyle">
      <property name="severity" value="ignore"/>
      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
    </module>
    <module name="ConstantName">
      <property name="applyToPrivate" value="false"/>
    </module>
    <module name="LocalFinalVariableName"/>
    <module name="LocalVariableName"/>
    <module name="MemberName"/>
    <module name="MethodName"/>
    <module name="PackageName"/>
    <module name="ParameterName"/>
    <module name="StaticVariableName"/>
    <module name="TypeName"/>
    <module name="AvoidStarImport"/>
    <module name="IllegalImport"/>
    <module name="RedundantImport"/>
    <module name="UnusedImports"/>
    <module name="MethodLength"/>
    <module name="ParameterNumber">
      <property name="max" value="8"/>
    </module>
    <module name="LineLength">
      <property name="max" value="160"/>
      <property name="tabWidth" value="4"/>
    </module>
    <module name="EmptyForIteratorPad"/>
    <module name="MethodParamPad"/>
    <module name="NoWhitespaceAfter"/>
    <module name="NoWhitespaceBefore"/>
    <module name="OperatorWrap">
      <property name="tokens" value="BAND,BOR,BSR,BXOR,COLON,DIV,EQUAL,GE,GT,LE,LITERAL_INSTANCEOF,LT,MINUS,MOD,NOT_EQUAL,PLUS,QUESTION,SL,SR,STAR"/>
    </module>
    <module name="ParenPad"/>
    <module name="TypecastParenPad"/>
    <module name="WhitespaceAfter">
      <property name="tokens" value="COMMA,SEMI"/>
    </module>
    <module name="WhitespaceAround">
      <property name="tokens" value="ASSIGN,BAND,BAND_ASSIGN,BOR,BOR_ASSIGN,BSR,BSR_ASSIGN,BXOR,BXOR_ASSIGN,COLON,DIV,DIV_ASSIGN,EQUAL,GE,GT,LAND,LCURLY,LE,LITERAL_ASSERT,LITERAL_CATCH,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_RETURN,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,LOR,LT,MINUS,MINUS_ASSIGN,MOD,MOD_ASSIGN,NOT_EQUAL,PLUS,PLUS_ASSIGN,QUESTION,SL,SLIST,SL_ASSIGN,SR,SR_ASSIGN,STAR,STAR_ASSIGN,LITERAL_ASSERT,TYPE_EXTENSION_AND,WILDCARD_TYPE"/>
      <property name="allowEmptyConstructors" value="true"/>
      <property name="allowEmptyMethods" value="true"/>
    </module>
    <module name="ModifierOrder"/>
    <module name="RedundantModifier"/>
    <module name="AvoidNestedBlocks"/>
    <module name="EmptyBlock"/>
    <module name="LeftCurly"/>
    <module name="NeedBraces"/>
    <module name="RightCurly"/>
    <module name="AvoidInlineConditionals">
      <property name="severity" value="ignore"/>
      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
    </module>
    <module name="DoubleCheckedLocking"/>
    <module name="EmptyStatement"/>
    <module name="EqualsHashCode"/>
    <module name="HiddenField">
      <property name="tokens" value="VARIABLE_DEF"/>
    </module>
    <module name="IllegalInstantiation"/>
    <module name="InnerAssignment">
      <property name="tokens" value="BAND_ASSIGN,BOR_ASSIGN,BSR_ASSIGN,BXOR_ASSIGN,DIV_ASSIGN,MINUS_ASSIGN,MOD_ASSIGN,PLUS_ASSIGN,SL_ASSIGN,SR_ASSIGN,STAR_ASSIGN"/>
    </module>
    <module name="MagicNumber">
      <property name="severity" value="ignore"/>
      <property name="ignoreNumbers" value="-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 16, 24, 32, 64, 100, 128, 200, 256, 1024, 2048, 4096, 99999, 999999, 99999999"/>
      <property name="ignoreHashCodeMethod" value="true"/>
      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
    </module>
    <module name="MissingSwitchDefault"/>
    <module name="RedundantThrows">
      <property name="allowUnchecked" value="true"/>
      <property name="allowSubclasses" value="true"/>
      <property name="suppressLoadErrors" value="true"/>
    </module>
    <module name="SimplifyBooleanExpression"/>
    <module name="SimplifyBooleanReturn"/>
    <module name="DesignForExtension">
      <property name="severity" value="ignore"/>
      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
    </module>
    <module name="FinalClass"/>
    <module name="HideUtilityClassConstructor"/>
    <module name="InterfaceIsType"/>
    <module name="VisibilityModifier"/>
    <module name="ArrayTypeStyle"/>
    <module name="FinalParameters"/>
    <module name="TodoComment">
      <property name="severity" value="ignore"/>
      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
    </module>
    <module name="UpperEll"/>
    <module name="MultipleStringLiterals">
      <property name="allowedDuplicates" value="8"/>
      <property name="ignoreStringsRegexp" value="^&quot;unchecked&quot;$"/>
    </module>
    <module name="MultipleVariableDeclarations">
      <property name="severity" value="ignore"/>
      <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="info"/>
    </module>
    <module name="NestedIfDepth">
      <property name="max" value="3"/>
    </module>
    <module name="NestedTryDepth">
      <property name="max" value="2"/>
    </module>
    <module name="PackageDeclaration"/>
    <module name="ParameterAssignment">
      <property name="severity" value="info"/>
    </module>
    <module name="ReturnCount">
      <property name="max" value="8"/>
    </module>
  </module>
  <module name="JavadocPackage">
    <property name="severity" value="ignore"/>
    <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
  </module>
  <module name="NewlineAtEndOfFile"/>
  <module name="Translation"/>
  <!-- <module name="FileLength"/> -->
  <module name="FileTabCharacter"/>
  <module name="RegexpSingleline">
    <property name="format" value="\s+$"/>
    <property name="message" value="Line has trailing spaces."/>
  </module>
</module>

FindBugs

FindBugsについては除外ルールも下記のような感じで。
要は、入れ物系のクラスで、Dateとかのgetterを許可するための除外ルールです(手抜き)。

<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
  <Match>
    <Class name="~.*ViewModel$" />
    <Bug code="EI2,EI"/>
  </Match>
...
</FindBugsFilter>

PMD

PMDの設定はこんな感じ。
CheckStyleFindBugsとかぶるものもありますが、うちでは併用(・ω・)

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ruleset xmlns="http://pmd.sf.net/ruleset/1.0.0" name="ruleset" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd">
   <description>PMD Plugin preferences rule set</description>
   <rule ref="rulesets/typeresolution.xml/LooseCoupling"/>
   <rule ref="rulesets/typeresolution.xml/CloneMethodMustImplementCloneable"/>
   <rule ref="rulesets/typeresolution.xml/UnusedImports"/>
   <!-- <rule ref="rulesets/typeresolution.xml/SignatureDeclareThrowsException"/> -->
   <rule ref="rulesets/braces.xml/IfStmtsMustUseBraces"/>
   <rule ref="rulesets/braces.xml/WhileLoopsMustUseBraces"/>
   <rule ref="rulesets/braces.xml/IfElseStmtsMustUseBraces"/>
   <rule ref="rulesets/braces.xml/ForLoopsMustUseBraces"/>
   <rule ref="rulesets/design.xml/UseSingleton"/>
   <rule ref="rulesets/design.xml/SimplifyBooleanReturns"/>
   <rule ref="rulesets/design.xml/SimplifyBooleanExpressions"/>
   <rule ref="rulesets/design.xml/SwitchStmtsShouldHaveDefault"/>
   <rule ref="rulesets/design.xml/AvoidDeeplyNestedIfStmts"/>
   <rule ref="rulesets/design.xml/AvoidReassigningParameters"/>
   <rule ref="rulesets/design.xml/SwitchDensity"/>
   <rule ref="rulesets/design.xml/ConstructorCallsOverridableMethod"/>
   <rule ref="rulesets/design.xml/AccessorClassGeneration"/>
   <rule ref="rulesets/design.xml/FinalFieldCouldBeStatic"/>
   <rule ref="rulesets/design.xml/CloseResource"/>
   <rule ref="rulesets/design.xml/NonStaticInitializer"/>
   <rule ref="rulesets/design.xml/DefaultLabelNotLastInSwitchStmt"/>
   <rule ref="rulesets/design.xml/NonCaseLabelInSwitchStatement"/>
   <rule ref="rulesets/design.xml/OptimizableToArrayCall"/>
   <rule ref="rulesets/design.xml/BadComparison"/>
   <rule ref="rulesets/design.xml/EqualsNull"/>
   <!-- <rule ref="rulesets/design.xml/ConfusingTernary"/> -->
   <rule ref="rulesets/design.xml/InstantiationToGetClass"/>
   <rule ref="rulesets/design.xml/IdempotentOperations"/>
   <!-- <rule ref="rulesets/design.xml/SimpleDateFormatNeedsLocale"/> -->
   <rule ref="rulesets/design.xml/ImmutableField"/>
   <!-- <rule ref="rulesets/design.xml/UseLocaleWithCaseConversions"/> -->
   <rule ref="rulesets/design.xml/AvoidProtectedFieldInFinalClass"/>
   <rule ref="rulesets/design.xml/AssignmentToNonFinalStatic"/>
   <rule ref="rulesets/design.xml/MissingStaticMethodInNonInstantiatableClass"/>
   <rule ref="rulesets/design.xml/AvoidSynchronizedAtMethodLevel"/>
   <rule ref="rulesets/design.xml/MissingBreakInSwitch"/>
   <rule ref="rulesets/design.xml/UseNotifyAllInsteadOfNotify"/>
   <rule ref="rulesets/design.xml/AvoidInstanceofChecksInCatchClause"/>
   <!-- <rule ref="rulesets/design.xml/AbstractClassWithoutAbstractMethod"/> -->
   <rule ref="rulesets/design.xml/SimplifyConditional"/>
   <rule ref="rulesets/design.xml/CompareObjectsWithEquals"/>
   <rule ref="rulesets/design.xml/PositionLiteralsFirstInComparisons"/>
   <rule ref="rulesets/design.xml/UnnecessaryLocalBeforeReturn"/>
   <rule ref="rulesets/design.xml/NonThreadSafeSingleton"/>
   <!-- <rule ref="rulesets/design.xml/UncommentedEmptyMethod"/> -->
   <rule ref="rulesets/design.xml/UncommentedEmptyConstructor"/>
   <rule ref="rulesets/design.xml/AvoidConstantsInterface"/>
   <rule ref="rulesets/design.xml/UnsynchronizedStaticDateFormatter"/>
   <rule ref="rulesets/design.xml/PreserveStackTrace"/>
   <rule ref="rulesets/design.xml/UseCollectionIsEmpty"/>
   <rule ref="rulesets/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal"/>
   <!-- <rule ref="rulesets/design.xml/EmptyMethodInAbstractClassShouldBeAbstract"/> -->
   <rule ref="rulesets/design.xml/SingularField"/>
   <rule ref="rulesets/design.xml/ReturnEmptyArrayRatherThanNull"/>
   <!-- <rule ref="rulesets/design.xml/AbstractClassWithoutAnyMethod"/> -->
   <rule ref="rulesets/design.xml/TooFewBranchesForASwitchStatement"/>
   <!-- <rule ref="rulesets/strictexception.xml/AvoidCatchingThrowable"/> -->
   <!-- <rule ref="rulesets/strictexception.xml/SignatureDeclareThrowsException"/> -->
   <rule ref="rulesets/strictexception.xml/ExceptionAsFlowControl"/>
   <rule ref="rulesets/strictexception.xml/AvoidCatchingNPE"/>
   <rule ref="rulesets/strictexception.xml/AvoidThrowingRawExceptionTypes"/>
   <rule ref="rulesets/strictexception.xml/AvoidThrowingNullPointerException"/>
   <rule ref="rulesets/strictexception.xml/AvoidRethrowingException"/>
   <rule ref="rulesets/strictexception.xml/DoNotExtendJavaLangError"/>
   <!-- <rule ref="rulesets/strictexception.xml/DoNotThrowExceptionInFinally"/> -->
   <rule ref="rulesets/strictexception.xml/AvoidThrowingNewInstanceOfSameException"/>
   <rule ref="rulesets/unusedcode.xml/UnusedPrivateField"/>
   <rule ref="rulesets/unusedcode.xml/UnusedLocalVariable"/>
   <rule ref="rulesets/unusedcode.xml/UnusedPrivateMethod"/>
   <rule ref="rulesets/unusedcode.xml/UnusedFormalParameter"/>
   <rule ref="rulesets/logging-java.xml/MoreThanOneLogger"/>
   <rule ref="rulesets/logging-java.xml/LoggerIsNotStaticFinal"/>
   <rule ref="rulesets/logging-java.xml/SystemPrintln"/>
   <rule ref="rulesets/logging-java.xml/AvoidPrintStackTrace"/>
   <!-- <rule ref="rulesets/strings.xml/AvoidDuplicateLiterals"/> -->
   <rule ref="rulesets/strings.xml/StringInstantiation"/>
   <rule ref="rulesets/strings.xml/StringToString"/>
   <rule ref="rulesets/strings.xml/InefficientStringBuffering"/>
   <rule ref="rulesets/strings.xml/UnnecessaryCaseChange"/>
   <rule ref="rulesets/strings.xml/UseStringBufferLength"/>
   <rule ref="rulesets/strings.xml/AppendCharacterWithChar"/>
   <rule ref="rulesets/strings.xml/ConsecutiveLiteralAppends"/>
   <rule ref="rulesets/strings.xml/UseIndexOfChar"/>
   <rule ref="rulesets/strings.xml/InefficientEmptyStringCheck"/>
   <rule ref="rulesets/strings.xml/InsufficientStringBufferDeclaration"/>
   <rule ref="rulesets/strings.xml/UselessStringValueOf"/>
   <rule ref="rulesets/strings.xml/StringBufferInstantiationWithChar"/>
   <rule ref="rulesets/strings.xml/UseEqualsToCompareStrings"/>
   <rule ref="rulesets/strings.xml/AvoidStringBufferField"/>
   <rule ref="rulesets/migrating.xml/ReplaceVectorWithList"/>
   <rule ref="rulesets/migrating.xml/ReplaceHashtableWithMap"/>
   <rule ref="rulesets/migrating.xml/ReplaceEnumerationWithIterator"/>
   <rule ref="rulesets/migrating.xml/AvoidEnumAsIdentifier"/>
   <rule ref="rulesets/migrating.xml/AvoidAssertAsIdentifier"/>
   <rule ref="rulesets/migrating.xml/IntegerInstantiation"/>
   <rule ref="rulesets/migrating.xml/ByteInstantiation"/>
   <rule ref="rulesets/migrating.xml/ShortInstantiation"/>
   <rule ref="rulesets/migrating.xml/LongInstantiation"/>
   <rule ref="rulesets/migrating.xml/JUnit4TestShouldUseBeforeAnnotation"/>
   <rule ref="rulesets/migrating.xml/JUnit4TestShouldUseAfterAnnotation"/>
   <!-- <rule ref="rulesets/migrating.xml/JUnit4TestShouldUseTestAnnotation"/> -->
   <rule ref="rulesets/migrating.xml/JUnit4SuitesShouldUseSuiteAnnotation"/>
   <rule ref="rulesets/migrating.xml/JUnitUseExpected"/>
   <rule ref="rulesets/j2ee.xml/UseProperClassLoader"/>
   <rule ref="rulesets/j2ee.xml/MDBAndSessionBeanNamingConvention"/>
   <rule ref="rulesets/j2ee.xml/RemoteSessionInterfaceNamingConvention"/>
   <rule ref="rulesets/j2ee.xml/LocalInterfaceSessionNamingConvention"/>
   <rule ref="rulesets/j2ee.xml/LocalHomeNamingConvention"/>
   <rule ref="rulesets/j2ee.xml/RemoteInterfaceNamingConvention"/>
   <rule ref="rulesets/j2ee.xml/DoNotCallSystemExit"/>
   <rule ref="rulesets/j2ee.xml/StaticEJBFieldShouldBeFinal"/>
   <!-- <rule ref="rulesets/j2ee.xml/DoNotUseThreads"/> -->
   <!-- <rule ref="rulesets/optimizations.xml/LocalVariableCouldBeFinal"/> -->
   <rule ref="rulesets/optimizations.xml/MethodArgumentCouldBeFinal"/>
   <!-- <rule ref="rulesets/optimizations.xml/AvoidInstantiatingObjectsInLoops"/> -->
   <rule ref="rulesets/optimizations.xml/UseArrayListInsteadOfVector"/>
   <rule ref="rulesets/optimizations.xml/SimplifyStartsWith"/>
   <rule ref="rulesets/optimizations.xml/UseStringBufferForStringAppends"/>
   <rule ref="rulesets/optimizations.xml/UseArraysAsList"/>
   <rule ref="rulesets/optimizations.xml/AvoidArrayLoops"/>
   <rule ref="rulesets/optimizations.xml/UnnecessaryWrapperObjectCreation"/>
   <rule ref="rulesets/optimizations.xml/AddEmptyString"/>
   <rule ref="rulesets/basic.xml/EmptyCatchBlock"/>
   <rule ref="rulesets/basic.xml/EmptyIfStmt"/>
   <rule ref="rulesets/basic.xml/EmptyWhileStmt"/>
   <rule ref="rulesets/basic.xml/EmptyTryBlock"/>
   <rule ref="rulesets/basic.xml/EmptyFinallyBlock"/>
   <rule ref="rulesets/basic.xml/EmptySwitchStatements"/>
   <rule ref="rulesets/basic.xml/JumbledIncrementer"/>
   <rule ref="rulesets/basic.xml/ForLoopShouldBeWhileLoop"/>
   <rule ref="rulesets/basic.xml/UnnecessaryConversionTemporary"/>
   <rule ref="rulesets/basic.xml/OverrideBothEqualsAndHashcode"/>
   <rule ref="rulesets/basic.xml/DoubleCheckedLocking"/>
   <rule ref="rulesets/basic.xml/ReturnFromFinallyBlock"/>
   <rule ref="rulesets/basic.xml/EmptySynchronizedBlock"/>
   <rule ref="rulesets/basic.xml/UnnecessaryReturn"/>
   <rule ref="rulesets/basic.xml/EmptyStaticInitializer"/>
   <rule ref="rulesets/basic.xml/UnconditionalIfStatement"/>
   <rule ref="rulesets/basic.xml/EmptyStatementNotInLoop"/>
   <rule ref="rulesets/basic.xml/BooleanInstantiation"/>
   <rule ref="rulesets/basic.xml/UnnecessaryFinalModifier"/>
   <rule ref="rulesets/basic.xml/CollapsibleIfStatements"/>
   <rule ref="rulesets/basic.xml/UselessOverridingMethod"/>
   <rule ref="rulesets/basic.xml/ClassCastExceptionWithToArray"/>
   <rule ref="rulesets/basic.xml/AvoidDecimalLiteralsInBigDecimalConstructor"/>
   <rule ref="rulesets/basic.xml/UselessOperationOnImmutable"/>
   <rule ref="rulesets/basic.xml/MisplacedNullCheck"/>
   <rule ref="rulesets/basic.xml/UnusedNullCheckInEquals"/>
   <rule ref="rulesets/basic.xml/AvoidThreadGroup"/>
   <rule ref="rulesets/basic.xml/BrokenNullCheck"/>
   <rule ref="rulesets/basic.xml/BigIntegerInstantiation"/>
   <rule ref="rulesets/basic.xml/AvoidUsingOctalValues"/>
   <rule ref="rulesets/basic.xml/AvoidUsingHardCodedIP"/>
   <rule ref="rulesets/basic.xml/CheckResultSet"/>
   <rule ref="rulesets/basic.xml/AvoidMultipleUnaryOperators"/>
   <rule ref="rulesets/basic.xml/EmptyInitializer"/>
   <!-- <rule ref="rulesets/sunsecure.xml/MethodReturnsInternalArray"/> -->
   <!-- <rule ref="rulesets/sunsecure.xml/ArrayIsStoredDirectly"/> -->
   <rule ref="rulesets/coupling.xml/CouplingBetweenObjects"/>
   <!-- <rule ref="rulesets/coupling.xml/ExcessiveImports"/> -->
   <rule ref="rulesets/coupling.xml/LooseCoupling"/>
   <rule ref="rulesets/imports.xml/DuplicateImports"/>
   <rule ref="rulesets/imports.xml/DontImportJavaLang"/>
   <rule ref="rulesets/imports.xml/UnusedImports"/>
   <rule ref="rulesets/imports.xml/ImportFromSamePackage"/>
   <!-- <rule ref="rulesets/imports.xml/TooManyStaticImports"/> -->
   <rule ref="rulesets/junit.xml/JUnitStaticSuite"/>
   <rule ref="rulesets/junit.xml/JUnitSpelling"/>
   <rule ref="rulesets/junit.xml/JUnitAssertionsShouldIncludeMessage"/>
   <rule ref="rulesets/junit.xml/JUnitTestsShouldIncludeAssert"/>
   <rule ref="rulesets/junit.xml/TestClassWithoutTestCases"/>
   <rule ref="rulesets/junit.xml/UnnecessaryBooleanAssertion"/>
   <rule ref="rulesets/junit.xml/UseAssertEqualsInsteadOfAssertTrue"/>
   <rule ref="rulesets/junit.xml/UseAssertSameInsteadOfAssertTrue"/>
   <rule ref="rulesets/junit.xml/UseAssertNullInsteadOfAssertTrue"/>
   <rule ref="rulesets/junit.xml/SimplifyBooleanAssertion"/>
   <rule ref="rulesets/controversial.xml/UnnecessaryConstructor"/>
   <!-- <rule ref="rulesets/controversial.xml/NullAssignment"/> -->
   <!-- <rule ref="rulesets/controversial.xml/OnlyOneReturn"/> -->
   <rule ref="rulesets/controversial.xml/UnusedModifier"/>
   <!-- <rule ref="rulesets/controversial.xml/AssignmentInOperand"/> -->
   <!-- <rule ref="rulesets/controversial.xml/AtLeastOneConstructor"/> -->
   <rule ref="rulesets/controversial.xml/DontImportSun"/>
   <rule ref="rulesets/controversial.xml/SuspiciousOctalEscape"/>
   <!-- <rule ref="rulesets/controversial.xml/CallSuperInConstructor"/> -->
   <rule ref="rulesets/controversial.xml/UnnecessaryParentheses"/>
   <rule ref="rulesets/controversial.xml/DefaultPackage"/>
   <rule ref="rulesets/controversial.xml/BooleanInversion"/>
   <!-- <rule ref="rulesets/controversial.xml/DataflowAnomalyAnalysis"/> -->
   <!-- <rule ref="rulesets/controversial.xml/AvoidFinalLocalVariable"/> -->
   <!-- <rule ref="rulesets/controversial.xml/AvoidUsingShortType"/> -->
   <rule ref="rulesets/controversial.xml/AvoidUsingVolatile"/>
   <rule ref="rulesets/controversial.xml/AvoidUsingNativeCode"/>
   <rule ref="rulesets/controversial.xml/AvoidAccessibilityAlteration"/>
   <rule ref="rulesets/controversial.xml/DoNotCallGarbageCollectionExplicitly"/>
   <!-- rule ref="rulesets/naming.xml/ShortVariable"/> -->
   <!-- <rule ref="rulesets/naming.xml/LongVariable"/> -->
   <!-- <rule ref="rulesets/naming.xml/ShortMethodName"/> -->
   <!-- <rule ref="rulesets/naming.xml/VariableNamingConventions"/> -->
   <rule ref="rulesets/naming.xml/MethodNamingConventions"/>
   <rule ref="rulesets/naming.xml/ClassNamingConventions"/>
   <!-- <rule ref="rulesets/naming.xml/AbstractNaming"/> -->
   <rule ref="rulesets/naming.xml/AvoidDollarSigns"/>
   <rule ref="rulesets/naming.xml/MethodWithSameNameAsEnclosingClass"/>
   <rule ref="rulesets/naming.xml/SuspiciousHashcodeMethodName"/>
   <rule ref="rulesets/naming.xml/SuspiciousConstantFieldName"/>
   <rule ref="rulesets/naming.xml/SuspiciousEqualsMethodName"/>
   <!-- <rule ref="rulesets/naming.xml/AvoidFieldNameMatchingTypeName"/> -->
   <!-- <rule ref="rulesets/naming.xml/AvoidFieldNameMatchingMethodName"/> -->
   <rule ref="rulesets/naming.xml/NoPackage"/>
   <rule ref="rulesets/naming.xml/PackageCase"/>
   <rule ref="rulesets/naming.xml/MisleadingVariableName"/>
   <rule ref="rulesets/naming.xml/BooleanGetMethodName"/>
   <!-- <rule ref="rulesets/codesize.xml/NPathComplexity"/> -->
   <!-- <rule ref="rulesets/codesize.xml/ExcessiveMethodLength"/> -->
   <rule ref="rulesets/codesize.xml/ExcessiveParameterList"/>
   <!-- <rule ref="rulesets/codesize.xml/ExcessiveClassLength"/> -->
   <rule ref="rulesets/codesize.xml/CyclomaticComplexity">
     <properties>
       <property name="reportLevel" value="15" />
     </properties>
   </rule>
   <!-- <rule ref="rulesets/codesize.xml/ExcessivePublicCount"/> -->
   <!-- <rule ref="rulesets/codesize.xml/TooManyFields"/> -->
   <rule ref="rulesets/codesize.xml/NcssMethodCount"/>
   <rule ref="rulesets/codesize.xml/NcssTypeCount"/>
   <rule ref="rulesets/codesize.xml/NcssConstructorCount"/>
   <!-- <rule ref="rulesets/codesize.xml/TooManyMethods"/> -->
   <rule ref="rulesets/finalizers.xml/EmptyFinalizer"/>
   <rule ref="rulesets/finalizers.xml/FinalizeOnlyCallsSuperFinalize"/>
   <rule ref="rulesets/finalizers.xml/FinalizeOverloaded"/>
   <rule ref="rulesets/finalizers.xml/FinalizeDoesNotCallSuperFinalize"/>
   <rule ref="rulesets/finalizers.xml/FinalizeShouldBeProtected"/>
   <rule ref="rulesets/finalizers.xml/AvoidCallingFinalize"/>
   <rule ref="rulesets/logging-jakarta-commons.xml/UseCorrectExceptionLogging"/>
   <rule ref="rulesets/logging-jakarta-commons.xml/ProperLogger"/>
   <!-- <rule ref="rulesets/javabeans.xml/BeanMembersShouldSerialize"/> -->
   <rule ref="rulesets/javabeans.xml/MissingSerialVersionUID"/>
   <rule ref="rulesets/clone.xml/ProperCloneImplementation"/>
   <rule ref="rulesets/clone.xml/CloneThrowsCloneNotSupportedException"/>
   <rule ref="rulesets/clone.xml/CloneMethodMustImplementCloneable"/>
</ruleset>

普段のチェックで指摘される項目で何が多いかと言えば、StringBuilderを使えやisEmpty()を使え系のうっかりミスを除けば、ParameterNumberやCyclomaticComplexityあたりの指摘でしょうか(・ω・)?
まあ、それらの大半は責務の誤りだったり冗長な記述だったりするので、人の目チェックで責務が正しくなるように指摘したり、処理を宣言的な記述になるように指摘すれば解決するといった感じです。


っということで、設定を晒してみたので突っ込み歓迎。
あと、JenkinsについてはJava以外の環境での話題なんかも欲しいですよね(・∀・)