com.helger.commons.error.SingleError 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
The newest version!
/*
* 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.time.LocalDateTime;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.xml.stream.Location;
import org.xml.sax.Locator;
import org.xml.sax.SAXParseException;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.OverrideOnDemand;
import com.helger.commons.builder.IBuilder;
import com.helger.commons.datetime.PDTFactory;
import com.helger.commons.equals.EqualsHelper;
import com.helger.commons.error.level.EErrorLevel;
import com.helger.commons.error.level.IErrorLevel;
import com.helger.commons.error.text.ConstantHasErrorText;
import com.helger.commons.error.text.DynamicHasErrorText;
import com.helger.commons.error.text.IHasErrorText;
import com.helger.commons.hashcode.HashCodeCalculator;
import com.helger.commons.hashcode.HashCodeGenerator;
import com.helger.commons.location.ILocation;
import com.helger.commons.location.SimpleLocation;
import com.helger.commons.string.ToStringGenerator;
import com.helger.commons.text.IMultilingualText;
import com.helger.commons.traits.IGenericImplTrait;
/**
* Default implementation of {@link IError}.
* Note: cannot be called Error
because this would conflict with
* the default Java Exception class.
*
* @author Philip Helger
* @since 8.5.0
*/
@Immutable
public class SingleError implements IError
{
private final LocalDateTime m_aErrorDT;
private final IErrorLevel m_aErrorLevel;
private final String m_sErrorID;
private final String m_sErrorFieldName;
private final ILocation m_aErrorLocation;
private final IHasErrorText m_aErrorText;
private final Throwable m_aLinkedException;
/**
* Constructor for the Builder only.
*
* @param aBuilder
* The builder to take the data from. May not be null
.
*/
protected SingleError (@Nonnull final AbstractBuilder , ?> aBuilder)
{
this (aBuilder.m_aErrorDT,
aBuilder.m_aErrorLevel,
aBuilder.m_sErrorID,
aBuilder.m_sErrorFieldName,
aBuilder.m_aErrorLocation,
aBuilder.m_aErrorText,
aBuilder.m_aLinkedException);
}
/**
* Constructor
*
* @param aErrorDT
* Error date time
* @param aErrorLevel
* Error level. May not be null
.
* @param sErrorID
* Error ID
* @param sErrorFieldName
* Error field name
* @param aErrorLocation
* Error location
* @param aErrorText
* Error text
* @param aLinkedException
* Linked exception
* @since 10.1.7
*/
public SingleError (@Nullable final LocalDateTime aErrorDT,
@Nonnull final IErrorLevel aErrorLevel,
@Nullable final String sErrorID,
@Nullable final String sErrorFieldName,
@Nullable final ILocation aErrorLocation,
@Nullable final IHasErrorText aErrorText,
@Nullable final Throwable aLinkedException)
{
m_aErrorDT = aErrorDT;
m_aErrorLevel = ValueEnforcer.notNull (aErrorLevel, "ErrorLevel");
m_sErrorID = sErrorID;
m_sErrorFieldName = sErrorFieldName;
m_aErrorLocation = aErrorLocation != null ? aErrorLocation : SimpleLocation.NO_LOCATION;
m_aErrorText = aErrorText;
m_aLinkedException = aLinkedException;
}
@Nullable
public LocalDateTime getErrorDateTime ()
{
return m_aErrorDT;
}
@Nonnull
public IErrorLevel getErrorLevel ()
{
return m_aErrorLevel;
}
@Override
@Nullable
public String getErrorID ()
{
return m_sErrorID;
}
@Override
@Nullable
public String getErrorFieldName ()
{
return m_sErrorFieldName;
}
@Override
@Nonnull
public ILocation getErrorLocation ()
{
return m_aErrorLocation;
}
@Override
@Nullable
public IHasErrorText getErrorTexts ()
{
return m_aErrorText;
}
@Override
@Nullable
public Throwable getLinkedException ()
{
return m_aLinkedException;
}
/**
* Overridable implementation of Throwable for the Linked exception field.
* This can be overridden to implement a different algorithm. This is
* customizable because there is no default way of comparing Exceptions in
* Java. If you override this method you must also override
* {@link #hashCodeLinkedException(HashCodeGenerator, Throwable)}!
*
* @param t1
* First Throwable. May be null
.
* @param t2
* Second Throwable. May be null
.
* @return true
if they are equals (or both null)
*/
@OverrideOnDemand
protected boolean equalsLinkedException (@Nullable final Throwable t1, @Nullable final Throwable t2)
{
if (EqualsHelper.identityEqual (t1, t2))
return true;
if (t1 == null || t2 == null)
return false;
return t1.getClass ().equals (t2.getClass ()) && EqualsHelper.equals (t1.getMessage (), t2.getMessage ());
}
@Override
public boolean equals (final Object o)
{
if (o == this)
return true;
if (o == null || !getClass ().equals (o.getClass ()))
return false;
final SingleError rhs = (SingleError) o;
return EqualsHelper.equals (m_aErrorDT, rhs.m_aErrorDT) &&
m_aErrorLevel.equals (rhs.m_aErrorLevel) &&
EqualsHelper.equals (m_sErrorID, rhs.m_sErrorID) &&
EqualsHelper.equals (m_sErrorFieldName, rhs.m_sErrorFieldName) &&
EqualsHelper.equals (m_aErrorLocation, rhs.m_aErrorLocation) &&
EqualsHelper.equals (m_aErrorText, rhs.m_aErrorText) &&
equalsLinkedException (m_aLinkedException, rhs.m_aLinkedException);
}
/**
* Overridable implementation of Throwable for the Linked exception field.
* This can be overridden to implement a different algorithm. This is
* customizable because there is no default way of hashing Exceptions in Java.
* If you override this method you must also override
* {@link #equalsLinkedException(Throwable, Throwable)}!
*
* @param aHCG
* The hash code generator to append to. Never null
.
* @param t
* The Throwable to append. May be null
.
*/
@OverrideOnDemand
protected void hashCodeLinkedException (@Nonnull final HashCodeGenerator aHCG, @Nullable final Throwable t)
{
if (t == null)
aHCG.append (HashCodeCalculator.HASHCODE_NULL);
else
aHCG.append (t.getClass ()).append (t.getMessage ());
}
@Override
public int hashCode ()
{
final HashCodeGenerator aHCG = new HashCodeGenerator (this).append (m_aErrorDT)
.append (m_aErrorLevel)
.append (m_sErrorID)
.append (m_sErrorFieldName)
.append (m_aErrorLocation)
.append (m_aErrorText);
hashCodeLinkedException (aHCG, m_aLinkedException);
return aHCG.getHashCode ();
}
@Override
public String toString ()
{
return new ToStringGenerator (this).appendIfNotNull ("ErrorDateTime", m_aErrorDT)
.append ("ErrorLevel", m_aErrorLevel)
.appendIfNotNull ("ErrorID", m_sErrorID)
.appendIfNotNull ("ErrorFieldName", m_sErrorFieldName)
.appendIfNotNull ("ErrorLocation", m_aErrorLocation)
.appendIfNotNull ("ErrorText", m_aErrorText)
.appendIfNotNull ("LinkedException", m_aLinkedException)
.getToString ();
}
/**
* Create a new empty error builder with the default error level from
* {@link Builder}.
*
* @return A new Error builder. Never null
.
*/
@Nonnull
public static Builder builder ()
{
return new Builder ();
}
/**
* Create a new error builder containing all the data from the provided error.
*
* @param aError
* The error to copy the data from
* @return A new Error builder containing all the data from the provided
* error. Never null
.
*/
@Nonnull
public static Builder builder (@Nonnull final IError aError)
{
return new Builder (aError);
}
/**
* Create a new empty error builder with the SUCCESS error level.
*
* @return A new Error builder with default error level
* {@link EErrorLevel#SUCCESS}. Never null
.
*/
@Nonnull
public static Builder builderSuccess ()
{
return builder ().errorLevel (EErrorLevel.SUCCESS);
}
/**
* Create a new empty error builder with the INFO error level.
*
* @return A new Error builder with default error level
* {@link EErrorLevel#INFO}. Never null
.
*/
@Nonnull
public static Builder builderInfo ()
{
return builder ().errorLevel (EErrorLevel.INFO);
}
/**
* Create a new empty error builder with the WARN error level.
*
* @return A new Error builder with default error level
* {@link EErrorLevel#WARN}. Never null
.
*/
@Nonnull
public static Builder builderWarn ()
{
return builder ().errorLevel (EErrorLevel.WARN);
}
/**
* Create a new empty error builder with the ERROR error level.
*
* @return A new Error builder with default error level
* {@link EErrorLevel#ERROR}. Never null
.
*/
@Nonnull
public static Builder builderError ()
{
return builder ().errorLevel (EErrorLevel.ERROR);
}
/**
* Create a new empty error builder with the FATAL ERROR error level.
*
* @return A new Error builder with default error level
* {@link EErrorLevel#FATAL_ERROR}. Never null
.
*/
@Nonnull
public static Builder builderFatalError ()
{
return builder ().errorLevel (EErrorLevel.FATAL_ERROR);
}
/**
* Abstract builder class for {@link SingleError} and derived classes.
*
* @author Philip Helger
* @param
* Result error type
* @param
* Implementation type
*/
public abstract static class AbstractBuilder >
implements
IGenericImplTrait ,
IBuilder
{
public static final IErrorLevel DEFAULT_ERROR_LEVEL = EErrorLevel.ERROR;
protected LocalDateTime m_aErrorDT;
protected IErrorLevel m_aErrorLevel = DEFAULT_ERROR_LEVEL;
protected String m_sErrorID;
protected String m_sErrorFieldName;
protected ILocation m_aErrorLocation;
protected IHasErrorText m_aErrorText;
protected Throwable m_aLinkedException;
protected AbstractBuilder ()
{}
protected AbstractBuilder (@Nonnull final IError aError)
{
ValueEnforcer.notNull (aError, "Error");
errorLevel (aError.getErrorLevel ());
errorID (aError.getErrorID ());
errorFieldName (aError.getErrorFieldName ());
errorLocation (aError.getErrorLocation ());
errorText (aError.getErrorTexts ());
linkedException (aError.getLinkedException ());
}
@Nonnull
public final IMPLTYPE dateTimeNow ()
{
return dateTime (PDTFactory.getCurrentLocalDateTime ());
}
@Nonnull
public final IMPLTYPE dateTime (@Nullable final LocalDateTime aErrorDT)
{
m_aErrorDT = aErrorDT;
return thisAsT ();
}
@Nonnull
public final IMPLTYPE errorLevel (@Nonnull final IErrorLevel aErrorLevel)
{
ValueEnforcer.notNull (aErrorLevel, "ErrorLevel");
m_aErrorLevel = aErrorLevel;
return thisAsT ();
}
@Nonnull
public final IMPLTYPE errorID (@Nullable final String sErrorID)
{
m_sErrorID = sErrorID;
return thisAsT ();
}
@Nonnull
public final IMPLTYPE errorFieldName (@Nullable final String sErrorFieldName)
{
m_sErrorFieldName = sErrorFieldName;
return thisAsT ();
}
/**
* Set a simple error location without line and column number
*
* @param sErrorLocation
* Error location string
* @return this for chaining
* @since 9.0.2
*/
@Nonnull
public final IMPLTYPE errorLocation (@Nullable final String sErrorLocation)
{
return errorLocation (new SimpleLocation (sErrorLocation));
}
@Nonnull
public final IMPLTYPE errorLocation (@Nullable final Locator aLocator)
{
return errorLocation (SimpleLocation.create (aLocator));
}
@Nonnull
public final IMPLTYPE errorLocation (@Nullable final SAXParseException aLocator)
{
return errorLocation (SimpleLocation.create (aLocator));
}
@Nonnull
public final IMPLTYPE errorLocation (@Nullable final Location aLocator)
{
return errorLocation (SimpleLocation.create (aLocator));
}
@Nonnull
public final IMPLTYPE errorLocation (@Nullable final ILocation aErrorLocation)
{
m_aErrorLocation = aErrorLocation;
return thisAsT ();
}
@Nonnull
public final IMPLTYPE errorText (@Nullable final String sErrorText)
{
return errorText (ConstantHasErrorText.createOnDemand (sErrorText));
}
@Nonnull
public final IMPLTYPE errorText (@Nullable final IMultilingualText aMLT)
{
if (aMLT == null)
m_aErrorText = null;
else
if (aMLT.texts ().size () == 1)
{
// If the multilingual text contains only a single locale, use it as a
// constant value
// If you don't like this behavior directly call #setErrorText with a
// DynamicHasErrorText
m_aErrorText = ConstantHasErrorText.createOnDemand (aMLT.texts ().getFirstValue ());
}
else
m_aErrorText = new DynamicHasErrorText (aMLT);
return thisAsT ();
}
@Nonnull
public final IMPLTYPE errorText (@Nullable final IHasErrorText aErrorText)
{
m_aErrorText = aErrorText;
return thisAsT ();
}
@Nonnull
public final IMPLTYPE linkedException (@Nullable final Throwable aLinkedException)
{
m_aLinkedException = aLinkedException;
return thisAsT ();
}
}
/**
* The default builder to build {@link SingleError} instances.
*
* @author Philip Helger
*/
public static final class Builder extends AbstractBuilder
{
public Builder ()
{}
public Builder (@Nonnull final IError aError)
{
super (aError);
}
@Override
@Nonnull
public SingleError build ()
{
if (m_aErrorLevel == null)
throw new IllegalStateException ("The error level must be provided");
return new SingleError (this);
}
}
}