SJISの漢字で第一水準と第二水準だけ入力可能にしたい

題名だけだとかなりアバウトな要件ですが。。。
現在担当しているWEBシステムは、HTMLコンテンツもUTF-8で規定しており、バックエンドのDBもユニコードの設定になっています。
導入先がすべてユニコードでOKと言ってくれれば幸せなのですが、まだまだそうはいかないのが現状です。
今回、導入先の周辺システムがすべてShift_JIS前提で構築されているとのことで、I/Fする際は文字コード変換しなければ
ならないと思っていたのですが、こちらが導入するシステムのDBに格納される漢字について注文がつきました。


「第一水準と第二水準以外の漢字は、DBに格納しないで欲しい」


なんだか、そういうことになってしまったようで。。。
そうは言っても、DBはユニコードで格納するように設定していますので、結局I/F時には文字コード変換が発生しますけど。。。
仕方なくDBに格納する前にチェックして、第一水準と第二水準以外の漢字が含まれていた場合はエラーにするように
コーディングするようになったみたいです。
この要件自体はよくあることだと思っていて、WEBで検索すればかなりの数がヒットすると思っていたのですが、
「そのものズバリ!」というものが検索できませんでした。


要件的には、以下のもの以外がNGということだと思います。

  • 半角英数OK
  • 全角ひらがな、カタカナOK
  • 第一水準、第二水準範囲の漢字OK

とうことで、以下のようなサンプルを作成してみました。
先に断っておきますけど(←多分未来の自分)、各種チェックに使用している境界値は、その時の要件によって変えてくださいね。
例えば下のコードの全角ひらがな、カタカナチェックに使用している境界(0x8140〜0x8490)は相当アバウトです。

String s = "てすと";
boolean isOk = true;
byte[] bt = null;

try {
    bt = s.getBytes("Shift_JIS");
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}

for (int i = 0; i < bt.length; i++) {
    byte firstByte = bt[i];
    String sFirst = Integer.toHexString(firstByte & 0xff);

    // 半角チェック
    if (sFirst.compareToIgnoreCase("20") >= 0 && sFirst.compareToIgnoreCase("80") < 0) {
        // OK.
    } else {
        byte secondByte = bt[i + 1];
        String doubleByteStr = sFirst.concat(Integer.toHexString(secondByte & 0xff));
        // 全角ひらがな、カタカナチェック
        if (doubleByteStr.compareToIgnoreCase("8140") >= 0 && doubleByteStr.compareToIgnoreCase("8490") <= 0) {
            // OK.
        }
        // 第一・第二水準チェック
        else if ((doubleByteStr.compareToIgnoreCase("889f") >= 0 && doubleByteStr.compareToIgnoreCase("9872") <= 0)
              || (doubleByteStr.compareToIgnoreCase("989f") >= 0 && doubleByteStr.compareToIgnoreCase("fc4b") <= 0)) {
            // OK.
        } else {
            isOk = false;
            break;
        }
        i++;
    }
}

こんな感じでOK〜と思っていたら落とし穴がありました。
以下の部分なのですが、

try {
    bt = s.getBytes("Shift_JIS");
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}


getBytesって、ユニコードには存在するけど、Shift_JISには無いようなコードが含まれていたら何かしら例外が発生すると
思ってテストしていたのですが、無い場合は0x3f(つまり"?")に変換してしまうようで、このままだと全く駄目です。
苦肉の策で以下のようなロジックを追加することにしました。

String uniStr = null;
String sjisStr = null;
int uni_hatena = 0;
int sjis_hatena = 0;

try {
    uniStr = new String(s.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}
for (int count = 0; count < uniStr.length(); count++) {
    if ("?".compareTo(uniStr.substring(count, count + 1)) == 0) uni_hatena++;
}
try {
    sjisStr = new String(s.getBytes("Shift_JIS"), "Shift_JIS");
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}
for (int count = 0; count < sjisStr.length(); count++) {
    if ("?".compareTo(sjisStr.substring(count, count + 1)) == 0) sjis_hatena++;
}
if (uni_hatena != sjis_hatena) {
    System.out.println("NG");
}

なにか、すごくかっこ悪い。。。
そしてこのロジック、正しいのか???
Shift_JIS表については、以下を参考にさせて頂きました。
http://www.seiai.ed.jp/sys/text/java/shiftjis_table.html
http://charset.7jp.net/sjis.html

他にもっといい方法ないか模索中です。