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