001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.util; 017 018import java.util.List; 019import java.util.ArrayList; 020import java.util.Locale; 021import java.text.Normalizer; 022 023/** 024 * HybsContains.java は、指定の AND OR 形式の文字列が含まれるかどうかを判定するクラスです。 025 * 026 * AND OR 形式の文字列 とは、「AAA BBB」は、AAA とBBB のAND形式、「CCC OR DDD」は、 027 * CCC と DDD のOR形式になります。 028 * 優先順位を付ける"(" などは使えません。常に、OR で分解後、スペース分解で、AND因子を求めます。 029 * 030 * 例) 031 * AAA BBB OR CCC ⇒「AAA BBB」OR 「CCC」 032 * AAA OR BBB CCC ⇒「AAA」OR 「BBB CCC」 033 * 034 * 判定方法は、ノーマル、大文字小文字を区別しない、全角半角を正規化するを指定します。 035 * 036 * @version 8.5 037 * @author Kazuhiko Hasegawa 038 * @since JDK17.0, 039 */ 040public final class HybsContains { 041 // List成分が、OR因子、文字列配列が、AND因子 042 private final List<String[]> andOrList = new ArrayList<>(); 043 private final boolean isIgnoreCase ; // true で大文字小文字を区別しない 044 private final boolean isNormalize ; // true でNormalize変換を使用する 045 046 /** 047 * コンストラクター 048 * 049 * AND OR 形式の文字列 を指定します。「OR」は、大文字固定で前後に半角スペースを入れます。 050 * AND の連結は、スペースで区切ります。OR の分割には、String#split を使いますが、ANDの 051 * 分割は、CSVTokenizer を使用するため、"xxx yyy"などで一連の文字列として処理できます。 052 * スペースで分割するため、ダブルクオートで括っても 前後のスペースは削除されます。 053 * 054 * @og.rev 8.5.0.0 (2023/04/21) 055 * 056 * @param andOrStr AND OR 形式の文字列 057 */ 058 public HybsContains( final String andOrStr ) { 059 this( andOrStr,false,false ); 060 } 061 062 /** 063 * コンストラクター 064 * 065 * @og.rev 8.5.0.0 (2023/04/21) 066 * 067 * @param andOrStr AND OR 形式の文字列 068 * @param isIgnoreCase true で大文字小文字を区別しない 069 * @param isNormalize true でNormalize変換を使用する 070 */ 071 public HybsContains( final String andOrStr, final boolean isIgnoreCase, final boolean isNormalize ) { 072 this.isIgnoreCase = isIgnoreCase; 073 this.isNormalize = isNormalize; 074 075 String base = andOrStr ; 076 if( isIgnoreCase ) { base = base.toUpperCase(Locale.JAPAN); } 077 if( isNormalize ) { base = Normalizer.normalize( base,Normalizer.Form.NFKC ); } 078 079 for( final String andStr : base.split( " OR " ) ) { // OR で分割 080 andOrList.add( StringUtil.csv2ArrayOnly( andStr,' ' ) ); // スペースで分割(文字列配列)をListに追加 081 } 082 } 083 084 /** 085 * 指定の文字列に、コンストラクタで指定したAND OR文字列が含まれるか判定します。 086 * 注意点としては、通常の String#contains() とは、引数が逆です。 087 * つまり、このメソッドの引数がベースとなって判定します。 088 * (通常の String#contains() は、引数が判定される部分文字列です) 089 * 090 * @og.rev 8.5.0.0 (2023/04/21) 091 * 092 * @param value 判定のベースとなる文字列 093 * @return AND OR文字列が含まれる場合は、true 094 */ 095 public boolean contains( final String value ) { 096 String base = value ; 097 if( isIgnoreCase ) { base = base.toUpperCase(Locale.JAPAN); } 098 if( isNormalize ) { base = Normalizer.normalize( base,Normalizer.Form.NFKC ); } 099 100 for( final String[] orAry : andOrList ) { // [A B C] [D] 101 boolean andFlag = true; 102 for( final String andStr : orAry ) { // [A B C] の配列を順番に処理 103 if( !base.contains( andStr ) ) { 104 andFlag = false; 105 break; // and 判定なので、含まないと即終了 106 } 107 } 108 if( andFlag ) { return true; } // or 判定なので、成立すれば、即完了 109 } 110 return false; 111 } 112 113 /** 114 * 指定の文字列に、コンストラクタで指定したAND OR文字列が含まれる場合、tag1 とtag2 で囲んだ。 115 * 文字列で置換した結果を返します。文字列が含まれない場合は、null を返します。 116 * このメソッドでは、各種置換後(大文字化や正規化)の文字列を返しますので、 117 * オリジナルの文字列と異なるのでご注意ください。 118 * 119 * @og.rev 8.5.0.0 (2023/04/21) 120 * 121 * @param value 判定のベースとなる文字列 122 * @param tag1 置換する場合の前文字列 123 * @param tag2 置換する場合の後文字列 124 * @return value 置換後の文字列(含まれない場合は、null) 125 */ 126 public String changeValue( final String value, final String tag1, final String tag2 ) { 127 String base = value ; 128 if( isIgnoreCase ) { base = base.toUpperCase(Locale.JAPAN); } 129 if( isNormalize ) { base = Normalizer.normalize( base,Normalizer.Form.NFKC ); } 130 131 final List<Integer[]> adrsList = new ArrayList<>(); 132 boolean orFlag = false; // ORフラグは、初期値 false 133 for( final String[] orAry : andOrList ) { 134 boolean andFlag = true; // ANDフラグは、初期値 true (一つでもfalseになれば false) 135 for( final String andStr : orAry ) { 136 boolean cngFlag = false; // 変更フラグは、初期値 false 137 int idx = base.lastIndexOf( andStr , base.length() ); // 後ろからbaseを検索 138 while( idx >= 0 ) { 139 cngFlag = true; // 変更フラグは、一つでも置換があれば、true 140 adrsList.add( new Integer[] { idx,idx + andStr.length() } ); 141 idx = base.lastIndexOf( andStr , idx-1 ); // 後ろからbaseを検索 142 } 143 andFlag = andFlag && cngFlag; // ANDフラグは、すべてが true でないと、true にならない。 144 } 145 orFlag = orFlag || andFlag ; // ORフラグは、一度でも true になれば、true 146 } 147 148 if( orFlag ) { 149 final boolean useOriginal = base.length() == value.length() ; // Normalize 変換時に、文字数が変わらなければ、オリジナルを置き換える。 150 final StringBuilder buf = new StringBuilder( useOriginal ? value : base ); 151 152 adrsList.sort( (s1, s2) -> s2[0] - s1[0] ); // アドレスを降順に並べ替える 153 for( final Integer[] adrs : adrsList ) { 154 buf.insert( adrs[1] , tag2 ); // 後ろから追加している。 155 buf.insert( adrs[0] , tag1 ); 156 } 157 158 return buf.toString(); 159 } 160 return null; 161 } 162}