001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package gov.nist.secauto.metaschema.databind.io; 007 008import gov.nist.secauto.metaschema.core.configuration.IConfiguration; 009import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration; 010import gov.nist.secauto.metaschema.core.metapath.DynamicContext; 011import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem; 012import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; 013import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; 014import gov.nist.secauto.metaschema.core.model.IBoundObject; 015import gov.nist.secauto.metaschema.core.model.constraint.DefaultConstraintValidator; 016import gov.nist.secauto.metaschema.core.model.constraint.IConstraintValidationHandler; 017import gov.nist.secauto.metaschema.core.model.constraint.LoggingConstraintValidationHandler; 018import gov.nist.secauto.metaschema.core.util.ObjectUtils; 019import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelAssembly; 020 021import java.io.IOException; 022import java.io.Reader; 023import java.net.URI; 024 025import edu.umd.cs.findbugs.annotations.NonNull; 026 027/** 028 * The base class of all format-specific deserializers. 029 * 030 * @param <CLASS> 031 * the bound class to deserialize to 032 */ 033public abstract class AbstractDeserializer<CLASS extends IBoundObject> 034 extends AbstractSerializationBase<DeserializationFeature<?>> 035 implements IDeserializer<CLASS> { 036 037 private IConstraintValidationHandler constraintValidationHandler; 038 039 /** 040 * Construct a new deserializer. 041 * 042 * @param definition 043 * the bound class information for the Java type this deserializer is 044 * operating on 045 */ 046 protected AbstractDeserializer(@NonNull IBoundDefinitionModelAssembly definition) { 047 super(definition); 048 } 049 050 /** 051 * Get the constraint validation handler configured for this deserializer, which 052 * will be used to validate loaded data. 053 * 054 * @return the deserializer 055 */ 056 @Override 057 @NonNull 058 public IConstraintValidationHandler getConstraintValidationHandler() { 059 synchronized (this) { 060 if (constraintValidationHandler == null) { 061 constraintValidationHandler = new LoggingConstraintValidationHandler(); 062 } 063 return ObjectUtils.notNull(constraintValidationHandler); 064 } 065 } 066 067 @Override 068 public void setConstraintValidationHandler(@NonNull IConstraintValidationHandler constraintValidationHandler) { 069 synchronized (this) { 070 this.constraintValidationHandler = constraintValidationHandler; 071 } 072 } 073 074 @Override 075 public INodeItem deserializeToNodeItem(Reader reader, URI documentUri) throws IOException { 076 077 INodeItem nodeItem; 078 try { 079 nodeItem = deserializeToNodeItemInternal(reader, documentUri); 080 } catch (Exception ex) { // NOPMD - this is intentional 081 throw new IOException(ex); 082 } 083 084 if (isValidating()) { 085 validate(nodeItem); 086 } 087 return nodeItem; 088 } 089 090 /** 091 * This abstract method delegates parsing to the concrete implementation. 092 * 093 * @param reader 094 * the reader instance to read data from 095 * @param documentUri 096 * the URI of the document that is being read 097 * @return a new node item containing the read contents 098 * @throws IOException 099 * if an error occurred while reading data from the stream 100 */ 101 @NonNull 102 protected abstract INodeItem deserializeToNodeItemInternal(@NonNull Reader reader, @NonNull URI documentUri) 103 throws IOException; 104 105 @Override 106 public final CLASS deserializeToValue(Reader reader, URI documentUri) throws IOException { 107 CLASS retval; 108 109 if (isValidating()) { 110 INodeItem nodeItem = deserializeToNodeItemInternal(reader, documentUri); 111 validate(nodeItem); 112 retval = ObjectUtils.asType(ObjectUtils.requireNonNull(nodeItem.getValue())); 113 } else { 114 retval = deserializeToValueInternal(reader, documentUri); 115 } 116 return retval; 117 } 118 119 private void validate(@NonNull INodeItem nodeItem) { 120 IDefinitionNodeItem<?, ?> definitionNodeItem; 121 if (nodeItem instanceof IDocumentNodeItem) { 122 definitionNodeItem = ((IDocumentNodeItem) nodeItem).getRootAssemblyNodeItem(); 123 } else if (nodeItem instanceof IDefinitionNodeItem) { 124 definitionNodeItem = (IDefinitionNodeItem<?, ?>) nodeItem; 125 } else { 126 throw new UnsupportedOperationException(String.format( 127 "The node item type '%s' is not supported for validation.", 128 nodeItem.getClass().getName())); 129 } 130 131 DynamicContext dynamicContext = new DynamicContext(nodeItem.getStaticContext()); 132 dynamicContext.setDocumentLoader(getBindingContext().newBoundLoader()); 133 DefaultConstraintValidator validator = new DefaultConstraintValidator(getConstraintValidationHandler()); 134 validator.validate(definitionNodeItem, dynamicContext); 135 validator.finalizeValidation(dynamicContext); 136 } 137 138 @NonNull 139 protected abstract CLASS deserializeToValueInternal(@NonNull Reader reader, @NonNull URI documentUri) 140 throws IOException; 141 142 @Override 143 public IDeserializer<CLASS> enableFeature(DeserializationFeature<?> feature) { 144 return set(feature, true); 145 } 146 147 @Override 148 public IDeserializer<CLASS> disableFeature(DeserializationFeature<?> feature) { 149 return set(feature, false); 150 } 151 152 @Override 153 public IDeserializer<CLASS> applyConfiguration( 154 @NonNull IConfiguration<DeserializationFeature<?>> other) { 155 IMutableConfiguration<DeserializationFeature<?>> config = getConfiguration(); 156 config.applyConfiguration(other); 157 configurationChanged(config); 158 return this; 159 } 160 161 @Override 162 public IDeserializer<CLASS> set(DeserializationFeature<?> feature, Object value) { 163 IMutableConfiguration<DeserializationFeature<?>> config = getConfiguration(); 164 config.set(feature, value); 165 configurationChanged(config); 166 return this; 167 } 168}