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.model; 028 029import gov.nist.secauto.metaschema.core.model.AbstractModule; 030import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; 031import gov.nist.secauto.metaschema.core.model.IFieldDefinition; 032import gov.nist.secauto.metaschema.core.model.IFlagDefinition; 033import gov.nist.secauto.metaschema.core.model.IModule; 034import gov.nist.secauto.metaschema.core.util.CollectionUtil; 035import gov.nist.secauto.metaschema.core.util.ObjectUtils; 036import gov.nist.secauto.metaschema.databind.IBindingContext; 037import gov.nist.secauto.metaschema.databind.model.annotations.Module; 038 039import java.lang.reflect.Constructor; 040import java.lang.reflect.InvocationTargetException; 041import java.net.URI; 042import java.util.ArrayList; 043import java.util.Arrays; 044import java.util.Collection; 045import java.util.Collections; 046import java.util.List; 047import java.util.Map; 048import java.util.function.Function; 049import java.util.stream.Collectors; 050 051import edu.umd.cs.findbugs.annotations.NonNull; 052 053public abstract class AbstractBoundModule 054 extends AbstractModule { 055 @NonNull 056 private final IBindingContext bindingContext; 057 private Map<String, IAssemblyClassBinding> assemblyDefinitions; 058 private Map<String, IFieldClassBinding> fieldDefinitions; 059 060 /** 061 * Create a new Module instance for a given class annotated by the 062 * {@link Module} annotation. 063 * <p> 064 * Will also load any imported Metaschemas. 065 * 066 * 067 * @param clazz 068 * the Module class 069 * @param bindingContext 070 * the Module binding context 071 * @return the new Module instance 072 */ 073 @NonNull 074 public static IModule createInstance( 075 @NonNull Class<? extends IModule> clazz, 076 @NonNull IBindingContext bindingContext) { 077 078 if (!clazz.isAnnotationPresent(Module.class)) { 079 throw new IllegalStateException(String.format("The class '%s' is missing the '%s' annotation", 080 clazz.getCanonicalName(), Module.class.getCanonicalName())); 081 } 082 083 Module moduleAnnotation = clazz.getAnnotation(Module.class); 084 085 List<IModule> importedModules; 086 if (moduleAnnotation.imports().length > 0) { 087 importedModules = new ArrayList<>(moduleAnnotation.imports().length); 088 for (Class<? extends IModule> importClass : moduleAnnotation.imports()) { 089 assert importClass != null; 090 IModule moduleImport = bindingContext.getModuleByClass(importClass); 091 importedModules.add(moduleImport); 092 } 093 } else { 094 importedModules = CollectionUtil.emptyList(); 095 } 096 return createInstance(clazz, bindingContext, importedModules); 097 } 098 099 @NonNull 100 private static IModule createInstance( 101 @NonNull Class<? extends IModule> clazz, 102 @NonNull IBindingContext bindingContext, 103 @NonNull List<? extends IModule> importedModules) { 104 105 Constructor<? extends IModule> constructor; 106 try { 107 constructor = clazz.getDeclaredConstructor(List.class, IBindingContext.class); 108 } catch (NoSuchMethodException ex) { 109 throw new IllegalArgumentException(ex); 110 } 111 112 try { 113 return ObjectUtils.notNull(constructor.newInstance(importedModules, bindingContext)); 114 } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) { 115 throw new IllegalArgumentException(ex); 116 } 117 } 118 119 /** 120 * Construct a new Module instance. 121 * 122 * @param importedModules 123 * Module imports associated with the Metaschema module 124 * @param bindingContext 125 * the Module binding context 126 */ 127 protected AbstractBoundModule( 128 @NonNull List<? extends IModule> importedModules, 129 @NonNull IBindingContext bindingContext) { 130 super(importedModules); 131 this.bindingContext = bindingContext; 132 } 133 134 /** 135 * Get the Module binding context. 136 * 137 * @return the context 138 */ 139 @NonNull 140 protected IBindingContext getBindingContext() { 141 return bindingContext; 142 } 143 144 @Override 145 public URI getLocation() { // NOPMD - intentional 146 // not known 147 return null; 148 } 149 150 @NonNull 151 protected Class<?>[] getAssemblyClasses() { 152 Class<?>[] retval; 153 if (getClass().isAnnotationPresent(Module.class)) { 154 Module moduleAnnotation = getClass().getAnnotation(Module.class); 155 retval = moduleAnnotation.assemblies(); 156 } else { 157 retval = new Class<?>[] {}; 158 } 159 return retval; 160 } 161 162 @NonNull 163 protected Class<?>[] getFieldClasses() { 164 Class<?>[] retval; 165 if (getClass().isAnnotationPresent(Module.class)) { 166 Module moduleAnnotation = getClass().getAnnotation(Module.class); 167 retval = moduleAnnotation.fields(); 168 } else { 169 retval = new Class<?>[] {}; 170 } 171 return retval; 172 } 173 174 protected void initDefinitions() { 175 synchronized (this) { 176 if (assemblyDefinitions == null) { 177 IBindingContext bindingContext = getBindingContext(); 178 this.assemblyDefinitions = Arrays.stream(getAssemblyClasses()) 179 .map(clazz -> { 180 assert clazz != null; 181 return (IAssemblyClassBinding) ObjectUtils.requireNonNull(bindingContext.getClassBinding(clazz)); 182 }) 183 .collect(Collectors.toUnmodifiableMap( 184 IAssemblyClassBinding::getName, 185 Function.identity())); 186 this.fieldDefinitions = Arrays.stream(getFieldClasses()) 187 .map(clazz -> { 188 assert clazz != null; 189 return (IFieldClassBinding) ObjectUtils.requireNonNull(bindingContext.getClassBinding(clazz)); 190 }) 191 .collect(Collectors.toUnmodifiableMap( 192 IFieldClassBinding::getName, 193 Function.identity())); 194 } 195 } 196 197 } 198 199 @SuppressWarnings("null") 200 protected @NonNull Map<String, ? extends IAssemblyDefinition> getAssemblyDefinitionMap() { 201 initDefinitions(); 202 return assemblyDefinitions; 203 } 204 205 @SuppressWarnings("null") 206 @Override 207 public Collection<? extends IAssemblyDefinition> getAssemblyDefinitions() { 208 return getAssemblyDefinitionMap().values(); 209 } 210 211 @Override 212 public IAssemblyDefinition getAssemblyDefinitionByName(@NonNull String name) { 213 return getAssemblyDefinitionMap().get(name); 214 } 215 216 protected Map<String, ? extends IFieldDefinition> getFieldDefinitionMap() { 217 initDefinitions(); 218 return fieldDefinitions; 219 } 220 221 @SuppressWarnings("null") 222 @Override 223 public Collection<? extends IFieldDefinition> getFieldDefinitions() { 224 return getFieldDefinitionMap().values(); 225 } 226 227 @Override 228 public IFieldDefinition getFieldDefinitionByName(@NonNull String name) { 229 return getFieldDefinitionMap().get(name); 230 } 231 232 @NonNull 233 public Map<String, ? extends IFlagDefinition> getFlagDefinitionMap() { 234 // FlagContainer are always inline 235 return CollectionUtil.emptyMap(); 236 } 237 238 @SuppressWarnings("null") 239 @Override 240 public Collection<? extends IFlagDefinition> getFlagDefinitions() { 241 // FlagContainer are always inline 242 return Collections.emptyList(); 243 } 244 245 @Override 246 public IFlagDefinition getFlagDefinitionByName(@NonNull String name) { // NOPMD - intentional 247 // FlagContainer are always inline 248 return null; 249 } 250}