001/* ========================================================================
002 * JCommon : a free general purpose class library for the Java(tm) platform
003 * ========================================================================
004 *
005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006 * 
007 * Project Info:  http://www.jfree.org/jcommon/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it 
010 * under the terms of the GNU Lesser General Public License as published by 
011 * the Free Software Foundation; either version 2.1 of the License, or 
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but 
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022 * USA.  
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025 * in the United States and other countries.]
026 * 
027 * --------
028 * Log.java
029 * --------
030 * (C)opyright 2002-2004, by Thomas Morgner and Contributors.
031 *
032 * Original Author:  Thomas Morgner (taquera@sherito.org);
033 * Contributor(s):   David Gilbert (for Object Refinery Limited);
034 *
035 * $Id: Log.java,v 1.5 2006/06/08 17:42:20 taqua Exp $
036 *
037 * Changes
038 * -------
039 * 29-Apr-2003 : Distilled from the JFreeReport project and moved into JCommon
040 * 11-Jun-2003 : Removing LogTarget did not work. 
041 * 
042 */
043
044package org.jfree.util;
045
046import java.util.ArrayList;
047import java.util.Arrays;
048import java.util.HashMap;
049
050/**
051 * A simple logging facility. Create a class implementing the {@link org.jfree.util.LogTarget}
052 * interface to use this feature.
053 *
054 * @author Thomas Morgner
055 */
056public class Log {
057
058    /**
059     * A simple message class.
060     */
061    public static class SimpleMessage {
062
063        /**
064         * The message.
065         */
066        private String message;
067
068        /**
069         * The parameters.
070         */
071        private Object[] param;
072
073        /**
074         * Creates a new message.
075         *
076         * @param message the message text.
077         * @param param1  parameter 1.
078         */
079        public SimpleMessage(final String message, final Object param1) {
080            this.message = message;
081            this.param = new Object[]{param1};
082        }
083
084        /**
085         * Creates a new message.
086         *
087         * @param message the message text.
088         * @param param1  parameter 1.
089         * @param param2  parameter 2.
090         */
091        public SimpleMessage(final String message, final Object param1,
092                             final Object param2) {
093            this.message = message;
094            this.param = new Object[]{param1, param2};
095        }
096
097        /**
098         * Creates a new message.
099         *
100         * @param message the message text.
101         * @param param1  parameter 1.
102         * @param param2  parameter 2.
103         * @param param3  parameter 3.
104         */
105        public SimpleMessage(final String message, final Object param1,
106                             final Object param2, final Object param3) {
107            this.message = message;
108            this.param = new Object[]{param1, param2, param3};
109        }
110
111        /**
112         * Creates a new message.
113         *
114         * @param message the message text.
115         * @param param1  parameter 1.
116         * @param param2  parameter 2.
117         * @param param3  parameter 3.
118         * @param param4  parameter 4.
119         */
120        public SimpleMessage(final String message, final Object param1,
121                             final Object param2, final Object param3,
122                             final Object param4) {
123            this.message = message;
124            this.param = new Object[]{param1, param2, param3, param4};
125        }
126
127        /**
128         * Creates a new message.
129         *
130         * @param message the message text.
131         * @param param   the parameters.
132         */
133        public SimpleMessage(final String message, final Object[] param) {
134            this.message = message;
135            this.param = param;
136        }
137
138        /**
139         * Returns a string representation of the message (useful for debugging).
140         *
141         * @return the string.
142         */
143        public String toString() {
144            final StringBuffer b = new StringBuffer();
145            b.append(this.message);
146            if (this.param != null) {
147                for (int i = 0; i < this.param.length; i++) {
148                    b.append(this.param[i]);
149                }
150            }
151            return b.toString();
152        }
153    }
154
155
156    /**
157     * The logging threshold.
158     */
159    private int debuglevel;
160
161    /**
162     * Storage for the log targets.
163     */
164    private LogTarget[] logTargets;
165
166    /** The log contexts. */
167    private HashMap logContexts;
168
169    /**
170     * the singleton instance of the Log system.
171     */
172    private static Log singleton;
173
174    /**
175     * Creates a new Log instance. The Log is used to manage the log targets.
176     */
177    protected Log() {
178        this.logContexts = new HashMap();
179        this.logTargets = new LogTarget[0];
180        this.debuglevel = 100;
181    }
182
183    /**
184     * Returns the singleton Log instance. A new instance is created if necessary.
185     *
186     * @return the singleton instance.
187     */
188    public static synchronized Log getInstance() {
189        if (singleton == null) {
190            singleton = new Log();
191        }
192        return singleton;
193    }
194
195    /**
196     * Redefines or clears the currently used log instance.
197     *
198     * @param log the new log instance or null, to return to the default implementation.
199     */
200    protected static synchronized void defineLog(final Log log) {
201        singleton = log;
202    }
203
204    /**
205     * Returns the currently defined debug level. The higher the level, the more details
206     * are printed.
207     *
208     * @return the debug level.
209     */
210    public int getDebuglevel() {
211        return this.debuglevel;
212    }
213
214    /**
215     * Defines the debug level for the log system.
216     *
217     * @param debuglevel the new debug level
218     * @see #getDebuglevel()
219     */
220    protected void setDebuglevel(final int debuglevel) {
221        this.debuglevel = debuglevel;
222    }
223
224    /**
225     * Adds a log target to this facility. Log targets get informed, via the LogTarget interface,
226     * whenever a message is logged with this class.
227     *
228     * @param target the target.
229     */
230    public synchronized void addTarget(final LogTarget target) {
231        if (target == null) {
232            throw new NullPointerException();
233        }
234        final LogTarget[] data = new LogTarget[this.logTargets.length + 1];
235        System.arraycopy(this.logTargets, 0, data, 0, this.logTargets.length);
236        data[this.logTargets.length] = target;
237        this.logTargets = data;
238    }
239
240    /**
241     * Removes a log target from this facility.
242     *
243     * @param target the target to remove.
244     */
245    public synchronized void removeTarget(final LogTarget target) {
246        if (target == null) {
247            throw new NullPointerException();
248        }
249        final ArrayList l = new ArrayList();
250        l.addAll(Arrays.asList(this.logTargets));
251        l.remove(target);
252
253        final LogTarget[] targets = new LogTarget[l.size()];
254        this.logTargets = (LogTarget[]) l.toArray(targets);
255    }
256
257    /**
258     * Returns the registered logtargets.
259     *
260     * @return the logtargets.
261     */
262    public LogTarget[] getTargets() {
263        return (LogTarget[]) this.logTargets.clone();
264    }
265
266    /**
267     * Replaces all log targets by the given target.
268     *
269     * @param target the new and only logtarget.
270     */
271    public synchronized void replaceTargets(final LogTarget target) {
272        if (target == null) {
273            throw new NullPointerException();
274        }
275        this.logTargets = new LogTarget[]{target};
276    }
277
278    /**
279     * A convenience method for logging a 'debug' message.
280     *
281     * @param message the message.
282     */
283    public static void debug(final Object message) {
284        log(LogTarget.DEBUG, message);
285    }
286
287    /**
288     * A convenience method for logging a 'debug' message.
289     *
290     * @param message the message.
291     * @param e       the exception.
292     */
293    public static void debug(final Object message, final Exception e) {
294        log(LogTarget.DEBUG, message, e);
295    }
296
297    /**
298     * A convenience method for logging an 'info' message.
299     *
300     * @param message the message.
301     */
302    public static void info(final Object message) {
303        log(LogTarget.INFO, message);
304    }
305
306    /**
307     * A convenience method for logging an 'info' message.
308     *
309     * @param message the message.
310     * @param e       the exception.
311     */
312    public static void info(final Object message, final Exception e) {
313        log(LogTarget.INFO, message, e);
314    }
315
316    /**
317     * A convenience method for logging a 'warning' message.
318     *
319     * @param message the message.
320     */
321    public static void warn(final Object message) {
322        log(LogTarget.WARN, message);
323    }
324
325    /**
326     * A convenience method for logging a 'warning' message.
327     *
328     * @param message the message.
329     * @param e       the exception.
330     */
331    public static void warn(final Object message, final Exception e) {
332        log(LogTarget.WARN, message, e);
333    }
334
335    /**
336     * A convenience method for logging an 'error' message.
337     *
338     * @param message the message.
339     */
340    public static void error(final Object message) {
341        log(LogTarget.ERROR, message);
342    }
343
344    /**
345     * A convenience method for logging an 'error' message.
346     *
347     * @param message the message.
348     * @param e       the exception.
349     */
350    public static void error(final Object message, final Exception e) {
351        log(LogTarget.ERROR, message, e);
352    }
353
354    /**
355     * Logs a message to the main log stream.  All attached log targets will also
356     * receive this message. If the given log-level is higher than the given debug-level
357     * in the main config file, no logging will be done.
358     *
359     * @param level   log level of the message.
360     * @param message text to be logged.
361     */
362    protected void doLog(int level, final Object message) {
363        if (level > 3) {
364            level = 3;
365        }
366        if (level <= this.debuglevel) {
367            for (int i = 0; i < this.logTargets.length; i++) {
368                final LogTarget t = this.logTargets[i];
369                t.log(level, message);
370            }
371        }
372    }
373
374    /**
375     * Logs a message to the main log stream.  All attached log targets will also
376     * receive this message. If the given log-level is higher than the given debug-level
377     * in the main config file, no logging will be done.
378     *
379     * @param level   log level of the message.
380     * @param message text to be logged.
381     */
382    public static void log(final int level, final Object message) {
383        getInstance().doLog(level, message);
384    }
385
386    /**
387     * Logs a message to the main log stream. All attached logTargets will also
388     * receive this message. If the given log-level is higher than the given debug-level
389     * in the main config file, no logging will be done.
390     * <p/>
391     * The exception's stacktrace will be appended to the log-stream
392     *
393     * @param level   log level of the message.
394     * @param message text to be logged.
395     * @param e       the exception, which should be logged.
396     */
397    public static void log(final int level, final Object message, final Exception e) {
398        getInstance().doLog(level, message, e);
399    }
400
401    /**
402     * Logs a message to the main log stream. All attached logTargets will also
403     * receive this message. If the given log-level is higher than the given debug-level
404     * in the main config file, no logging will be done.
405     * <p/>
406     * The exception's stacktrace will be appended to the log-stream
407     *
408     * @param level   log level of the message.
409     * @param message text to be logged.
410     * @param e       the exception, which should be logged.
411     */
412    protected void doLog(int level, final Object message, final Exception e) {
413        if (level > 3) {
414            level = 3;
415        }
416
417        if (level <= this.debuglevel) {
418            for (int i = 0; i < this.logTargets.length; i++) {
419                final LogTarget t = this.logTargets[i];
420                t.log(level, message, e);
421            }
422        }
423    }
424
425    /**
426     * Initializes the logging system. Implementors should
427     * override this method to supply their own log configuration.
428     */
429    public void init() {
430        // this method is intentionally empty.
431    }
432
433    /**
434     * Returns true, if the log level allows debug messages to be
435     * printed.
436     *
437     * @return true, if messages with an log level of DEBUG are allowed.
438     */
439    public static boolean isDebugEnabled() {
440        return getInstance().getDebuglevel() >= LogTarget.DEBUG;
441    }
442
443    /**
444     * Returns true, if the log level allows informational
445     * messages to be printed.
446     *
447     * @return true, if messages with an log level of INFO are allowed.
448     */
449    public static boolean isInfoEnabled() {
450        return getInstance().getDebuglevel() >= LogTarget.INFO;
451    }
452
453    /**
454     * Returns true, if the log level allows warning messages to be
455     * printed.
456     *
457     * @return true, if messages with an log level of WARN are allowed.
458     */
459    public static boolean isWarningEnabled() {
460        return getInstance().getDebuglevel() >= LogTarget.WARN;
461    }
462
463    /**
464     * Returns true, if the log level allows error messages to be
465     * printed.
466     *
467     * @return true, if messages with an log level of ERROR are allowed.
468     */
469    public static boolean isErrorEnabled() {
470        return getInstance().getDebuglevel() >= LogTarget.ERROR;
471    }
472
473    /**
474     * Creates a log context.
475     * 
476     * @param context  the class (<code>null</code> not permitted).
477     * 
478     * @return A log context.
479     */
480    public static LogContext createContext(final Class context) {
481        return createContext(context.getName());
482    }
483
484    /**
485     * Creates a log context.
486     * 
487     * @param context  the label for the context.
488     * 
489     * @return A log context.
490     */
491    public static LogContext createContext(final String context) {
492        return getInstance().internalCreateContext(context);
493    }
494
495    /**
496     * Creates a log context.
497     * 
498     * @param context  the name of the logging context (a common prefix).
499     * 
500     * @return A log context.
501     */
502    protected LogContext internalCreateContext(final String context) {
503        synchronized (this) {
504            LogContext ctx = (LogContext) this.logContexts.get(context);
505            if (ctx == null) {
506                ctx = new LogContext(context);
507                this.logContexts.put(context, ctx);
508            }
509            return ctx;
510        }
511    }
512    
513}