1
2
3
4
5
6
7
8
9 package tsukuba_bunko.peko.scenario;
10
11 import java.util.StringTokenizer;
12
13 import org.xml.sax.Attributes;
14
15 import tsukuba_bunko.peko.Logger;
16
17 import tsukuba_bunko.peko.scenario.SceneContext;
18
19
20 /***
21 * PSML の処理に必要なユーティリティメソッドを提供します。
22 * @author $Author: ppoi $
23 * @version $Revision: 1.1 $
24 */
25 public final class PSMLUtil {
26
27 /***
28 * <code>PSMLUtil</code> のインスタンスは生成できません。
29 */
30 private PSMLUtil()
31 {
32 }
33
34
35 /***
36 * ISO 制御文字を除去します。
37 * @param string 除去対象の文字列
38 * @return 除去処理後の文字列
39 */
40 public static String removeISOControlChar( String string )
41 {
42 if( (string == null) || (string.length() == 0) ) {
43 return string;
44 }
45
46 int beginIndex = -1;
47 boolean isISOControlCharSequence = false;
48
49 int length = string.length();
50 StringBuffer buffer = null;
51 for( int current = 0; current < length; ++current ) {
52 if( Character.isISOControl(string.charAt(current)) ) {
53 if( !isISOControlCharSequence ) {
54 if( buffer == null ) {
55 if( current == 0 ) {
56 buffer = new StringBuffer();
57 }
58 else {
59 buffer = new StringBuffer( string.substring(0, current) );
60 }
61 }
62 else {
63 buffer.append( string.substring(beginIndex, current) );
64 }
65 isISOControlCharSequence = true;
66 beginIndex = -1;
67 }
68 }
69 else {
70 if( isISOControlCharSequence ) {
71 beginIndex = current;
72 isISOControlCharSequence = false;
73 }
74 }
75 }
76 if( beginIndex != -1 ) {
77 buffer.append( string.substring(beginIndex) );
78 }
79
80 if( buffer != null ) {
81 return new String( buffer );
82 }
83 else {
84 return string;
85 }
86 }
87
88
89 /***
90 * PSML 1.0 "Scene" ネームスペース、ローカルネームスペースの順で属性値を検索します。
91 * @param localName 属性名のローカル名
92 * @return 属性値。属性が定義されていない場合は <code>null</code>
93 */
94 public static String getAttributeValue( Attributes attrs, String localName )
95 {
96 String value = attrs.getValue( ElementHandler.NAMESPACE_SCENE, localName );
97 if( value == null ) {
98 value = attrs.getValue( localName );
99 }
100
101 if( (value == null) || (value.length() == 0) ) {
102 return null;
103 }
104 else {
105 value = PSMLUtil.removeISOControlChar(value).trim();
106 if( value.length() == 0 ) {
107 Logger.warn( MessageIDs.SCN0011W, new Object[]{localName} );
108 return null;
109 }
110 else {
111 return value;
112 }
113 }
114 }
115
116 /***
117 * <code>attrs</code> からテストを取り出し、シナリオ文脈 <code>context</code> で評価可能かどうかを判定します。
118 * @param attrs 要素の属性セット
119 * @param context 現在のシナリオ文脈
120 * @return 評価可能の場合 <code>true</code>、評価不可の場合 <code>false</code>
121 */
122 public static final boolean isEvaluatable( Attributes attrs, SceneContext context )
123 {
124 return isEvaluatable( PSMLUtil.getAttributeValue(attrs, "test"), context );
125 }
126
127 /***
128 * <code>target</code> で指定されるテストを使用し、シナリオ文脈 <code>context</code> で評価可能かどうかを判定します。
129 * @param target テスト
130 * @param context 現在のシナリオ文脈
131 * @return 評価可能の場合 <code>true</code>、評価不可の場合 <code>false</code>
132 */
133 public static final boolean isEvaluatable( String target, SceneContext context )
134 {
135 if( (target == null) || (target.length() == 0) ) {
136 return true;
137 }
138 else if( context == null ) {
139 return false;
140 }
141 else {
142 boolean result = false;
143
144 boolean multiple = (target.charAt(0) == '[');
145 boolean inBracket = false;
146 boolean individualResult = true;
147
148 StringTokenizer st = new StringTokenizer( target, "[],", true );
149 String token = null;
150 String lastToken = null;
151 int position = 0;
152 while( st.hasMoreTokens() ) {
153 lastToken = token;
154 token = st.nextToken();
155 position += token.length();
156 token = token.trim();
157
158 if( !multiple ) {
159 if( "[".equals(token) || "]".equals(token) ) {
160 Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
161 return false;
162 }
163 else if( !",".equals(token) ) {
164 if( token.charAt(0) == '!' ) {
165 if( token.length() == 1 ) {
166 Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
167 return false;
168 }
169 token = token.substring( 1 );
170 individualResult = individualResult && !context.isDeclaredFlag(token);
171 }
172 else {
173 individualResult = individualResult && context.isDeclaredFlag(token);
174 }
175 }
176 }
177 else {
178 if( inBracket ) {
179 if( "]".equals(token) ) {
180 if( (lastToken != null) && ",".equals(lastToken) ) {
181 Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
182 return false;
183 }
184 else {
185 result = result || individualResult;
186 individualResult = true;
187 inBracket = false;
188 }
189 }
190 else if( !",".equals(token) ) {
191 if( token.charAt(0) == '!' ) {
192 if( token.length() == 1 ) {
193 Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
194 return false;
195 }
196 token = token.substring( 1 );
197 individualResult = individualResult && !context.isDeclaredFlag(token);
198 }
199 else {
200 individualResult = individualResult && context.isDeclaredFlag(token);
201 }
202 }
203 }
204 else {
205 if( "[".equals(token) ) {
206 inBracket = true;
207 }
208 else {
209 Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
210 return false;
211 }
212 }
213 }
214 }
215
216 if( multiple ) {
217 if( !"]".equals(token) ) {
218 Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
219 return false;
220 }
221 }
222 else {
223 if( ",".equals(token) ) {
224 Logger.warn( MessageIDs.SCN0008W, new Object[]{String.valueOf(position), token} );
225 return false;
226 }
227 else {
228 result = individualResult;
229 }
230 }
231 return result;
232 }
233 }
234 }