Skip to content

Commit

Permalink
Improved error handling for data type conformance issues. Related to G…
Browse files Browse the repository at this point in the history
  • Loading branch information
david-waltermire committed Oct 28, 2024
1 parent dd7c637 commit 300e4ec
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 35 deletions.
6 changes: 3 additions & 3 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
</distributionManagement>

<scm>
<url>${scm.url}/tree/develop/core</url>
<tag>HEAD</tag>
</scm>
<url>${scm.url}/tree/develop/core</url>
<tag>HEAD</tag>
</scm>

<dependencies>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
import gov.nist.secauto.metaschema.core.model.util.JsonUtil;
import gov.nist.secauto.metaschema.core.model.util.XmlEventUtil;

import org.codehaus.stax2.XMLEventReader2;
Expand Down Expand Up @@ -93,24 +94,31 @@ public boolean isXmlMixed() {
@Override
public TYPE parse(XMLEventReader2 eventReader) throws IOException {
StringBuilder builder = new StringBuilder();
XMLEvent nextEvent;
try {
XMLEvent nextEvent;
while (!(nextEvent = eventReader.peek()).isEndElement()) {
if (nextEvent.isCharacters()) {
Characters characters = nextEvent.asCharacters();
builder.append(characters.getData());
// advance past current event
eventReader.nextEvent();
} else {
if (!nextEvent.isCharacters()) {
throw new IOException(String.format("Invalid content '%s' at %s", XmlEventUtil.toString(nextEvent),
XmlEventUtil.toString(nextEvent.getLocation())));
}
Characters characters = nextEvent.asCharacters();
builder.append(characters.getData());
// advance past current event
eventReader.nextEvent();
}

// trim leading and trailing whitespace
@SuppressWarnings("null")
@NonNull String value = builder.toString().trim();
return parse(value);
String value = builder == null ? null : builder.toString().trim();
try {
return parse(value);
} catch (IllegalArgumentException ex) {
throw new IOException(
String.format("Malformed data '%s'%s. %s",
value,
XmlEventUtil.generateLocationMessage(nextEvent),
ex.getLocalizedMessage()),
ex);
}
} catch (XMLStreamException ex) {
throw new IOException(ex);
}
Expand All @@ -124,11 +132,20 @@ public TYPE parse(XMLEventReader2 eventReader) throws IOException {
public TYPE parse(JsonParser parser) throws IOException {
String value = parser.getValueAsString();
if (value == null) {
throw new IOException("Unable to parse field value as text");
throw new IOException("Unable to null value as text");
}
// skip over value
parser.nextToken();
return parse(value);
try {
return parse(value);
} catch (IllegalArgumentException ex) {
throw new IOException(
String.format("Malformed data '%s'%s. %s",
value,
JsonUtil.generateLocationMessage(parser),
ex.getLocalizedMessage()),
ex);
}
}

@SuppressWarnings("null")
Expand Down Expand Up @@ -175,7 +192,8 @@ public ITEM_TYPE cast(IAnyAtomicItem item) {
ITEM_TYPE retval;
if (item == null) {
throw new InvalidValueForCastFunctionException("item is null");
} else if (getItemClass().isAssignableFrom(item.getClass())) {
}
if (getItemClass().isAssignableFrom(item.getClass())) {
@SuppressWarnings("unchecked") ITEM_TYPE typedItem = (ITEM_TYPE) item;
retval = typedItem;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ default boolean isAtomic() {
* @return a supplier that will provide new instances of the parsed data
* @throws IOException
* if an error occurs while parsing
* @throws IllegalArgumentException
* if the provided value is invalid based on the data type
* @see #parse(String)
*/
@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,17 @@ public IBoundObject readItemField(IBoundObject parentItem, IBoundDefinitionModel
@Override
public Object readItemFieldValue(IBoundObject parentItem, IBoundFieldValue fieldValue) throws IOException {
// read the field value's value
return readScalarItem(fieldValue);
return checkMissingFieldValue(readScalarItem(fieldValue));
}

@NonNull
private Object checkMissingFieldValue(Object value) throws IOException {
if (value == null && LOGGER.isWarnEnabled()) {
LOGGER.atWarn().log("Missing property value%s",
JsonUtil.generateLocationMessage(getReader()));
}
// TODO: change nullness annotations to be @Nullable
return ObjectUtils.notNull(value);
}

@Override
Expand Down Expand Up @@ -491,8 +501,17 @@ public void accept(

// the field will be the JSON key
String key = ObjectUtils.notNull(parser.currentName());
Object value = jsonKey.getDefinition().getJavaTypeAdapter().parse(key);
jsonKey.setValue(parent, ObjectUtils.notNull(value.toString()));
try {
Object value = jsonKey.getDefinition().getJavaTypeAdapter().parse(key);
jsonKey.setValue(parent, ObjectUtils.notNull(value.toString()));
} catch (IllegalArgumentException ex) {
throw new IOException(
String.format("Malformed data '%s'%s. %s",
key,
JsonUtil.generateLocationMessage(parser),
ex.getLocalizedMessage()),
ex);
}

// skip to the next token
parser.nextToken();
Expand Down Expand Up @@ -626,14 +645,14 @@ private final class JsomValueKeyProblemHandler implements IJsonProblemHandler {
@NonNull
private final IJsonProblemHandler delegate;
@NonNull
private final IBoundInstanceFlag jsonValueKyeFlag;
private final IBoundInstanceFlag jsonValueKeyFlag;
private boolean foundJsonValueKey; // false

private JsomValueKeyProblemHandler(
@NonNull IJsonProblemHandler delegate,
@NonNull IBoundInstanceFlag jsonValueKyeFlag) {
@NonNull IBoundInstanceFlag jsonValueKeyFlag) {
this.delegate = delegate;
this.jsonValueKyeFlag = jsonValueKyeFlag;
this.jsonValueKeyFlag = jsonValueKeyFlag;
}

@Override
Expand All @@ -656,9 +675,17 @@ public boolean handleUnknownProperty(
} else {
// handle JSON value key
String key = ObjectUtils.notNull(parser.currentName());
Object keyValue = jsonValueKyeFlag.getJavaTypeAdapter().parse(key);
jsonValueKyeFlag.setValue(ObjectUtils.notNull(parentItem), keyValue);

try {
Object keyValue = jsonValueKeyFlag.getJavaTypeAdapter().parse(key);
jsonValueKeyFlag.setValue(ObjectUtils.notNull(parentItem), keyValue);
} catch (IllegalArgumentException ex) {
throw new IOException(
String.format("Malformed data '%s'%s. %s",
key,
JsonUtil.generateLocationMessage(parser),
ex.getLocalizedMessage()),
ex);
}
// advance past the field name
JsonUtil.assertAndAdvance(parser, JsonToken.FIELD_NAME);

Expand All @@ -672,7 +699,6 @@ public boolean handleUnknownProperty(
}
return retval;
}

}

private class ModelInstanceReadHandler<ITEM>
Expand Down Expand Up @@ -719,7 +745,8 @@ public Map<String, ITEM> readMap() throws IOException {

IBoundInstanceModel<?> instance = getCollectionInfo().getInstance();

@SuppressWarnings("PMD.UseConcurrentHashMap") Map<String, ITEM> items = new LinkedHashMap<>();
@SuppressWarnings("PMD.UseConcurrentHashMap")
Map<String, ITEM> items = new LinkedHashMap<>();

// A map value is always wrapped in a START_OBJECT, since fields are used for
// the keys
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,20 @@ protected void readFlagInstances(
XmlEventUtil.generateLocationMessage(attribute)));
}
} else {
// get the attribute value
Object value = instance.getDefinition().getJavaTypeAdapter().parse(ObjectUtils.notNull(attribute.getValue()));
// apply the value to the parentObject
instance.setValue(targetObject, value);
flagInstanceMap.remove(qname);
try {
// get the attribute value
Object value = instance.getDefinition().getJavaTypeAdapter().parse(ObjectUtils.notNull(attribute.getValue()));
// apply the value to the parentObject
instance.setValue(targetObject, value);
flagInstanceMap.remove(qname);
} catch (IllegalArgumentException ex) {
throw new IOException(
String.format("Malformed data '%s'%s. %s",
attribute.getValue(),
XmlEventUtil.generateLocationMessage(start),
ex.getLocalizedMessage()),
ex);
}
}
}

Expand Down Expand Up @@ -453,6 +462,7 @@ private <DEF extends IBoundDefinitionModelComplex> IBoundObject readDefinitionEl
public Object readItemFlag(
IBoundObject parent,
IBoundInstanceFlag flag) throws IOException {
// should never be called
throw new UnsupportedOperationException("handled by readFlagInstances()");
}

Expand Down Expand Up @@ -481,7 +491,7 @@ public Object readItemField(
XmlEventUtil.requireStartElement(getReader(), wrapper);
}

Object retval = readScalarItem(instance);
Object retval = checkMissingFieldValue(readScalarItem(instance));

if (wrapper != null) {
XmlEventUtil.skipWhitespace(getReader());
Expand Down Expand Up @@ -534,7 +544,19 @@ public IBoundObject readItemField(
public Object readItemFieldValue(
IBoundObject parent,
IBoundFieldValue fieldValue) throws IOException {
return readScalarItem(fieldValue);
return checkMissingFieldValue(readScalarItem(fieldValue));
}

@NonNull
private Object checkMissingFieldValue(Object value) throws IOException {
if (value == null) {
StartElement start = getStartElement();
throw new IOException(
String.format("Missing value in element '%s'%s",
start.getName(),
XmlEventUtil.generateLocationMessage(start)));
}
return value;
}

private void handleAssemblyDefinitionBody(
Expand Down Expand Up @@ -578,7 +600,7 @@ public IBoundObject readItemAssembly(
this::handleAssemblyDefinitionBody);
}

@NonNull
@Nullable
private Object readScalarItem(@NonNull IFeatureScalarItemValueHandler handler)
throws IOException {
return handler.getJavaTypeAdapter().parse(getReader());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@
public interface IFeatureScalarItemValueHandler
extends IItemValueHandler<Object>, IValuedMutable {

/**
* Apply the string value.
* <p>
* This first parses the value using the underlying data type implementation and
* then applies the parsed value.
*
* @param parent
* the parent object to apply the value to
* @param text
* the value to parse
* @throws IllegalArgumentException
* if the text was malformed
* @see #getJavaTypeAdapter()
*/
default void setValue(@NonNull Object parent, @NonNull String text) {
Object item = getValueFromString(text);
setValue(parent, item);
Expand All @@ -27,6 +41,16 @@ default String toStringFromItem(@NonNull Object parent) {
return item == null ? null : getJavaTypeAdapter().asString(item);
}

/**
* Parse a string value using the underlying data type implementation.
*
* @param text
* the value to parse
* @return the parsed value
* @throws IllegalArgumentException
* if the text was malformed
* @see #getJavaTypeAdapter()
*/
default Object getValueFromString(@NonNull String text) {
return getJavaTypeAdapter().parse(text);
}
Expand Down

0 comments on commit 300e4ec

Please sign in to comment.