Skip to content

Commit

Permalink
Added XML serialization of results
Browse files Browse the repository at this point in the history
  • Loading branch information
phax committed Nov 27, 2024
1 parent 89ddcb9 commit fa0d761
Show file tree
Hide file tree
Showing 11 changed files with 1,718 additions and 102 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ Please ensure that your stack size is at least 1MB (for Saxon). Using the Oracle

# News and noteworthy

* v10.0.3 - work in progress
* Added default XML serialization of validation results
* v10.0.2 - 2024-09-16
* Re-release due to Maven Central issue
* v10.0.1 - 2024-09-16
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
* 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.phive.result;

import java.net.MalformedURLException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.error.level.EErrorLevel;
import com.helger.commons.error.level.IErrorLevel;
import com.helger.commons.io.resource.ClassPathResource;
import com.helger.commons.io.resource.FileSystemResource;
import com.helger.commons.io.resource.IReadableResource;
import com.helger.commons.io.resource.URLResource;
import com.helger.commons.io.resource.inmemory.IMemoryReadableResource;
import com.helger.commons.io.resource.wrapped.IWrappedReadableResource;
import com.helger.commons.state.ETriState;
import com.helger.commons.string.StringHelper;

/**
* Contains stateless phive result helper methods.
*
* @author Philip Helger
*/
@Immutable
public final class PhiveResultHelper
{
public static final String VALUE_ERRORLEVEL_SUCCESS = "SUCCESS";
public static final String VALUE_ERRORLEVEL_WARN = "WARN";
public static final String VALUE_ERRORLEVEL_ERROR = "ERROR";

public static final String VALUE_TRISTATE_TRUE = "TRUE";
public static final String VALUE_TRISTATE_FALSE = "FALSE";
public static final String VALUE_TRISTATE_UNDEFINED = "UNDEFINED";

private PhiveResultHelper ()
{}

public static boolean isConsideredError (@Nonnull final IErrorLevel aErrorLevel)
{
return aErrorLevel.isGE (EErrorLevel.ERROR);
}

public static boolean isConsideredWarning (@Nonnull final IErrorLevel aErrorLevel)
{
return aErrorLevel.isGE (EErrorLevel.WARN);
}

/**
* Get the string of the error level. One of <code>"ERROR"</code>,
* <code>"WARN"</code> or <code>"SUCCESS"</code>.<br>
* See {@link #VALUE_ERRORLEVEL_SUCCESS}, {@link #VALUE_ERRORLEVEL_WARN},
* {@link #VALUE_ERRORLEVEL_ERROR}
*
* @param aErrorLevel
* The error level to convert. May not be <code>null</code>.
* @return A non-<code>null</code> value string.
*/
@Nonnull
@Nonempty
public static String getErrorLevelValue (@Nonnull final IErrorLevel aErrorLevel)
{
ValueEnforcer.notNull (aErrorLevel, "ErrorLevel");

if (PhiveResultHelper.isConsideredError (aErrorLevel))
return VALUE_ERRORLEVEL_ERROR;
if (PhiveResultHelper.isConsideredWarning (aErrorLevel))
return VALUE_ERRORLEVEL_WARN;
return VALUE_ERRORLEVEL_SUCCESS;
}

@Nullable
public static IErrorLevel getAsErrorLevel (@Nullable final String sErrorLevel)
{
if (VALUE_ERRORLEVEL_ERROR.equals (sErrorLevel))
return EErrorLevel.ERROR;
if (VALUE_ERRORLEVEL_WARN.equals (sErrorLevel))
return EErrorLevel.WARN;
if (VALUE_ERRORLEVEL_SUCCESS.equals (sErrorLevel))
return EErrorLevel.SUCCESS;
return null;
}

/**
* Get the tri-state representation of the provided value. Either
* {@link #VALUE_TRISTATE_TRUE} or {@link #VALUE_TRISTATE_FALSE}.
*
* @param b
* boolean value to get converted.
* @return A non-<code>null</code> value string.
* @see #getTriStateValue(ETriState)
*/
@Nonnull
@Nonempty
public static String getTriStateValue (final boolean b)
{
return b ? VALUE_TRISTATE_TRUE : VALUE_TRISTATE_FALSE;
}

/**
* Get the tri-state representation of the provided value. Either
* {@link #VALUE_TRISTATE_TRUE}, {@link #VALUE_TRISTATE_FALSE} or
* {@link #VALUE_TRISTATE_UNDEFINED}.
*
* @param eTriState
* Tri-state value to get converted. May not be <code>null</code>.
* @return A non-<code>null</code> value string.
* @see #getTriStateValue(boolean)
*/
@Nonnull
public static String getTriStateValue (@Nonnull final ETriState eTriState)
{
ValueEnforcer.notNull (eTriState, "TriState");

if (eTriState.isUndefined ())
return VALUE_TRISTATE_UNDEFINED;
return getTriStateValue (eTriState.isTrue ());
}

/**
* Convert the provided value into a tri-state value. Must be one of
* {@link #VALUE_TRISTATE_TRUE}, {@link #VALUE_TRISTATE_FALSE} or
* {@link #VALUE_TRISTATE_UNDEFINED}.
*
* @param sTriState
* Source value. May be <code>null</code>.
* @return <code>null</code> if the provided value is unknown.
*/
@Nullable
public static ETriState getAsTriState (@Nullable final String sTriState)
{
if (VALUE_TRISTATE_TRUE.equals (sTriState))
return ETriState.TRUE;
if (VALUE_TRISTATE_FALSE.equals (sTriState))
return ETriState.FALSE;
if (VALUE_TRISTATE_UNDEFINED.equals (sTriState))
return ETriState.UNDEFINED;
return null;
}

@Nonnull
@Nonempty
public static String getArtifactPathType (@Nonnull final IReadableResource aRes)
{
if (aRes instanceof ClassPathResource)
return "classpath";
if (aRes instanceof FileSystemResource)
return "file";
if (aRes instanceof URLResource)
return "url";
if (aRes instanceof IMemoryReadableResource)
return "in-memory";
if (aRes instanceof IWrappedReadableResource)
return "wrapped";
return "unknown";
}

@Nullable
public static IReadableResource getAsValidationResource (@Nullable final String sArtefactPathType,
@Nullable final String sArtefactPath)
{
if (StringHelper.hasNoText (sArtefactPathType))
return null;
if (StringHelper.hasNoText (sArtefactPath))
return null;

if ("file".equals (sArtefactPathType))
return new FileSystemResource (sArtefactPath);

if ("url".equals (sArtefactPathType))
try
{
return new URLResource (sArtefactPath);
}
catch (final MalformedURLException ex)
{
return null;
}

// Default to class path
return new ClassPathResource (sArtefactPath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
import com.helger.commons.string.StringHelper;
import com.helger.json.IJsonObject;
import com.helger.json.JsonObject;
import com.helger.xml.microdom.IMicroElement;
import com.helger.xml.microdom.MicroElement;
import com.helger.xml.microdom.util.MicroHelper;

/**
* This is a work around to read "exceptions" from external sources (like JSON)
Expand All @@ -36,9 +39,26 @@
*/
public class PhiveRestoredException extends Exception
{
public static final String JSON_CLASS = "class";
public static final String JSON_MESSAGE = "message";
public static final String JSON_STACK_TRACE = "stackTrace";
public static final String FIELD_CLASS = "class";
/**
* @deprecated Use {@link #FIELD_CLASS} instead
*/
@Deprecated (forRemoval = true, since = "10.0.3")
public static final String JSON_CLASS = FIELD_CLASS;

public static final String FIELD_MESSAGE = "message";
/**
* @deprecated Use {@link #FIELD_MESSAGE} instead
*/
@Deprecated (forRemoval = true, since = "10.0.3")
public static final String JSON_MESSAGE = FIELD_MESSAGE;

public static final String FIELD_STACK_TRACE = "stackTrace";
/**
* @deprecated Use {@link #FIELD_STACK_TRACE} instead
*/
@Deprecated (forRemoval = true, since = "10.0.3")
public static final String JSON_STACK_TRACE = FIELD_STACK_TRACE;

private final String m_sClassName;
private final String m_sMessage;
Expand Down Expand Up @@ -88,12 +108,40 @@ public ICommonsList <String> getAllStackTraceLines ()
return m_aStackTraceLines.getClone ();
}

@Nullable
public static IJsonObject getAsJson (@Nonnull final String sClassName,
@Nullable final String sMessage,
@Nonnull final String sStackTraceLines)
{
return new JsonObject ().add (FIELD_CLASS, sClassName)
.addIfNotNull (FIELD_MESSAGE, sMessage)
.add (FIELD_STACK_TRACE, sStackTraceLines);
}

@Nullable
public IJsonObject getAsJson ()
{
return new JsonObject ().add (JSON_CLASS, m_sClassName)
.addIfNotNull (JSON_MESSAGE, m_sMessage)
.add (JSON_STACK_TRACE, StringHelper.getImploded ('\n', m_aStackTraceLines));
return getAsJson (m_sClassName, m_sMessage, StringHelper.getImploded ('\n', m_aStackTraceLines));
}

@Nullable
public static IMicroElement getAsXML (@Nonnull final String sElementName,
@Nonnull final String sClassName,
@Nullable final String sMessage,
@Nonnull final String sStackTraceLines)
{
final IMicroElement ret = new MicroElement (sElementName);
ret.appendElement (FIELD_CLASS).appendText (sClassName);
if (sMessage != null)
ret.appendElement (FIELD_MESSAGE).appendText (sMessage);
ret.appendElement (FIELD_STACK_TRACE).appendText (sStackTraceLines);
return ret;
}

@Nullable
public IMicroElement getAsXML (@Nonnull final String sElementName)
{
return getAsXML (sElementName, m_sClassName, m_sMessage, StringHelper.getImploded ('\n', m_aStackTraceLines));
}

@Nullable
Expand All @@ -102,9 +150,27 @@ public static PhiveRestoredException createFromJson (@Nullable final IJsonObject
if (aObj == null)
return null;

final String sClassName = aObj.getAsString (JSON_CLASS);
final String sMessage = aObj.getAsString (JSON_MESSAGE);
final ICommonsList <String> aStackTraceLines = StringHelper.getExploded ('\n', aObj.getAsString (JSON_STACK_TRACE));
final String sClassName = aObj.getAsString (FIELD_CLASS);
final String sMessage = aObj.getAsString (FIELD_MESSAGE);
final ICommonsList <String> aStackTraceLines = StringHelper.getExploded ('\n',
aObj.getAsString (FIELD_STACK_TRACE));
if (sClassName == null)
return null;

return new PhiveRestoredException (sClassName, sMessage, aStackTraceLines);
}

@Nullable
public static PhiveRestoredException createFromXML (@Nullable final IMicroElement aObj)
{
if (aObj == null)
return null;

final String sClassName = MicroHelper.getChildTextContentTrimmed (aObj, FIELD_CLASS);
final String sMessage = MicroHelper.getChildTextContentTrimmed (aObj, FIELD_MESSAGE);
final ICommonsList <String> aStackTraceLines = StringHelper.getExploded ('\n',
MicroHelper.getChildTextContentTrimmed (aObj,
FIELD_STACK_TRACE));
if (sClassName == null)
return null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.helger.commons.location.ILocation;
import com.helger.json.IJsonObject;
import com.helger.json.JsonObject;
import com.helger.phive.result.PhiveResultHelper;

/**
* A flexible builder that can be used to convert data from an {@link IError}
Expand All @@ -42,7 +43,7 @@ public class JsonErrorBuilder implements IBuilder <IJsonObject>
{
private LocalDateTime m_aErrorDateTime;
private IErrorLevel m_aErrorLevel;
private Function <IErrorLevel, String> m_aErrorLevelToJson = PhiveJsonHelper::getJsonErrorLevel;
private Function <IErrorLevel, String> m_aErrorLevelToJson = PhiveResultHelper::getErrorLevelValue;
private String m_sErrorID;
private String m_sErrorFieldName;
private ILocation m_aErrorLocation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import com.helger.phive.api.executorset.IValidationExecutorSet;
import com.helger.phive.api.result.ValidationResult;
import com.helger.phive.api.validity.EExtendedValidity;
import com.helger.phive.result.PhiveResultHelper;

/**
* A helper class that allows to heavily customize the creation of validation
Expand All @@ -51,8 +52,8 @@ public class JsonValidationResultListHelper
{
private IValidationExecutorSet <?> m_aVES;
private Function <IValidationExecutorSet <?>, IJsonObject> m_aVESToJson = PhiveJsonHelper::getJsonVES;
private Function <IReadableResource, String> m_aArtifactPathTypeToJson = PhiveJsonHelper::getArtifactPathType;
private Function <IErrorLevel, String> m_aErrorLevelToJson = PhiveJsonHelper::getJsonErrorLevel;
private Function <IReadableResource, String> m_aArtifactPathTypeToJson = PhiveResultHelper::getArtifactPathType;
private Function <IErrorLevel, String> m_aErrorLevelToJson = PhiveResultHelper::getErrorLevelValue;
private BiFunction <IError, Locale, IJsonObject> m_aErrorToJson = PhiveJsonHelper::getJsonError;
private MutableInt m_aWarningCount;
private MutableInt m_aErrorCount;
Expand Down Expand Up @@ -121,7 +122,7 @@ public JsonValidationResultListHelper errorCount (@Nullable final MutableInt a)
* "interrupted" : boolean,
* "mostSevereErrorLevel" : string,
* "results" : array {
* "success" : string, // as defined by {@link PhiveJsonHelper#getJsonTriState(ETriState)}
* "success" : string, // as defined by {@link PhiveResultHelper#getTriStateValue(ETriState)}
* "artifactType" : string,
* "artifactPathType" : string?,
* "artifactPath" : string,
Expand Down Expand Up @@ -171,13 +172,13 @@ public void applyTo (@Nonnull final IJsonObject aResponse,
if (aVR.isSkipped ())
{
bValidationInterrupted = true;
aVRT.add (PhiveJsonHelper.JSON_SUCCESS, PhiveJsonHelper.getJsonTriState (ETriState.UNDEFINED));
aVRT.add (PhiveJsonHelper.JSON_SUCCESS, PhiveResultHelper.getTriStateValue (ETriState.UNDEFINED));
}
else
{
// Backwards compatible decision
final boolean bIsValid = aVR.getErrorList ().containsNoError ();
aVRT.add (PhiveJsonHelper.JSON_SUCCESS, PhiveJsonHelper.getJsonTriState (bIsValid));
aVRT.add (PhiveJsonHelper.JSON_SUCCESS, PhiveResultHelper.getTriStateValue (bIsValid));
if (!bIsValid)
eWorstValidity = EExtendedValidity.INVALID;
}
Expand All @@ -193,10 +194,10 @@ public void applyTo (@Nonnull final IJsonObject aResponse,
if (aError.getErrorLevel ().isGT (aMostSevere))
aMostSevere = aError.getErrorLevel ();

if (PhiveJsonHelper.isConsideredError (aError.getErrorLevel ()))
if (PhiveResultHelper.isConsideredError (aError.getErrorLevel ()))
nErrors++;
else
if (PhiveJsonHelper.isConsideredWarning (aError.getErrorLevel ()))
if (PhiveResultHelper.isConsideredWarning (aError.getErrorLevel ()))
nWarnings++;

if (m_aErrorToJson != null)
Expand Down
Loading

0 comments on commit fa0d761

Please sign in to comment.