[Java]100 VSCode:Extension Pack for Javaとjavacコマンドの比較

表題の件、Javaソースコードのコンパイルエラー時に得られる情報量に雲泥の差がありました。Extension Pack for Javaは設定を変えればjavacと同レベルになるのでしょうがデフォルトがあまりにもしょぼ過ぎました。

# VSCode Extension Pack for Java
--------------------------------------------------
Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
        Unreachable catch block for InvalidPropertiesFormatException. This exception is never thrown from the try statement body
        Unreachable catch block for FileNotFoundException. This exception is never thrown from the try statement body
        Unreachable catch block for IOException. This exception is never thrown from the try statement body
        at XMLtoSetProperty.main(XMLtoSetProperty.java:32)
--------------------------------------------------

# javacコマンド
--------------------------------------------------
XMLtoSetProperty.java:32: エラー: 例外FileNotFoundExceptionは対応するtry文の本体ではスローされません
		} catch (FileNotFoundException e){
		  ^
XMLtoSetProperty.java:34: エラー: 例外IOExceptionは対応するtry文の本体ではスローされません
		} catch (IOException e){
		  ^
XMLtoSetProperty.java:36: エラー: 例外InvalidPropertiesFormatExceptionはすでに捕捉されています
		} catch (InvalidPropertiesFormatException e){
		  ^
XMLtoSetProperty.java:58: エラー: 例外IOExceptionは報告されません。スローするには、捕捉または宣言する必要があります
				in.close();
				        ^
XMLtoSetProperty.java:54: エラー: 例外FileNotFoundExceptionは報告されません。スローするには、捕捉または宣言する必要があります
			in = new FileInputStream(file);
			     ^
XMLtoSetProperty.java:55: エラー: 例外IOExceptionは報告されません。スローするには、捕捉または宣言する必要があります
			settings.loadFromXML(in);
			                    ^
エラー6個
--------------------------------------------------

VSCodeの出力内容でコードを修正できる方はあまりいないでしょう。javacコマンドであれば直すべき箇所が一目瞭然で一発修正できます。

開発速度への影響が大きいのでVSCodeでの実行がうまくいかない時は即コマンドで確認すべきだと痛感しました。

[Java] 99 OS名とLinuxディストリビューション名の取得

自製アプリのクロスプラットフォーム化にあたりOSやディストリビューションによってフォントサイズを変える必要が生じたため、以下のメソッドを作成して対応しました。

UbuntuやLinux Mint以外で”cat /etc/issue”コマンドにてディストリビューション名が分かるかどうかは不明です。

言語仕様とは言え、変数(フォントサイズ)を一々配列に格納するのは面倒です。

// Windows,Macはサイズ8,Ubuntuは7,Linux Mintは10に設定

public static int font_size(){
	String OS = System.getProperty("os.name").toLowerCase();
	int[] font_sizes = new int[1];
	if (OS.contains("linux")){
		try{
			Process p = Runtime.getRuntime().exec("cat /etc/issue");
			try{
				p.waitFor();
				InputStream is = p.getInputStream();
				InputStreamReader inputStreamReader = new InputStreamReader(is);
				Stream<String> streamOfString= new BufferedReader(inputStreamReader).lines();
				String str = streamOfString.collect(Collectors.joining());
								
				if (str.contains("Ubuntu")){
					int font_size = 7;
					font_sizes[0] = font_size;
				}else if (str.contains("Mint")){
					int font_size = 10;
					font_sizes[0] = font_size;
				}else{
					int font_size = 8;
					font_sizes[0] = font_size;
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	} else {
		int font_size = 8;
		font_sizes[0] = font_size;
	}
	return font_sizes[0];
}

[Java] 98 JColorChooserのshowDialogメソッドを自製

前回の続きです。

前の記事のコードではJcolorChooserダイアログの取消ボタンが効かず、変更した色のRGB値が返されてしまいます。そこでActionListenerとしてokListenerとcancelListenerを追加しました。

それぞれのアクションとして戻り値のRGBA値を作成しますが、public修飾子が使えずこれらをActionListenerの外に渡す方法が分からなくて少々苦労しました。

結局、初期設定で作成しておいたint配列color_caseに移し替えるという何とも泥臭い方法で取り出しました。Javaの仕様ではこうするしかないようです。

okListenerとcancelListenerを今回のように使っているコード例はGoogle検索上位では見あたらなかったのでアップしておきます。

<メソッドのみ>

static int[] showDialog(Component component, String title, Color initialColor) throws HeadlessException{
		int[] color_case = new int[4];
		JColorChooser chooser = new JColorChooser(initialColor != null ? initialColor : Color.white);

		String swatches = UIManager.getString("ColorChooser.swatchesNameText");
		String hsv = UIManager.getString("ColorChooser.hsvNameText");
		String hsl = UIManager.getString("ColorChooser.hslNameText");
		String rgb = UIManager.getString("ColorChooser.rgbNameText");
		String cmyk = UIManager.getString("ColorChooser.cmykNameText");

		List<String> list = Arrays.asList(hsv,hsl,rgb);

		for (AbstractColorChooserPanel p : chooser.getChooserPanels()) {
			if (!list.contains(p.getDisplayName())) {
				chooser.removeChooserPanel(p);
			}
		}
		
		ColorTracker tracker = new ColorTracker(chooser,initialColor);

		ActionListener okListener = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				int[] colors = tracker.getColor();
				color_case[0] = colors[0];
				color_case[1] = colors[1];
				color_case[2] = colors[2];
				color_case[3] = colors[3];
			}
		};

		ActionListener cancelListener = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				int[] colors = tracker.getInitial();
				color_case[0] = colors[0];
				color_case[1] = colors[1];
				color_case[2] = colors[2];
				color_case[3] = colors[3];
			}
		};

		// ダイアログを作成
		JDialog dialog = JColorChooser.createDialog(component, title, true, chooser, okListener, cancelListener);
		dialog.setVisible(true);

		return color_case;
	}

	static class ColorTracker implements Serializable{
		JColorChooser chooser;
		Color color;
		int red;
		int green;
		int blue;
		int alpha;
		int[] colors;
	  
		ColorTracker(JColorChooser Chooser,Color initialColor){
			this.chooser = Chooser;
			this.color = initialColor;
			
		}
	  
		int[] getColor(){
			this.color = this.chooser.getColor();

			colors = new int[4];
			colors[0] = this.color.getRed();
			colors[1] = this.color.getGreen();
			colors[2] = this.color.getBlue();
			colors[3] = this.color.getAlpha();

			return colors;
		}
		int[] getInitial(){
			colors = new int[4];
			colors[0] = this.color.getRed();
			colors[1] = this.color.getGreen();
			colors[2] = this.color.getBlue();
			colors[3] = this.color.getAlpha();

			return colors;
		}
	}

