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