Source code: com/trapezium/chisel/ProcessedFile.java
1 /*
2 * @(#)ProcessedFile.java
3 *
4 * Copyright (c) 1998-2000 by Trapezium Development LLC. All Rights Reserved.
5 *
6 * The information in this file is the property of Trapezium Development LLC
7 * and may be used only in accordance with the terms of the license granted
8 * by Trapezium.
9 */
10 package com.trapezium.chisel;
11
12 import java.awt.*;
13 import java.awt.event.*;
14 import java.io.*;
15 import java.net.URL;
16 import java.util.*;
17 import java.util.zip.*;
18
19 import com.trapezium.factory.FactoryData;
20 import com.trapezium.factory.FactoryChain;
21 import com.trapezium.factory.QueuedRequestFactory;
22 import com.trapezium.factory.TokenStreamFactory;
23 import com.trapezium.factory.FactoryResponseListener;
24 import com.trapezium.factory.ParserFactory;
25 import com.trapezium.util.GlobalProgressIndicator;
26 import com.trapezium.util.ProgressIndicator;
27 import com.trapezium.vrml.*;
28 import com.trapezium.vrml.grammar.*;
29 import com.trapezium.edit.TokenEditor;
30 import com.trapezium.edit.EditLintVisitor;
31 import com.trapezium.parse.TokenEnumerator;
32 import com.trapezium.chisel.gui.ChiselController;
33 import com.trapezium.html.HTMLgenerator;
34 import com.trapezium.vrml.visitor.X3dWriter;
35
36 import com.trapezium.edit.Document;
37
38 /** a ProcessedFile object is a FactoryData object that knows how to load
39 itself, reload itself from its original source, and save itself. */
40 public class ProcessedFile extends FactoryData implements Serializable {
41
42 /** URL for the source; null if source is a local file */
43 //URL url;
44
45 /** file to save to; if the source is a remote URL, this is a locally cached
46 copy of the retrieved file. If the source is a local URL, then this
47 File is just the URL opened as a file */
48 //File file;
49
50 /** way to create the data given the source */
51 FactoryChain loader;
52
53 // for the moment, ParserFactory is not thread-safe, only have one for entire system
54 QueuedRequestFactory parser;
55 QueuedRequestFactory tokenFactory;
56 Session session;
57
58 public void wipeout() {
59 super.wipeout();
60 if ( parser != null ) {
61 parser.wipeout();
62 parser = null;
63 }
64 if ( tokenFactory != null ) {
65 tokenFactory.wipeout();
66 tokenFactory = null;
67 }
68 if ( loader != null ) {
69 loader.wipeout();
70 loader = null;
71 }
72 session = null;
73 }
74
75 public QueuedRequestFactory getParserFactory() {
76 return( parser );
77 }
78
79 public QueuedRequestFactory getTokenFactory() {
80 return( tokenFactory );
81 }
82
83 public ProcessedFile() {
84 this(null);
85 }
86
87 /* create a ProcessedFile, load it and parse it */
88 static int id = 1;
89 int myId;
90
91 boolean newlyOpened;
92 public boolean isNewlyOpened() {
93 return( newlyOpened );
94 }
95
96 public void notNewlyOpened() {
97 newlyOpened = false;
98 }
99
100
101 int versionNumber;
102 public ProcessedFile(String source) {
103 myId = id;
104 newlyOpened = true;
105 id++;
106 versionNumber = 1;
107 //System.out.println( "Creating processedFile " + myId );
108 setUrl(source);
109 loader = new FactoryChain( ChiselSet.VALIDATORS );
110 tokenFactory = new TokenStreamFactory();
111 parser = new ParserFactory();
112 loader.addFactory(tokenFactory);
113 loader.addFactory(parser);
114 }
115
116 /** Get the version number for the file */
117 public int getVersion() {
118 return( versionNumber );
119 }
120
121 /** Get the version number as a String */
122 public String getVersionString() {
123 return( "v" + versionNumber + ": " );
124 }
125
126 /** Set the version number for the file */
127 public void setVersion( int versionNo ) {
128 versionNumber = versionNo;
129 markVersion( versionNumber );
130 Scene scene = getScene();
131 if ( scene != null ) {
132 GlobalProgressIndicator.setProgressIndicator( ChiselSet.getProgressIndicator( ChiselSet.VALIDATORS ), "Serializing...", scene.getVrmlElementCount() );
133 getDocument().setViewerVersion( versionNo, scene );
134 }
135 }
136
137 public void initProgressIndicator( String title ) {
138 Scene scene = getScene();
139 GlobalProgressIndicator.setProgressIndicator( ChiselSet.getProgressIndicator( ChiselSet.VALIDATORS ), title, scene.getVrmlElementCount() );
140 }
141
142
143 /** Update a single listener text field, sets the text and checks box off.
144 *
145 * @param count count used to update
146 * @param strheader String identifying type of data
147 * @param frl FactoryResponseListener, where update is sent
148 * @param testProfile text is appended here for progress check
149 *
150 * @return true if the file is not totally clean, otherwise false
151 */
152 boolean updateListener( int count, String strheader, FactoryResponseListener frl, StringBuffer testProfile ) {
153 String txt = count + strheader;
154 frl.setText( txt );
155 boolean flag = (count > 0);
156 if ( frl instanceof ChiselRow ) {
157 ChiselRow cr = (ChiselRow)frl;
158 if ( cr.isAutomaticallyChecked() ) {
159 setRowInfo(cr, flag);
160 }
161 }
162 if ( testProfile != null ) {
163 testProfile.append( txt );
164 }
165 return( flag );
166 }
167
168 /** Update the summary info in all the chisel rows.
169 *
170 * @param cleanCount number of times file cleaned, some chisels automatically updated
171 * only on first pass through (where cleanCount==0)
172 *
173 * @return true if the file is not totally cleaned and the info profile has
174 * changed. The info profile is necessary to prevent useless repeated
175 * attempts to clean a file, which cannot be cleaned for whatever reason.
176 */
177 String infoProfile = null;
178 int cleanCount = 0;
179 public void setCleanCount( int ccval ) {
180 cleanCount = ccval;
181 }
182 public int getCleanCount() {
183 return( cleanCount );
184 }
185 public boolean updateInfo() {
186 boolean needSignal = false;
187 StringBuffer testProfile = new StringBuffer();
188 EditLintVisitor lv = getLintInfo();
189 if ( lv != null ) {
190 FactoryResponseListener frl = ChiselSet.getSpecializedListener( ChiselSet.InlineCountListener );
191 if ( frl != null ) {
192 frl.setText( lv.getInlineCount() + " inline" );
193 }
194
195 // unused DEF's don't set the signal
196 frl = ChiselSet.getSpecializedListener( ChiselSet.UnusedDEFCountListener );
197 if ( frl != null ) {
198 frl.setText( lv.getUnusedDEFCount() + " unused" );
199 /* boolean flag = (lv.getUnusedDEFCount() > 0);
200 // turn it off it was on
201 if ( frl instanceof ChiselRow ) {
202 setRowInfo((ChiselRow)frl, flag);
203 }*/
204 }
205
206 frl = ChiselSet.getSpecializedListener( ChiselSet.DefaultFieldCountListener );
207 if ( frl != null ) {
208 needSignal = updateListener( lv.getDefaultFieldCount(), " default", frl, testProfile ) || needSignal;
209 }
210 frl = ChiselSet.getSpecializedListener( ChiselSet.UnusedCoordCountListener );
211 FactoryResponseListener unusedListener = frl;
212 if ( frl != null ) {
213 needSignal = updateListener( lv.getUnusedCoordCount(), " unused", frl, testProfile ) || needSignal;
214 }
215 frl = ChiselSet.getSpecializedListener( ChiselSet.UnusedPROTOinterfaceCountListener );
216 if ( frl != null ) {
217 updateListener( lv.getUnusedPROTOinterfaceCount(), " unused", frl, testProfile );
218 }
219 frl = ChiselSet.getSpecializedListener( ChiselSet.DuplicateFieldCountListener );
220 if ( frl != null ) {
221 needSignal = updateListener( lv.getDuplicateFieldCount(), " repeated", frl, testProfile ) || needSignal;
222 }
223 frl = ChiselSet.getSpecializedListener( ChiselSet.SingleColorIFScountListener );
224 if ( frl != null ) {
225 needSignal = updateListener( lv.getSingleColorIFScount(), " single colored IFS", frl, testProfile ) || needSignal;
226 }
227 frl = ChiselSet.getSpecializedListener( ChiselSet.EmptyIndexedFaceSetCountListener );
228 if ( frl != null ) {
229 needSignal = updateListener( lv.getEmptyIndexedFaceSetCount(), " empty", frl, testProfile ) || needSignal;
230 }
231 frl = ChiselSet.getSpecializedListener( ChiselSet.ElevationGridCountListener );
232 if ( frl != null ) {
233 if ( lv.getElevationGridCount() == 1 ) {
234 frl.setText( "1 grid" );
235 } else {
236 frl.setText( lv.getElevationGridCount() + " grids" );
237 }
238 }
239 frl = ChiselSet.getSpecializedListener( ChiselSet.TransformCountListener );
240 if ( frl != null ) {
241 if ( lv.getTransformCount() == 1 ) {
242 frl.setText( "1 transform" );
243 } else {
244 frl.setText( lv.getTransformCount() + " transforms" );
245 }
246 }
247 frl = ChiselSet.getSpecializedListener( ChiselSet.DEFUSECountListener );
248 if ( frl != null ) {
249 int defcount = lv.getDEFcount();
250 int usecount = lv.getUSEcount();
251 frl.setText( defcount + " DEF, " + usecount + " USE" );
252 }
253 frl = ChiselSet.getSpecializedListener( ChiselSet.DEFUSECountListener2 );
254 if ( frl != null ) {
255 int defcount = lv.getDEFcount();
256 int usecount = lv.getUSEcount();
257 frl.setText( defcount + " DEF, " + usecount + " USE" );
258 }
259 frl = ChiselSet.getSpecializedListener( ChiselSet.DupIndexCountListener );
260 if ( frl != null ) {
261 needSignal = updateListener( lv.getDupIndexCount(), " repeated", frl, testProfile ) || needSignal;
262 }
263 frl = ChiselSet.getSpecializedListener( ChiselSet.BadFaceCountListener );
264 if ( frl != null ) {
265 needSignal = updateListener( lv.getBadFaceCount(), " bad", frl, testProfile ) || needSignal;
266 }
267 frl = ChiselSet.getSpecializedListener( ChiselSet.RepeatedValueCountListener );
268 if ( frl != null ) {
269 boolean flag = updateListener( lv.getRepeatedValueCount(), " repeated", frl, testProfile );
270 if ( frl instanceof ChiselRow ) {
271 if ( flag ) {
272 setRowInfo((ChiselRow)unusedListener, flag );
273 }
274 }
275 needSignal = needSignal || flag;
276 }
277 frl = ChiselSet.getSpecializedListener( ChiselSet.BadRouteListener );
278 if ( frl != null ) {
279 updateListener( lv.getBadRouteCount(), " bad", frl, null );
280 }
281 frl = ChiselSet.getSpecializedListener( ChiselSet.NormalCountListener );
282 if ( frl != null ) {
283 updateListener( lv.getNormalCount(), " normal", frl, null );
284 }
285 frl = ChiselSet.getSpecializedListener( ChiselSet.UnnecessaryKeyValueListener );
286 if ( frl != null ) {
287 if ( cleanCount == 0 ) {
288 needSignal = updateListener( lv.getUnnecessaryKeyValueCount(), " unnecessary", frl, testProfile ) || needSignal;
289 } else {
290 updateListener( lv.getUnnecessaryKeyValueCount(), " unnecessary", frl, testProfile );
291 if ( frl instanceof ChiselRow ) {
292 setRowInfo( (ChiselRow)frl, false );
293 }
294 }
295 }
296 frl = ChiselSet.getSpecializedListener( ChiselSet.UnindexedValueListener );
297 if ( frl != null ) {
298 updateListener( lv.getUnindexedValueCount(), " unindexed values", frl, null );
299 }
300 frl = ChiselSet.getSpecializedListener( ChiselSet.InterpolatorCountListener );
301 if ( frl != null ) {
302 if ( lv.getInterpolatorCount() == 1 ) {
303 frl.setText( "1 interpolator" );
304 } else {
305 frl.setText( lv.getInterpolatorCount() + " interpolators" );
306 }
307 }
308 frl = ChiselSet.getSpecializedListener( ChiselSet.InterpolatorCountListener2 );
309 if ( frl != null ) {
310 if ( lv.getInterpolatorCount() == 1 ) {
311 frl.setText( "1 interpolator" );
312 } else {
313 frl.setText( lv.getInterpolatorCount() + " interpolators" );
314 }
315 }
316 frl = ChiselSet.getSpecializedListener( ChiselSet.UselessNodeCountListener );
317 if ( frl != null ) {
318 needSignal = updateListener( lv.getUselessNodeCount(), " useless", frl, testProfile ) || needSignal;
319 }
320 frl = ChiselSet.getSpecializedListener( ChiselSet.ValueNodeCountListener );
321 if ( frl != null ) {
322 if ( lv.getValueNodeCount() == 1 ) {
323 frl.setText( "1 value node" );
324 } else {
325 frl.setText( lv.getValueNodeCount() + " value nodes" );
326 }
327 }
328 frl = ChiselSet.getSpecializedListener( ChiselSet.PROTOInstanceCountListener );
329 if ( frl != null ) {
330 if ( lv.getPROTOInstanceCount() == 1 ) {
331 frl.setText( "1 PROTO instance" );
332 } else {
333 frl.setText( lv.getPROTOInstanceCount() + " PROTO instances" );
334 }
335 }
336 }
337 if ( infoProfile == null ) {
338 infoProfile = new String( testProfile );
339 } else {
340 String infoTestProfile = new String( testProfile );
341 if ( infoTestProfile.compareTo( infoProfile ) == 0 ) {
342 needSignal = false;
343 } else {
344 infoProfile = infoTestProfile;
345 }
346 }
347 return( needSignal );
348 }
349
350 void setRowInfo(ChiselRow cr, boolean flag) {
351 cr.rowReady();
352 ChiselController cc = cr.getChiselController();
353 cr.setEnabled( flag );
354 cc.setValue( String.valueOf( flag ));
355 cc.repaint();
356 }
357
358 /** DocumentLoader interface */
359 public void reload( Object scene ) {
360 System.out.println( "Reloading scene..." );
361 if ( scene instanceof Scene ) {
362 Scene s = (Scene)scene;
363 TokenEnumerator te = s.getTokenEnumerator();
364 if ( te instanceof TokenEditor ) {
365 getDocument().setLines( (TokenEditor)te );
366 }
367 setScene( s );
368 cleanCount = 0;
369 updateInfo();
370 }
371 }
372
373 FactoryResponseListener parsingListener = null;
374 public void setParsingListener( FactoryResponseListener f ) {
375 parsingListener = f;
376 tokenFactory.setListener( f );
377 parser.addListener( f );
378 }
379
380 public void mergeErrors( BitSet errorMarks, BitSet warningMarks, BitSet nonconformanceMarks ) {
381 mergeLintErrors( parsingListener, errorMarks, warningMarks, nonconformanceMarks );
382 }
383
384 /** Set who is going to be called when each stage of the loader is done */
385 public void setDoneListener( FactoryResponseListener f ) {
386 //loader.setListener( f );
387 loader.addListener( f ); // should be safe, since it rejects duplicates
388 }
389
390
391 //
392 // The ProcessedFile FactoryChain consists of a large number of chisels, which
393 // may be independently activated/deactivated via the GUI, or may be deactivated
394 // if there are validation errors. The latter case means that we do not allow
395 // chiselling of files which contain errors.
396 //
397 // The "enableChisel" method turns on chisels by name, based on the
398 // name associated with the row visualization.
399 //
400 public void enableChisel( String chiselName, FactoryResponseListener theListener ) {
401 loader.enableChisel( chiselName, theListener );
402 }
403
404 public void disableChisel( String chiselName ) {
405 loader.disableChisel( chiselName );
406 }
407
408 public void removeFactory( String factoryName ) {
409 loader.removeFactory( factoryName );
410 }
411
412 public int getId() {
413 return( myId );
414 }
415
416 /** Generate local name for file */
417 public String generateName() {
418 return( generateName( false ));
419 }
420
421 /** Generate a name for file.
422 *
423 * @param stripNumber strip off "_<N>" portion of name.
424 */
425 public String generateName( boolean stripNumber ) {
426 File file = getFile();
427 if ( file != null ) {
428 String name = file.getName();
429 if (( saveAsCounter > 1 ) && stripNumber ) {
430 int underscoreIdx = name.lastIndexOf( "_" );
431 int dotIdx = name.lastIndexOf( "." );
432 if (( underscoreIdx != -1 ) && ( dotIdx != -1 ) && ( underscoreIdx < dotIdx )) {
433 String beforeUnderscore = name.substring( 0, underscoreIdx );
434 String dotAndAfter = name.substring( dotIdx );
435 return( beforeUnderscore + dotAndAfter );
436 }
437 }
438 return( name );
439 } else {
440 return( "unknown.wrl" );
441 }
442 }
443
444 public String generateX3dName() {
445 File file = getFile();
446 if ( file != null ) {
447 String name = file.getName();
448 int dotIdx = name.lastIndexOf( "." );
449 if ( dotIdx != -1 ) {
450 String beforeDot = name.substring( 0, dotIdx );
451 return( beforeDot + ".x3d" );
452 } else {
453 return( name + ".x3d" );
454 }
455 }
456 return( "unknown.x3d" );
457 }
458
459 public String generateChiseledName() {
460 File file = getFile();
461 if ( file != null ) {
462 String name = file.getName();
463 int dotIdx = name.lastIndexOf( "." );
464 if ( dotIdx != -1 ) {
465 String beforeDot = name.substring( 0, dotIdx );
466 return( beforeDot + ".chiseled.wrl" );
467 } else {
468 return( name + ".chiseled.wrl" );
469 }
470 } else {
471 return( "unknown.chiseled.wrl" );
472 }
473 }
474
475 public String getLocalName() {
476 File file = getFile();
477 if (file != null) {
478 return file.getName();
479 } else {
480 return null;
481 }
482 }
483
484 /** generate a name for "save as", just the file name, with an appended
485 * save as counter (local to the processed file).
486 */
487 int saveAsCounter = 1;
488 public String generateSaveAsName() {
489 String name = generateName( true );
490 if ( name.indexOf( "." ) == -1 ) {
491 name = name + ".wrl";
492 }
493 String beforeDot = name.substring( 0, name.indexOf( "." ));
494 String dotAndAfter = name.substring( name.indexOf( "." ));
495 saveAsCounter++;
496 String newName = beforeDot + "_" + saveAsCounter + dotAndAfter;
497 return( newName );
498 }
499 int X3DsaveAsCounter = 1;
500 public String generateX3dSaveAsName() {
501 String name = generateName( true );
502 if ( name.indexOf( "." ) == -1 ) {
503 name = name + ".x3d";
504 }
505 String beforeDot = name.substring( 0, name.indexOf( "." ));
506 String dotAndAfter = name.substring( name.indexOf( "." ));
507 X3DsaveAsCounter++;
508 String newName = beforeDot + "_" + X3DsaveAsCounter + dotAndAfter;
509 return( newName );
510 }
511
512 /** generate a name for gzip, name.chiseled.wrz
513 */
514 public String generateGzipName() {
515 return( generateGzipName( false ));
516 }
517
518 /** Generate a name for gzip, name.chiseled.wrz.
519 *
520 * @param stripNumber true if the "_<N>" part of the name is to be stripped
521 * out.
522 */
523 public String generateGzipName( boolean stripNumber ) {
524 String name = getLocalName();
525 if ( name == null ) {
526 name = "unknown.wrz";
527 }
528 if ( name.indexOf( "." ) == -1 ) {
529 name = name + ".wrz";
530 }
531 String beforeDot = name.substring( 0, name.indexOf( "." ));
532 if ( stripNumber && ( saveAsCounter > 1 )) {
533 if ( beforeDot.indexOf( "_" ) > 0 ) {
534 beforeDot = beforeDot.substring( 0, beforeDot.lastIndexOf( "_" ));
535 }
536 }
537 String newName = beforeDot + ".chiseled.wrz";
538 return( newName );
539 }
540
541 /** generate a name for gzip save as, name_<N>.chiseled.wrz
542 */
543 public String generateGzipSaveAsName() {
544 String name = generateGzipName( true );
545 String beforeChiseled = name.substring( 0, name.indexOf( ".chiseled" ));
546 saveAsCounter++;
547 String newName = beforeChiseled + "_" + saveAsCounter + ".chiseled.wrz";
548 return( newName );
549 }
550
551
552 public void addFactory( QueuedRequestFactory f ) {
553 loader.addFactory( f );
554 }
555
556 public void addOptimizer( Optimizer o ) {
557 loader.addOptimizer( o, parser, getBaseFilePath(), getNameWithoutPath() );
558 }
559
560 public void addFactory( QueuedRequestFactory f, QueuedRequestFactory prev ) {
561 loader.insertFactory( f, prev );
562 //load();
563 }
564
565 public void insertFactory( QueuedRequestFactory f ) {
566 loader.insertFactory( f, tokenFactory );
567 }
568
569 public String getBaseFilePath() {
570 String name = getUrl();
571 if ( name != null ) {
572 if ( name.lastIndexOf( '/' ) != -1 ) {
573 name = name.substring( 0, name.lastIndexOf( '/' ));
574 } else if ( name.lastIndexOf( '\\' ) != -1 ) {
575 name = name.substring( 0, name.lastIndexOf( '\\' ));
576 } else {
577 name = null;
578 }
579 }
580 //System.out.println( "ProcessedFile base name is '" + name + "'" );
581 return( name );
582
583 }
584 public String getNameWithoutPath() {
585 String name = getUrl();
586 if ( name != null ) {
587 if ( name.lastIndexOf( '/' ) != -1 ) {
588 return( name.substring( name.lastIndexOf( '/' ) + 1 ));
589 } else if ( name.lastIndexOf( '\\' ) != -1 ) {
590 return( name.substring( name.lastIndexOf( '\\' ) + 1 ));
591 } else {
592 return( name );
593 }
594 }
595 return( null );
596 }
597
598 public String getNameWithoutVersion() {
599 String name = getUrl();
600 File file = getFile();
601 if (file != null) {
602 name = file.getName();
603 }
604 return name;
605 }
606
607 public String getName() {
608 String name = getNameWithoutVersion();
609 return getVersionString() + name;
610 }
611
612 Document doc;
613 MenuItem undoItem;
614 MenuItem redoItem;
615 public void setUndoItem( MenuItem undoItem ) {
616 this.undoItem = undoItem;
617 undoItem.setEnabled( false );
618 }
619
620 public void setRedoItem( MenuItem redoItem ) {
621 this.redoItem = redoItem;
622 redoItem.setEnabled( false );
623 }
624 public void clear_undo() {
625 if ( doc != null ) {
626 doc.clear_undo();
627 }
628 }
629
630 /** Remove all resialized files */
631 public void removeSerialFiles() {
632 if ( doc != null ) {
633 doc.removeSerialFiles( versionNumber );
634 }
635 }
636
637 /** Get the Document associated with this ProcessedFile */
638 public Document getDocument() {
639 return( doc );
640 }
641
642 /** Set the Document associated with this ProcessedFile */
643 public void setDocument( Document doc ) {
644 this.doc = doc;
645 if ( doc != null ) {
646 doc.setUndoItem( undoItem );
647 doc.setRedoItem( redoItem );
648 doc.setNodeSelector( getScene() );
649 }
650 }
651
652 /** kick off the loader chain of processing */
653 Date startTime;
654 public void load() {
655 load( true );
656 }
657
658 /** Start running loader factories.
659 *
660 * @param resetStartTime if true, "startTime" is set to current time
661 */
662 void load( boolean resetStartTime ) {
663 if ( resetStartTime ) {
664 startTime = new Date();
665 }
666 loader.submit(this);
667 }
668
669 public Date getStartTime() {
670 return( startTime );
671 }
672
673 /** Save the file in text format
674 */
675 public void asciiSave() {
676 asciiSave( getFile(), false, false );
677 }
678
679 /** Save the file in gzip format
680 */
681 public void gzipSave() {
682 asciiSave( getFile(), true, false );
683 }
684
685 public void x3dSave() {
686 asciiSave( getFile(), false, true );
687 }
688
689
690 class SaveThread extends Thread {
691 boolean gzip;
692 boolean x3d;
693 int numberLines;
694 PrintStream out;
695 TokenEditor sceneTokenEditor;
696
697 SaveThread( TokenEditor sceneTokenEditor, boolean gzip, boolean x3d, int numberLines, PrintStream out ) {
698 this.sceneTokenEditor = sceneTokenEditor;
699 this.gzip = gzip;
700 this.x3d = x3d;
701 this.numberLines = numberLines;
702 this.out = out;
703 }
704
705 public void run() {
706 Properties props = ChiselProperties.getProperties();
707 String unixFormatProperty = props.getProperty("workspace.saveInUnixFormat");
708 boolean saveInUnixFormat = (unixFormatProperty == null) ? false : ("true".equalsIgnoreCase(unixFormatProperty));
709 if ( x3d ) {
710 Scene s = getScene();
711 X3dWriter x3dw = new X3dWriter( out, sceneTokenEditor );
712 s.twoPassTraverse( x3dw );
713 x3dw.finished();
714 } else if ( gzip ) {
715 for ( int i = 0; i < numberLines; i++ ) {
716 GlobalProgressIndicator.markProgress();
717 if ( saveInUnixFormat ) {
718 out.print( sceneTokenEditor.getNospaceLineAt( i ));
719 out.print( '\n' );
720 } else {
721 out.println( sceneTokenEditor.getNospaceLineAt( i ));
722 }
723 }
724 } else {
725 for ( int i = 0; i < numberLines; i++ ) {
726 GlobalProgressIndicator.markProgress();
727 if ( saveInUnixFormat ) {
728 out.print( sceneTokenEditor.getTabLineAt( i ));
729 out.print( '\n' );
730 } else {
731 out.println( sceneTokenEditor.getTabLineAt( i ));
732 }
733 }
734 }
735 out.flush();
736 out.close();
737 System.out.println( "Finished saving file, saved " + numberLines + " lines" );
738 GlobalProgressIndicator.reset();
739 if ( Chisel.singletonChisel != null ) {
740 Chisel.singletonChisel.updateHeaderLine();
741 }
742 saving = false;
743 }
744 }
745
746 boolean saving;
747 SaveThread saver;
748
749 /** Save the file in text format
750 *
751 * @param file file to save it to
752 * @param gzip if true, save in gzip format
753 * @param x3d if true, save in experimental X3d format
754 */
755 public void asciiSave( File file, boolean gzip, boolean x3d ) {
756 if ( saving ) {
757 return;
758 }
759 saving = true;
760 setGzip( gzip );
761 Scene scene = getScene();
762 System.out.println( "Saving file..." );
763 try {
764 FileOutputStream fos = new FileOutputStream(file);
765 OutputStream fo = fos;
766 if ( gzip ) {
767 fo = new GZIPOutputStream( fo );
768 }
769 PrintStream out = new PrintStream( fo );
770 TokenEditor sceneTokenEditor = (TokenEditor) scene.getTokenEnumerator();
771 int numberLines = sceneTokenEditor.getNumberLines();
772 String progressStr = "Saving " + file.getName() + " ";
773 if ( gzip ) {
774 progressStr = "GZIP saving " + file.getName() + " ";
775 }
776 GlobalProgressIndicator.setProgressIndicator( ChiselSet.getProgressIndicator( ChiselSet.VALIDATORS ), progressStr, numberLines );
777 SaveThread saveThread = new SaveThread( sceneTokenEditor, gzip, x3d, numberLines, out );
778 saveThread.start();
779 saver = saveThread;
780 } catch( Exception e ) {
781 e.printStackTrace();
782 saving = false;
783 }
784 }
785
786 public void waitForSave() {
787 if ( saver != null ) {
788 try {
789 saver.join();
790 } catch ( Exception e ) {
791 }
792 }
793 }
794
795 public void synchTokenEditor() {
796 Scene scene = getScene();
797 TokenEditor sceneTokenEditor = (TokenEditor)scene.getTokenEnumerator();
798 setTokenEditor( sceneTokenEditor );
799 if ( sceneTokenEditor.isDirty() ) {
800 sceneTokenEditor.retokenize();
801 Scene newScene = new Scene( scene.getUrl(), sceneTokenEditor );
802 VRML97parser parser = new VRML97parser();
803 parser.Build( sceneTokenEditor, newScene );
804 setScene( newScene );
805 }
806 }
807
808 void dump( String loc ) {
809 Scene s = getScene();
810 if ( s != null ) {
811 TokenEnumerator te = s.getTokenEnumerator();
812 if ( te != null ) {
813 System.out.println( loc + ", have " + te.getNumberLines() + " lines" );
814 } else {
815 System.out.println( loc + ", token enumerator is null" );
816 }
817 } else {
818 System.out.println( loc + ", scene is null" );
819 }
820 }
821
822 /** store info about which chisel is operating on this file
823 *
824 * @param chisel the chisel that is going to operate on the file
825 */
826 public void chiselWith( Optimizer chisel ) {
827 if ( session == null ) {
828 session = new Session();
829 }
830 session.chiselWith( chisel );
831 }
832
833 /** Save the current version in the session object */
834 void markVersion( int version ) {
835 if ( session == null ) {
836 session = new Session();
837 }
838 session.markVersion( version );
839 }
840
841 /** Save the current percent reduction in the session object */
842 public void markPercent() {
843 if ( session == null ) {
844 session = new Session();
845 }
846 session.markPercent( getPercentX100() );
847 }
848
849 /** Generate an HTML report for the chisel session.
850 *
851 * @param htmlFileName destination of the HTML report
852 * @param wrlFileName name of the file that was chiseled
853 * @param originalName original name of the file
854 */
855 public void generateHtml( String htmlFileName, String wrlFileName, String originalName ) {
856 if ( session == null ) {
857 session = new Session();
858 }
859 session.generateHtml( htmlFileName, wrlFileName, originalName );
860 }
861
862 /**
863 * The Session class tracks all the operations done on the file being
864 * processed.
865 */
866 class Session {
867 /** the list of operations */
868 Vector entries;
869 ChiselSessionEntry lastChisel;
870 public Session() {
871 entries = new Vector();
872 }
873
874 /** store info about the chisel operating on the file */
875 void chiselWith( Optimizer chisel ) {
876 lastChisel = new ChiselSessionEntry( chisel );
877 entries.addElement( lastChisel );
878 if ( chisel.getNumberOptions() > 0 ) {
879 entries.addElement( new ChiselOptionSettings( chisel.getNumberOptions(), chisel ));
880 }
881 // dump();
882 }
883
884 /** store info when a new version is made */
885 void markVersion( int version ) {
886 entries.addElement( new VersionSessionEntry( version ));
887 // dump();
888 }
889 void markPercent( int percentX100 ) {
890 if ( lastChisel != null ) {
891 lastChisel.markPercent( percentX100 );
892 }
893 // dump();
894 }
895
896 /** Generate an HTML report to a specific file.
897 *
898 * @param htmlFileName the name of the file to contain the html
899 * @param wrlFileName current name of the chiseled file
900 * @param originalName original name of the chiseled file, only
901 * used if different from the current name.
902 */
903 void generateHtml( String htmlFileName, String wrlFileName, String originalName ) {
904 try {
905 OutputStream os = new FileOutputStream( htmlFileName );
906 HTMLgenerator htmlGen = new HTMLgenerator( os );
907 htmlGen.header( "Chisel " + Chisel.version + ": " + wrlFileName );
908 if (( originalName != null ) && ( originalName.compareTo( wrlFileName ) != 0 )) {
909 htmlGen.locateText( "Original file: <A HREF=\"" + originalName + "\">" + originalName + "</A>", "B" );
910 }
911 htmlGen.pText( "<BR>" );
912 htmlGen.locateText( "Chiseled file: <A HREF=\"" + wrlFileName + "\">" + wrlFileName + "</A>", "B" );
913
914 htmlGen.startTable( null, "H4", 1, 4, 4 );
915 htmlGen.startRow();
916 htmlGen.columnHeader( "Chisel", "CENTER" );
917 htmlGen.columnHeader( "% reduction", "CENTER" );
918 htmlGen.columnHeader( "Total Reduced", "CENTER" );
919 htmlGen.endRow();
920 ChiselSessionEntry prevCSE = null;
921
922 int numberEntries = entries.size();
923 for ( int i = 0; i < numberEntries; i++ ) {
924 SessionEntry s = (SessionEntry)entries.elementAt( i );
925 if ( s instanceof ChiselSessionEntry ) {
926 ChiselSessionEntry cse = (ChiselSessionEntry)s;
927 SessionEntry nextEntry = null;
928 if ( i < (numberEntries - 1 )) {
929 nextEntry = (SessionEntry)entries.elementAt( i + 1 );
930 }
931 cse.genRow( htmlGen, prevCSE, nextEntry );
932 prevCSE = cse;
933 }
934 }
935 htmlGen.terminate();
936 System.out.println( "HTML report in " + htmlFileName );
937 } catch ( Exception e ) {
938 System.out.println( "Could not generate HTML" );
939 e.printStackTrace();
940 }
941 }
942
943 void dump() {
944 for ( int i = 0; i < entries.size(); i++ ) {
945 SessionEntry s = (SessionEntry)entries.elementAt( i );
946 System.out.println( s.getName() );
947 if ( s instanceof ChiselSessionEntry ) {
948 ChiselSessionEntry cs = (ChiselSessionEntry)s;
949 System.out.println( "reduction " + cs.getTotalReduction() );
950 }
951 }
952 }
953 }
954
955 /** ChiselOptionSettings saves the settings for a specific chisel */
956 class ChiselOptionSettings extends SessionEntry {
957 Vector optionValues;
958 Optimizer chisel;
959
960 ChiselOptionSettings( int numberOptions, Optimizer chisel ) {
961 super( "settings" );
962 this.chisel = chisel;
963 optionValues = new Vector( numberOptions );
964 for ( int i = 0; i < numberOptions; i++ ) {
965 optionValues.addElement( chisel.getOptionValue( i ));
966 }
967 }
968
969 /** Generate a row entry, which is a table-within-a-table */
970 void genRow( HTMLgenerator htmlGen ) {
971 htmlGen.startTable( null, "H4", 1, 0, 0 );
972 for ( int i = 0; i < optionValues.size(); i++ ) {
973 htmlGen.startRow( "RIGHT" );
974 htmlGen.genColumn( (String)chisel.getOptionLabel( i ) );
975 Object x = optionValues.elementAt( i );
976 if ( x instanceof String ) {
977 htmlGen.genColumn( (String)x );
978 } else {
979 htmlGen.genColumn( "unknown" );
980 }
981 htmlGen.endRow();
982 }
983 htmlGen.endTable();
984 }
985 }
986
987 /** SessionEntry is the base class for all entries */
988 class SessionEntry {
989 String name;
990 public SessionEntry( String name ) {
991 this.name = name;
992 }
993 public String getName() {
994 return( name );
995 }
996 }
997
998 class ChiselSessionEntry extends SessionEntry{
999 int numberOptions;
1000 Object[] options;
1001 int percentX100;
1002 Optimizer chisel;
1003
1004 public ChiselSessionEntry( Optimizer chisel ) {
1005 super( chisel.getClass().getName() );
1006 this.chisel = chisel;
1007 numberOptions = chisel.getNumberOptions();
1008 percentX100 = 10000;
1009 if ( numberOptions > 0 ) {
1010 options = new Object[ numberOptions ];
1011 for ( int i = 0; i < numberOptions; i++ ) {
1012 options[i] = chisel.getOptionValue( i );
1013 }
1014 }
1015 }
1016
1017 /** Generate a row of text for a particular chisel */
1018 public void genRow( HTMLgenerator htmlGen, ChiselSessionEntry prevEntry, SessionEntry nextEntry ) {
1019 htmlGen.startRow( "LEFT" );
1020 if ( nextEntry instanceof ChiselOptionSettings ) {
1021 ChiselOptionSettings cos = (ChiselOptionSettings)nextEntry;
1022 htmlGen.genColumnStart( chisel.getActionMessage() );
1023 cos.genRow( htmlGen );
1024 htmlGen.genColumnEnd();
1025 } else {
1026 htmlGen.genColumn( chisel.getActionMessage() );
1027 }
1028 htmlGen.genColumn( getDelta( prevEntry ));
1029 htmlGen.genColumn( getTotalReduction() );
1030 htmlGen.endRow();
1031 }
1032
1033 void markPercent( int percentX100 ) {
1034// System.out.println( "markPercent " + percentX100 );
1035 this.percentX100 = percentX100;
1036 }
1037 String redStr;
1038
1039 /** Get the string representing the percent reduction */
1040 String getTotalReduction() {
1041// if ( redStr != null ) {
1042// System.out.println( "Reusing redStr, percentX100 is " + percentX100 );
1043// return( redStr );
1044// }
1045 System.out.println( "percentX100 is " + percentX100 );
1046 int px = 10000 - percentX100;
1047 int percent = px/100;
1048 int hundredths = px%100;
1049 if ( hundredths < 10 ) {
1050 redStr = new String( percent + ".0" + hundredths + "%" );
1051 } else {
1052 redStr = new String( percent + "." + hundredths + "%" );
1053 }
1054 return( redStr );
1055 }
1056
1057 public int getX100() {
1058 return( percentX100 );
1059 }
1060
1061 String getDelta( ChiselSessionEntry prevEntry ) {
1062 int prevX100 = 10000;
1063 if ( prevEntry != null ) {
1064 prevX100 = prevEntry.getX100();
1065 }
1066 int diff = prevX100 - percentX100;
1067 if ( diff < 0 ) {
1068 return( "*" );
1069 } else {
1070 int percent = diff/100;
1071 int hundredths = diff%100;
1072 if ( hundredths < 10 ) {
1073 return( new String( percent + ".0" + hundredths + "%" ));
1074 } else {
1075 return( new String( percent + "." + hundredths + "%" ));
1076 }
1077 }
1078 }
1079 }
1080
1081 class VersionSessionEntry extends SessionEntry {
1082 int version;
1083 public VersionSessionEntry( int version ) {
1084 super( "Version " + version );
1085 this.version = version;
1086 }
1087 }
1088}
1089
1090