Java11 String の strip と Wicket の TextField 入力値の取得

Java11 になって、地味に嬉しいのが、java.lang.String の strip() メソッドでしょう。

Java10 までは、Wicket の TextField<String> の入力値の取得も、
できるだけ Optional を使って書いても、結局、前後に入力された全角空白を取り除く処理を
書かないとなりませんでした。

final TextField<String> itemField = new TextField<>("item", new Model<>());

// ↓ TextFieldを取得取得する form  submit の中の処理として、、
Optional.ofNullable(itemField.getModelObject()).ifPresent(e->{
     // 前後に入力された全角空白を取り除く処理を書く。
     // 前後に入力された全角空白を取り除いた結果、空文字でなければ
     // 入力値としての処理を行う。
});

または、Optional で、map () の中で全角空白をこのような処置をして orElse や orElseThrow をしなくては
なりませんでした。

Java11から、 追加された strip() と isBlank() を使って、Optional の ifPresent で
処理するようにしたければ、、

Optional.ofNullable(itemField.getModelObject())
.map(e->e.strip())
.filter(e->!e.isBlank())
.ifPresent(e->{
     // 入力された値  e を処理する
});

strip() の他に追加された

stripLeading() 前方で全角空白と空白(タブ文字も)を削除
stripTrailing() 後方で全角空白と空白(タブ文字も)を削除

左(Left) 右(Right) の名称にして欲しかった。。。

Wicket Bootstrap用の Pagenation

昨日の投稿、WicketのPagingNavigatorで、先頭(first)と末尾(last)を表示させない方法 - Oboe吹きプログラマの黙示録
に続いて、Wicket で、Bootstrap用の PagingNavigator を作りました。
↓ 以下のような描画になります。(色は別にCSSで指定)
f:id:posturan:20181017212338j:plain
まず、Bootsrap をページで読むようにします。

<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>

(注意)この link タグ、タグの終わりを rel="stylesheet"> と書いてしまうと、
WicketAJAX デバッガで
ERROR: Error in parsing: This page contains the following errors:error on line 4 at column 8:
Opening and ending tag mismatch: link line 0 and head

と怒られてしまいます。無視しても悪さはないと思いますが鬱陶しいので、
ちゃんと タグ終了、 /> を書きます。

org.apache.wicket.markup.html.navigation.paging.PagingNavigator を参考にBootstrap用の PagingNavigator を
用意します。

import org.apache.wicket.Component;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.markup.html.navigation.paging.IPageable;
import org.apache.wicket.markup.html.navigation.paging.IPagingLabelProvider;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigation;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigationIncrementLink;
import org.apache.wicket.markup.html.navigation.paging.PagingNavigationLink;
import org.apache.wicket.markup.html.panel.Panel;
/**
 * Bootstrap 用 PagingNavigator
 */
public class BootstrapPagingNavigator extends Panel{
   private static final long serialVersionUID = 1L;
   private PagingNavigation pagingNavigation;
   private final IPageable pageable;
   private final IPagingLabelProvider labelProvider;
   /**
    * Constructor.
    */
   public BootstrapPagingNavigator(final String id, final IPageable pageable){
      this(id, pageable, null);
   }
   /**
    * Constructor.
    */
   public BootstrapPagingNavigator(final String id, final IPageable pageable
, final IPagingLabelProvider labelProvider){
      super(id);
      this.pageable = pageable;
      this.labelProvider = labelProvider;
   }
   public final IPageable getPageable(){
      return pageable;
   }
   @Override
   protected void onInitialize(){
      super.onInitialize();
      pagingNavigation = new PagingNavigation("navigation", pageable, labelProvider);
      add(pagingNavigation);

      add(new PagingNavigationIncrementLink<Void>("prev", pageable, -1)
            .add(new TitleAppender("Go to previous")));
      add(new PagingNavigationIncrementLink<Void>("next", pageable, 1)
            .add(new TitleAppender("Go to next")));
   }
   /**
    * Create a new pagenumber link.
    */
   protected AbstractLink newPagingNavigationLink(String id, IPageable pageable, int pageNumber){
      return new PagingNavigationLink<Void>(id, pageable, pageNumber);
   }
   /**
    * Gets the pageable navigation component for configuration purposes.
    * @return the associated pageable navigation.
    */
   public final PagingNavigation getPagingNavigation(){
      return pagingNavigation;
   }
   /**
    * Appends title attribute to navigation links
    */
   private final class TitleAppender extends Behavior{
      private static final long serialVersionUID = 1L;
      private final String title;
      public TitleAppender(String title){
         this.title = title;
      }
      @Override
      public void onComponentTag(Component component, ComponentTag tag){
         tag.put("title", title);
      }
   }
}

この Panel のHTMLです。

