/**
  * Create a list of ModuleDefinitions given the results of parsing the definition.
  *
  * @param moduleDescriptors The list of ModuleDescriptors resulting from parsing the definition.
  * @return a list of ModuleDefinitions
  */
 protected List<ModuleDefinition> createModuleDefinitions(
     List<ModuleDescriptor> moduleDescriptors) {
   List<ModuleDefinition> moduleDefinitions =
       new ArrayList<ModuleDefinition>(moduleDescriptors.size());
   for (ModuleDescriptor moduleDescriptor : moduleDescriptors) {
     moduleDefinitions.add(moduleDescriptor.getModuleDefinition());
   }
   return moduleDefinitions;
 }
 /**
  * Validates that all deployment properties (of the form "module.<modulename>.<key>" do indeed
  * reference module names that belong to the stream/job definition).
  */
 private void validateDeploymentProperties(D definition, Map<String, String> properties) {
   List<ModuleDescriptor> modules =
       parser.parse(definition.getName(), definition.getDefinition(), definitionKind);
   Set<String> moduleLabels = new HashSet<String>(modules.size());
   for (ModuleDescriptor md : modules) {
     moduleLabels.add(md.getModuleLabel());
   }
   for (Map.Entry<String, String> pair : properties.entrySet()) {
     Matcher matcher = DEPLOYMENT_PROPERTY_PATTERN.matcher(pair.getKey());
     Assert.isTrue(
         matcher.matches(),
         String.format("'%s' does not match '%s'", pair.getKey(), DEPLOYMENT_PROPERTY_PATTERN));
     String moduleName = matcher.group(1);
     Assert.isTrue(
         "*".equals(moduleName) || moduleLabels.contains(moduleName),
         String.format(
             "'%s' refers to a module that is not in the list: %s", pair.getKey(), moduleLabels));
   }
 }
  @Override
  public void addProposals(
      String text,
      List<ModuleDescriptor> parseResult,
      CompletionKind kind,
      int detailLevel,
      List<String> proposals) {
    // List is in reverse order
    ModuleDescriptor lastModule = parseResult.get(0);
    String lastModuleName = lastModule.getModuleName();
    ModuleType lastModuleType = lastModule.getType();
    ModuleDefinition lastModuleDefinition =
        moduleDefinitionRepository.findByNameAndType(lastModuleName, lastModuleType);

    Set<String> alreadyPresentOptions = new HashSet<String>(lastModule.getParameters().keySet());
    for (ModuleOption option : moduleOptionsMetadataResolver.resolve(lastModuleDefinition)) {
      if (shouldShowOption(option, detailLevel)
          && !alreadyPresentOptions.contains(option.getName())) {
        proposals.add(
            String.format("%s%s--%s=", text, text.endsWith(" ") ? "" : " ", option.getName()));
      }
    }
  }
  /**
   * {@inheritDoc}
   *
   * <p>Before deleting the stream, perform an undeploy. This causes a graceful shutdown of the
   * modules in the stream before it is deleted from the repository.
   */
  @Override
  protected void beforeDelete(StreamDefinition definition) {
    super.beforeDelete(definition);

    // Load module definitions and set them on StreamDefinition; this is used by
    // StreamDefinitionRepositoryUtils.deleteDependencies to delete dependent modules

    // todo: this parsing and setting of ModuleDefinitions should not be needed once we refactor the
    // ModuleDependencyRepository so that the dependencies are also stored in ZooKeeper.
    List<ModuleDefinition> moduleDefinitions = new ArrayList<ModuleDefinition>();
    try {
      for (ModuleDescriptor request :
          parser.parse(definition.getName(), definition.getDefinition(), ParsingContext.stream)) {
        moduleDefinitions.add(new ModuleDefinition(request.getModuleName(), request.getType()));
      }
    } catch (StreamDefinitionException e) {
      // we can ignore an exception for a tap whose stream no longer exists
      if (!(XDDSLMessages.UNRECOGNIZED_STREAM_REFERENCE.equals(e.getMessageCode())
          && definition.getDefinition().trim().startsWith("tap:"))) {
        throw e;
      }
    }
    definition.setModuleDefinitions(moduleDefinitions);
  }