/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;
import org.tmatesoft.svn.core.wc.SVNBasicClient;
/**
* The <b>SVNErrorMessage</b> class represents error and warning messages describing
* reasons of exceptions occurred during runtime. An error message may be of two levels:
* <ul>
* <li>Error type</li>
* <li>Warning type</li>
* </ul>
* An error message may contain an error messages stack trace, what is useful for
* error reason investigations. Also such a message contains an error code ({@link SVNErrorCode})
* what gives an ability to find out what kind of an error it is.
*
* <p>
* Error messages may be formatted. <b>SVNErrorMessage</b> performs formatting with the
* help of the JDK's {@link MessageFormat} class. To make a formatted message, use
* {@link MessageFormat} parsable format patterns and provide an array of related objects
* when creating an <b>SVNErrorMessage</b>.
*
* <p>
* Error messages may be supplied within exceptions of the main exception type -
* {@link SVNException}.
*
* @version 1.3
* @author TMate Software Ltd.
* @since 1.2
*/
public class SVNErrorMessage implements Serializable {
private static final long serialVersionUID = 4845L;
/**
* Error messages of this type are considered to be errors (most critical) rather
* than warnings.
*/
public static final int TYPE_ERROR = 0;
/**
* Error messages of this type are considered to be warnings, what in certain
* situations may be OK.
*/
public static final int TYPE_WARNING = 1;
private Object[] myObjects;
private String myMessage;
private SVNErrorCode myErrorCode;
private int myType;
private SVNErrorMessage myChildErrorMessage;
private Throwable myThrowable;
private boolean dontShowErrorCode;
private static final Object[] EMPTY_ARRAY = new Object[0];
/**
* This is a type of an error message denoting an error of an unknown nature.
* This corresponds to an {@link SVNErrorCode#UNKNOWN} error.
*/
public static SVNErrorMessage UNKNOWN_ERROR_MESSAGE = create(SVNErrorCode.UNKNOWN);
/**
* Creates an error message given an error code.
*
* @param code an error code
* @return a new error message
*/
public static SVNErrorMessage create(SVNErrorCode code) {
return create(code, "", TYPE_ERROR);
}
/**
* Creates an error message given an error code and description.
*
* @param code an error code
* @param message an error description
* @return a new error message
*/
public static SVNErrorMessage create(SVNErrorCode code, String message) {
return create(code, message, TYPE_ERROR);
}
/**
* Creates an error message given an error code and cause.
*
* @param code an error code
* @param cause cause of the error
* @return a new error message
*/
public static SVNErrorMessage create(SVNErrorCode code, Throwable cause) {
if (cause != null) {
return new SVNErrorMessage(code, cause.getMessage(), new Object[0], cause, TYPE_ERROR);
}
return create(code);
}
/**
* Creates an error message given an error code, description and may be a related
* object to be formatted with the error description.
* To format the provided <code>object</code> with the <code>message</code>, you
* should use valid format patterns parsable for {@link MessageFormat}.
*
* @param code an error code
* @param message an error description
* @param object an object related to the error <code>message</code>
* @return a new error message
*/
public static SVNErrorMessage create(SVNErrorCode code, String message, Object object) {
return create(code, message, object, TYPE_ERROR);
}
/**
* Creates an error message given an error code, description and may be related
* objects to be formatted with the error description.
* To format the provided <code>objects</code> with the <code>message</code>, you
* should use valid format patterns parsable for {@link MessageFormat}.
*
* @param code an error code
* @param message an error description
* @param objects an array of objects related to the error <code>message</code>
* @return a new error message
*/
public static SVNErrorMessage create(SVNErrorCode code, String message, Object... objects) {
return create(code, message, objects, TYPE_ERROR);
}
/**
* Creates an error message given an error code, description and a type (
* whether it's a warning or an error).
*
* @param code an error code
* @param message an error description
* @param type an error type
* @return a new error message
*/
public static SVNErrorMessage create(SVNErrorCode code, String message, int type) {
return create(code, message, null, type, null);
}
/**
* Creates an error message given an error code, description, an error type
* (whether it's a warning or an error) and may be a related object to be
* formatted with the error description. To format the provided <code>object</code>
* with the <code>message</code>, you should use valid format patterns parsable for
* {@link MessageFormat}.
*
* @param code an error code
* @param message an error description
* @param object an object related to the error <code>message</code>
* @param type an error type
* @return a new error message
*/
public static SVNErrorMessage create(SVNErrorCode code, String message, Object object, int type) {
return new SVNErrorMessage(code == null ? SVNErrorCode.BASE : code, message == null ? "" : message,
object == null ? new Object[] {"NULL"} : new Object[] {object}, null, type);
}
/**
* Creates an error message given an error code, description, an error type
* (whether it's a warning or an error) and may be related objects to be
* formatted with the error description. To format the provided <code>objects</code>
* with the <code>message</code>, you should use valid format patterns parsable for
* {@link MessageFormat}.
*
* @param code an error code
* @param message an error description
* @param objects an array of objects related to the error <code>message</code>
* @param type an error type
* @return a new error message
*/
public static SVNErrorMessage create(SVNErrorCode code, String message, Object[] objects, int type) {
return create(code, message, objects, type, null);
}
/**
* Creates an error message given an error code, description, an error type
* (whether it's a warning or an error) and may be related objects to be
* formatted with the error description and an optional cause.
* To format the provided <code>objects</code>
* with the <code>message</code>, you should use valid format patterns parsable for
* {@link MessageFormat}.
*
* @param code an error code
* @param message an error description
* @param objects an array of objects related to the error <code>message</code>
* @param type an error type
* @param cause cause of the error
* @return a new error message
*/
public static SVNErrorMessage create(SVNErrorCode code, String message, Object[] objects, int type, Throwable cause) {
return new SVNErrorMessage(code == null ? SVNErrorCode.BASE : code, message == null ? "" : message,
objects == null ? EMPTY_ARRAY : objects, cause, type);
}
protected SVNErrorMessage(SVNErrorCode code, String message, Object[] relatedObjects, Throwable th, int type) {
myErrorCode = code;
if (message != null && message.startsWith("svn: ")) {
message = message.substring("svn: ".length());
}
myMessage = message;
myObjects = relatedObjects;
myType = type;
myThrowable = th;
}
/**
* Returns the type of the error (whether it's a warning or an error).
*
* @return the type of this error message
*/
public int getType() {
return myType;
}
/**
* Returns the error code of the error.
*
* @return th error code of the error
*/
public SVNErrorCode getErrorCode() {
return myErrorCode;
}
/**
* Returns an error description formatted with the
* related objects if needed. This call is equivalent to
* a call to {@link #toString()}
*
* @return an error message
*/
public String getMessage() {
return toString();
}
/**
* Gets a string representation of the entire stack trace of
* error messages (if they were provided) starting with the initial
* cause of the error.
*
* @return a string representing a full list of error messages
*/
public String getFullMessage() {
SVNErrorMessage err = this;
StringBuffer buffer = new StringBuffer();
while (err != null) {
buffer.append(err.getMessage());
if (err.hasChildErrorMessage()) {
buffer.append('\n');
}
err = err.getChildErrorMessage();
}
return buffer.toString();
}
/**
* Returns an error description which may contain message format
* patterns.
*
* @return an error description
*/
public String getMessageTemplate() {
return myMessage;
}
/**
* Returns objects (if any) that were provided to be formatted
* with the error description. Objects are formatted by the standard
* {@link MessageFormat} engine.
*
* @return an array of objects
*/
public Object[] getRelatedObjects() {
return myObjects;
}
/**
* Returns an error message (if any) that was returned from a
* deeper method call. So the topmost error messages have the
* entire chain of error messages down to the real error cause.
*
* @return a child error message object (if this object is not the
* first one)
*/
public SVNErrorMessage getChildErrorMessage() {
return myChildErrorMessage;
}
/**
* Says if this error message object has got a child error message.
*
* @return <span class="javakeyword">true</span> if has,
* <span class="javakeyword">false</span> otherwise (for
* example, an initial error message would not have a child
* error message)
*/
public boolean hasChildErrorMessage() {
return myChildErrorMessage != null;
}
/**
* Returns throwable that is cause of the error if any.
*
* @return throwable that caused error or null if not applicable or not known.
*/
public Throwable getCause() {
return myThrowable;
}
/**
* Returns a string representation of this error message object
* formatting (if needed) the error description with the provided related objects.
* If no error description pattern has been provided, the return
* value includes a string representation of the error code (see {@link SVNErrorCode}).
*
* @return a string representing this object.
*/
public String toString() {
StringBuffer line = new StringBuffer();
if (getType() == TYPE_WARNING && getErrorCode() == SVNErrorCode.REPOS_POST_COMMIT_HOOK_FAILED) {
line.append("Warning: ");
} else {
if (getType() == TYPE_WARNING) {
line.append("svn: warning: ");
if(isErrorCodeShouldShown()) {
line.append("W").append(myErrorCode.getCode()).append(": ");
}
} else {
line.append("svn: ");
if(isErrorCodeShouldShown()) {
line.append("E").append(myErrorCode.getCode()).append(": ");
}
}
}
if ("".equals(myMessage)) {
line.append(myErrorCode.getDescription());
} else {
line.append(myObjects.length > 0 ? MessageFormat.format(myMessage, myObjects) : myMessage);
}
return line.toString();
}
public boolean isErrorCodeShouldShown() {
return !dontShowErrorCode && SVNBasicClient.isWC17Supported()
&& getErrorCode() != SVNErrorCode.EXTERNAL_PROGRAM;
}
/**
* Sets a child error message for this one.
*
* @param childMessage a child error message
*/
public void setChildErrorMessage(SVNErrorMessage childMessage) {
if (this == childMessage) {
return;
}
SVNErrorMessage parent = this;
SVNErrorMessage child = childMessage;
while (child != null) {
if (this == child) {
parent.setChildErrorMessage(null);
break;
}
parent = child;
child = child.getChildErrorMessage();
}
myChildErrorMessage = childMessage;
}
/**
* Wraps this error message into a new one that is returned as
* a parent error message. A parent message is set the error code
* of this error message, a new error description and this error
* message as its child.
*
* @param parentMessage a parent error description
* @return a parent error message
*/
public SVNErrorMessage wrap(String parentMessage){
SVNErrorMessage parentError = SVNErrorMessage.create(this.getErrorCode(), parentMessage);
parentError.setChildErrorMessage(this);
return parentError;
}
/**
* Wraps this error message into a new one that is returned as
* a parent error message. A parent message is set the error code
* of this error message, a new error description and this error
* message as its child.
*
* @param parentMessage a parent error description
* @param relatedObject an object to be formatted with <code>parentMessage</code>
* @return a parent error message
*/
public SVNErrorMessage wrap(String parentMessage, Object relatedObject){
SVNErrorMessage parentError = SVNErrorMessage.create(this.getErrorCode(), parentMessage, relatedObject);
parentError.setChildErrorMessage(this);
return parentError;
}
/**
* Wraps this error message into a new one that is returned as
* a parent error message. A parent message is set the error code
* of this error message, a new error description <code>parentMessage</code> with corresponding <code>relatedObjects</code> to
* format the error description, and this error message as its child.
*
* @param parentMessage a parent error description
* @param relatedObjects objects to be formatted with <code>parentMessage</code>
* @return a parent error message
*/
public SVNErrorMessage wrap(String parentMessage, Object[] relatedObjects){
SVNErrorMessage parentError = SVNErrorMessage.create(this.getErrorCode(), parentMessage, relatedObjects);
parentError.setChildErrorMessage(this);
return parentError;
}
/**
* Returns true if this message is a warning message, not error one.
*
* @return <span class="javakeyword">true</span> if this error message
* is of type {@link #TYPE_WARNING}} and <span class="javakeyword">false</span>
* otherwise
*/
public boolean isWarning() {
return myType == TYPE_WARNING;
}
/**
* Sets the type of this error message.
*
* <p>
* <code>type</code> must be either {@link #TYPE_ERROR} or {@link #TYPE_WARNING}.
* This method is intended for inner (within internals) purposes only and
* must not be used by API users.
* @param type error message type
*
* @return <span class="javakeyword">true</span> if the type of this
* error message is changed, <span class="javakeyword">false</span> if
* the type passed is not recognized and thus ignored
*/
public boolean setType(int type) {
if (type == TYPE_ERROR) {
myType = TYPE_ERROR;
return true;
} else if (type == TYPE_WARNING) {
myType = TYPE_WARNING;
return true;
}
return false;
}
/**
* Follows the children chain and returns the error message of the last child in this chain.
* Starts with {@link #getChildErrorMessage()}.
*
* @return error message of the last element in the children chain
*/
public SVNErrorMessage getRootErrorMessage() {
SVNErrorMessage err = this;
while (err.myChildErrorMessage != null) {
err = err.myChildErrorMessage;
}
return err;
}
public boolean isDontShowErrorCode() {
return dontShowErrorCode;
}
public void setDontShowErrorCode(boolean dontShowErrorCode) {
this.dontShowErrorCode = dontShowErrorCode;
}
public SVNErrorMessage findChildWithErrorCode(SVNErrorCode errorCode) {
final Set<SVNErrorMessage> seen = new HashSet<SVNErrorMessage>();
for (SVNErrorMessage errorMessage = this; errorMessage != null; errorMessage = errorMessage.getChildErrorMessage()) {
if (seen.contains(errorMessage)) {
return null;
}
if (errorMessage.getErrorCode() == errorCode) {
return errorMessage;
}
seen.add(errorMessage);
}
return null;
}
public boolean hasChildWithErrorCode(SVNErrorCode errorCode) {
return findChildWithErrorCode(errorCode) != null;
}
public void initCause(Throwable cause) {
if (myThrowable == null) {
myThrowable = cause;
}
}
}