[Java] 97 JColorChooserクラス

自製アプリに色調整の機能を追加するためダイアログを作成しようとしましたが、取り掛かる前にJColorChooserという便利なクラスを見つけました。機能的にはこれで十分です。

下記コードは色調整ダイアログを表示するボタンのアクションです。listを書き換えると表示タブを変更・並べ替えできます。今回は”swatches”以外の4つのタブを選択しています。またデフォルトにある下段のプレビュー部を消してみました。

デフォルトの5つのタブから取捨選択したChooserはcreateDialogでダイアログを作成する際に引数として使用します。

自製showDialogメソッドはJColorChooser.showDialogメソッドとは異なりOKボタンを押した時のRGB値を返してくれないので、これを追跡して返すColorTrackerクラスを用意しました。GitHubにあったコードに手を入れて作成しました。

思いのほか手間が掛かり半日を要しましたが、C/C++で書く苦労を考えれば大分マシでしょう。

<ライブラリは省略>

public class AdjustButtonAction{
    public static void main(){
      adj_btn.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e){
				if(e.getSource() == adj_btn){
					Color defaultColor = new Color(65,105,225);
					Color color = showDialog(parent, "色の調整", defaultColor);

					if(color == null) {
						System.out.println("選択されず");
					}else {
						System.out.println("R " + color.getRed());
						System.out.println("G " + color.getGreen());
						System.out.println("B " + color.getBlue());
        			}
				}
			}
		});
    }
	
	static Color showDialog(Component component, String title, Color initialColor) throws HeadlessException{
		JColorChooser chooser = new JColorChooser(initialColor != null ? initialColor : Color.white);
		
		// デフォルトの下段にあるプレビューを非表示にする
		chooser.setPreviewPanel(new JPanel());

		String swatches = UIManager.getString("ColorChooser.swatchesNameText");
		String hsv = UIManager.getString("ColorChooser.hsvNameText");
		String hsl = UIManager.getString("ColorChooser.hslNameText");
		String rgb = UIManager.getString("ColorChooser.rgbNameText");
		String cmyk = UIManager.getString("ColorChooser.cmykNameText");
		System.out.println("swatches " + swatches);
		System.out.println("hsv " + hsv);
		System.out.println("hsl " + hsl);
		System.out.println("rgb " + rgb);
		System.out.println("cmyk " + cmyk);

		// 色フォーマットのタブを選択
		List<String> list = Arrays.asList(hsv,hsl,rgb,cmyk);

		for (AbstractColorChooserPanel p : chooser.getChooserPanels()) {
			if (!list.contains(p.getDisplayName())) {
				chooser.removeChooserPanel(p);
			}
		}
	
		// ダイアログ作成
		ColorTracker ok = new ColorTracker(chooser);
		JDialog dialog = JColorChooser.createDialog(component, title, true, chooser, null, null);

		dialog.setVisible(true); 

		return ok.getColor();

	}
	static class ColorTracker implements ActionListener, Serializable{
		JColorChooser chooser;
		Color color;
	  
		ColorTracker(JColorChooser Chooser){
			this.chooser = Chooser;
		}
	  
		public void actionPerformed(ActionEvent e){
		}
	  
		Color getColor(){
			this.color = this.chooser.getColor();
			System.out.println("this.color " + this.color);
		return this.color;
		}
	}
}
--------------------------------------------------
出力
--------------------------------------------------
swatches サンプル(S)
hsv HSV(H)
hsl HSL(L)
rgb RGB(G)
cmyk CMYK
this.color java.awt.Color[r=65,g=105,b=225]
R 65
G 105
B 225

