com.helger.commons.error.ErrorTextProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ph-commons Show documentation
Show all versions of ph-commons Show documentation
Java 1.8+ Library with tons of utility classes required in all projects
/*
* Copyright (C) 2014-2024 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* 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.helger.commons.error;
import java.util.Locale;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.annotation.ReturnsMutableCopy;
import com.helger.commons.collection.impl.CommonsArrayList;
import com.helger.commons.collection.impl.ICommonsList;
import com.helger.commons.id.IHasID;
import com.helger.commons.lang.EnumHelper;
import com.helger.commons.lang.ICloneable;
import com.helger.commons.lang.StackTraceHelper;
import com.helger.commons.location.ILocation;
import com.helger.commons.string.StringHelper;
import com.helger.commons.string.ToStringGenerator;
/**
* Default implementation of {@link IErrorTextProvider} with a customizable
* layout.
*
* @author Philip Helger
* @since 8.5.1
*/
@NotThreadSafe
public class ErrorTextProvider implements IErrorTextProvider, ICloneable
{
public static final char PLACEHOLDER = '$';
public static final String PLACEHOLDER_STR = Character.toString (PLACEHOLDER);
public enum EField implements IHasID
{
CONSTANT ("const"),
ERROR_DATETIME ("datetime"),
ERROR_LEVEL ("level"),
ERROR_ID ("id"),
ERROR_FIELD_NAME ("fieldname"),
ERROR_LOCATION ("location"),
ERROR_TEXT ("text"),
ERROR_LINKED_EXCEPTION_CLASS ("%lec"),
ERROR_LINKED_EXCEPTION_MESSAGE ("%lem"),
ERROR_LINKED_EXCEPTION_STACK_TRACE ("%lest"),
ERROR_LINKED_EXCEPTION_CAUSE_CLASS ("%lecc"),
ERROR_LINKED_EXCEPTION_CAUSE_MESSAGE ("%lecm"),
ERROR_LINKED_EXCEPTION_CAUSE_STACK_TRACE ("%lecst");
private final String m_sID;
EField (@Nonnull @Nonempty final String sID)
{
m_sID = sID;
}
@Nonnull
@Nonempty
public String getID ()
{
return m_sID;
}
public boolean isPlaceholderRequired ()
{
return this != CONSTANT;
}
@Nullable
public static EField getFromIDOrNull (@Nullable final String sID)
{
return EnumHelper.getFromIDOrNull (EField.class, sID);
}
}
/**
* Base interface for objects that format a single field.
*
* @author Philip Helger
*/
public interface IFormattableItem
{
/**
* @return the error field this is about. Never null
.
*/
@Nonnull
EField getField ();
/**
* @return The unformatted text. Never null
.
*/
@Nonnull
String getUnformattedText ();
/**
* Return a formatted version of {@link #getUnformattedText()} by replacing
* all {@link #PLACEHOLDER} chars with the provided replacement.
*
* @param sReplacement
* The replacement string. Must not be null
.
* @return The formatted text with the replacement applied.
*/
@Nonnull
String getFormattedText (@Nonnull String sReplacement);
}
/**
* Default placeholder based implementation of {@link IFormattableItem}.
*
* @author Philip Helger
*/
@Immutable
public static class FormattableItem implements IFormattableItem
{
private final EField m_eField;
private final String m_sText;
public FormattableItem (@Nonnull final EField eField, @Nonnull @Nonempty final String sText)
{
m_eField = ValueEnforcer.notNull (eField, "Field");
m_sText = ValueEnforcer.notEmpty (sText, "Text");
if (eField.isPlaceholderRequired ())
ValueEnforcer.isTrue (m_sText.contains (PLACEHOLDER_STR),
() -> "Text '" + sText + "' is missing placeholder '" + PLACEHOLDER + "'");
}
@Nonnull
public final EField getField ()
{
return m_eField;
}
@Nonnull
@Nonempty
public String getUnformattedText ()
{
return m_sText;
}
@Nonnull
public String getFormattedText (@Nonnull final String sReplacement)
{
return StringHelper.replaceAll (m_sText, PLACEHOLDER_STR, sReplacement);
}
@Override
public String toString ()
{
return new ToStringGenerator (this).append ("Field", m_eField).append ("Text", m_sText).getToString ();
}
}
public static final IErrorTextProvider DEFAULT = new ErrorTextProvider ().addItem (EField.ERROR_DATETIME, "[$]")
.addItem (EField.ERROR_LEVEL, "[$]")
.addItem (EField.ERROR_ID, "[$]")
.addItem (EField.ERROR_FIELD_NAME, "in [$]")
.addItem (EField.ERROR_LOCATION, "@ $")
.addItem (EField.ERROR_TEXT, "$")
.addItem (EField.ERROR_LINKED_EXCEPTION_CLASS,
"($:")
.addItem (EField.ERROR_LINKED_EXCEPTION_MESSAGE,
"$)")
.setFieldSeparator (" ");
private final ICommonsList m_aItems;
private String m_sFieldSep = " ";
public ErrorTextProvider ()
{
m_aItems = new CommonsArrayList <> ();
}
/**
* Copy constructor.
*
* @param aOther
* Object to copy from. May not be null
.
* @since 8.5.5
*/
public ErrorTextProvider (@Nonnull final ErrorTextProvider aOther)
{
m_aItems = aOther.m_aItems.getClone ();
m_sFieldSep = aOther.m_sFieldSep;
}
@Nonnull
@ReturnsMutableCopy
public ICommonsList getAllItems ()
{
return m_aItems.getClone ();
}
/**
* Add an error item to be disabled.
*
* @param eField
* The field to be used. May not be null
.
* @param sText
* The text that should contain the placeholder ({@value #PLACEHOLDER})
* that will be replaced
* @return this for chaining
*/
@Nonnull
public ErrorTextProvider addItem (@Nonnull final EField eField, @Nonnull @Nonempty final String sText)
{
return addItem (new FormattableItem (eField, sText));
}
@Nonnull
public ErrorTextProvider addItem (@Nonnull final IFormattableItem aItem)
{
ValueEnforcer.notNull (aItem, "Item");
m_aItems.add (aItem);
return this;
}
@Nonnull
public String getFieldSeparator ()
{
return m_sFieldSep;
}
@Nonnull
public ErrorTextProvider setFieldSeparator (@Nonnull final String sFieldSep)
{
m_sFieldSep = ValueEnforcer.notNull (sFieldSep, "FieldSep");
return this;
}
@Nonnull
public String getErrorText (@Nonnull final IError aError, @Nonnull final Locale aContentLocale)
{
final StringBuilder aSB = new StringBuilder ();
for (final IFormattableItem aItem : m_aItems)
switch (aItem.getField ())
{
case CONSTANT:
{
aSB.append (aItem.getUnformattedText ());
break;
}
case ERROR_DATETIME:
{
if (aError.hasErrorDateTime ())
{
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (aError.getErrorDateTime ().toString ()));
}
break;
}
case ERROR_LEVEL:
{
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (aError.getErrorLevel ().getID ()));
break;
}
case ERROR_ID:
{
if (aError.hasErrorID ())
{
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (aError.getErrorID ()));
}
break;
}
case ERROR_FIELD_NAME:
{
if (aError.hasErrorFieldName ())
{
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (aError.getErrorFieldName ()));
}
break;
}
case ERROR_LOCATION:
{
final ILocation aLocation = aError.getErrorLocation ();
if (aLocation.isAnyInformationPresent ())
{
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (aLocation.getAsString ()));
}
break;
}
case ERROR_TEXT:
{
final String sErrorText = aError.getErrorText (aContentLocale);
if (StringHelper.hasText (sErrorText))
{
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (sErrorText));
}
break;
}
case ERROR_LINKED_EXCEPTION_CLASS:
{
final Throwable aLinkedEx = aError.getLinkedException ();
if (aLinkedEx != null)
{
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (aLinkedEx.getClass ().getName ()));
}
break;
}
case ERROR_LINKED_EXCEPTION_MESSAGE:
{
final Throwable aLinkedEx = aError.getLinkedException ();
if (aLinkedEx != null)
{
final String sMsg = StringHelper.getNotNull (aLinkedEx.getMessage (), "");
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (sMsg));
}
break;
}
case ERROR_LINKED_EXCEPTION_STACK_TRACE:
{
final Throwable aLinkedEx = aError.getLinkedException ();
if (aLinkedEx != null)
{
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (StackTraceHelper.getStackAsString (aLinkedEx)));
}
break;
}
case ERROR_LINKED_EXCEPTION_CAUSE_CLASS:
{
final Throwable aLinkedEx = aError.getLinkedException ();
final Throwable aCause = aLinkedEx != null ? aLinkedEx.getCause () : null;
if (aCause != null)
{
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (aCause.getClass ().getName ()));
}
break;
}
case ERROR_LINKED_EXCEPTION_CAUSE_MESSAGE:
{
final Throwable aLinkedEx = aError.getLinkedException ();
final Throwable aCause = aLinkedEx != null ? aLinkedEx.getCause () : null;
if (aCause != null)
{
final String sMsg = StringHelper.getNotNull (aCause.getMessage ());
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (sMsg));
}
break;
}
case ERROR_LINKED_EXCEPTION_CAUSE_STACK_TRACE:
{
final Throwable aLinkedEx = aError.getLinkedException ();
final Throwable aCause = aLinkedEx != null ? aLinkedEx.getCause () : null;
if (aCause != null)
{
if (aSB.length () > 0)
aSB.append (m_sFieldSep);
aSB.append (aItem.getFormattedText (StackTraceHelper.getStackAsString (aCause)));
}
break;
}
default:
throw new IllegalStateException ("Unsupported error field " + aItem.getField ());
}
return aSB.toString ();
}
@Nonnull
@ReturnsMutableCopy
public ErrorTextProvider getClone ()
{
return new ErrorTextProvider (this);
}
@Override
public String toString ()
{
return new ToStringGenerator (this).append ("Items", m_aItems)
.append ("FieldSeparator", m_sFieldSep)
.getToString ();
}
}