/*
* Copyright (c) 2002-2018 Gargoyle Software Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gargoylesoftware.htmlunit;
import java.io.
Serializable;
import java.util.
Arrays;
import java.util.
LinkedList;
import java.util.
List;
import org.apache.commons.logging.
Log;
import org.apache.commons.logging.
LogFactory;
/**
* This class can be used to print messages to the logger. The first parameter
* can be a message-object containing format specifiers such as ("%o", "%s",
* "%d", "%i", "%f"). The logging methods are null-safe, so if the number of
* format specifiers and the numbers of parameters don't match, no exception is thrown.
*
* The default logger uses Apache Commons Logging.
*
* @author Andrea Martino
*/
public class
WebConsole implements
Serializable {
/**
* A simple logging interface abstracting logging APIs.
*/
public interface
Logger {
/**
* Is trace logging currently enabled?
* <p>
* Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than trace.
*
* @return true if trace is enabled in the underlying logger.
*/
boolean
isTraceEnabled();
/**
* Logs a message with trace log level.
*
* @param message log this message
*/
void
trace(
Object message);
/**
* Is debug logging currently enabled?
* <p>
* Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than debug.
*
* @return true if debug is enabled in the underlying logger.
*/
boolean
isDebugEnabled();
/**
* Logs a message with debug log level.
*
* @param message log this message
*/
void
debug(
Object message);
/**
* Is info logging currently enabled?
* <p>
* Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than info.
*
* @return true if info is enabled in the underlying logger.
*/
boolean
isInfoEnabled();
/**
* Logs a message with info log level.
*
* @param message log this message
*/
void
info(
Object message);
/**
* Is warn logging currently enabled?
* <p>
* Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than warn.
*
* @return true if warn is enabled in the underlying logger.
*/
boolean
isWarnEnabled();
/**
* Logs a message with warn log level.
*
* @param message log this message
*/
void
warn(
Object message);
/**
* Is error logging currently enabled?
* <p>
* Call this method to prevent having to perform expensive operations
* (for example, <code>String</code> concatenation)
* when the log level is more than error.
*
* @return true if error is enabled in the underlying logger.
*/
boolean
isErrorEnabled();
/**
* Logs a message with error log level.
*
* @param message log this message
*/
void
error(
Object message);
}
/**
* This interface can be implemented by clients that want to customize
* the way parameters and objects are logged.
*/
public interface
Formatter {
/**
* Function that is used to print an object to the console.
* @param o object to be printed
* @return a string representation of the passed object
*/
String printObject(
Object o);
/**
* Function that is used to print an object as string using
* format specifiers.
* @param o object to be printed using string format specifiers
* @return a string representation of the passed object
*/
String parameterAsString(
Object o);
/**
* Function that is used to print an object as integer using
* format specifiers.
* @param o object to be printed using integer format specifiers
* @return a string representation of the passed object
*/
String parameterAsInteger(
Object o);
/**
* Function that is used to print an object as float using
* format specifiers.
* @param o object to be printed using float format specifiers
* @return a string representation of the passed object
*/
String parameterAsFloat(
Object o);
}
private
Formatter formatter_ = new
DefaultFormatter();
private
Logger logger_ = new
DefaultLogger();
/**
* Sets the Formatter.
* @param formatter the formatter
*/
public void
setFormatter(final
Formatter formatter) {
formatter_ =
formatter;
}
/**
* Returns the current Formatter.
* @return the Formatter
*/
public
Formatter getFormatter() {
return
formatter_;
}
/**
* Sets the Logger_.
* @param logger the logger
*/
public void
setLogger(final
Logger logger) {
logger_ =
logger;
}
/**
* Returns the current Logger.
* @return the logger
*/
public
Logger getLogger() {
return
logger_;
}
/**
* Prints the passed objects using logger trace level.
* @param args the logging parameters
*/
public void
trace(final
Object...
args) {
if (
logger_.
isTraceEnabled()) {
logger_.
trace(
process(
args));
}
}
/**
* Prints the passed objects using logger debug level.
* @param args the logging parameters
*/
public void
debug(final
Object...
args) {
if (
logger_.
isDebugEnabled()) {
logger_.
debug(
process(
args));
}
}
/**
* Prints the passed objects using logger info level.
* @param args the logging parameters
*/
public void
info(final
Object...
args) {
if (
logger_.
isInfoEnabled()) {
logger_.
info(
process(
args));
}
}
/**
* Prints the passed objects using logger warn level.
* @param args the logging parameters
*/
public void
warn(final
Object...
args) {
if (
logger_.
isWarnEnabled()) {
logger_.
warn(
process(
args));
}
}
/**
* Prints the passed objects using logger error level.
* @param args the logging parameters
*/
public void
error(final
Object...
args) {
if (
logger_.
isErrorEnabled()) {
logger_.
error(
process(
args));
}
}
/**
* This method is used by all the public method to process the passed
* parameters.
*
* If the last parameter implements the Formatter interface, it will be
* used to format the parameters and print the object.
* @param objs the logging parameters
* @return a String to be printed using the logger
*/
private
String process(final
Object[]
objs) {
if (
objs == null) {
return "null";
}
final
StringBuilder sb = new
StringBuilder();
final
LinkedList<
Object>
args = new
LinkedList<>(
Arrays.
asList(
objs));
final
Formatter formatter =
getFormatter();
if (
args.
size() > 1 &&
args.
get(0) instanceof
String) {
final
StringBuilder msg = new
StringBuilder((
String)
args.
remove(0));
int
startPos =
msg.
indexOf("%");
while (
startPos > -1 &&
startPos <
msg.
length() - 1 &&
args.
size() > 0) {
if (
startPos != 0 &&
msg.
charAt(
startPos - 1) == '%') {
// double %
msg.
replace(
startPos,
startPos + 1, "");
}
else {
final char
type =
msg.
charAt(
startPos + 1);
String replacement = null;
switch (
type) {
case 'o':
case 's':
replacement =
formatter.
parameterAsString(
pop(
args));
break;
case 'd':
case 'i':
replacement =
formatter.
parameterAsInteger(
pop(
args));
break;
case 'f':
replacement =
formatter.
parameterAsFloat(
pop(
args));
break;
default:
break;
}
if (
replacement != null) {
msg.
replace(
startPos,
startPos + 2,
replacement);
startPos =
startPos +
replacement.
length();
}
else {
startPos++;
}
}
startPos =
msg.
indexOf("%",
startPos);
}
sb.
append(
msg);
}
for (final
Object o :
args) {
if (
sb.
length() != 0) {
sb.
append(' ');
}
sb.
append(
formatter.
printObject(
o));
}
return
sb.
toString();
}
/**
* This method removes and returns the first (i.e. at index 0) element in
* the passed list if it exists. Otherwise, null is returned.
* @param list the
* @return the first object in the list or null
*/
private static
Object pop(final
List<
Object>
list) {
return
list.
isEmpty() ? null :
list.
remove(0);
}
/**
* This class is the default formatter used by WebConsole.
*/
private static class
DefaultFormatter implements
Formatter,
Serializable {
@
Override
public
String printObject(final
Object o) {
return
parameterAsString(
o);
}
@
Override
public
String parameterAsString(final
Object o) {
if (
o != null) {
return
o.
toString();
}
return "null";
}
@
Override
public
String parameterAsInteger(final
Object o) {
if (
o instanceof
Number) {
return
Integer.
toString(((
Number)
o).
intValue());
}
else if (
o instanceof
String) {
try {
return
Integer.
toString(
Integer.
parseInt((
String)
o));
}
catch (final
NumberFormatException e) {
// Swallow the exception and return below
}
}
return "NaN";
}
@
Override
public
String parameterAsFloat(final
Object o) {
if (
o instanceof
Number) {
return
Float.
toString(((
Number)
o).
floatValue());
}
else if (
o instanceof
String) {
try {
return
Float.
toString(
Float.
parseFloat((
String)
o));
}
catch (final
NumberFormatException e) {
// Swallow the exception and return below
}
}
return "NaN";
}
}
/**
* This class is the default logger used by WebConsole.
*/
private static class
DefaultLogger implements
Logger,
Serializable {
/** Logging support. */
private static final
Log LOG =
LogFactory.
getLog(
WebConsole.class);
@
Override
public boolean
isTraceEnabled() {
return
LOG.
isTraceEnabled();
}
@
Override
public void
trace(final
Object message) {
if (
LOG.
isTraceEnabled()) {
LOG.
trace(
message);
}
}
@
Override
public boolean
isDebugEnabled() {
return
LOG.
isDebugEnabled();
}
@
Override
public void
debug(final
Object message) {
if (
LOG.
isDebugEnabled()) {
LOG.
debug(
message);
}
}
@
Override
public boolean
isInfoEnabled() {
return
LOG.
isInfoEnabled();
}
@
Override
public void
info(final
Object message) {
LOG.
info(
message);
}
@
Override
public boolean
isWarnEnabled() {
return
LOG.
isWarnEnabled();
}
@
Override
public void
warn(final
Object message) {
LOG.
warn(
message);
}
@
Override
public boolean
isErrorEnabled() {
return
LOG.
isErrorEnabled();
}
@
Override
public void
error(final
Object message) {
LOG.
error(
message);
}
}
}