Model.orElseGet(SerializableSupplier<? extends T> other) は使えるかも

Wicket で、IModel インスタンスを書く時 、コーディングの癖で、 new 演算子による
new Model<>() を書くことが多い。
Model.of があるのは知ってはいたけど、なぜかあまり書かなかった。

https://issues.apache.org/jira/browse/WICKET-6412

Model.orElse を書いてフォームの submit でも、結局は問題は起きるようで、
String の TextField を使うときに、使うのが良いかも。

それよりも、Java8 ラムダになって、Model クラスでも、

   IModel<T> orElseGet(SerializableSupplier<? extends T> other)

を書けることの方が魅力的です。

ストアドファンクション生成 Error Code: 1419

他人が構築したDBを引き継ぐのは、嫌なもので状況を把握しないまま、
MySQL ストアドファンクションを追加したら、

Error Code: 1419. You do not have the SUPER privilege and binary logging is
enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)

マニュアルには、こう書いてある。

「バイナリロギングを有効にしているサーバーに対するこの危険から保護するために、
ストアドファンクションの作成者は、必要な通常の CREATE ROUTINE 権限に加え、SUPER 権限も持つ必要があります。
同様に、ALTER FUNCTION を使用するには、ユーザーは ALTER ROUTINE 権限に加え、SUPER 権限を持つ必要があります。
SUPER 権限がないと、エラーが発生します。」


しかたないので、権限のある root で入り、log_bin_trust_function_creators を調べる。

mysql> SHOW VARIABLES LIKE 'log_bin_trust_function_creators';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| log_bin_trust_function_creators | OFF   |
+---------------------------------+-------+

なので、ONにするために、

mysql> set global log_bin_trust_function_creators=1;
Query OK, 0 rows affected (0.00 sec)

確認、

mysql> SHOW VARIABLES LIKE 'log_bin_trust_function_creators';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| log_bin_trust_function_creators | ON    |
+---------------------------------+-------+
1 row in set (0.00 sec)

これで、無事、ストアドファンクション生成できるようになる。

グルーピング&ソートの続き

先日のグルーピング&ソートに続いて、、、
グルーピングした時にソートもする。 - Oboe吹きプログラマの黙示録

同じく Foo なるクラスオブジェクトのリストで、グルーピングして並べた時に、先頭だけキーをそのままで
他をキーが表示されないように置き換えなどの細工をする。

key  :  value
-------------
  A  : 1
     : 2
     : 3
  B  : 11
     : 12
  C  : 31

のように表示できるようにリストを変更したいする。すぐに思いつくのが、groupingBy して作った Map の
entrySet() ストリームを流して、flatMap で、変更する方法、、

List<Foo> results = list.stream().collect(Collectors.groupingBy(t->t.key, Collectors.mapping(u->u, Collectors.toList())))
.entrySet().stream().flatMap(e->{
      AtomicInteger i = new AtomicInteger(0);
      return e.getValue().stream().sorted((o1, o2)->new Integer(o1.value).compareTo(new Integer(o2.value))).map(t->{
         if (i.getAndIncrement() > 0) t.key = " ";
         return t;
      });
   }
).collect(Collectors.toList());

でも、AtomicInteger生成文が入って気持ち悪い。
遅くて効率が悪いかもしれなけど、Stream.concat を使ってみる。

List<Foo> results = list.stream().collect(Collectors.groupingBy(t->t.key, Collectors.mapping(u->u, Collectors.toList())))
.entrySet().stream().flatMap(e->{
   List<Foo> _list = e.getValue().stream()
   .sorted((o1, o2)->new Integer(o1.value).compareTo(new Integer(o2.value))).collect(Collectors.toList());
   return Stream.concat(_list.stream().limit(1)
      , _list.stream().skip(1).map(t->{
         t.key = " ";
         return t;
   }));
}).collect(Collectors.toList());

キーそのものもソートまで実施するなら、こんなんじゃダメなので、

Map<String, List<Foo>> map =
list.stream().collect(Collectors.groupingBy(e->e.key, Collectors.mapping(t->t,
   Collectors.collectingAndThen(Collectors.toCollection(ArrayList::new)
      , t->{
          List<Foo> _list =
          t.stream().sorted((o1, o2)->new Integer(o1.value)
          .compareTo(new Integer(o2.value))).collect(Collectors.toList());
          return Stream.concat(_list.stream().limit(1), _list.stream().skip(1).map(u->{
                u.key = " ";
                return u;
        })).collect(Collectors.toList());
    })
)));

ソートもされるけど、何だか遅そう。。

グルーピングした時にソートもする。

グルーピングしながら、ソートした結果リストを求めることを
いざコーディングしようとすると、即時、思いつかないのが残念でメモ。

Collectors.collectingAndThen を使うのが重要

サンプル、以下、文字列の key と value があるクラスオブジェクト

public class Foo{
   public String key;
   public String value;
   public Foo(String key, String value){
      this.key = key;
      this.value = value;
   }
}

