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 /** true で大文字小文字を区別しない */ 044 private final boolean isIgnoreCase ; 045 /** true でNormalize変換を使用する */ 046 private final boolean isNormalize ; 047 048 /** 049 * コンストラクター 050 * 051 * AND OR 形式の文字列 を指定します。「OR」は、大文字固定で前後に半角スペースを入れます。 052 * AND の連結は、スペースで区切ります。OR の分割には、String#split を使いますが、ANDの 053 * 分割は、CSVTokenizer を使用するため、"xxx yyy"などで一連の文字列として処理できます。 054 * スペースで分割するため、ダブルクオートで括っても 前後のスペースは削除されます。 055 * 056 * @og.rev 8.5.0.0 (2023/04/21) 057 * 058 * @param andOrStr AND OR 形式の文字列 059 */ 060 public HybsContains( final String andOrStr ) { 061 this( andOrStr,false,false ); 062 } 063 064 /** 065 * コンストラクター 066 * 067 * @og.rev 8.5.0.0 (2023/04/21) 068 * 069 * @param andOrStr AND OR 形式の文字列 070 * @param isIgnoreCase true で大文字小文字を区別しない 071 * @param isNormalize true でNormalize変換を使用する 072 */ 073 public HybsContains( final String andOrStr, final boolean isIgnoreCase, final boolean isNormalize ) { 074 this.isIgnoreCase = isIgnoreCase; 075 this.isNormalize = isNormalize; 076 077 String base = andOrStr ; 078 if( isIgnoreCase ) { base = base.toUpperCase(Locale.JAPAN); } 079 if( isNormalize ) { base = Normalizer.normalize( base,Normalizer.Form.NFKC ); } 080 081 for( final String andStr : base.split( " OR " ) ) { // OR で分割 082 andOrList.add( StringUtil.csv2ArrayOnly( andStr,' ' ) ); // スペースで分割(文字列配列)をListに追加 083 } 084 } 085 086 /** 087 * 指定の文字列に、コンストラクタで指定したAND OR文字列が含まれるか判定します。 088 * 注意点としては、通常の String#contains() とは、引数が逆です。 089 * つまり、このメソッドの引数がベースとなって判定します。 090 * (通常の String#contains() は、引数が判定される部分文字列です) 091 * 092 * @og.rev 8.5.0.0 (2023/04/21) 093 * 094 * @param value 判定のベースとなる文字列 095 * @return AND OR文字列が含まれる場合は、true 096 */ 097 public boolean contains( final String value ) { 098 String base = value ; 099 if( isIgnoreCase ) { base = base.toUpperCase(Locale.JAPAN); } 100 if( isNormalize ) { base = Normalizer.normalize( base,Normalizer.Form.NFKC ); } 101 102 // 8.5.5.1 (2024/02/29) PMD 7.0.0 OnlyOneReturn メソッドには終了ポイントが 1 つだけ必要 103 boolean rtnFlag = false; 104 for( final String[] orAry : andOrList ) { // [A B C] [D] 105 boolean andFlag = true; 106 for( final String andStr : orAry ) { // [A B C] の配列を順番に処理 107 if( !base.contains( andStr ) ) { 108 andFlag = false; 109 break; // and 判定なので、含まないと即終了 110 } 111 } 112// if( andFlag ) { return true; } // or 判定なので、成立すれば、即完了 113 if( andFlag ) { rtnFlag = true; break; } // or 判定なので、成立すれば、即完了 114 } 115// return false; 116 return rtnFlag; 117 } 118 119 /** 120 * 指定の文字列に、コンストラクタで指定したAND OR文字列が含まれる場合、tag1 とtag2 で囲んだ。 121 * 文字列で置換した結果を返します。文字列が含まれない場合は、null を返します。 122 * このメソッドでは、各種置換後(大文字化や正規化)の文字列を返しますので、 123 * オリジナルの文字列と異なるのでご注意ください。 124 * 125 * @og.rev 8.5.0.0 (2023/04/21) 126 * 127 * @param value 判定のベースとなる文字列 128 * @param tag1 置換する場合の前文字列 129 * @param tag2 置換する場合の後文字列 130 * @return value 置換後の文字列(含まれない場合は、null) 131 */ 132 public String changeValue( final String value, final String tag1, final String tag2 ) { 133 String base = value ; 134 if( isIgnoreCase ) { base = base.toUpperCase(Locale.JAPAN); } 135 if( isNormalize ) { base = Normalizer.normalize( base,Normalizer.Form.NFKC ); } 136 137 final List<Integer[]> adrsList = new ArrayList<>(); 138 boolean orFlag = false; // ORフラグは、初期値 false 139 for( final String[] orAry : andOrList ) { 140 boolean andFlag = true; // ANDフラグは、初期値 true(一つでもfalseになれば false) 141 for( final String andStr : orAry ) { 142 boolean cngFlag = false; // 変更フラグは、初期値 false 143 int idx = base.lastIndexOf( andStr , base.length() ); // 後ろからbaseを検索 144 while( idx >= 0 ) { 145 cngFlag = true; // 変更フラグは、一つでも置換があれば、true 146 adrsList.add( new Integer[] { idx,idx + andStr.length() } ); 147 idx = base.lastIndexOf( andStr , idx-1 ); // 後ろからbaseを検索 148 } 149 andFlag = andFlag && cngFlag; // ANDフラグは、すべてが true でないと、true にならない。 150 } 151 orFlag = orFlag || andFlag ; // ORフラグは、一度でも true になれば、true 152 } 153 154 // 8.5.5.1 (2024/02/29) PMD 7.0.0 OnlyOneReturn メソッドには終了ポイントが 1 つだけ必要 155 String rtn = null; 156 if( orFlag ) { 157 final boolean useOriginal = base.length() == value.length() ; // Normalize 変換時に、文字数が変わらなければ、オリジナルを置き換える。 158 final StringBuilder buf = new StringBuilder( useOriginal ? value : base ); 159 160 adrsList.sort( (s1, s2) -> s2[0] - s1[0] ); // アドレスを降順に並べ替える 161 for( final Integer[] adrs : adrsList ) { 162 buf.insert( adrs[1] , tag2 ); // 後ろから追加している。 163 buf.insert( adrs[0] , tag1 ); 164 } 165 166// return buf.toString(); 167 rtn = buf.toString(); 168 } 169// return null; 170 return rtn; 171 } 172}