content: /*******************************************************************************
   * Copyright (C) 2021, 1C-Soft LLC and others.
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   *
   * Contributors:
   *     1C-Soft LLC - initial API and implementation
   *******************************************************************************/
  package com.e1c.v8codestyle.internal;
  
  import java.text.MessageFormat;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  
  import org.eclipse.core.resources.IProject;
  import org.eclipse.core.runtime.IConfigurationElement;
  import org.eclipse.core.runtime.IProgressMonitor;
  import org.eclipse.core.runtime.IStatus;
  import org.eclipse.core.runtime.Platform;
  
  import com.e1c.v8codestyle.IProjectOptionManager;
  import com.e1c.v8codestyle.IProjectOptionProvider;
  import com.e1c.v8codestyle.ProjectOption;
  import com.google.inject.Singleton;
  
  /**
   * Default implementation of {@link IProjectOptionManager} service, that loads {@link IProjectOptionProvider providers}
   * form extension point.
   *
   * @author Dmitriy Marmyshev
   */
  @Singleton
  public class ProjectOptionManager
      implements IProjectOptionManager
  {
  
      private static final String EXTENSION_POINT_ID = "projectOptions"; //$NON-NLS-1$
  
      private static final String ATTR_CLASS = "class"; //$NON-NLS-1$
  
      private volatile Map<String, IProjectOptionProvider> providers;
  
      private volatile List<ProjectOption> availableOptions;
  
      @Override
      public List<ProjectOption> getAvailableOptions()
      {
          if (availableOptions == null)
          {
              synchronized (this)
              {
                  if (availableOptions != null)
                  {
                      return availableOptions;
                  }
  
                  availableOptions = loadAvailableOptions();
              }
          }
          return availableOptions;
      }
  
      @Override
      public ProjectOption getOption(String optionId)
      {
          if (optionId == null)
          {
              return null;
          }
          return getAvailableOptions().stream()
              .filter(o -> optionId.equalsIgnoreCase(o.getOptionId()))
              .findAny()
              .orElse(null);
      }
  
      @Override
      public boolean isOptionEnabled(IProject project, String optionId)
      {
          IProjectOptionProvider provider = getProviders().get(optionId);
          if (provider != null)
          {
              return provider.isEnabled(project);
          }
          return false;
      }
  
      @Override
      public boolean isOptionEnabled(IProject project, ProjectOption option)
      {
          IProjectOptionProvider provider = getProviders().get(option.getOptionId());
          if (provider != null)
          {
              return provider.isEnabled(project);
          }
          return false;
      }
  
      @Override
      public void save(IProject project, ProjectOption option, boolean value, IProgressMonitor monitor)
      {
          IProjectOptionProvider provider = getProviders().get(option.getOptionId());
          if (provider != null)
          {
              provider.saveEnable(project, value, monitor);
          }
      }
  
      private Map<String, IProjectOptionProvider> getProviders()
      {
          if (providers == null)
          {
              synchronized (this)
              {
                  if (providers != null)
                  {
                      return providers;
                  }
  
                  providers = loadProviders();
              }
          }
          return providers;
      }
  
      private Map<String, IProjectOptionProvider> loadProviders()
      {
          Map<String, IProjectOptionProvider> result = new HashMap<>();
  
          IConfigurationElement[] elements = Platform.getExtensionRegistry()
              .getExtensionPoint(CorePlugin.PLUGIN_ID, EXTENSION_POINT_ID)
              .getConfigurationElements();
  
          for (int i = 0; i < elements.length; i++)
          {
              IConfigurationElement element = elements[i];
              try
              {
                  IProjectOptionProvider provider = (IProjectOptionProvider)element.createExecutableExtension(ATTR_CLASS);
                  if (result.containsKey(provider.getId()))
                  {
                      IProjectOptionProvider other = result.get(provider.getId());
                      String message = MessageFormat.format(
                          "Contribution project option provider with ID \"{0}\" duplicated:" //$NON-NLS-1$
                              + " class 1; {1} and class 2: {2}", //$NON-NLS-1$
                          provider.getId(), provider.getClass().getName(), other.getClass().getName());
                      IStatus status = CorePlugin.createErrorStatus(message, null);
                      CorePlugin.log(status);
                  }
                  else
                  {
                      result.put(provider.getId(), provider);
                  }
              }
              catch (Exception e)
              {
                  CorePlugin.logError(e);
              }
          }
          return result;
      }
  
      private List<ProjectOption> loadAvailableOptions()
      {
          List<ProjectOption> result = new ArrayList<>();
  
          for (Entry<String, IProjectOptionProvider> entry : getProviders().entrySet())
          {
  
              IProjectOptionProvider provider = entry.getValue();
              ProjectOption option = new ProjectOption(provider.getId(), provider.getOrder(), provider.getPresentation(),
                  provider.getDescription(), provider.getDefault());
              result.add(option);
          }
          Collections.sort(result, (o1, o2) -> o1.getOrder() - o2.getOrder());
          return result;
      }
  
  }
  