001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package gov.nist.secauto.metaschema.databind;
007
008import gov.nist.secauto.metaschema.core.model.IBoundObject;
009import gov.nist.secauto.metaschema.core.model.IModule;
010import gov.nist.secauto.metaschema.core.model.IModuleLoader;
011import gov.nist.secauto.metaschema.core.util.CollectionUtil;
012import gov.nist.secauto.metaschema.databind.IBindingContext.IBindingMatcher;
013import gov.nist.secauto.metaschema.databind.model.IBoundDefinitionModelComplex;
014import gov.nist.secauto.metaschema.databind.model.IBoundModule;
015import gov.nist.secauto.metaschema.databind.model.metaschema.binding.MetaschemaModelModule;
016
017import java.util.ArrayList;
018import java.util.Collection;
019import java.util.HashSet;
020import java.util.List;
021import java.util.Set;
022import java.util.concurrent.locks.Lock;
023import java.util.concurrent.locks.ReentrantLock;
024
025import edu.umd.cs.findbugs.annotations.NonNull;
026
027public class PostProcessingModuleLoaderStrategy
028    implements IBindingContext.IModuleLoaderStrategy {
029  @NonNull
030  private final List<IModuleLoader.IModulePostProcessor> modulePostProcessors;
031  // private final Set<IModule> resolvedModules = new HashSet<>();
032  // private final Lock resolvedModulesLock = new ReentrantLock();
033  private final IBindingContext.IModuleLoaderStrategy delegate;
034  private final Set<IModule> postProcessedModules = new HashSet<>();
035  private final Lock postProcessedModulesLock = new ReentrantLock();
036
037  public PostProcessingModuleLoaderStrategy(
038      @NonNull List<IModuleLoader.IModulePostProcessor> modulePostProcessors) {
039    this(modulePostProcessors, new SimpleModuleLoaderStrategy());
040  }
041
042  public PostProcessingModuleLoaderStrategy(
043      @NonNull List<IModuleLoader.IModulePostProcessor> modulePostProcessors,
044      @NonNull IBindingContext.IModuleLoaderStrategy delegate) {
045    this.modulePostProcessors = CollectionUtil.unmodifiableList(new ArrayList<>(modulePostProcessors));
046    this.delegate = delegate;
047  }
048
049  @NonNull
050  protected List<IModuleLoader.IModulePostProcessor> getModulePostProcessors() {
051    return modulePostProcessors;
052  }
053
054  @Override
055  public IBoundModule loadModule(Class<? extends IBoundModule> clazz, IBindingContext bindingContext) {
056    return delegate.loadModule(clazz, bindingContext);
057  }
058
059  @Override
060  public void postProcessModule(IModule module, IBindingContext bindingContext) {
061    processModule(module);
062    delegate.postProcessModule(module, bindingContext);
063  }
064
065  @Override
066  public IBoundModule registerModule(IModule module, IBindingContext bindingContext) {
067    IBoundModule boundModule;
068    postProcessedModulesLock.lock();
069    try {
070      // process before registering
071      processModule(module);
072
073      boundModule = delegate.registerModule(module, bindingContext);
074
075      // ensure the resulting bound module is not processed again
076      postProcessedModules.add(boundModule);
077    } finally {
078      postProcessedModulesLock.unlock();
079    }
080    return boundModule;
081  }
082
083  private void processModule(@NonNull IModule module) {
084    postProcessedModulesLock.lock();
085    try {
086      if (!postProcessedModules.contains(module)) {
087        // do not post-process the built-in Metaschema module, since it has already been
088        // pre-processed
089        if (!(module instanceof MetaschemaModelModule)) {
090          for (IModuleLoader.IModulePostProcessor postProcessor : getModulePostProcessors()) {
091            postProcessor.processModule(module);
092          }
093        }
094        postProcessedModules.add(module);
095      }
096    } finally {
097      postProcessedModulesLock.unlock();
098    }
099  }
100
101  @Override
102  public Collection<IBindingMatcher> getBindingMatchers() {
103    return delegate.getBindingMatchers();
104  }
105
106  @Override
107  public IBoundDefinitionModelComplex getBoundDefinitionForClass(
108      Class<? extends IBoundObject> clazz,
109      IBindingContext bindingContext) {
110
111    //
112    // resolvedModulesLock.lock();
113    // try {
114    // if (!resolvedModules.contains(module)) {
115    // // add first, to avoid loops
116    // resolvedModules.add(module);
117    // for (IModuleLoader.IModulePostProcessor postProcessor :
118    // getModulePostProcessors()) {
119    // postProcessor.processModule(module);
120    // }
121    // }
122    // } finally {
123    // resolvedModulesLock.unlock();
124    // }
125    return delegate.getBoundDefinitionForClass(clazz, bindingContext);
126  }
127}