[Java] 96 Microsoft Storeアプリの申請

最初の申請から審査通過までに約1ヶ月掛かりました。諦めかけた時もありましたが、とても有用な記事の助けも借りてなんとか登録できました。

Visual Studioではなくコマンドラインで提出用appxパッケージを作成するのは、かなりハードルが高かったです。あれだけ苦労したAppleによる公証が楽勝に思えるレベルです。

以下、経過をメモしておきます。
————————————————–
2021年12月下旬:初回提出
8日後:不合格通知(クラッシュする可能性があるとのこと)

翌日:2回目提出
6日後:反応がないため催促メール送信
2日後:不合格通知

同日:3回目提出
翌日:クラッシュが起こる具体的条件を教えて欲しい旨をメールで伝えた
翌日:不合格通知受取、クラッシュする様子を撮影したスクショ動画とイベントビューアのログが添付されていた

同日:4回目提出(ここまではexeインストーラによるインストールおよび動作を確認して提出)
5日後:不合格通知受取、その後しばらくはラズパイをいじるなどして気分転換

8日後:5回目提出(ストアアプリとしてインストール後の挙動を確認した上で提出)
同日:合格通知受取、提出から3時間半で連絡あり
————————————————–

4回目まではappxパッケージに自己署名してからインストールするというやり方を知らなかったので、インストール後のストアアプリとしての動作確認が向こう任せになっていました。

Microsoftパートナーセンターの案内では最長3営業日かかるとありますが、結果が芳しくないとさらに放置されます。こちらから働きかけないと動いてくれません。なおメールは英語でのやりとりになります。

まあデベロッパー登録料が1847円(初期登録料のみで更新料無料)という格安サービスですから、一連のやりとりで十分元は取れました。

その後はバージョンアップの申請もあっさり通過して、すこぶる順調です。

参考記事

[Java] 95 プレビュー版classファイルの実行

プレビュー版classファイル(Java17の場合はver 17.65535)は正式なビルドはできませんが、以下コマンドで実行できます。

仮jarファイルをjpackageコマンドで正常なパッケージにはできません。正確に表現するとパッケージには一応できますがインストールしても起動しません。あくまでプロトタイプとしてのコードということでしょう。

# Testプロジェクトのbin/baseにあるメインクラスを実行
cd /Test/bin
java --enable-preview base.Main

# 仮jarの実行
java -jar --enable-preview test.jar

[Java] 94 classファイルのJDKバージョン確認(逆コンパイル)

VSCodeが親切にも自動コンパイルして生成してくれたclassファイル。ビルドして得られたjarファイルが実行できないという事態が発生しました。
エラー内容はJava.lang.UnsupportedClassVersionErrorです。

VSCodeが使っているJDKのバージョンを確認しようとコマンドパレットからConfigure Java Runtimeを見ると設定画面がクラッシュしていました。

