1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.databind.model;
7   
8   import gov.nist.secauto.metaschema.core.metapath.StaticContext;
9   import gov.nist.secauto.metaschema.core.model.AbstractModule;
10  import gov.nist.secauto.metaschema.core.model.IBoundObject;
11  import gov.nist.secauto.metaschema.core.model.ISource;
12  import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
13  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
14  import gov.nist.secauto.metaschema.databind.IBindingContext;
15  import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaModule;
16  import gov.nist.secauto.metaschema.databind.model.annotations.NsBinding;
17  
18  import java.lang.reflect.Array;
19  import java.util.Arrays;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.LinkedHashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.function.Function;
26  import java.util.stream.Collectors;
27  
28  import edu.umd.cs.findbugs.annotations.NonNull;
29  import nl.talsmasoftware.lazy4j.Lazy;
30  
31  public abstract class AbstractBoundModule
32      extends AbstractModule<
33          IBoundModule,
34          IBoundDefinitionModelComplex,
35          IBoundDefinitionFlag,
36          IBoundDefinitionModelField<?>,
37          IBoundDefinitionModelAssembly>
38      implements IBoundModule {
39    @NonNull
40    private final IBindingContext bindingContext;
41    @NonNull
42    private final Lazy<Map<Integer, IBoundDefinitionModelAssembly>> assemblyDefinitions;
43    @NonNull
44    private final Lazy<Map<Integer, IBoundDefinitionModelField<?>>> fieldDefinitions;
45    @NonNull
46    private final Lazy<StaticContext> staticContext;
47    @NonNull
48    private final ISource source;
49  
50    /**
51     * Construct a new Module instance.
52     *
53     * @param importedModules
54     *          Module imports associated with the Metaschema module
55     * @param bindingContext
56     *          the Module binding context
57     */
58    protected AbstractBoundModule(
59        @NonNull List<? extends IBoundModule> importedModules,
60        @NonNull IBindingContext bindingContext) {
61      super(importedModules);
62      this.bindingContext = bindingContext;
63      this.assemblyDefinitions = ObjectUtils.notNull(Lazy.lazy(() -> Arrays.stream(getAssemblyClasses())
64          .map(clazz -> {
65            assert clazz != null;
66            return (IBoundDefinitionModelAssembly) ObjectUtils
67                .requireNonNull(bindingContext.getBoundDefinitionForClass(clazz));
68          })
69          .collect(Collectors.toUnmodifiableMap(
70              def -> def.getDefinitionQName().getIndexPosition(),
71              Function.identity()))));
72      this.fieldDefinitions = ObjectUtils.notNull(Lazy.lazy(() -> Arrays.stream(getFieldClasses())
73          .map(clazz -> {
74            assert clazz != null;
75            return (IBoundDefinitionModelField<?>) ObjectUtils
76                .requireNonNull(bindingContext.getBoundDefinitionForClass(clazz));
77          })
78          .collect(Collectors.toUnmodifiableMap(
79              def -> def.getDefinitionQName().getIndexPosition(),
80              Function.identity()))));
81      this.staticContext = ObjectUtils.notNull(Lazy.lazy(() -> {
82        StaticContext.Builder builder = StaticContext.builder()
83            .defaultModelNamespace(getXmlNamespace());
84  
85        getNamespaceBindings()
86            .forEach(
87                (prefix, ns) -> builder.namespace(
88                    ObjectUtils.requireNonNull(prefix),
89                    ObjectUtils.requireNonNull(ns)));
90        return builder.build();
91      }));
92      this.source = ISource.moduleSource(this);
93    }
94  
95    @Override
96    public ISource getSource() {
97      return source;
98    }
99  
100   @Override
101   public String getLocationHint() {
102     return ObjectUtils.notNull(getClass().getName());
103   }
104 
105   @Override
106   public StaticContext getModuleStaticContext() {
107     return ObjectUtils.notNull(staticContext.get());
108   }
109 
110   @Override
111   @NonNull
112   public IBindingContext getBindingContext() {
113     return bindingContext;
114   }
115 
116   @Override
117   public Map<String, String> getNamespaceBindings() {
118     return ObjectUtils.notNull(Arrays.stream(getNsBindings())
119         .collect(Collectors.toMap(
120             NsBinding::prefix,
121             NsBinding::uri,
122             (v1, v2) -> v2,
123             LinkedHashMap::new)));
124   }
125 
126   @SuppressWarnings({ "null" })
127   @NonNull
128   protected NsBinding[] getNsBindings() {
129     return getClass().isAnnotationPresent(MetaschemaModule.class)
130         ? getClass().getAnnotation(MetaschemaModule.class).nsBindings()
131         : (NsBinding[]) Array.newInstance(NsBinding.class, 0);
132   }
133 
134   /**
135    * Get the assembly instance annotations associated with this bound choice
136    * group.
137    *
138    * @return the annotations
139    */
140   @SuppressWarnings({ "null", "unchecked" })
141   @NonNull
142   protected Class<? extends IBoundObject>[] getAssemblyClasses() {
143     return getClass().isAnnotationPresent(MetaschemaModule.class)
144         ? getClass().getAnnotation(MetaschemaModule.class).assemblies()
145         : (Class<? extends IBoundObject>[]) Array.newInstance(Class.class, 0);
146   }
147 
148   /**
149    * Get the field instance annotations associated with this bound choice group.
150    *
151    * @return the annotations
152    */
153   @SuppressWarnings({ "null", "unchecked" })
154   @NonNull
155   protected Class<? extends IBoundObject>[] getFieldClasses() {
156     return getClass().isAnnotationPresent(MetaschemaModule.class)
157         ? getClass().getAnnotation(MetaschemaModule.class).fields()
158         : (Class<? extends IBoundObject>[]) Array.newInstance(Class.class, 0);
159   }
160 
161   /**
162    * Get the mapping of assembly definition effective name to definition.
163    *
164    * @return the mapping
165    */
166   protected Map<Integer, IBoundDefinitionModelAssembly> getAssemblyDefinitionMap() {
167     return assemblyDefinitions.get();
168   }
169 
170   @SuppressWarnings("null")
171   @Override
172   public Collection<IBoundDefinitionModelAssembly> getAssemblyDefinitions() {
173     return getAssemblyDefinitionMap().values();
174   }
175 
176   @Override
177   public IBoundDefinitionModelAssembly getAssemblyDefinitionByName(@NonNull Integer name) {
178     return getAssemblyDefinitionMap().get(name);
179   }
180 
181   /**
182    * Get the mapping of field definition effective name to definition.
183    *
184    * @return the mapping
185    */
186   protected Map<Integer, IBoundDefinitionModelField<?>> getFieldDefinitionMap() {
187     return fieldDefinitions.get();
188   }
189 
190   @SuppressWarnings("null")
191   @Override
192   public Collection<IBoundDefinitionModelField<?>> getFieldDefinitions() {
193     return getFieldDefinitionMap().values();
194   }
195 
196   @Override
197   public IBoundDefinitionModelField<?> getFieldDefinitionByName(@NonNull Integer name) {
198     return getFieldDefinitionMap().get(name);
199   }
200 
201   @SuppressWarnings("null")
202   @Override
203   public Collection<IBoundDefinitionFlag> getFlagDefinitions() {
204     // Flags are always inline, so they do not have separate definitions
205     return Collections.emptyList();
206   }
207 
208   @Override
209   public IBoundDefinitionFlag getFlagDefinitionByName(@NonNull IEnhancedQName name) {
210     // Flags are always inline, so they do not have separate definitions
211     return null;
212   }
213 }