001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.databind; 028 029import gov.nist.secauto.metaschema.core.datatype.DataTypeService; 030import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; 031import gov.nist.secauto.metaschema.core.model.IModule; 032import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet; 033import gov.nist.secauto.metaschema.core.util.CollectionUtil; 034import gov.nist.secauto.metaschema.core.util.ObjectUtils; 035import gov.nist.secauto.metaschema.databind.io.BindingException; 036import gov.nist.secauto.metaschema.databind.io.DefaultBoundLoader; 037import gov.nist.secauto.metaschema.databind.io.Format; 038import gov.nist.secauto.metaschema.databind.io.IBoundLoader; 039import gov.nist.secauto.metaschema.databind.io.IDeserializer; 040import gov.nist.secauto.metaschema.databind.io.ISerializer; 041import gov.nist.secauto.metaschema.databind.io.json.DefaultJsonDeserializer; 042import gov.nist.secauto.metaschema.databind.io.json.DefaultJsonSerializer; 043import gov.nist.secauto.metaschema.databind.io.xml.DefaultXmlDeserializer; 044import gov.nist.secauto.metaschema.databind.io.xml.DefaultXmlSerializer; 045import gov.nist.secauto.metaschema.databind.io.yaml.DefaultYamlDeserializer; 046import gov.nist.secauto.metaschema.databind.io.yaml.DefaultYamlSerializer; 047import gov.nist.secauto.metaschema.databind.model.IAssemblyClassBinding; 048import gov.nist.secauto.metaschema.databind.model.IClassBinding; 049 050import java.util.LinkedList; 051import java.util.List; 052import java.util.Map; 053import java.util.Objects; 054import java.util.Set; 055 056import javax.xml.namespace.QName; 057 058import edu.umd.cs.findbugs.annotations.NonNull; 059 060/** 061 * The implementation of a {@link IBindingContext} provided by this library. 062 * <p> 063 * This implementation caches Module information, which can dramatically improve 064 * read and write performance at the cost of some memory use. Thus, using the 065 * same singleton of this class across multiple I/O operations will improve 066 * overall read and write performance when processing the same types of data. 067 * <p> 068 * Serializers and deserializers provided by this class using the 069 * {@link #newSerializer(Format, Class)} and 070 * {@link #newDeserializer(Format, Class)} methods will 071 * <p> 072 * This class is synchronized and is thread-safe. 073 */ 074public class DefaultBindingContext implements IBindingContext { 075 private static DefaultBindingContext singleton; 076 @NonNull 077 private final IModuleLoaderStrategy moduleLoaderStrategy; 078 @NonNull 079 private final List<IBindingMatcher> bindingMatchers = new LinkedList<>(); 080 081 /** 082 * Get the singleton instance of this binding context. 083 * 084 * @return the binding context 085 */ 086 @NonNull 087 public static DefaultBindingContext instance() { 088 synchronized (DefaultBindingContext.class) { 089 if (singleton == null) { 090 singleton = new DefaultBindingContext(); 091 } 092 } 093 return ObjectUtils.notNull(singleton); 094 } 095 096 /** 097 * Construct a new binding context. 098 * 099 * @param externalConstraintSets 100 * the set of external constraints to configure this binding to use 101 */ 102 public DefaultBindingContext(@NonNull Set<IConstraintSet> externalConstraintSets) { 103 // only allow extended classes 104 moduleLoaderStrategy = new ExternalConstraintsModuleLoaderStrategy(this, externalConstraintSets); 105 } 106 107 /** 108 * Construct a new binding context. 109 */ 110 public DefaultBindingContext() { 111 // only allow extended classes 112 moduleLoaderStrategy = new SimpleModuleLoaderStrategy(this); 113 } 114 115 @Override 116 public IModule getModuleByClass(@NonNull Class<? extends IModule> clazz) { 117 return moduleLoaderStrategy.getModuleByClass(clazz); 118 } 119 120 @Override 121 public IClassBinding getClassBinding(@NonNull Class<?> clazz) { 122 return moduleLoaderStrategy.getClassBinding(clazz); 123 } 124 125 @Override 126 public Map<Class<?>, IClassBinding> getClassBindingsByClass() { 127 return moduleLoaderStrategy.getClassBindingsByClass(); 128 } 129 130 @Override 131 public <TYPE extends IDataTypeAdapter<?>> TYPE getJavaTypeAdapterInstance(@NonNull Class<TYPE> clazz) { 132 return DataTypeService.getInstance().getJavaTypeAdapterByClass(clazz); 133 } 134 135 /** 136 * {@inheritDoc} 137 * <p> 138 * A serializer returned by this method is thread-safe. 139 */ 140 @Override 141 public <CLASS> ISerializer<CLASS> newSerializer(@NonNull Format format, @NonNull Class<CLASS> clazz) { 142 Objects.requireNonNull(format, "format"); 143 IAssemblyClassBinding classBinding = (IAssemblyClassBinding) getClassBinding(clazz); 144 if (classBinding == null) { 145 throw new IllegalStateException(String.format("Class '%s' is not bound", clazz.getClass().getName())); 146 } 147 ISerializer<CLASS> retval; 148 switch (format) { 149 case JSON: 150 retval = new DefaultJsonSerializer<>(classBinding); 151 break; 152 case XML: 153 retval = new DefaultXmlSerializer<>(classBinding); 154 break; 155 case YAML: 156 retval = new DefaultYamlSerializer<>(classBinding); 157 break; 158 default: 159 throw new UnsupportedOperationException(String.format("Unsupported format '%s'", format)); 160 } 161 return retval; 162 } 163 164 /** 165 * {@inheritDoc} 166 * <p> 167 * A deserializer returned by this method is thread-safe. 168 */ 169 @Override 170 public <CLASS> IDeserializer<CLASS> newDeserializer(@NonNull Format format, @NonNull Class<CLASS> clazz) { 171 IAssemblyClassBinding classBinding = (IAssemblyClassBinding) getClassBinding(clazz); 172 if (classBinding == null) { 173 throw new IllegalStateException(String.format("Class '%s' is not bound", clazz.getName())); 174 } 175 IDeserializer<CLASS> retval; 176 switch (format) { 177 case JSON: 178 retval = new DefaultJsonDeserializer<>(classBinding); 179 break; 180 case XML: 181 retval = new DefaultXmlDeserializer<>(classBinding); 182 break; 183 case YAML: 184 retval = new DefaultYamlDeserializer<>(classBinding); 185 break; 186 default: 187 throw new UnsupportedOperationException(String.format("Unsupported format '%s'", format)); 188 } 189 190 return retval; 191 } 192 193 @Override 194 public DefaultBindingContext registerBindingMatcher(@NonNull IBindingMatcher matcher) { 195 synchronized (this) { 196 bindingMatchers.add(matcher); 197 } 198 return this; 199 } 200 201 /** 202 * Get the binding matchers that are associated with this class. 203 * 204 * @return the list of matchers 205 * @see #registerBindingMatcher(IBindingMatcher) 206 */ 207 @NonNull 208 protected List<? extends IBindingMatcher> getBindingMatchers() { 209 synchronized (this) { 210 return CollectionUtil.unmodifiableList(bindingMatchers); 211 } 212 } 213 214 @Override 215 public Class<?> getBoundClassForXmlQName(@NonNull QName rootQName) { 216 Class<?> retval = null; 217 for (IBindingMatcher matcher : getBindingMatchers()) { 218 retval = matcher.getBoundClassForXmlQName(rootQName); 219 if (retval != null) { 220 break; 221 } 222 } 223 return retval; 224 } 225 226 @Override 227 public Class<?> getBoundClassForJsonName(@NonNull String rootName) { 228 Class<?> retval = null; 229 for (IBindingMatcher matcher : getBindingMatchers()) { 230 retval = matcher.getBoundClassForJsonName(rootName); 231 if (retval != null) { 232 break; 233 } 234 } 235 return retval; 236 } 237 238 @Override 239 public IBoundLoader newBoundLoader() { 240 return new DefaultBoundLoader(this); 241 } 242 243 @Override 244 public <CLASS> CLASS copyBoundObject(@NonNull CLASS other, Object parentInstance) throws BindingException { 245 IClassBinding classBinding = getClassBinding(other.getClass()); 246 if (classBinding == null) { 247 throw new IllegalStateException(String.format("Class '%s' is not bound", other.getClass().getName())); 248 } 249 @SuppressWarnings("unchecked") CLASS retval = (CLASS) classBinding.copyBoundObject(other, parentInstance); 250 return retval; 251 } 252}