Wicket TextField と type="search"

昔、Wicket 1.5 の頃は、
<input type="search" に対して、TextField<String> の代わりに、TextArea<String> を使用することで対応できた。
今の Wickrt8 ではこれはダメだ。

TextField<String> で、<input type="search" は、
  must be applied to a tag with [type] attribute matching any of [text], not [search]
のエラーになる。

Chrome でも、IE や Edge のように、<input type="search" > を使用したい。
つまり、
フォーカスが当たって、
f:id:posturan:20190808213026j:plain
キー入力したら、、
f:id:posturan:20190808213051j:plain
と入力文字をクリアする「×」が出てくるいわゆる type="search" のフィールドにしたいのだ。

しかたなく、jQuery で対応する。

$("input[type='text']").focus(function(eo){
    $(this).prop('type','search');
}).blur(function(eo){
    $(this).prop('type','text');
});

これで無理やりフォーカスが当たったら、type="search" にして
フォーカスがはずれたら元に戻す。

<input wicket:id="item" id="item" type="text">

なら、

$("#item").focus(function(eo){
    $(this).prop('type','search');
}).blur(function(eo){
    $(this).prop('type','text');
});

そういえば昔、
oboe2uran.hatenablog.com

なんて書いていたな。。。。

依存のJARがどのJARファイルか調べる。

使用しているクラスやインターフェースがどの JARファイルを使用(依存)なのか、
きちんとバージョンなど把握したい場合が、開発作業中に時々ある。

Eclipse を使っているのであれば、次のように参照、見つけることができる。

対象のクラスやインターフェースをクリックしてフォーカスを充てる。
f:id:posturan:20190807200028j:plain

F3キー押下して、宣言を開く、
開いたら、アウトライン ビューを表示する。
アウトライン ビューで対象を右クリック→「表示」→パッケージエクスプローラをクリック

f:id:posturan:20190807200354j:plain

すると、パッケージエクスプローラで、JARファイルを参照することができる。
f:id:posturan:20190807200732j:plain

ATOM インストールしたら入れておきたいパッケージ

メモ:ATOM インストールしたら入れておきたいパッケージをリストアップ

https://atom.io/packages/japanese-menu

https://atom.io/packages/markdown-preview-enhanced

https://atom.io/packages/minimap-bookmarks

https://atom.io/packages/minimap-find-and-replace

https://atom.io/packages/plantuml-preview
https://atom.io/packages/plantuml-generator
https://atom.io/packages/language-plantuml


そして、markdown-preview の設定として、、
oboe2uran.hatenablog.com

oboe2uran.hatenablog.com

Stream で処理する正規表現

Java8 でも使えるように書いてみた。。

昨日の、 正規表現マッチのストリーム処理を考察 - Oboe吹きプログラマの黙示録
に続いて、思いついたので、まとめてみた。

import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.Spliterators.AbstractSpliterator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
 * RegExpress.java
 */
public final class RegExpress{
   private RegExpress(){
   }

   public static Stream<String> matchToStream(String regex, CharSequence input){
      Matcher m = Pattern.compile(regex).matcher(input);
      return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>(){
         @Override
         public boolean hasNext(){
            return m.find();
         }
         @Override
         public String next(){
            return m.group();
         }
      }, Spliterator.ORDERED), false);
   }

   public static Stream<MatchResult> findMatches(String regex, CharSequence input){
      Matcher matcher = Pattern.compile(regex).matcher(input);
      Spliterator<MatchResult> spliterator
= new AbstractSpliterator<MatchResult>(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.NONNULL){
         @Override
         public boolean tryAdvance(Consumer<? super MatchResult> action){
            if (!matcher.find())
               return false;
            action.accept(matcher.toMatchResult());
            return true;
         }
      };
      return StreamSupport.stream(spliterator, false);
   }

   public static void results(String regex, String input, Consumer<String> c){
      Matcher m = Pattern.compile(regex).matcher(input);
      while(m.find()){
         c.accept(m.group());
      }
   }

   public static void resultMatch(String regex, String input, Consumer<MatchResult> c){
      Matcher m = Pattern.compile(regex).matcher(input);
      while(m.find()){
         c.accept(m.toMatchResult());
      }
   }

   public static void resultMatch(String regex, String input, BiConsumer<MatchResult, Integer> c){
      Matcher m = Pattern.compile(regex).matcher(input);
      AtomicInteger i = new AtomicInteger(0);
      while(m.find()){
         c.accept(m.toMatchResult(), i.getAndIncrement());
      }
   }

   public static String replace(String regex, String string, BiFunction<String, Integer, String> f){
      Matcher m = Pattern.compile(regex).matcher(string);
      AtomicInteger i = new AtomicInteger(0);
      AtomicInteger x = new AtomicInteger(0);
      return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>(){
         @Override
         public boolean hasNext(){
            return m.find();
         }
         @Override
         public String next(){
            return string.substring(i.getAndSet(m.end()), m.start())
                    + f.apply(m.group(), x.getAndIncrement());
         }
      }, Spliterator.ORDERED), false).collect(Collectors.joining()) + string.substring(i.get());
   }
}

