001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package gov.nist.secauto.metaschema.databind.model;
007
008import gov.nist.secauto.metaschema.core.metapath.StaticContext;
009import gov.nist.secauto.metaschema.core.model.AbstractModule;
010import gov.nist.secauto.metaschema.core.model.IBoundObject;
011import gov.nist.secauto.metaschema.core.model.ISource;
012import gov.nist.secauto.metaschema.core.util.ObjectUtils;
013import gov.nist.secauto.metaschema.databind.IBindingContext;
014import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaModule;
015import gov.nist.secauto.metaschema.databind.model.annotations.NsBinding;
016
017import java.lang.reflect.Array;
018import java.util.Arrays;
019import java.util.Collection;
020import java.util.Collections;
021import java.util.LinkedHashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.function.Function;
025import java.util.stream.Collectors;
026
027import javax.xml.namespace.QName;
028
029import edu.umd.cs.findbugs.annotations.NonNull;
030import nl.talsmasoftware.lazy4j.Lazy;
031
032public abstract class AbstractBoundModule
033    extends AbstractModule<
034        IBoundModule,
035        IBoundDefinitionModelComplex,
036        IBoundDefinitionFlag,
037        IBoundDefinitionModelField<?>,
038        IBoundDefinitionModelAssembly>
039    implements IBoundModule {
040  @NonNull
041  private final IBindingContext bindingContext;
042  @NonNull
043  private final Lazy<Map<QName, IBoundDefinitionModelAssembly>> assemblyDefinitions;
044  @NonNull
045  private final Lazy<Map<QName, IBoundDefinitionModelField<?>>> fieldDefinitions;
046  @NonNull
047  private final Lazy<StaticContext> staticContext;
048  @NonNull
049  private final ISource source;
050
051  /**
052   * Construct a new Module instance.
053   *
054   * @param importedModules
055   *          Module imports associated with the Metaschema module
056   * @param bindingContext
057   *          the Module binding context
058   */
059  protected AbstractBoundModule(
060      @NonNull List<? extends IBoundModule> importedModules,
061      @NonNull IBindingContext bindingContext) {
062    super(importedModules);
063    this.bindingContext = bindingContext;
064    this.assemblyDefinitions = ObjectUtils.notNull(Lazy.lazy(() -> Arrays.stream(getAssemblyClasses())
065        .map(clazz -> {
066          assert clazz != null;
067          return (IBoundDefinitionModelAssembly) ObjectUtils
068              .requireNonNull(bindingContext.getBoundDefinitionForClass(clazz));
069        })
070        .collect(Collectors.toUnmodifiableMap(
071            IBoundDefinitionModelAssembly::getDefinitionQName,
072            Function.identity()))));
073    this.fieldDefinitions = ObjectUtils.notNull(Lazy.lazy(() -> Arrays.stream(getFieldClasses())
074        .map(clazz -> {
075          assert clazz != null;
076          return (IBoundDefinitionModelField<?>) ObjectUtils
077              .requireNonNull(bindingContext.getBoundDefinitionForClass(clazz));
078        })
079        .collect(Collectors.toUnmodifiableMap(
080            IBoundDefinitionModelField::getDefinitionQName,
081            Function.identity()))));
082    this.staticContext = ObjectUtils.notNull(Lazy.lazy(() -> {
083      StaticContext.Builder builder = StaticContext.builder()
084          .defaultModelNamespace(getXmlNamespace());
085
086      getNamespaceBindings()
087          .forEach(
088              (prefix, ns) -> builder.namespace(
089                  ObjectUtils.requireNonNull(prefix),
090                  ObjectUtils.requireNonNull(ns)));
091      return builder.build();
092    }));
093    this.source = ISource.moduleSource(this);
094  }
095
096  @Override
097  public ISource getSource() {
098    return source;
099  }
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}