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.DefaultConfiguration; 009import gov.nist.secauto.metaschema.core.configuration.IConfiguration; 010import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration; 011import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; 012import gov.nist.secauto.metaschema.core.model.AbstractResourceResolver; 013import gov.nist.secauto.metaschema.core.model.IBoundObject; 014import gov.nist.secauto.metaschema.core.util.ObjectUtils; 015import gov.nist.secauto.metaschema.databind.IBindingContext; 016import gov.nist.secauto.metaschema.databind.io.ModelDetector.Result; 017 018import org.eclipse.jdt.annotation.NotOwning; 019import org.eclipse.jdt.annotation.Owning; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.net.URI; 024import java.net.URL; 025import java.util.Map; 026 027import edu.umd.cs.findbugs.annotations.NonNull; 028 029/** 030 * A default implementation of an {@link IBoundLoader}. 031 */ 032public class DefaultBoundLoader 033 extends AbstractResourceResolver 034 implements IBoundLoader { 035 public static final int LOOK_AHEAD_BYTES = 32_768; 036 // @NonNull 037 // private static final JsonFactory JSON_FACTORY = new JsonFactory(); 038 // @NonNull 039 // private static final XmlFactory XML_FACTORY = new XmlFactory(); 040 // @NonNull 041 // private static final YAMLFactory YAML_FACTORY = new YAMLFactory(); 042 043 private FormatDetector formatDetector; 044 045 private ModelDetector modelDetector; 046 047 @NonNull 048 private final IBindingContext bindingContext; 049 @NonNull 050 private final IMutableConfiguration<DeserializationFeature<?>> configuration; 051 052 /** 053 * Construct a new loader instance, using the provided {@link IBindingContext}. 054 * 055 * @param bindingContext 056 * the Module binding context to use to load Java types 057 */ 058 public DefaultBoundLoader(@NonNull IBindingContext bindingContext) { 059 this.bindingContext = bindingContext; 060 this.configuration = new DefaultConfiguration<>(); 061 } 062 063 @NonNull 064 private IMutableConfiguration<DeserializationFeature<?>> getConfiguration() { 065 return configuration; 066 } 067 068 @Override 069 public boolean isFeatureEnabled(DeserializationFeature<?> feature) { 070 return getConfiguration().isFeatureEnabled(feature); 071 } 072 073 @Override 074 public Map<DeserializationFeature<?>, Object> getFeatureValues() { 075 return getConfiguration().getFeatureValues(); 076 } 077 078 @Override 079 public IBoundLoader applyConfiguration(@NonNull IConfiguration<DeserializationFeature<?>> other) { 080 getConfiguration().applyConfiguration(other); 081 resetDetector(); 082 return this; 083 } 084 085 @SuppressWarnings("PMD.NullAssignment") 086 private void resetDetector() { 087 // reset the detector 088 formatDetector = null; 089 } 090 091 @Override 092 public IBoundLoader set(DeserializationFeature<?> feature, Object value) { 093 getConfiguration().set(feature, value); 094 resetDetector(); 095 return this; 096 } 097 098 @Override 099 public IBindingContext getBindingContext() { 100 return bindingContext; 101 } 102 103 @Override 104 public Format detectFormat(@NonNull URI uri) throws IOException { 105 URI resourceUri = resolve(uri); 106 URL resource = resourceUri.toURL(); 107 108 try (InputStream is = ObjectUtils.notNull(resource.openStream())) { 109 return detectFormat(is).getFormat(); 110 } 111 } 112 113 @Override 114 public FormatDetector.Result detectFormat(@NonNull InputStream is) throws IOException { 115 return getFormatDetector().detect(is); 116 } 117 118 @NonNull 119 private FormatDetector getFormatDetector() { 120 if (formatDetector == null) { 121 formatDetector = new FormatDetector(getConfiguration()); 122 } 123 assert formatDetector != null; 124 return formatDetector; 125 } 126 127 @NonNull 128 private ModelDetector getModelDetector() { 129 if (modelDetector == null) { 130 modelDetector = new ModelDetector( 131 getBindingContext(), 132 getConfiguration()); 133 } 134 assert modelDetector != null; 135 return modelDetector; 136 } 137 138 @Override 139 @Owning 140 public Result detectModel(@NotOwning InputStream is, Format format) throws IOException { 141 return getModelDetector().detect(is, format); 142 } 143 144 @Override 145 public <CLASS extends IBoundObject> CLASS load(@NonNull URI uri) throws IOException { 146 URI resourceUri = resolve(uri); 147 URL resource = resourceUri.toURL(); 148 149 try (InputStream is = ObjectUtils.notNull(resource.openStream())) { 150 return load(is, uri); 151 } 152 } 153 154 @SuppressWarnings("unchecked") 155 @Override 156 @NonNull 157 public <CLASS extends IBoundObject> CLASS load( 158 @NotOwning @NonNull InputStream is, 159 @NonNull URI documentUri) 160 throws IOException { 161 FormatDetector.Result formatMatch = getFormatDetector().detect(is); 162 Format format = formatMatch.getFormat(); 163 164 try (InputStream formatStream = formatMatch.getDataStream()) { 165 try (ModelDetector.Result modelMatch = detectModel(formatStream, format)) { 166 167 IDeserializer<?> deserializer = getDeserializer( 168 modelMatch.getBoundClass(), 169 format, 170 getConfiguration()); 171 try (InputStream modelStream = modelMatch.getDataStream()) { 172 return (CLASS) deserializer.deserialize(modelStream, documentUri); 173 } 174 } 175 } 176 } 177 178 @Override 179 public <CLASS extends IBoundObject> CLASS load(Class<CLASS> clazz, URI uri) throws IOException { 180 URI resourceUri = resolve(uri); 181 URL resource = resourceUri.toURL(); 182 183 try (InputStream is = ObjectUtils.notNull(resource.openStream())) { 184 return load(clazz, is, resourceUri); 185 } 186 } 187 188 @Override 189 public <CLASS extends IBoundObject> CLASS load(Class<CLASS> clazz, InputStream is, URI documentUri) 190 throws IOException { 191 // we cannot close this stream, since it will cause the underlying stream to be 192 // closed 193 FormatDetector.Result match = getFormatDetector().detect(is); 194 Format format = match.getFormat(); 195 196 try (InputStream remainingStream = match.getDataStream()) { 197 // is autoclosing ok? 198 return load(clazz, format, remainingStream, documentUri); 199 } 200 } 201 202 @Override 203 @NonNull 204 public <CLASS extends IBoundObject> CLASS load( 205 @NonNull Class<CLASS> clazz, 206 @NonNull Format format, 207 @NonNull InputStream is, 208 @NonNull URI documentUri) throws IOException { 209 210 IDeserializer<CLASS> deserializer = getDeserializer(clazz, format, getConfiguration()); 211 return deserializer.deserialize(is, documentUri); 212 } 213 214 @Override 215 public IDocumentNodeItem loadAsNodeItem(URI uri) throws IOException { 216 URI resourceUri = resolve(uri); 217 URL resource = resourceUri.toURL(); 218 219 try (InputStream is = ObjectUtils.notNull(resource.openStream())) { 220 return loadAsNodeItem(is, resourceUri); 221 } 222 } 223 224 @NonNull 225 private IDocumentNodeItem loadAsNodeItem(@NonNull InputStream is, @NonNull URI documentUri) throws IOException { 226 FormatDetector.Result formatMatch = getFormatDetector().detect(is); 227 Format format = formatMatch.getFormat(); 228 229 try (InputStream formatStream = formatMatch.getDataStream()) { 230 return loadAsNodeItem(format, formatStream, documentUri); 231 } 232 } 233 234 @Override 235 public IDocumentNodeItem loadAsNodeItem(Format format, URI uri) throws IOException { 236 URI resourceUri = resolve(uri); 237 URL resource = resourceUri.toURL(); 238 239 try (InputStream is = ObjectUtils.notNull(resource.openStream())) { 240 return loadAsNodeItem(format, is, resourceUri); 241 } 242 } 243 244 @Override 245 public IDocumentNodeItem loadAsNodeItem(Format format, InputStream is, URI documentUri) 246 throws IOException { 247 try (ModelDetector.Result modelMatch = detectModel(is, format)) { 248 249 IDeserializer<?> deserializer = getDeserializer( 250 modelMatch.getBoundClass(), 251 format, 252 getConfiguration()); 253 try (InputStream modelStream = modelMatch.getDataStream()) { 254 return (IDocumentNodeItem) deserializer.deserializeToNodeItem(modelStream, documentUri); 255 } 256 } 257 } 258 259 @NonNull 260 private <CLASS extends IBoundObject> IDeserializer<CLASS> getDeserializer( 261 @NonNull Class<CLASS> clazz, 262 @NonNull Format format, 263 @NonNull IConfiguration<DeserializationFeature<?>> config) { 264 IDeserializer<CLASS> retval = getBindingContext().newDeserializer(format, clazz); 265 retval.applyConfiguration(config); 266 return retval; 267 } 268}