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