<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns:wicket="http://wicket.apache.org">
<body>
<wicket:panel>
   <nav>
      <ul class="pagination">
         <li class="page-item">
            <a wicket:id="prev" rel="prev" class="page-link" aria-label="Previous">
               <span aria-hidden="true">&laquo;</span>
               <span class="sr-only">Previous</span>
            </a>
         </li>
         <li wicket:id="navigation" class="page-item">
            <a wicket:id="pageLink" class="page-link"><span wicket:id="pageNumber">1</span></a>
         </li>
         <li class="page-item">
            <a wicket:id="next" rel="next" class="page-link" aria-label="Next">
               <span aria-hidden="true">&raquo;</span>
               <span class="sr-only">Next</span>
            </a>
         </li>
      </ul>
   </nav>
</wicket:panel>
</body>
</html>

使用する側のHTML と CSS

<table>
   <tbody>
      <tr>
         <td>
            <div wicket:id="paging" class="page-navi"></div>
         </td>
         <td style="padding-left: 2rem"><span wicket:id="records">99</span></td>
      </tr>
   </tbody>
</table>

CSS、表示中のページ、及び、先頭ページなら previous のリンク、最終ページなら next のリンク
に、disabaled="disabled" が付くので、これに対してクリックしても無駄なことを表現するように
デザインします。

.page-navi{
   padding: 10px 20px;
}
.page-navi table{
   border-spacing: 0;
   margin-bottom: 10px;
}
.page-navi a{
   padding: 2;
   text-decoration: none;
   font-size: 1.2rem;
   width: 4rem;
   text-align: center;
   background-color: #fbfad4;
}
.page-navi a[disabled='disabled']{
   color: #ff00ff !important;
   font-weight: bold;
}
.page-navi a[aria-label='Previous'][disabled='disabled'],
.page-navi a[aria-label='Next'][disabled='disabled']{
    color: #c0c0c0 !important;
}

WicketのPagingNavigatorで、先頭(first)と末尾(last)を表示させない方法

Wicket の Pagination を表示する PagingNavigation

public PagingNavigation(final String id, final IPageable pageable)

通常は、

Dataview dataview =  /* org.apache.wicket.markup.repeater.data.DataView の生成 */

add(new PagingNavigator("paging", dataview));

と書くと、
f:id:posturan:20181016221652j:plain
の表示であるが、先頭(first)と末尾(last)を表示させないようにするには、トリッキーなことをする。
f:id:posturan:20181016221904j:plain
このようにするには、PagingNavigator の onInitialize をオーバーライドして、
先頭(first)と末尾(last)を、onRender() で何もしない処理で付け直してしまうのである。

PagingNavigator pagingNavigator = new PagingNavigator("paging", dataview){
   @Override
   protected void onInitialize(){
      super.onInitialize();
      remove("first");
      add(new WebMarkupContainer("first"){
         @Override
         protected void onRender(){
         }
      });
      remove("last");
      add(new WebMarkupContainer("last"){
         @Override
         protected void onRender(){
         }
      });
   }
};
add(pagingNavigator);

これは、Wicket の PagingNavigator .html が、以下のHTMLだからだ。

<?xml version="1.0" encoding="UTF-8" ?>
<!--
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
-->
<html xmlns:wicket="http://wicket.apache.org">
<body>
<wicket:panel>
	<a wicket:id="first" class="first">&lt;&lt;</a>
	<a wicket:id="prev" rel="prev" class="prev">&lt;</a>
	<span wicket:id="navigation" class="goto">
		<a wicket:id="pageLink" href="#"><span wicket:id="pageNumber">5</span></a>
	</span>
	<a wicket:id="next" rel="next" class="next">&gt;</a>
	<a wicket:id="last" class="last">&gt;&gt;</a>
</wicket:panel>
</body>
</html>

しかし、これを Bootstrap の PagingNavigation として描画したい場合は、
https://getbootstrap.com/docs/4.1/components/pagination/

span で囲んだ a タグの繰り返しではなく、
li で囲んだ a タグの繰り返しで描画しなくてはならないとなると、
結局、Wicket PagingNavigator を、Bootstrap 用のHTMLを用意して
リメイク(別名クラスで再コーディング)した PagingNavigator を作らなきゃならないのか!?
それとも、Wicket PagingNavigatorで描画させた後で、spanタグを li タグで置換するか?
span → li 置換を JavaScript で実行するなんて JSを書くのが嫌なだけでなく、
危険かもしれないし。。。

ページ中央配置のHTML

コンテンツをページの中央配置する方法はいくつかあると思うが
代表的な方法を2つメモ。
position: absolute と margin: auto を指定する方法
HTML

<body>
<div class="center-content box">
   <h3>Center</h3>
</div>
</body>

CSS

body{
   margin: 0;
   padding: 0;
   box-sizing: border-box;
   font-size: calc(16px + 0.2vw);
   -webkit-text-size-adjust: 100%;
   font-family: 'Hiragino Kaku Gothic ProN', 'ヒラギノ角ゴ ProN W3', Meiryo, メイリオ, sans-serif;
}
h1,h2,h3,h4,h5,h6,h7,h8{ margin: 0; }
.center-content{
   position: absolute;
   top: 0px;
   right: 0px;
   bottom: 0px;
   left: 0px;
   margin: auto;
}
.box{
   background-color: #f5f5c6;
   width: 300px;
   height: 200px;
   display: flex;
   align-items: center;
   justify-content: space-around;
}

