1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package tsukuba_bunko.peko.scenario;
20
21 import java.io.FileNotFoundException;
22 import java.io.InputStream;
23
24 import java.net.URL;
25
26 import javax.xml.parsers.ParserConfigurationException;
27 import javax.xml.parsers.SAXParser;
28 import javax.xml.parsers.SAXParserFactory;
29
30 import org.xml.sax.Attributes;
31 import org.xml.sax.InputSource;
32 import org.xml.sax.SAXException;
33 import org.xml.sax.SAXParseException;
34 import org.xml.sax.helpers.DefaultHandler;
35
36 import tsukuba_bunko.peko.ActionControler;
37 import tsukuba_bunko.peko.Logger;
38 import tsukuba_bunko.peko.PekoSystem;
39
40 import tsukuba_bunko.peko.resource.ResourceManager;
41
42 import tsukuba_bunko.peko.scenario.select.SelectCoordinator;
43
44 import tsukuba_bunko.peko.scenario.stage.StageCoordinator;
45
46 import tsukuba_bunko.peko.scenario.text.TextCoordinator;
47
48 import tsukuba_bunko.peko.session.Session;
49
50
51 /***
52 * シーンの処理を行うプロセッサモジュールです。
53 * @author $Author: ppoi $
54 * @version $Revision: 1.8 $
55 */
56 public class SceneProcessor extends DefaultHandler implements Runnable {
57
58 /***
59 * PSML 1.0 Scene DTD Public ID
60 */
61 public static final String PSML_PUBLICID = "-//Tsukuba Bunko//DTD PSML 1.0 Scene//EN";
62
63 /***
64 * PSML 1.0 Scene (ILLEGAL) DTD Public ID
65 */
66 public static final String PSML_PUBLICID_ILLEGAL = "-//Tsukuba Bunko//DTD PSML Scene 1.0//EN";
67
68 /***
69 * PSML 1.0 Scene DTD System ID
70 */
71 public static final String PSML_SYSTEMID = "http://softlab.tsukuba-bunko.org/dtd/psml10-scene.dtd";
72
73
74 /***
75 * PSML 解析を行う SAX Parser
76 */
77 protected SAXParser _parser = null;
78
79 /***
80 * 処理状態フラグ
81 */
82 protected boolean _running = false;
83
84 /***
85 * 中止フラグ
86 */
87 protected boolean _aborted = false;
88
89 /***
90 * 処理中のシーン
91 */
92 protected URL _sceneURL = null;
93
94 /***
95 * 処理中のシーンのコンテクスト
96 */
97 protected SceneContext _sceneContext = null;
98
99 /***
100 * この SceneProcessor を制御する ScenarioProcessor
101 */
102 protected ScenarioProcessor _owner = null;
103
104 /***
105 * 現在のツリー中のレベル
106 */
107 protected int _level = -1;
108
109 /***
110 * ElementHandler が処理中のサブツリーの開始レベル
111 */
112 protected int _processStartedLevel = -1;
113
114 /***
115 * 評価しないサブツリーの開始レベル
116 */
117 protected int _ignoreStartedLevel = -1;
118
119 /***
120 * ハンドラレジストリ
121 */
122 protected HandlerRegistry _registry = null;
123
124 /***
125 * 現在要素を処理中の ElementHandler
126 */
127 protected ElementHandler _handler = null;
128
129 /***
130 * スタートのノード位置
131 */
132 protected SceneContext.Node _startNode = null;
133
134
135 /***
136 * TextCanvas コーディネータ
137 */
138 protected TextCoordinator _textCoordinator = null;
139
140 /***
141 * StageCanvas コーディネータ
142 */
143 protected StageCoordinator _stageCoordinator = null;
144
145 /***
146 * SelectCanvas コーディネータ
147 */
148 protected SelectCoordinator _selectCoordinator = null;
149
150
151 /***
152 * <code>SceneProcessor</code> のインスタンスを生成します。
153 * @param owner この SceneProcessor インスタンスを制御する ScenarioProcessor
154 */
155 public SceneProcessor( ScenarioProcessor owner )
156 {
157 super();
158 _owner = owner;
159 _registry = HandlerRegistry.newInstance();
160 getSAXParser();
161 Logger.debug( "[scenario] create SceneProcessor." );
162 }
163
164
165 /***
166 * シーンコンテクストを取得します。
167 * @return シーンコンテクスト
168 */
169 public SceneContext getSceneContext()
170 {
171 return _sceneContext;
172 }
173
174
175 /***
176 * ハンドラレジストリを取得します。
177 * @return ハンドラレジストリ
178 */
179 public HandlerRegistry getHandlerRegistry()
180 {
181 return _registry;
182 }
183
184 /***
185 * TextCanvas コーディネータを設定します。
186 * @param coordinator TextCanvas コーディネータ
187 */
188 public void setTextCoordinator( TextCoordinator coordinator )
189 {
190 _textCoordinator = coordinator;
191 }
192
193 /***
194 * TextCanvas コーディネータを取得します。
195 * @return TextCanvas コーディネータ
196 */
197 public TextCoordinator getTextCoordinator()
198 {
199 return _textCoordinator;
200 }
201
202 /***
203 * StageCanvas コーディネータを設定します。
204 * @param coordinator StageCanvas コーディネータ
205 */
206 public void setStageCoordinator( StageCoordinator coordinator )
207 {
208 _stageCoordinator = coordinator;
209 }
210
211 /***
212 * StageCanvas コーディネータを取得します。
213 * @return StageCanvas コーディネータ
214 */
215 public StageCoordinator getStageCoordinator()
216 {
217 return _stageCoordinator;
218 }
219
220 /***
221 * SelectCanvas コーディネータを設定します。
222 * @param coordinator SelectCanvas コーディネータ
223 */
224 public void setSelectCoordinator( SelectCoordinator coordinator )
225 {
226 _selectCoordinator = coordinator;
227 }
228
229 /***
230 * SelectCanvas コーディネータを取得します。
231 * @return SelectCanvas コーディネータ
232 */
233 public SelectCoordinator getSelectCoordinator()
234 {
235 return _selectCoordinator;
236 }
237
238
239 /***
240 * シーンの処理を開始します。
241 * @param sceneName 処理するシーンのシーン名
242 */
243 public void process( String sceneName, Session session )
244 {
245 if( _running ) {
246 IllegalStateException ise = new IllegalStateException( "scene processor is still running." );
247 Logger.fatal( MessageIDs.SCN0007F );
248 PekoSystem.showErrorDialog( MessageIDs.SCN0007F.getMessage(), ise.fillInStackTrace(), true );
249 }
250 _sceneURL = getSceneURL( sceneName );
251 _sceneContext = new SceneContext( sceneName, session, this );
252 _startNode = _sceneContext.getLastCommittedNode();
253 if( _startNode != null ) {
254 Logger.debug( "[scenario.scene] start from: " + _startNode.getPath() );
255 }
256
257 Thread thread = new Thread( this );
258 session.setSceneContext( _sceneContext, thread );
259
260 _textCoordinator.prepare( _sceneContext, thread );
261 _stageCoordinator.prepare( _sceneContext, thread );
262 _selectCoordinator.prepare( _sceneContext, thread );
263
264 _aborted = false;
265 thread.start();
266 }
267
268 /***
269 * シーンの処理を中止します。
270 */
271 public void abort()
272 {
273 _aborted = true;
274 _textCoordinator.dormantize();
275 _stageCoordinator.dormantize();
276 _selectCoordinator.dormantize();
277 Logger.debug( "[scenario] release stopped thread." );
278 ActionControler controler = PekoSystem.getInstance().getActionControler();
279 controler.start();
280 }
281
282 /***
283 * シーン処理が中止されているかどうかを判定します。
284 * @return シーン処理が中止されている場合 <code>true</code>、それ以外の場合 <code>false</code>
285 */
286 public boolean isAborted()
287 {
288 return _aborted;
289 }
290
291 /***
292 * PSML Scene データの構文解析を行う SAX パーザを取得します。
293 * @return PSML Scene データの構文解析を行う SAX パーザ
294 */
295 protected SAXParser getSAXParser()
296 {
297 if( _parser == null ) {
298 try {
299 SAXParserFactory factory = SAXParserFactory.newInstance();
300 factory.setNamespaceAware( true );
301 if( "on".equals(System.getProperty("debug", "off")) ) {
302 Logger.debug( "[scenario.scene] set PSML validating on." );
303 factory.setValidating( true );
304 }
305 else {
306 Logger.debug( "[scenario.scene] set PSML validating off." );
307 factory.setValidating( false );
308 }
309 _parser = factory.newSAXParser();
310 }
311 catch( SAXException se ) {
312 Logger.fatal( MessageIDs.SCN0000F );
313 PekoSystem.showErrorDialog( MessageIDs.SCN0000F.getMessage(), se, true );
314 }
315 catch( ParserConfigurationException pce ) {
316 Logger.fatal( MessageIDs.SCN0000F );
317 PekoSystem.showErrorDialog( MessageIDs.SCN0000F.getMessage(), pce, true );
318 }
319 }
320 return _parser;
321 }
322
323 /***
324 * シーンデータの URL を取得します。
325 * @param scene シーン名
326 * @return シーンデータの URL
327 */
328 protected URL getSceneURL( String scene )
329 {
330 ResourceManager resources = ResourceManager.getInstance();
331 URL sceneDir = resources.getLocationResources().getScenesDirecotryURL();
332 try {
333 return new URL( sceneDir, scene );
334 }
335 catch( Exception e ) {
336 Logger.fatal( MessageIDs.SCN0006F, e );
337 PekoSystem.showErrorDialog( MessageIDs.SCN0006F.getMessage(), e, true );
338 return null;
339 }
340 }
341
342
343
344
345 public void run()
346 {
347 _running = true;
348 InputStream is = null;
349 try {
350 Logger.debug( _sceneURL.toString() );
351 is = _sceneURL.openStream();
352 SAXParser parser = getSAXParser();
353 parser.parse( is, this );
354 }
355 catch( SAXParseException spe ) {
356 PekoSystem.getInstance().getActionControler().returnTitle( true );
357 }
358 catch( FileNotFoundException fnfe ) {
359 Logger.error( MessageIDs.SCN0014E, new Object[]{getSceneContext().getSceneName()} );
360 PekoSystem.getInstance().getActionControler().returnTitle( true );
361 }
362 catch( Exception e ) {
363 Logger.fatal( MessageIDs.SCN0005F, new Object[]{_sceneContext.getCurrentPath()}, e );
364 PekoSystem.showErrorDialog( MessageIDs.SCN0005F.getMessage(new Object[]{_sceneContext.getCurrentPath()}), e, true );
365 }
366 finally {
367 if( is != null ) {
368 try {
369 is.close();
370 Logger.debug( "[scene] close scene. scene=" + getSceneContext().getSceneName() );
371 }
372 catch( Exception e ) {
373
374 }
375 }
376 }
377 _running = false;
378 _owner.pushSceneProcessor( this );
379 }
380
381
382
383
384
385 public void startDocument()
386 throws SAXException
387 {
388 if( _aborted ) {
389 return;
390 }
391
392 _level = 0;
393 _processStartedLevel = -1;
394 _ignoreStartedLevel = -1;
395 _handler = null;
396 }
397
398 public void endDocument()
399 throws SAXException
400 {
401 if( _aborted ) {
402 return;
403 }
404 else {
405 _owner.sceneEnded( this );
406 }
407 }
408
409 public void startElement( String namespaceURI, String localName, String qName, Attributes attrs )
410 throws SAXException
411 {
412 if( _aborted ) {
413 return;
414 }
415
416 ++_level;
417 _sceneContext.pushNode( qName );
418
419 if( _startNode != null ) {
420 if( _sceneContext.isCurrentNode(_startNode) ) {
421 _startNode = null;
422 }
423 else {
424 return;
425 }
426 }
427 else if( _ignoreStartedLevel != -1 ) {
428 return;
429 }
430 else if( !PSMLUtil.isEvaluatable(attrs, _sceneContext) ) {
431 _ignoreStartedLevel = _level;
432 return;
433 }
434
435 if( _handler == null ) {
436 _handler = _registry.getElementHandler( namespaceURI, localName );
437 if( _handler == null ) {
438 return;
439 }
440 else {
441 _sceneContext.saveCurrentNode();
442 _handler.setSceneContext( _sceneContext );
443 _processStartedLevel = _level;
444 _handler.startDocument();
445 }
446 }
447 try {
448 _handler.startElement( namespaceURI, localName, qName, attrs );
449 }
450 catch( Exception e ) {
451 Logger.fatal( MessageIDs.SCN0005F, new Object[]{_sceneContext.getCurrentPath()}, e );
452 PekoSystem.showErrorDialog( MessageIDs.SCN0005F.getMessage(new Object[]{_sceneContext.getCurrentPath()}), e, true );
453 }
454 }
455
456 public void endElement( String namespaceURI, String localName, String qName )
457 throws SAXException
458 {
459 if( _aborted ) {
460 return;
461 }
462
463 if( _startNode != null ) {
464
465 }
466 else if( _level == _ignoreStartedLevel ) {
467 _ignoreStartedLevel = -1;
468 }
469 else if( _ignoreStartedLevel != -1 ) {
470
471 }
472 else if( _handler != null ) {
473 try {
474 _handler.endElement( namespaceURI, localName, qName );
475 if( _level == _processStartedLevel ) {
476 _handler.endDocument();
477 if( _handler.isEndOfScene() ) {
478 _owner.sceneEnded( this );
479 _aborted = true;
480 }
481 _handler = null;
482 _processStartedLevel = -1;
483 }
484 }
485 catch( Exception e ) {
486 Logger.fatal( MessageIDs.SCN0005F, new Object[]{_sceneContext.getCurrentPath()}, e );
487 PekoSystem.showErrorDialog( MessageIDs.SCN0005F.getMessage(new Object[]{_sceneContext.getCurrentPath()}), e, true );
488 }
489 }
490
491 --_level;
492 _sceneContext.popNode();
493 }
494
495 public void characters( char[] ch, int begin, int length )
496 throws SAXException
497 {
498 if( _aborted ) {
499 return;
500 }
501
502 if( (_ignoreStartedLevel != -1 ) || (_handler == null) ) {
503 return;
504 }
505 else if( _handler != null ) {
506 _handler.characters( ch, begin, length );
507 }
508 }
509
510 public void processingInstruction( String target, String data )
511 {
512 }
513
514
515
516
517 public void warning( SAXParseException exception )
518 {
519 Logger.warn( MessageIDs.SCN0002W, new Object[]{exception.getMessage(), _sceneContext.getCurrentPath()}, exception );
520 }
521
522 public void error( SAXParseException exception )
523 {
524 Logger.error( MessageIDs.SCN0003E, new Object[]{exception.getMessage(), _sceneContext.getCurrentPath()}, exception );
525 }
526
527 public void fatalError( SAXParseException exception )
528 {
529 Logger.error( MessageIDs.SCN0004E, new Object[]{exception.getMessage(), _sceneContext.getCurrentPath()}, exception );
530 }
531
532
533
534
535
536 public InputSource resolveEntity( String publicId, String systemId )
537 {
538 boolean isPSMLScene = false;
539 if( publicId != null ) {
540 if( SceneProcessor.PSML_PUBLICID.equals(publicId) ) {
541 isPSMLScene = true;
542 }
543 else if( SceneProcessor.PSML_PUBLICID_ILLEGAL.equals(publicId) ) {
544 Logger.warn( MessageIDs.SCN0013W );
545 isPSMLScene = true;
546 }
547 }
548 else if( systemId != null ) {
549 if( SceneProcessor.PSML_SYSTEMID.equals(systemId) ) {
550 isPSMLScene = true;
551 }
552 }
553
554 if( isPSMLScene ) {
555 InputStream is = null;
556 Logger.debug( "Load internal PSML 1.0 Scene DTD." );
557 try {
558 is = getClass().getClassLoader().getResourceAsStream( "psml10-scene.dtd" );
559 }
560 catch( Exception e ) {
561 Logger.warn( MessageIDs.SCN0001W, e );
562 return null;
563 }
564
565 if( is != null ) {
566 return new InputSource( is );
567 }
568 else {
569 Logger.warn( MessageIDs.SCN0001W );
570 return null;
571 }
572 }
573 else {
574 return null;
575 }
576 }
577 }