使用例

RegExpress.matchToStream("[0-9]+", string).forEach(e->System.out.println("["+e+"]"));

RegExpress.findMatches("[0-9]+", string)
.forEach(m->System.out.println("["+m.group()+"] start="+m.start()+" end="+m.end()));

RegExpress.resultMatch("[0-9]+", string, m->{
   System.out.println("["+m.group()+"] start="+m.start()+" end="+m.end());
});

RegExpress.resultMatch("[0-9]+", string, (m, i)->{
   System.out.println(i+":["+m.group()+"] start="+m.start()+" end="+m.end());
});

String res = RegExpress.replace("[0-9]+", string, (e, i)->"{"+i+":"+e+"}");

正規表現マッチのストリーム処理を考察

Java8 では、正規表現で分割 split したストリームは取得できるけど、
  java.util.regex.Pattern の splitAsStream(CharSequence input) ⇒ Stream<String>
マッチした文字列のストリームは標準では提供されていない。

自然に Matcher find() 実行ループをメソッドにすれば、

public static void results(String regex, String input, Consumer<String> c){
   Matcher m = Pattern.compile(regex).matcher(input);
   while(m.find()){
      c.accept(m.group());
   }
}

Consumer<String> にマッチした文字列を順番に処理するラムダは書ける。

Stream 生成するには、↑をリストに一旦格納するよりも、、
Java8 では、java.util.regex.Matcher から、spliterator を生成するなどの方法ぐらいしか
良い方法はない。

public static Stream<String> matchToStream(Pattern pattern, CharSequence target){
   Matcher m = pattern.matcher(target);
   return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator<String>(){
      @Override
      public boolean hasNext(){
         return m.find();
      }
      @Override
      public String next(){
         return m.group();
      }
   }, Spliterator.ORDERED), false);
}

Java9 以上なら、java.util.regex.Matcher に、
 String replaceAll(Function<MatchResult, String> replacer) や、
 Stream<MatchResult>を取得する results()
があって、
Function で置換する為のストリームがあって便利だが、
MatchResult で、start() end() group() は参照できても
何番目のマッチなのか?までは、AtomicInteger など外にカウンタを置かないと
認識できない。

Java8 でもStream<MatchResult>を取得するものを
yipuran-core の中に用意はした。。。
 org.yipuran.regex.MatcherStream#findMatches(Pattern pattern, CharSequence input)
けど、何番目のマッチなのか?までは、相変わらずこのままでは取得できない。
Stream<MatchResult>を取得するだけなのだから当然である。
Stream<MatchResult>から collect(Collector.toList()) でリストにすれば順番が確定はするが、
その後で、再び Stream の処理をするのはナンセンスである。

BiConsumer でカウンタ付きの Consumer

public void resultMatch(String regex, String target, BiConsumer<MatchResult, Integer> b){
   AtomicInteger i = new AtomicInteger(0);
   Pattern.compile(regex).matcher(target).results().forEach(m->b.accept(m, i.getAndIncrement()));
}

なんか、メソッドを作るほどの事でもない。。。

でも、何番目にマッチするかを認識しながら、置換を行う場合、、、

public static String replace(String regex, String string, BiFunction<String, Integer, String> f){
   Matcher m = Pattern.compile(regex).matcher(string);
   AtomicInteger i = new AtomicInteger(0);
   AtomicInteger x = new AtomicInteger(0);
   return StreamSupport.stream(   Spliterators.spliteratorUnknownSize(new Iterator<String>(){
      @Override
      public boolean hasNext(){
         return m.find();
      }
      @Override
      public String next(){
         return string.substring(i.getAndSet(m.end()), m.start()) + f.apply(m.group(), x.getAndIncrement());
      }
   }, Spliterator.ORDERED), false)
   .collect(Collectors.joining()) + string.substring(i.get());
}

あるいは、yipuran-core の MatcherStream.findMatches を使用して、、

public static String replace(String regex, String string, BiFunction<MatchResult, Integer, String> f) {
   AtomicInteger i = new AtomicInteger(0);
   AtomicInteger x = new AtomicInteger(0);
   return Stream.concat( MatcherStream.findMatches(Pattern.compile(regex), string)
   .collect(()->new ArrayList<String>(), (r, t)->{
      r.add( string.substring(i.getAndSet(t.end()), t.start()) + f.apply(t, x.getAndIncrement())  );
   },(r, u)->{}).stream(), Stream.of(string.substring(i.get())))
   .collect(Collectors.joining());
}

