ATOMシステム規定のアプリで開くが動作しない、対処

ATOM以前より使っていたプラグイン、最近のATOM 1.60.0 で、前にも入れて使ったことのある
oboe2uran.hatenablog.com
この、open-unsupported-files を入れて、
Excel をダブルクリックで Excel アプリで開けずに、エラー
 Uncaught TypeError: shell.openItem is not a function
となってしまう。

エラーメッセージを頼りに原因は、以下を見ると、、、
Uncaught TypeError: shell.openItem is not a function · Issue #20 · skandasoft/open-unsupported-files · GitHub

Electronのドキュメント
shell.openItemは完全に非推奨になったから。。。

対処方法があって、open-unsupported-files.coffee で、使っている shell.openItem を
shell.openPath に変えること。

ATOMをインストールしたパッケージ  open-unsupported-files/lib/ の下に
open-unsupported-files.coffee が存在するはずで、

C:\Users\{ユーザ名}\.atom\packages\open-unsupported-files\lib\
open-unsupported-files.coffee
を開き、

を開き、
21行目あたりの shell.openItem の箇所

      shell.openItem(path)

以下に変更する。

      shell.openPath(path)

ATOMを再起動すれば、ダブルクリックで xlsx 拡張子は、Excel で開けるようになる。

guice タイプセーフな annotatedWith

Google guice で JSR330 の @Named に対して、
バインド定義で @Namedアノテーションがついていることを限定する場合、

binder().bind(Logic.class).annotatedWith(Names.named("HIGH")).to(HighLogic.class);

と、com.google.inject.name.Namesnamed(String) メソッドで、文字列を渡すように書かなければならず、
誤って想定しない文字列を書いてしまうと実行時までバグが気付かず、
タイプセーフとなっていない。

列挙型をアノテーションで指定させよう→ アノテーションと共にアノテーション実装を用意してバインド判定に
使えるように
しなくてはならない。

という敷居の高さもでてくるが、1つこのパターンでアノテーション実装を書いておけば
流用できるだろう。

public enum Level{
    HIGH, MIDDLE, LOW;
}

この列挙型 Level を指定するアノテーションを以下のように、アノテーションの実装とともに
用意する。
アノテーションを implements したクラス実装定義を、アノテーションの定義で書いてしまう。

import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
 * PointLevel
 */
@Retention(RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@BindingAnnotation
public @interface PointLevel {
    Level value();

    class Implemention implements PointLevel, Serializable{
        private static final long serialVersionUID = 0;
        private final Level value;

        public static Implemention post(Level level){
            return new Implemention(level);
        }
        Implemention(Level value) {
            this.value = value;
        }
        @Override
        public Level value() {
            return this.value;
        }
        @Override
        public int hashCode() {
            return (127 * "value".hashCode()) ^ value.hashCode();
        }
        @Override
          public String toString() {
            return "@" + PointLevel.class.getName();
        }
        @Override
        public Class<? extends Annotation> annotationType() {
            return PointLevel.class;
        }
    }
}

↑ ここで、hashCode実装を怠ってはならない!


インジェクション先は、以下のように書く。

@Inject @PointLevel(Level.MIDDLE) private Logic alogic;

Guice の Module で、バインド定義は、例えば以下のように annotatedwith で
生成インスタンスを限定する。

binder().bind(Logic.class).annotatedWith(PointLevel.Implemention.post(Level.HIGH))
.to(HighLogic.class);
binder().bind(Logic.class).annotatedWith(PointLevel.Implemention.post(Level.MIDDLE))
.to(MiddleLogic.class);
binder().bind(Logic.class).annotatedWith(PointLevel.Implemention.post(Level.LOW))
.to(LowLogic.class);

OGNL式、インナークラスの場合

先日、
oboe2uran.hatenablog.com
を書きましたが、
滅多にないというかそんなクラス定義をして何の意味があるのだと思うが、、
インナークラスで定数を定義している場合、、
$でインナークラスを指定することになる。

前回のサンプルを例にすると、、

package org.sample;

public clsss Access{
    
     public static class Www {
           public static final String WHITE = "10201";
           public static final String BLUE  = "10202";
     }
}

の場合、、
$文字で区切って

<select id="getItems" resultType="org.sample.dto.Gstore">
SELECT g.* FROM group_stores g 
LEFT JOIN (SELECT id FROM items
 WHERE view_code IN ( '${@org.sample.Access$Www@WHITE}', '${@org.sample.Access$Www@BLUE}' ) ) i
ON g.code = i.id
</select>

bind定義を書かないでクラスに @Injectをした時の guice

通常 Google guice の使い方は、インターフェースに対する実装クラスのインスタンス
割り当てられるように bind定義を書いてDIを実装する。

うっかりというか、諸事情で見落として、
 @Inject private Foo foo;
と書いた Fooクラスが、インターフェースでもなくただのクラスで、引数なしのデフォルト
コンストラクタを持つクラスだった場合、
bind定義も書かなかった場合、
インジェクションされずに、null で Foo のメソッドを実行できないのか?

いや、guice は、この場合、bind定義を書かなくても、引数なしのデフォルト
コンストラクタを持つ場合は、NullpointerException にはならずに、インスタンス
インジェクションしてくれてメソッドを実行できる。

この柔軟さが良いような、もっとちゃんとチェックしてエラーにして欲しいような
悩ましい

継承時のlombok @Data

継承があるオブジェクトでは、@Data を使うと警告
Generating equals/hashCode implementation but without a call to superclass,
even though this class does not extend java.lang.Object.
If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.

@EqualsAndHashCodeを書かずにいるとビルド時に警告されるのですが、デフォルトはfalse
callSuper=false は、super.equals(Object) を呼び出さずに equals を自作する必要性を意味します。
一般には、@EqualsAndHashCode(callSuper=true) を使うことが多いでしょう。
継承元クラスのフィールドも toString() で出力させるなら、@ToString(callSuper=true)


サンプル

@Data
public class Fruit{
    private int basePrice;
}
@Data
@EqualsAndHashCode(callSuper=true)
@ToString(callSuper=true)
public class Apple extends Fruit{
    private String name;
 pricate String barcode;
}

equals で対象外にしたいフィールドがある場合は、exclude を@EqualsAndHashCodeで指定します。

@Data
@EqualsAndHashCode(callSuper=true, exclude={"barcode"})
@ToString(callSuper=true)
public class Apple extends Fruit{
    private String name;
    private String barcode;
}

exclude = { } で複数指定できます。

区切り文字で区切られた最後のワード

区切り文字「.」で区切られた文字列から最後を求める
aaa.bbb.ccc.ddd.eee

区切り文字 「.」でない区切った各塊を表す正規表現が [^.]* であるから
区切り文字 「.」で区切られた最後のワードを求める正規表現
[^.]+$

区切り文字+区切り文字 「.」で区切られた最後のワードを求める正規表現
\.[^.]+$


先頭から最後の区切り文字 「.」までの正規表現
^.+\.

2個目までの区切り文字 「.」までの正規表現
^([^.]+.){2}


「.」区切りは難しい。
簡単な「/」区切り文字の場合、、

aaa/bbb/ccc/ddd/eee
に対して、、

区切り文字 「/」で区切られた最後のワードを求める正規表現
[^/]+$

区切り文字+区切り文字 「/」で区切られた最後のワードを求める正規表現
/[^/]+$

先頭から最後の区切り文字 「/」までの正規表現
^.+/

2個目までの区切り文字 「/」までの正規表現
^([^/]+/){2}