あえて、value には、数値の文字列が入る約束として、この value で、グルーピング結果のコレクションがソート
されるようにする。

List の Map を生成

Map<String, List<Foo>> map =
list.stream().collect(Collectors.groupingBy(e->e.key, Collectors.mapping(t->t,
   Collectors.collectingAndThen(Collectors.toCollection(ArrayList::new)
         , t->t.stream().sorted((o1, o2)->new Integer(o1.value).compareTo(new Integer(o2.value)))
               .collect(Collectors.toList())
   )
)));

Set の Map を生成、 toList() を toSet() に返るだけですが。

Map<String, Set<Foo>> map =
list.stream().collect(Collectors.groupingBy(e->e.key, Collectors.mapping(t->t,
   Collectors.collectingAndThen(Collectors.toCollection(ArrayList::new)
         , t->t.stream().sorted((o1, o2)->new Integer(o1.value).compareTo(new Integer(o2.value)))
               .collect(Collectors.toSet())
   )
)));

chromeブラウザ左下に出るURL表示を出ないようにする

方法があった。
stackoverflow で見つけました。

$(function(){
   $("body").on('mouseover', 'a', function(e){
      var $link = $(this),
      href = $link.attr('href') || $link.data("href");
      $link.off('click.chrome');
      $link.on('click.chrome', function(){
         /* window.location.href = href; */
         this.location.href = href;
      })
      .attr('data-href', href)
      .css({ cursor: 'pointer' })
      .removeAttr('href');
   });
});

注意しなければならないのは、必ず、<a> タグの href属性を書いておくこと。
mouseover のイベントで、href 属性を data属性である data-href に置き換えて書込み
ブラウザ左下に URLが出るのを抑止、
再びマウスが上にきてクリックされても、data-hrefの残されてた URLを
location.href へのセットでページ遷移させる方法だ。

stackoverflow.com

DropDownChoice の IChoiceRenderer

2年近く前、Wicket6 → 7 になった時、DropDownChoice の為の IChoiceRenderer が、
 public T getObject(String id, IModel> choices)
が増えて、choices のgetModelObject() で取ってくるものをこのメソッドで返さなくてはならなくなり、
当時、以下のように、optionタグの value属性値を返せるようにするインターフェースを用意して
更に、IChoiceRenderer継承と、DropDownChoice がそれを使うように継承したものを作った。。
IChoiceRenderer ラムダ化 - Oboe吹きプログラマの黙示録

でも、やはりDropDownChoice の継承と、選択要素にoptionタグの value属性値を返せるようにするインターフェース
を付けるなんて方法は、選択要素の構造に手を加えるので良くない。
かと言って、IChoiceRenderer の無名インナークラスのコーディングは、長くなって嫌だ。

option タグの value を返すもの。bodyを返すもの。選択されたものを判断して返すもの。
これらの処理関数を埋め込めるラムダ式になっていれば良い。

import java.util.List;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.model.IModel;
import org.danekja.java.util.function.serializable.SerializableBiPredicate;
import org.danekja.java.util.function.serializable.SerializableFunction;
/**
 * IChoiceRenderer を提供
 */
public class ChoiceRender{
   private ChoiceRender(){}

   public static <T> IChoiceRenderer<T> of(SerializableFunction<T, String> idfunction
         , SerializableFunction<T, String> displayfunction
         , SerializableBiPredicate<T, String> bipredicate
   ){
      return new IChoiceRenderer<T>(){
         @Override
         public Object getDisplayValue(T t){
            return displayfunction.apply(t);
         }
         @Override
         public String getIdValue(T t, int index){
            return idfunction.apply(t);
         }
         @Override
         public T getObject(String id, IModel<? extends List<? extends T>> choices){
            return choices.getObject().stream().filter(e->bipredicate.test(e, id)).findFirst().orElse(null);
         }
      };
   }
}


例)
以下のような対象クラスがあったとする。

public class Foo implements Serializable{
   public int val;
   public String name;
   public Foo(int val, String name){
      this.val = val;
      this.name = name;
   }
}
final DropDownChoice<Foo> selectFoo =
new DropDownChoice<Foo>("select1", new Model<>(), Arrays.asList(new Foo(1,"A"), new Foo(2,"B"), new Foo(3,"C") )
      , ChoiceRender.of(t->Integer.toString(t.val), t->t.name, (t,u)->Integer.toString(t.val).equals(u)) ) ;
queue(selectFoo);

そして、optionタグの value も body も同じものとして生成することも簡単に書ける。

例えば、"a","b","c"

final DropDownChoice<String> selectChar =
new DropDownChoice<String>("select2", new Model<>(), Arrays.asList("a","b","c")
   , ChoiceRender.of(t->t, t->t, (t,u)->t.equals(u)) ) ;
queue(selectChar);

フォーム送信されて受け取るところ。。

Optional.ofNullable(selectChar.getModelObject()).ifPresent(s->{
  // 
});