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