これだと、Stream.concat で Stream を2つ作って結合であまり良くないかも。

Python dictionary → Java Map

Python dictionary を 標準出力してJavaでMap として読み込む。

先日書いたものの勢いにまかせて、、、

Python 標準出力→Java受け取り - Oboe吹きプログラマの黙示録

Javaからプロセス起動で実行するPython と文字列の受け渡し - Oboe吹きプログラマの黙示録

では、リストではなく、Pythonの dictionary を 標準出力してJavaでMap として読み込む。というのも、
Base64 エンコードで受け渡せば安定して実行できる、

先日書いた標準 IOをまとめた Python コードに、辞書をBase64エンコードして標準出力するメソッドを
追加する。

util ディレクトリに置く stdio.py

# -*- coding: UTF-8 -*-
from base64 import b64encode
class StdIO:
    # 標準入力
    # escape=True  : inputした文字をUnicode文字列に変換してリストにする
    #                標準入力=Unicode入力を想定(Javaプロセス標準出力から受信する場合はTrueにする)
    # escape=False : inputした文字を変換なしでリストにする。
    #                標準入力=通常
    def input(self, escape=True):
        inlist = []
        try:
            if escape:
                while True:
                    inp = input('')
                    if inp == '': break
                    inlist.append(inp.encode().decode('unicode-escape'))
            else:
                while True:
                    inp = input('')
                    if inp == '': break
                    inlist.append(inp)
        except EOFError:
            pass
        return inlist
    # リストを標準出力
    #   escape=True  : リスト要素をunicode-escape にエンコードして出力
    #   code : 出力する文字コード、デフォルト 'utf-8'  ユニコード指定は、'unicode-escape'
    def printList(self, list, base64=False, code='utf-8'):
        outlist = []
        if base64==True:
            for e in list:
                data = e.encode(code)
                outlist.append(b64encode(data).decode())
        else:
            for e in list:
                data = e.encode(code)
                outlist.append(data)
        print(outlist)
    # 辞書をBase64エンコードして標準出力
    def printDictionary(self, dict):
        o = {}
        for k, v in dict.items():
            o[b64encode(("%s" % k).encode('utf-8')).decode()] = b64encode(("%s" % v).encode('utf-8')).decode()
        print(o)

Javaからプロセス起動する Python
testdictout.py

# -*- coding: UTF-8 -*-
from util.stdio import StdIO

dict = { 'a':'A', 'b':'B', 10:100 , "A":"あ" }

stdio = StdIO()
stdio.printDictionary(dict)

Java のコード

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import org.yipuran.function.ThrowableConsumer;
import org.yipuran.util.process.ScriptExecutor;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
try(ByteArrayOutputStream out = new ByteArrayOutputStream()){
   ScriptExecutor.runStream(()->"python script/testdictout.py"
   , ThrowableConsumer.of(i->i.transferTo(out))
   , (t, e)->{
      System.out.println("stderr : " + t );
      e.printStackTrace();
   });
   String response = out.toString();

   System.out.println(response);
   // 解析

   System.out.println("====== Google gson による解析 =========");

   Map<String, String> getmap = new GsonBuilder().serializeNulls()
   .create().fromJson(response,  new TypeToken<Map<String, String>>(){}.getType());
   
   Map<String, String> map = getmap.entrySet().stream()
   .collect(()->new HashMap<String, String>()
         , (r, t)->r.put(new String(Base64.getDecoder().decode(t.getKey()), StandardCharsets.UTF_8)
                        , new String(Base64.getDecoder().decode(t.getValue()), StandardCharsets.UTF_8))
         , (r, u)->r.putAll(u));

   map.entrySet().stream().forEach(e->{
      System.out.println(e.getKey() + " : " + e.getValue());
   });

}catch(IOException ex){
   ex.printStackTrace();
}finally{
}

結果、、、、

{'YQ==': 'QQ==', 'Yg==': 'Qg==', 'MTA=': 'MTAw', 'QQ==': '44GC'}

====== Google gson による解析 =========
a : A
A : あ
b : B
10 : 100

でも、こんなことするのは、
大量のデータには効率悪いだろうな。。

Python 標準出力→Java受け取り

先日書いた中の Python 標準出力→Java受け取りは、
Javaからプロセス起動で実行するPython と文字列の受け渡し - Oboe吹きプログラマの黙示録
リストを長い正規表現で解析する方法でこれでは、ちょっと効率が悪そうで
汚いコードである。
b' ' などとPython に出力させるのでなく、いっそのこと
Base64 エンコードさせたリストを出力させて Java で読み込んだ方が
スマートで安全だ。

Python 側、util ディレクトリに配置する stdio.py は次のようにする。

