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