1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package complex.framework.autosave;
25 
26 // __________ Imports __________
27 
28 // structs, const, ...
29 import com.sun.star.beans.PropertyValue;
30 import com.sun.star.bridge.XUnoUrlResolver;
31 
32 // exceptions
33 import com.sun.star.container.NoSuchElementException;
34 import com.sun.star.uno.Exception;
35 import com.sun.star.uno.RuntimeException;
36 import java.io.IOException;
37 import java.lang.InterruptedException;
38 import java.net.ConnectException;
39 
40 // interfaces
41 import com.sun.star.lang.XMultiServiceFactory;
42 import com.sun.star.uno.Any;
43 
44 // helper
45 import com.sun.star.uno.IBridge;
46 import com.sun.star.uno.UnoRuntime;
47 
48 // others
49 import javax.swing.*;
50 import javax.swing.border.*;
51 import java.awt.*;
52 import java.lang.*;
53 import java.io.*;
54 import java.util.*;
55 import java.sql.*;
56 
57 // __________ Implementation __________
58 
59 /**
60  * Implements a log mechanism to create a protocol of all steps of e.g. an api test
61  * It provides the possibility to write the logged meesages to a file and/or
62  * to stdout/stderr (if necessary at the same time!).
63  *
64  *  TODO
65  *      - implement filter, which e.g. suppress showing of INFO data
66  */
67 public class Protocol extends JComponent
68 {
69     // ____________________
70     /**
71      * Note: Following values can be combined - they are interpreted as flags.
72      *
73      * @const   MODE_STDOUT             messages are logged to stdout
74      * @const   MODE_STDERR             messages are logged to stderr
75      * @const   MODE_ASCII              messages are logged to an ascii file
76      * @const   MODE_HTML               messages are logged to a html file
77      *
78      * @const   TYPE_SCOPE_OPEN         open, mark or count a new scope for following log statements
79      * @const   TYPE_SCOPE_CLOSE        close, mark or count the current scope
80      * @const   TYPE_TESTMARK           it marks the beginning of a (sub)test, can be used for statistic purposes
81      * @const   TYPE_OK                 this protocol line is marked as a OK message
82      * @const   TYPE_ERROR              this protocol line is marked as an error
83      * @const   TYPE_WARNING            this protocol line is marked as a warning
84      * @const   TYPE_INFO               this protocol line represent some debug data for analyzing
85      */
86     public  static final int    MODE_STDOUT             = 1;
87     public  static final int    MODE_STDERR             = 2;
88     public  static final int    MODE_ASCII              = 4;
89     public  static final int    MODE_HTML               = 8;
90 
91     public  static final int    TYPE_OK                 =    1;
92     public  static final int    TYPE_ERROR              =    2;
93     public  static final int    TYPE_WARNING            =    4;
94     public  static final int    TYPE_INFO               =    8;
95     public  static final int    TYPE_SCOPE_OPEN         =   16;
96     public  static final int    TYPE_SCOPE_CLOSE        =   32;
97     public  static final int    TYPE_TESTMARK           =   64;
98     public  static final int    TYPE_ERROR_INFO         =  128;
99     public  static final int    TYPE_WARNING_INFO       =  256;
100     public  static final int    TYPE_STATISTIC          =  512;
101     public  static final int    TYPE_LINK               = 1024;
102 
103     public  static final int    FILTER_NONE             = 0;
104     public  static final int    FILTER_OK               = TYPE_OK;
105     public  static final int    FILTER_ERROR            = TYPE_ERROR;
106     public  static final int    FILTER_WARNING          = TYPE_WARNING;
107     public  static final int    FILTER_INFO             = TYPE_INFO;
108     public  static final int    FILTER_SCOPES           = TYPE_SCOPE_OPEN | TYPE_SCOPE_CLOSE;
109     public  static final int    FILTER_TESTMARK         = TYPE_TESTMARK;
110     public  static final int    FILTER_ERROR_INFO       = TYPE_ERROR_INFO;
111     public  static final int    FILTER_WARNING_INFO     = TYPE_WARNING_INFO;
112     public  static final int    FILTER_STATISTIC        = TYPE_STATISTIC;
113     public  static final int    FILTER_LINK             = TYPE_LINK;
114 
115     // ____________________
116     /**
117      */
118     private static final int    MARK_DIFF               = 5;
119 
120     private static final String BGCOLOR_LINECOL         = "#95CC77";
121     private static final String FGCOLOR_LINECOL_NORMAL  = "#ffffbd";
122     private static final String FGCOLOR_LINECOL_MARKED  = "#000088";
123 
124     private static final String BGCOLOR_STANDARD        = "#ffffff";
125     private static final String FGCOLOR_STANDARD        = "#000000";
126 
127     private static final String BGCOLOR_SCOPE           = "#eeeeee";
128     private static final String FGCOLOR_SCOPE           = "#000000";
129 
130     private static final String BGCOLOR_TIMESTAMP       = "#e0e0e0";
131     private static final String FGCOLOR_TIMESTAMP       = "#000000";
132 
133     private static final String BGCOLOR_TESTMARK        = "#0000ff";
134     private static final String FGCOLOR_TESTMARK        = "#ffffff";
135 
136     private static final String BGCOLOR_OK              = "#88dd88";
137     private static final String FGCOLOR_OK              = "#ffffff";
138 
139     private static final String BGCOLOR_WARNING         = "#ffff00";
140     private static final String FGCOLOR_WARNING         = "#000000";
141 
142     private static final String BGCOLOR_WARNING_INFO    = "#ffffcc";
143     private static final String FGCOLOR_WARNING_INFO    = "#000000";
144 
145     private static final String BGCOLOR_ERROR           = "#ff0000";
146     private static final String FGCOLOR_ERROR           = "#ffff00";
147 
148     private static final String BGCOLOR_ERROR_INFO      = "#ffbbbb";
149     private static final String FGCOLOR_ERROR_INFO      = "#000000";
150 
151     private static final String BGCOLOR_INFO            = "#eeeeee";
152     private static final String FGCOLOR_INFO            = "#000000";
153 
154     private static final String BGCOLOR_STATISTIC       = "#0000ff";
155     private static final String FGCOLOR_STATISTIC       = "#ffffff";
156 
157     private static final String BGCOLOR_LINK            = BGCOLOR_INFO;
158     private static final String FGCOLOR_LINK            = FGCOLOR_INFO;
159 
160     // ____________________
161     /**
162      * @member  m_nMode         the mode, in which this protocol object runs
163      * @member  m_nFilter       can be used to filter log messages by type
164      * @member  m_sFileName     we need it to open the log file on demand (if nMode require such log file)
165      * @member  m_nLine         used as line number for the protocol
166      * @member  m_nScope        used to format scopes
167      * @member  m_nErrors       count errors in protocol
168      * @member  m_nWarnings     count warnings in protocol
169      * @member  m_nTestMarks    count test marker in protocol
170      */
171     private int                 m_nMode     ;
172     private int                 m_nFilter   ;
173     private String              m_sFileName ;
174     private long                m_nLine     ;
175     private long                m_nScope    ;
176     private long                m_nErrors   ;
177     private long                m_nWarnings ;
178     private long                m_nTestMarks;
179     private Timestamp           m_aStartTime;
180     private Timestamp           m_aEndTime  ;
181 
182     // ____________________
183     /**
184      * special helper class to represent one line of a protocol.
185      * Such line can be specified as a special one (ERROR, WARNING ...).
186      * That makes it possible to analyze the whole protocol using tools.
187      */
188     class ProtocolLine
189     {
190         /// the line number of this protocol line (size of the vector of all protocol lines cn be used to count such lines!)
191         private long m_nLine;
192         /// deepness of the current scope
193         private long m_nScope;
194         /// mark line as an error, warning, data entry ... (see const definitions before)
195         private int m_nType;
196         /// of course, we have to know the logged message too :-)
197         private String m_sMessage;
198         /// and it can be useful to know the current time, when this line was created
199         private Timestamp m_aStamp;
200 
201         /** ctor for fast initializing of such line */
202         public ProtocolLine( long   nLine    ,
203                              long   nScope   ,
204                              int    nType    ,
205                              String sMessage )
206         {
207             m_aStamp   = new Timestamp(System.currentTimeMillis());
208             m_nLine    = nLine   ;
209             m_nScope   = nScope  ;
210             m_nType    = nType   ;
211             m_sMessage = sMessage;
212         }
213 
214         /** format this line as an ascii string for writing log files */
215         public synchronized String toString()
216         {
217             StringBuffer sLine = new StringBuffer(1000);
218 
219             // insert line number
220             // Use right bound notation and format 6 digits!
221             sLine.append("["    );
222             if (m_nLine<10)
223                 sLine.append("     ");
224             else
225             if (m_nLine<100)
226                 sLine.append("    ");
227             else
228             if (m_nLine<1000)
229                 sLine.append("   ");
230             else
231             if (m_nLine<10000)
232                 sLine.append("  ");
233             else
234             if (m_nLine<100000)
235                 sLine.append(" ");
236             sLine.append(m_nLine);
237             sLine.append("] "   );
238 
239             // add time stamp
240             // close with a "TAB" ... because some time stamps are not normalized to
241             // a well defined string length .-)
242             sLine.append(m_aStamp.toString()+" \t");
243 
244             // add special line type
245             if ((m_nType & TYPE_OK) == TYPE_OK)
246                 sLine.append(" OK           ");
247             else
248             if ((m_nType & TYPE_ERROR) == TYPE_ERROR)
249                 sLine.append(" ERROR        ");
250             else
251             if ((m_nType & TYPE_ERROR_INFO) == TYPE_ERROR_INFO)
252                 sLine.append(" ERROR INFO   ");
253             else
254             if ((m_nType & TYPE_WARNING) == TYPE_WARNING)
255                 sLine.append(" WARNING      ");
256             else
257             if ((m_nType & TYPE_WARNING_INFO) == TYPE_WARNING_INFO)
258                 sLine.append(" WARNING INFO ");
259             else
260             if ((m_nType & TYPE_INFO) == TYPE_INFO)
261                 sLine.append(" INFO         ");
262             else
263             if ((m_nType & TYPE_TESTMARK) == TYPE_TESTMARK)
264                 sLine.append(" TEST         ");
265             else
266             if ((m_nType & TYPE_LINK) == TYPE_LINK)
267                 sLine.append(" LINK         ");
268             else
269             if ((m_nType & TYPE_STATISTIC) == TYPE_STATISTIC)
270                 sLine.append(" STATISTIC    ");
271             else
272             if (
273                 ((m_nType & TYPE_SCOPE_OPEN ) == TYPE_SCOPE_OPEN ) ||
274                 ((m_nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE)
275                )
276                 sLine.append(" SCOPE        ");
277             else
278                 sLine.append("              ");
279 
280             // add scope information
281             for (int s=0; s<m_nScope; ++s)
282                 sLine.append(" ");
283 
284             if ((m_nType & TYPE_SCOPE_OPEN) == TYPE_SCOPE_OPEN)
285                 sLine.append(" { ");
286             else
287             if ((m_nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE)
288                 sLine.append(" } ");
289             else
290                 sLine.append("   ");
291 
292             // add message
293             sLine.append(m_sMessage);
294             sLine.append("\n"      );
295 
296             return sLine.toString();
297         }
298 
299         /**
300          * format this line as a string for writing log files
301          * using the html format
302          */
303         public synchronized String toHTML()
304         {
305             StringBuffer sLine = new StringBuffer(1000);
306             sLine.append("<tr>");
307 
308             // insert line number
309             if (m_nLine % MARK_DIFF == 0)
310                 impl_generateColoredHTMLCell(sLine, new Long(m_nLine).toString(), BGCOLOR_LINECOL, FGCOLOR_LINECOL_MARKED, true);
311             else
312                 impl_generateColoredHTMLCell(sLine, new Long(m_nLine).toString(), BGCOLOR_LINECOL, FGCOLOR_LINECOL_NORMAL, false);
313 
314             // add time stamp
315             impl_generateColoredHTMLCell(sLine, m_aStamp.toString()+" ", BGCOLOR_TIMESTAMP, FGCOLOR_TIMESTAMP, false);
316 
317             // add log type info
318             boolean bTypeCellFilled = false;
319             if ((m_nType & TYPE_ERROR_INFO) == TYPE_ERROR_INFO)
320             {
321                 impl_generateColoredHTMLCell(sLine, "ERROR INFO", BGCOLOR_ERROR_INFO, FGCOLOR_ERROR_INFO, false);
322                 bTypeCellFilled = true;
323             }
324             else
325             if ((m_nType & TYPE_ERROR) == TYPE_ERROR)
326             {
327                 impl_generateColoredHTMLCell(sLine, "ERROR", BGCOLOR_ERROR, FGCOLOR_ERROR, true);
328                 bTypeCellFilled = true;
329             }
330             else
331             if ((m_nType & TYPE_WARNING_INFO) == TYPE_WARNING_INFO)
332             {
333                 impl_generateColoredHTMLCell(sLine, "WARNING INFO", BGCOLOR_WARNING_INFO, FGCOLOR_WARNING_INFO, false);
334                 bTypeCellFilled = true;
335             }
336             else
337             if ((m_nType & TYPE_WARNING) == TYPE_WARNING)
338             {
339                 impl_generateColoredHTMLCell(sLine, "WARNING", BGCOLOR_WARNING, FGCOLOR_WARNING, true);
340                 bTypeCellFilled = true;
341             }
342             else
343             if ((m_nType & TYPE_OK) == TYPE_OK)
344             {
345                 impl_generateColoredHTMLCell(sLine, "OK", BGCOLOR_OK, FGCOLOR_OK, true);
346                 bTypeCellFilled = true;
347             }
348             else
349             if ((m_nType & TYPE_INFO) == TYPE_INFO)
350             {
351                 impl_generateColoredHTMLCell(sLine, "INFO", BGCOLOR_INFO, FGCOLOR_INFO, false);
352                 bTypeCellFilled = true;
353             }
354             else
355             if ((m_nType & TYPE_TESTMARK) == TYPE_TESTMARK)
356             {
357                 impl_generateColoredHTMLCell(sLine, "TEST", BGCOLOR_TESTMARK, FGCOLOR_TESTMARK, true);
358                 bTypeCellFilled = true;
359             }
360             else
361             if ((m_nType & TYPE_STATISTIC) == TYPE_STATISTIC)
362             {
363                 impl_generateColoredHTMLCell(sLine, "STATISTIC", BGCOLOR_STATISTIC, FGCOLOR_STATISTIC, false);
364                 bTypeCellFilled = true;
365             }
366             else
367             if ((m_nType & TYPE_LINK) == TYPE_LINK)
368             {
369                 impl_generateColoredHTMLCell(sLine, "LINK", BGCOLOR_LINK, FGCOLOR_LINK, false);
370                 bTypeCellFilled = true;
371             }
372             else
373             if (
374                 ((m_nType & TYPE_SCOPE_OPEN ) == TYPE_SCOPE_OPEN ) ||
375                 ((m_nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE)
376                )
377             {
378                 impl_generateColoredHTMLCell(sLine, "SCOPE", BGCOLOR_SCOPE, FGCOLOR_SCOPE, false);
379                 bTypeCellFilled = true;
380             }
381 
382             // if no tyope information was added to the current coloum, we must
383             // write any content into this cell. Otherwise some browser
384             // shows a strange layout!
385             if (! bTypeCellFilled)
386                 impl_generateColoredHTMLCell(sLine, " ", BGCOLOR_STANDARD, FGCOLOR_STANDARD, false);
387 
388             // add scope information
389             sLine.append("<td>");
390             for (int s=0; s<m_nScope; ++s)
391                 sLine.append("&nbsp;&nbsp;&nbsp;");
392             String sColor = "#000000";
393             if ((m_nScope % 2) == 0)
394                 sColor = "#808080";
395             if ((m_nType & TYPE_SCOPE_OPEN) == TYPE_SCOPE_OPEN)
396                 sLine.append("<font color=\""+sColor+"\">{ "+m_nScope+"</font>");
397             else
398             if ((m_nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE)
399                 sLine.append("<font color=\""+sColor+"\">"+m_nScope+" }</font>");
400             sLine.append("</td>\n");
401 
402             // add message
403             sLine.append("<td>"    );
404             sLine.append(m_sMessage);
405             sLine.append("</td>\n" );
406 
407             sLine.append("</tr>\n" );
408 
409             return sLine.toString();
410         }
411 
412         /** detect, if this line object represent an error */
413         public synchronized boolean isError()
414         {
415             return (
416                     ((m_nType & TYPE_ERROR) == TYPE_ERROR) &&
417                     ((m_nType & TYPE_INFO ) != TYPE_INFO )
418                    );
419         }
420 
421         /** detect, if this line object represent a warning */
422         public synchronized boolean isWarning()
423         {
424             return (
425                     ((m_nType & TYPE_WARNING) == TYPE_WARNING) &&
426                     ((m_nType & TYPE_INFO   ) != TYPE_INFO   )
427                    );
428         }
429 
430         /** detect, if this line object represent a marked position */
431         public synchronized boolean isTestMark()
432         {
433             return ((m_nType & TYPE_TESTMARK) == TYPE_TESTMARK);
434         }
435 
436         /**
437          * create a colored table cell formated as HTML.
438          *
439          * @param   sCell
440          *          an outside string buffer, which can be
441          *          used to generate the
442          *          needed HTML code there.
443          *
444          * @param   sContent
445          *          the text content of this cell.
446          *
447          * @param   sBGColor
448          *          a string, which represent the background color
449          *          coded in HTML.
450          *
451          * @param   sFGColor
452          *          a string, which represent the foreground color
453          *          coded in HTML.
454          *
455          * @param   bBold
456          *          enable/disable bold state for the text content.
457          */
458         private void impl_generateColoredHTMLCell(StringBuffer sCell   ,
459                                                   String       sContent,
460                                                   String       sBGColor,
461                                                   String       sFGColor,
462                                                   boolean      bBold   )
463         {
464             sCell.append("<td bgcolor=\""+sBGColor+"\">");
465             sCell.append("<font color=\""+sFGColor+"\">");
466             if (bBold)
467                 sCell.append("<b>");
468             sCell.append(sContent);
469             if (bBold)
470                 sCell.append("</b>");
471             sCell.append("</font></td>\n");
472         }
473     }
474 
475     // ____________________
476     /**
477      * ctor
478      * It creates a new instance of this class and innitialize it in the right mode.
479      *
480      * @param nMode
481      *          specify how the log should be generated.
482      *
483      * @param nFilter
484      *          can be used to filter log messages by it's type.
485      *
486      * @param sFileName
487      *          the name of the log file (if nMode requires a log file)
488      */
489     public Protocol(int    nMode    ,
490                     int    nFilter  ,
491                     String sFileName)
492     {
493         m_nMode      = nMode;
494         m_nFilter    = nFilter;
495         m_sFileName  = sFileName;
496         m_nLine      = 0;
497         m_nScope     = 1;
498         m_nWarnings  = 0;
499         m_nErrors    = 0;
500         m_aStartTime = new Timestamp(System.currentTimeMillis());
501     }
502 
503     // ____________________
504     /**
505      * For some modes it's necessary, that we write some additional
506      * informations to the log. E.g. for html we must generate close targets.
507      */
508     public synchronized void finish()
509     {
510         // Preferr HTML ... because we can't write ASCII and HTML contents to the same log file!
511         String sContent;
512         if ((m_nMode & MODE_HTML) == MODE_HTML)
513             sContent = impl_generateHTMLFooter();
514         else
515         if ((m_nMode & MODE_ASCII) == MODE_ASCII)
516             sContent = impl_generateAsciiFooter();
517         else
518             return;
519 
520         impl_writeToLogFile(m_sFileName, true, sContent);
521     }
522 
523     // ____________________
524     /**
525      * log an unspecified message.
526      *
527      * Sometimes it's not necessary to set a special type for an message.
528      * The pure message seams to be enough. The type of such "pure messages"
529      * will be set to INFO.
530      *
531      * @param   sMessage
532      *              the pure message
533      *
534      * @see     #log(int, String)
535      */
536     public synchronized void log( /*IN*/ String sMessage )
537     {
538         log(TYPE_INFO, sMessage);
539     }
540 
541     // ____________________
542     /**
543      * log an exception.
544      *
545      * It uses all informations available by this exception object
546      * to generate the log. So exceptions are printed out using a
547      * standard format.
548      *
549      * @param   exThrowable
550      *              the exception
551      */
552     public synchronized void log( /*IN*/ Throwable exThrowable )
553     {
554         log(TYPE_SCOPE_OPEN | TYPE_ERROR, "exception \""+exThrowable.getMessage()+"\"");
555 
556         StackTraceElement[] lStack = exThrowable.getStackTrace();
557         for (int i=0; i<lStack.length; ++i)
558            log(TYPE_ERROR_INFO, lStack[i].toString());
559 
560         log(TYPE_SCOPE_CLOSE | TYPE_ERROR_INFO, "");
561     }
562 
563     // ____________________
564     /**
565      * log different property arrays.
566      *
567      * @param   lProps
568      *              the array of properties
569      */
570     public synchronized void log( /*IN*/ com.sun.star.beans.NamedValue[] lProps )
571     {
572         StringBuffer sValues = new StringBuffer(1000);
573         impl_logPropertyArray(sValues, lProps);
574 
575         log(TYPE_SCOPE_OPEN  | TYPE_INFO, "property array ["+lProps.length+"]:");
576         log(TYPE_SCOPE_CLOSE | TYPE_INFO, sValues.toString()                   );
577     }
578 
579     public synchronized void log( /*IN*/ com.sun.star.beans.PropertyValue[] lProps )
580     {
581         StringBuffer sValues = new StringBuffer(1000);
582         impl_logPropertyArray(sValues, lProps);
583 
584         log(TYPE_SCOPE_OPEN  | TYPE_INFO, "property array ["+lProps.length+"]:");
585         log(TYPE_SCOPE_CLOSE | TYPE_INFO, sValues.toString()                   );
586     }
587 
588     public synchronized void log( /*IN*/ com.sun.star.beans.NamedValue aProp )
589     {
590         StringBuffer sValue = new StringBuffer(1000);
591         impl_logProperty(sValue, aProp);
592 
593         log(TYPE_SCOPE_OPEN  | TYPE_INFO, "property:"      );
594         log(TYPE_SCOPE_CLOSE | TYPE_INFO, sValue.toString());
595     }
596 
597     public synchronized void log( /*IN*/ com.sun.star.beans.PropertyValue aProp )
598     {
599         StringBuffer sValue = new StringBuffer(1000);
600         impl_logProperty(sValue, aProp);
601 
602         log(TYPE_SCOPE_OPEN  | TYPE_INFO, "property:"      );
603         log(TYPE_SCOPE_CLOSE | TYPE_INFO, sValue.toString());
604     }
605 
606     public synchronized void log( /*IN*/ Object aAny )
607     {
608         StringBuffer sValue = new StringBuffer(1000);
609         impl_logAny(sValue, aAny);
610 
611         log(TYPE_SCOPE_OPEN  | TYPE_INFO, "any:"           );
612         log(TYPE_SCOPE_CLOSE | TYPE_INFO, sValue.toString());
613     }
614 
615     // ____________________
616     /**
617      * log a message.
618      *
619      * It looks for the internal set mode and decide, how this message
620      * will be handled. Then it generates a special object which represent
621      * one protocol line, format it and print it out.
622      *
623      * @param nType
624      *          mark a line as a special one or open/close scopes
625      *
626      * @param sMessage
627      *          the message, which the outside code wish to be written into the log
628      */
629     public synchronized void log( /*IN*/ int    nType    ,
630                                   /*IN*/ String sMessage )
631     {
632         nType = (nType & ~m_nFilter);
633         if (nType == 0)
634             return;
635 
636         ++m_nLine;
637 
638         // it's necessary to open scopes before creatig the protocol line
639         // to guarantee right tab handling for new scope value!
640         if ((nType & TYPE_SCOPE_OPEN) == TYPE_SCOPE_OPEN)
641             ++m_nScope;
642 
643         // create the protocol line
644         ProtocolLine aLine     = new ProtocolLine(m_nLine, m_nScope, nType, sMessage);
645         String       sAsciiLog = aLine.toString();
646         String       sHTMLLog  = aLine.toHTML();
647 
648         // it's necessary to close scope after creatig the protocol line
649         // to guarantee right tab handling for old scope value!
650         if (
651             ( m_nScope                  >  0               ) &&
652             ((nType & TYPE_SCOPE_CLOSE) == TYPE_SCOPE_CLOSE)
653            )
654         {
655             --m_nScope;
656         }
657 
658         // update statistic values
659         if (aLine.isTestMark())
660             ++m_nTestMarks;
661         if (aLine.isWarning())
662             ++m_nWarnings;
663         if (aLine.isError())
664             ++m_nErrors;
665 
666         // no else - it's a bit field of possible reactions!
667         if ((m_nMode & MODE_STDOUT) == MODE_STDOUT)
668             System.out.print(sAsciiLog);
669         // no else - it's a bit field of possible reactions!
670         if ((m_nMode & MODE_STDERR) == MODE_STDERR)
671             System.err.print(sAsciiLog);
672         // no else - it's a bit field of possible reactions!
673         // But these both conditions must be handled together.
674         // Because we can't generate different types of log contents to the same log file.
675         // We preferr HTML if both types are set.
676         if (
677             ((m_nMode & MODE_HTML ) == MODE_HTML ) ||
678             ((m_nMode & MODE_ASCII) == MODE_ASCII)
679            )
680         {
681             boolean bAppend = (m_nLine>1);
682             String  sContent;
683             if ((m_nMode & MODE_HTML) == MODE_HTML)
684             {
685                 if (! bAppend)
686                     sContent = impl_generateHTMLHeader()+sHTMLLog;
687                 else
688                     sContent = sHTMLLog;
689             }
690             else
691             {
692                 if (! bAppend)
693                     sContent = impl_generateAsciiHeader()+sAsciiLog;
694                 else
695                     sContent = sAsciiLog;
696             }
697 
698             impl_writeToLogFile(m_sFileName, bAppend, sContent);
699         }
700     }
701 
702     // ____________________
703     public synchronized void defineHyperlink( /*IN*/ String sTarget     ,
704                                               /*IN*/ String sDescription)
705     {
706         if ((m_nMode & MODE_HTML) != MODE_HTML)
707             return;
708 
709         StringBuffer sLog = new StringBuffer(1000);
710         sLog.append("<a href=\"");
711         sLog.append(sTarget     );
712         sLog.append("\">"       );
713         sLog.append(sDescription);
714         sLog.append("</a>"      );
715 
716         log(TYPE_LINK, sLog.toString());
717     }
718 
719     // ____________________
720     /**
721      * log the current statistic values
722      * We write it into our protocol buffer and
723      * reset it.
724      */
725     public synchronized void logStatistics()
726     {
727                   m_aEndTime = new Timestamp(System.currentTimeMillis());
728         Timestamp aDiff      = new Timestamp(m_aEndTime.getTime()-m_aStartTime.getTime());
729 
730         int nLogType = TYPE_STATISTIC;
731         if (m_nErrors > 0)
732             nLogType = TYPE_ERROR_INFO;
733         else
734         if (m_nWarnings > 0)
735             nLogType = TYPE_WARNING_INFO;
736 
737         log(nLogType | TYPE_SCOPE_OPEN , "statistic:"                      );
738         log(nLogType                   , "tests        = "+m_nTestMarks    );
739         log(nLogType                   , "errors       = "+m_nErrors       );
740         log(nLogType                   , "warnings     = "+m_nWarnings     );
741         log(nLogType                   , "elapsed time = "+aDiff.toString());
742         log(nLogType | TYPE_SCOPE_CLOSE, ""                                );
743 
744         resetStatistics();
745     }
746 
747     public synchronized void resetStatistics()
748     {
749         m_nTestMarks = 0;
750         m_nWarnings  = 0;
751         m_nErrors    = 0;
752         m_aStartTime = new Timestamp(System.currentTimeMillis());
753     }
754 
755     // ____________________
756     /**
757      * returns a generic html header for generating html log files
758      *
759      * It's used from the method finish() to generate a valid html formated file.
760      * For that its necessary to open some special html targets like e.g. <html>.
761      *
762      * @return  A string, which includes the whole header.
763      *
764      * @see     #finish()
765      * @see     #impl_generateHTMLFooter()
766      */
767     private String impl_generateHTMLHeader()
768     {
769         return "<html>\n<head>\n<title>"+m_sFileName+"</title>\n</head>\n<body>\n<table>\n";
770     }
771 
772     private String impl_generateAsciiHeader()
773     {
774         return "********************************************************************************\n";
775     }
776 
777     private String impl_generateHTMLFooter()
778     {
779         return "\n</table>\n</body>\n</html>\n";
780     }
781 
782     private String impl_generateAsciiFooter()
783     {
784         return "\n\n";
785     }
786 
787     // ____________________
788     /**
789      * helper to log different representations of a property(array)
790      *
791      * @param   sOut
792      *              used to generate the log output there.
793      *
794      * @param   lProps
795      *              represent the property(array) to be logged.
796      */
797     private void impl_logPropertyArray( /*OUT*/ StringBuffer                       sOut   ,
798                                         /*IN */ com.sun.star.beans.PropertyValue[] lProps )
799     {
800         int i = 0;
801         int c = lProps.length;
802 
803         for (i=0; i<c; ++i)
804             impl_logProperty(sOut, lProps[i]);
805     }
806 
807     private void impl_logPropertyArray( /*OUT*/ StringBuffer                    sOut   ,
808                                         /*IN */ com.sun.star.beans.NamedValue[] lProps )
809     {
810         int i = 0;
811         int c = lProps.length;
812 
813         for (i=0; i<c; ++i)
814             impl_logProperty(sOut, lProps[i]);
815     }
816 
817     private void impl_logProperty( /*OUT*/ StringBuffer                  sOut  ,
818                                    /*IN*/  com.sun.star.beans.NamedValue aProp )
819     {
820         sOut.append("\""+aProp.Name+"\" = ");
821         impl_logAny(sOut, aProp.Value);
822     }
823 
824     private void impl_logProperty( /*OUT*/ StringBuffer                     sOut  ,
825                                    /*IN*/  com.sun.star.beans.PropertyValue aProp )
826     {
827         sOut.append("\""+aProp.Name+"\" = ");
828         impl_logAny(sOut, aProp.Value);
829     }
830 
831     // ____________________
832     /**
833      * it trys to convert the given any into a suitable string notation .-)
834     */
835     private synchronized void impl_logAny( /*OUT*/ StringBuffer sOut ,
836                                            /*IN */ Object       aAny )
837     {
838         try
839         {
840             if (com.sun.star.uno.AnyConverter.isVoid(aAny))
841             {
842                 sOut.append("[void] {");
843             }
844             else
845             if (com.sun.star.uno.AnyConverter.isChar(aAny))
846             {
847                 sOut.append("[char] {");
848                 sOut.append(com.sun.star.uno.AnyConverter.toChar(aAny));
849                 sOut.append("}");
850             }
851             else
852             if (com.sun.star.uno.AnyConverter.isBoolean(aAny))
853             {
854                 sOut.append("[boolean] {");
855                 if (com.sun.star.uno.AnyConverter.toBoolean(aAny))
856                     sOut.append("TRUE");
857                 else
858                     sOut.append("FALSE");
859                 sOut.append("}");
860             }
861             else
862             if (com.sun.star.uno.AnyConverter.isByte(aAny))
863             {
864                 sOut.append("[byte] {");
865                 sOut.append(com.sun.star.uno.AnyConverter.toByte(aAny));
866                 sOut.append("}");
867             }
868             else
869             if (com.sun.star.uno.AnyConverter.isShort(aAny))
870             {
871                 sOut.append("[short] {");
872                 sOut.append(com.sun.star.uno.AnyConverter.toShort(aAny));
873                 sOut.append("}");
874             }
875             else
876             if (com.sun.star.uno.AnyConverter.isInt(aAny))
877             {
878                 sOut.append("[int] {");
879                 sOut.append(com.sun.star.uno.AnyConverter.toInt(aAny));
880                 sOut.append("}");
881             }
882             else
883             if (com.sun.star.uno.AnyConverter.isLong(aAny))
884             {
885                 sOut.append("[long] {");
886                 sOut.append(com.sun.star.uno.AnyConverter.toLong(aAny));
887                 sOut.append("}");
888             }
889             else
890             if (com.sun.star.uno.AnyConverter.isFloat(aAny))
891             {
892                 sOut.append("[float] {");
893                 sOut.append(com.sun.star.uno.AnyConverter.toFloat(aAny));
894                 sOut.append("}");
895             }
896             else
897             if (com.sun.star.uno.AnyConverter.isDouble(aAny))
898             {
899                 sOut.append("[double] {");
900                 sOut.append(com.sun.star.uno.AnyConverter.toDouble(aAny));
901                 sOut.append("}");
902             }
903             else
904             if (com.sun.star.uno.AnyConverter.isString(aAny))
905             {
906                 sOut.append("[string] {");
907                 sOut.append(com.sun.star.uno.AnyConverter.toString(aAny));
908                 sOut.append("}");
909             }
910             else
911             if (com.sun.star.uno.AnyConverter.isEnum(aAny))
912             {
913                 sOut.append("[enum] {");
914                 sOut.append("}");
915             }
916             else
917             if (com.sun.star.uno.AnyConverter.isType(aAny))
918             {
919                 sOut.append("[type] {");
920                 sOut.append("}");
921             }
922             else
923             if (com.sun.star.uno.AnyConverter.isArray(aAny))
924             {
925                 if (aAny instanceof java.lang.String[])
926                 {
927                     sOut.append("[sequence< string >] {");
928                     sOut.append("}");
929                 }
930                 else
931                 if (aAny instanceof com.sun.star.beans.PropertyValue[])
932                 {
933                     sOut.append("[sequence< PropertyValue >] {");
934                     com.sun.star.beans.PropertyValue[] lSubProps = (com.sun.star.beans.PropertyValue[])com.sun.star.uno.AnyConverter.toArray(aAny);
935                     impl_logPropertyArray(sOut, lSubProps);
936                     sOut.append("}");
937                 }
938                 else
939                 if (aAny instanceof com.sun.star.beans.NamedValue[])
940                 {
941                     sOut.append("[sequence< NamedValue >] {");
942                     com.sun.star.beans.NamedValue[] lSubProps = (com.sun.star.beans.NamedValue[])com.sun.star.uno.AnyConverter.toArray(aAny);
943                     impl_logPropertyArray(sOut, lSubProps);
944                     sOut.append("}");
945                 }
946                 else
947                 {
948                     sOut.append("[unknown array] {-}");
949                 }
950             }
951             else
952             if (com.sun.star.uno.AnyConverter.isObject(aAny))
953             {
954                 sOut.append("[object] {");
955                 // TODO
956                 sOut.append("}");
957             }
958 
959             if ((m_nMode & MODE_HTML) == MODE_HTML)
960                 sOut.append("<br>");
961             else
962                 sOut.append("\n");
963         }
964         catch(com.sun.star.lang.IllegalArgumentException exIll)
965         {
966             sOut.append("Got exception during property conversion.\n");
967             sOut.append(exIll.getMessage());
968             sOut.append("\n");
969         }
970     }
971 
972     // ____________________
973     /**
974      * Writes the given content to the specified log file.
975      */
976     private void impl_writeToLogFile(String  sFileName,
977                                      boolean bAppend  ,
978                                      String  sContent )
979     {
980         try
981         {
982             FileWriter aLogFile = new FileWriter(sFileName, bAppend);
983             aLogFile.write(sContent);
984             aLogFile.flush();
985             aLogFile.close();
986             aLogFile = null;
987         }
988         catch (java.io.IOException exIO)
989         {
990             System.err.println("Can't dump protocol into log file.");
991             System.err.println(exIO);
992             exIO.printStackTrace();
993         }
994     }
995 }
996