# -*- coding: UTF-8 -*-
from base64 import b64encode
class StdIO:
    # 標準入力
    # escape=True  : inputした文字をUnicode文字列に変換してリストにする
    #                標準入力=Unicode入力を想定(Javaプロセス標準出力から受信する場合はTrueにする)
    # escape=False : inputした文字を変換なしでリストにする。
    #                標準入力=通常
    def input(self, escape=True):
        inlist = []
        try:
            if escape:
                while True:
                    inp = input('')
                    if inp == '': break
                    inlist.append(inp.encode().decode('unicode-escape'))
            else:
                while True:
                    inp = input('')
                    if inp == '': break
                    inlist.append(inp)
        except EOFError:
            pass
        return inlist
    # リストを標準出力
    #   escape=True  : リスト要素をunicode-escape にエンコードして出力
    #   code : 出力する文字コード、デフォルト 'utf-8'  ユニコード指定は、'unicode-escape'
    def printList(self, list, base64=False, code='utf-8'):
        outlist = []
        if base64==True:
            for e in list:
                data = e.encode(code)
                outlist.append(b64encode(data).decode())
        else:
            for e in list:
                data = e.encode(code)
                outlist.append(data)
        print(outlist)

Java が呼び出す Python のコード
sample.py

# -*- coding: UTF-8 -*-

from util.stdio import StdIO

stdio = StdIO()
list = stdio.input()
# Java に返すリストは、Base64 エンコードする
stdio.printList(list, True)

Javaのコード

import org.yipuran.csv4j.CSVParser;
import org.yipuran.function.ThrowableConsumer;
import org.yipuran.util.process.ScriptExecutor;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

Python から受け取った文字列を以下3通りの方法で解析する。
・StringTokenizer での抽出
・org.yipuran.csv4j.CSVParser での抽出・・・1行のCSVとして解析させる方法
Google gson での抽出・・・JSONのリストとして解析させる方法

StringTokenizer または 1行CSVとして解析する方法は、誰でもすぐに思いつくが、
JSONのリストとして解析させる方法は、なかなか思いつかないであろう。

List<String> list = Arrays.asList("A", "B'B", "C\"", "'\"", "あいうえ1234");

try(ByteArrayOutputStream out = new ByteArrayOutputStream()){
   ScriptExecutor.runStream(()->"python sample.py"
   , ()->list.stream().map(e->Unicodes.encode(e) + "\n").collect(Collectors.toList())
   , ThrowableConsumer.of(i->i.transferTo(out))
   , (t, e)->{
      System.out.println("stderr : " + t );
      e.printStackTrace();
   });
   String response = out.toString();

   System.out.println(response);
   // 解析
   System.out.println("====== StringTokenizer による抽出 =========");

   List<String> list1 
   = Collections.list(new StringTokenizer(response.replaceAll("[\r\n]", ""), ","))
   .stream()
   .map(e->e.toString().replaceFirst("^(\\['| {0,1}')", "").replaceFirst("'\\]{0,1}$", ""))
   .map(e->new String(Base64.getDecoder().decode(e), StandardCharsets.UTF_8))
   .collect(Collectors.toList());

   list1.stream()
   .forEach(e->{
      System.out.println("[" + e+ "]");
   });

   System.out.println("====== org.yipuran.csv4j.CSVParser による抽出 =========");

   List<String> list2 
   = new CSVParser().tokenize(response.replaceAll("[\r\n\\[\\]]", ""))
   .stream()
   .map(e->e.replaceAll("'", ""))
   .map(e->new String(Base64.getDecoder().decode(e), StandardCharsets.UTF_8))
   .collect(Collectors.toList());

   list2.stream()
   .forEach(e->{
      System.out.println("[" + e+ "]");
   });

   System.out.println("====== Google gson による抽出 =========");

   List<String> list3 = new GsonBuilder().serializeNulls().create()
   .fromJson(response, new TypeToken<List<String>>(){}.getType());
   List<String> list4 = list3.stream()
   .map(e->new String(Base64.getDecoder().decode(e), StandardCharsets.UTF_8))
   .collect(Collectors.toList());

   list4.stream()
   .forEach(e->{
      System.out.println("[" + e+ "]");
   });
}catch(IOException ex){
   ex.printStackTrace();
}finally{
}

実行結果は、、、

['QQ==', 'QidC', 'QyI=', 'JyI=', '44GC44GE44GG44GIMTIzNA==']

====== StringTokenizer による抽出 =========
[A]
[B'B]
[C"]
['"]
[あいうえ1234]
====== org.yipuran.csv4j.CSVParser による抽出 =========
[A]
[B'B]
[C"]
['"]
[あいうえ1234]
====== Google gson による抽出 =========
[A]
[B'B]
[C"]
['"]
[あいうえ1234]