display: flex の枠で囲む方法
HTML

<body>
<section class="out-section">
	<div class="center-content box">
	   <h3>Center</h3>
	</div>
</section>
</body>

CSS ( body は ↑と同じ、)height: 100vh; を指定する

body{
   margin: 0;
   padding: 0;
}
.out-section{
   height: 100vh;
   display: flex;
   align-items: center;
   justify-content: center;
}
.center-content{
   background-color: #f5f5c6;
   width: 300px;
   height: 200px;
   display: flex;
   align-items: center;
   justify-content: space-around;
}

section が無くて body に直接、、
HTML

<body>
    <div class="center-content box">
         <h3>Center</h3>
     </div>
</body>

CSS

body{
   margin: 0;
   padding: 0;
   box-sizing: border-box;
   font-size: calc(16px + 0.2vw);
   -webkit-text-size-adjust: 100%;
   font-family: 'Hiragino Kaku Gothic ProN', 'ヒラギノ角ゴ ProN W3', Meiryo, メイリオ, sans-serif;
   height: 100vh;
   display: flex;
   align-items: center;
   justify-content: center;
}
h1,h2,h3,h4,h5,h6,h7,h8{ margin: 0; }
.center-content{
   background-color: #f5f5c6;
   width: 300px;
   height: 200px;
   display: flex;
   align-items: center;
   justify-content: 

Java11 からの HttpClient

先月、OpenJDK 11 もリリースされて、Java9 でインキュベータとして出てた HttpClient が、
java.net.http パッケージに入った。
去年、Java9 の HttpClient を試す - Oboe吹きプログラマの黙示録
を書いたのだが、
リクエストの送信も、Java11 から、、HttpResponse.BodyHandlers というのを
使えるようになっていて、
例として、、

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://www.google.co.jp/"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

と、Java11 からもっと簡単に書けるようになっている。
HttpResponse<T> を取得する HTTP送信メソッドは、JavaDoc では、

// Receives the response body as a String
HttpResponse<String> response = client
.send(request, BodyHandlers.ofString());

// Receives the response body as a file
HttpResponse<Path> response = client
.send(request, BodyHandlers.ofFile(Paths.get("example.html")));

// Receives the response body as an InputStream
HttpResponse<InputStream> response = client
.send(request, BodyHandlers.ofInputStream());

// Discards the response body
HttpResponse<Void> response = client
.send(request, BodyHandlers.discarding());  }

この2番目と3番目、HttpResponse<Path> と HttpResponse<InputStream>
これは、強力だと思う。

HttpResponse<Path> を受け取る方法は、HTTP応答をファイルとして書き込む方法であり
書き込んだファイルの Path を受け取る。
HttpResponse<InputStream> を受け取る方法は、HTTP応答を InputStream で受け取る。
HTTP応答を InputStream で受け取る場合は、body() メソッドが InputStream を返す。

JavaScript版の祝日計算も更新

2019年、5月1日が祝日、10月22日が即位礼正殿の儀として祝日になると
先日、内閣政府からの発表で、Javaプログラムとして作成した祝日計算も更新したので
JavaScript版の方を更新した。

ja.osdn.net

Java祝日計算の対応

昨日、やっと2019年に追加される祝日が、政府内閣から発表、
祝日の間に挟まれて休日になる「国民の休日」が、敬老の日秋分の日で、数年に1回しか発生しない
パターンから、2019年だけのプログラム泣かせになってしまった。
ちなみに、秋分の日は、敬老の日の前には99%来ないそうだ。。(宇宙自然科学の異変が発生しない限り。。)

Java版の方だけでも先に更新しました。
バージョンは 3.3 になります。
2019年5月1日の祝日名を何と呼ぶことにしたら良いのか判らず、
プログラムでは「平成の次の即位日」と、みっともない名称にしています。
2019年 10月22日 は、「即位礼正殿の儀」なんだそうだ。

まあ、バージョン3.2にした時に、天皇誕生日だけはちゃんと移動したから良しとしよう。

この後、JavaScript 版を修正に手をつけようと思う。Java版より JavaScript 版の方が
jQurry ui の DatePicker カレンダーに祝日マークを付けるのに役に立つので
Java 版は、少々飽きてきた。

元々、10年前に書いたコードだったし、汚いコードでも今回もスピードを優先、
でも結局、速くないけど、自分の書いたコードが嫌になってきた。

ja.osdn.net

バージョン3.4で、
指定年の月の祝日、振替休日、国民の休日、の日付と名称文字列エントリのリストを返す
static メソッドを追加した。

今まで、祝日、振替休日、国民の休日別の扱いとして取得するメソッドだけでは、不便だからだ。

public static List> listHolidays(int year)