/**
   * @return Returns the filename hint for an attribute set. The first take is the attribute sets
   *     resource associated with the algorithm. If this fails, we try to name the file after the
   *     algorithm itself.
   */
  static IPath getDefaultHint(String componentId, String prefix) {
    final ProcessingComponentDescriptor component =
        WorkbenchCorePlugin.getDefault().getComponent(componentId);

    String nameHint = component.getAttributeSetsResource();
    if (StringUtils.isBlank(nameHint)) {
      // Try a fallback.
      nameHint = FileDialogs.sanitizeFileName(prefix + componentId + "-attributes.xml");
    }

    return new Path(nameHint);
  }
  @SuppressForbidden(reason = "C2 integration (File API)")
  @Override
  protected void doStart() throws ElasticsearchException {
    try {
      Settings.Builder builder = Settings.builder();
      Path pluginConfigPath = environment.configFile().resolve(ClusteringPlugin.PLUGIN_NAME);

      if (!Files.isDirectory(pluginConfigPath)) {
        Path srcConfig = Paths.get("src/main/config");
        if (Files.isDirectory(srcConfig)) {
          // Allow running from within the IDE.
          pluginConfigPath = srcConfig;
        } else {
          throw new ElasticsearchException("Config folder missing: " + pluginConfigPath);
        }
      } else {
        logger.info("Configuration files at: {}", pluginConfigPath.toAbsolutePath());
      }

      for (String configName :
          new String[] {"config.yml", "config.yaml", "config.json", "config.properties"}) {
        try {
          Path resolved = pluginConfigPath.resolve(configName);
          if (resolved != null && Files.exists(resolved)) {
            builder.loadFromPath(resolved);
          }
        } catch (NoClassDefFoundError e) {
          logger.warn("Could not parse: {}", e, configName);
        }
      }
      Settings c2Settings = builder.build();

      // Parse suite descriptors with loggers turned off (shut them up a bit).
      final Path suitePath = pluginConfigPath.resolve(c2Settings.get(DEFAULT_SUITE_PROPERTY_NAME));
      if (!Files.isRegularFile(suitePath)) {
        throw new ElasticsearchException(
            "Could not find algorithm suite: " + suitePath.toAbsolutePath().normalize());
      }

      final ResourceLookup suiteLookup =
          new ResourceLookup(new DirLocator(suitePath.getParent().toFile()));
      final IResource suiteResource = suiteLookup.getFirst(suitePath.getFileName().toString());

      final List<String> failed = Lists.newArrayList();
      final ProcessingComponentSuite suite =
          LoggerUtils.quietCall(
              new Callable<ProcessingComponentSuite>() {
                public ProcessingComponentSuite call() throws Exception {
                  ProcessingComponentSuite suite =
                      ProcessingComponentSuite.deserialize(suiteResource, suiteLookup);
                  for (ProcessingComponentDescriptor desc : suite.removeUnavailableComponents()) {
                    failed.add(desc.getId());
                    if (isNoClassDefFound(desc.getInitializationFailure())) {
                      logger.debug("Algorithm not available on classpath: {}", desc.getId());
                    } else {
                      logger.warn(
                          "Algorithm initialization failed: {}",
                          desc.getInitializationFailure(),
                          desc.getId());
                    }
                  }
                  return suite;
                }
              },
              Logger.getLogger(ProcessingComponentDescriptor.class),
              Logger.getLogger(ReflectionUtils.class));

      algorithms = Lists.newArrayList();
      for (ProcessingComponentDescriptor descriptor : suite.getAlgorithms()) {
        algorithms.add(descriptor.getId());
      }
      algorithms = Collections.unmodifiableList(algorithms);

      if (!algorithms.isEmpty()) {
        logger.info("Available clustering components: {}", Joiner.on(", ").join(algorithms));
      }
      if (!failed.isEmpty()) {
        logger.info("Unavailable clustering components: {}", Joiner.on(", ").join(failed));
      }

      final Path resourcesPath =
          pluginConfigPath
              .resolve(c2Settings.get(DEFAULT_RESOURCES_PROPERTY_NAME, "."))
              .toAbsolutePath()
              .normalize();

      logger.info("Lexical resources dir: {}", resourcesPath);

      final ResourceLookup resourceLookup =
          new ResourceLookup(
              new DirLocator(resourcesPath.toFile()),
              new ClassLoaderLocator(ControllerSingleton.class.getClassLoader()));

      // Change the default resource lookup to include the configured location.
      Map<String, Object> c2SettingsAsMap = Maps.newHashMap();
      DefaultLexicalDataFactoryDescriptor.attributeBuilder(c2SettingsAsMap)
          .resourceLookup(resourceLookup);
      c2SettingsAsMap.putAll(c2Settings.getAsMap());

      // Set up the license for Lingo3G, if it's available.
      Path lingo3gLicense = scanForLingo3GLicense(environment, pluginConfigPath);
      if (lingo3gLicense != null && Files.isReadable(lingo3gLicense)) {
        c2SettingsAsMap.put("license", new FileResource(lingo3gLicense.toFile()));
      } else if (algorithms.contains("lingo3g")) {
        logger.warn(
            "Lingo3G is on classpath, but no licenses have been found. Check out the documentation.");
      }

      // Create component pool.
      Integer poolSize = c2Settings.getAsInt(DEFAULT_COMPONENT_SIZE_PROPERTY_NAME, 0);
      if (poolSize > 0) {
        controller = ControllerFactory.createPooling(poolSize);
      } else {
        controller = ControllerFactory.createPooling();
      }
      controller.init(c2SettingsAsMap, suite.getComponentConfigurations());
    } catch (Exception e) {
      throw new ElasticsearchException("Could not start Carrot2 controller.", e);
    }

    if (algorithms == null || algorithms.isEmpty()) {
      throw new ElasticsearchException(
          "No registered/ available clustering algorithms? Check the logs, it's odd.");
    }
  }