仕方ないので以下コマンドでclassファイルを逆コンパイルし、コンパイラのJDKバージョンを調べました。

javap -v classファイル名 | grep "version"
--------------------------------------------------
出力
--------------------------------------------------
minor version: 65535
major version: 61

一方、問題なくビルドできたclassファイルの逆コンパイル結果は以下の通りです。

minor version: 0
major version: 61

どちらもメジャーバージョンは17 (61マイナス44)で同じでしたが、マイナーバージョンが違っていました。

Macに入れているJDKの内容からVSCodeが使っているJDKはEclipse Temurin 17であることが判明しました。
*2022/1/30追記 ver 17.65535はTemurin版ではなくプレビュー機能で作成されたclassファイルのバージョン番号でした。つまりbinフォルダに自動生成されるclassファイルは元から正常なビルドができないということになります。確認すると実行コマンドのオプションが”–enable-preview”になっていました。テストで作成されたclassファイルを誤ってビルドに使わないための機能と理解しました。

VSCodeのConfigure Java Runtime設定がクラッシュしていなければ、もっと早く原因を突き止められたでしょう。

# MacにインストールしたJDKのリスト出力
/usr/libexec/java_home -V
--------------------------------------------------
出力
--------------------------------------------------
Matching Java Virtual Machines (4):
    17.0.1 (arm64) "Oracle Corporation" - "Java SE 17.0.1" /Library/Java/JavaVirtualMachines/jdk-17.0.1.jdk/Contents/Home
    17.0.1 (x86_64) "Eclipse Temurin" - "Eclipse Temurin 17" /Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
    1.8.321.07 (x86_64) "Oracle Corporation" - "Java" /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
    1.8.0_302 (x86_64) "Eclipse Temurin" - "Eclipse Temurin 8" /Library/Java/JavaVirtualMachines/temurin-8.jdk/Contents/Home

最近VSCodeには変数やメソッドのスコープ(修飾子)やライブラリのimportだけチェックしてもらい、コンパイルから先はターミナルで操作しています。拡張機能によるコンパイル&ビルド&実行は思う様に動いてくれないことが多々あるため使わなくなりました。

[Java] 93 カスタムJREの作成

カスタムJREをjar実行ファイルにバンドルすることで自製アプリのサイズが3分の1になりました。

# jar実行ファイルの依存モジュールを調べる
jdeps --list-deps test.jar

# カスタムJREの作成コマンド例
jlink --compress=2 \
    --module-path "/Library/Java/JavaVirtualMachines/jdk-17.0.1.jdk/Contents/Home/jmods" \
    --add-modules java.base,java.datatransfer,java.desktop,java.xml \
    --output [ランタイムイメージの出力先フォルダパス] \
    --strip-debug \
    --no-header-files

[Java] 92 Swing, JavaFXの比較

GUIアプリのSwingからJavaFXへの移植に際しJavaFXに問題があることが発覚したため、今後の方向性を検討するための資料として表にまとめました。

今のところはSwingに戻ってLook & Feelを自製するつもりです。ボタンの角を丸めたいだけなので比較的簡単にできると思います。あと原因がわからないのですが、JavaFXの精細性が低いのは意外でした。

Swingコンポーネントを埋め込むSwingFXは起動がかなり遅くなったので却下です。なおSwingFXはOracleのサイトで例示されていたクラス名です。ライブラリではないのでご注意ください。

格子状に並べたボタンの拡大画像
上:Swing 下:JavaFX

[Java] 91 JavaFX Borderの線種

JavaFXではボタンの枠線を二重線にできないことが発覚しました。実線、点線、破線のみです。ホバリングの際に二重線を出現させたかったのですががっかりです。線の表現力はSwingの方が数段上のようです。あとボタンの高さをどうしても変えられません。

5日間苦闘しながらJavaFXを再学習してきましたが、どうやら休止せざるを得なくなりました。グラデーションの掛かった図形を作ったりしたのに残念です。JavaScriptやWordPressの時もそうでしたが、CSS内の優先順位が見えなくて思う様に設定できずいつも苦労します。

またSwingに戻ってGUIアプリの作り込みに取り組んでいきます。問題の見た目はデフォルトがダサいだけで、手を入れればそれなりになると思います。

2022/1/18追記
枠線を二重線にできないのはSwingも同じ様です。SwingのL&F Metalではおそらくinsetsを設定して外側に複数の線を描いています。
JavaFXへのSwingコンポーネント埋め込みが可能な様なのでもう少し頑張ってみます。