<commit_before>/*
 * Copyright 2000-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jetbrains.idea.devkit.dom.impl;

import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.ObjectUtils;
import com.intellij.util.PairProcessor;
import com.intellij.util.xml.ConvertContext;
import com.intellij.util.xml.DomUtil;
import com.intellij.util.xml.ElementPresentationManager;
import com.intellij.util.xml.ResolvingConverter;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.dom.*;

import java.util.*;

public class ActionOrGroupResolveConverter extends ResolvingConverter<ActionOrGroup> {

  @NotNull
  @Override
  public Collection<? extends ActionOrGroup> getVariants(ConvertContext context) {
    final List<ActionOrGroup> variants = new ArrayList<ActionOrGroup>();
    PairProcessor<String, ActionOrGroup> collectProcessor = new PairProcessor<String, ActionOrGroup>() {
      @Override
      public boolean process(String s, ActionOrGroup actionOrGroup) {
        variants.add(actionOrGroup);
        return true;
      }
    };
    processActionOrGroup(context, collectProcessor);
    return variants;
  }

  @Nullable
  @Override
  public ActionOrGroup fromString(@Nullable @NonNls final String value, ConvertContext context) {
    if (StringUtil.isEmptyOrSpaces(value)) return null;

    final ActionOrGroup[] result = {null};
    PairProcessor<String, ActionOrGroup> findProcessor = new PairProcessor<String, ActionOrGroup>() {
      @Override
      public boolean process(String s, ActionOrGroup actionOrGroup) {
        if (Comparing.strEqual(value, s)) {
          result[0] = actionOrGroup;
          return false;
        }
        return true;
      }
    };
    processActionOrGroup(context, findProcessor);
    return result[0];
  }

  @Nullable
  @Override
  public String toString(@Nullable ActionOrGroup group, ConvertContext context) {
    return group == null ? null : getName(group);
  }

  @Override
  public String getErrorMessage(@Nullable String s, ConvertContext context) {
    return "Cannot resolve action or group '" + s + "'";
  }

  @Nullable
  @Override
  public LookupElement createLookupElement(ActionOrGroup actionOrGroup) {
    if (actionOrGroup instanceof Action) {
      Action action = (Action)actionOrGroup;
      final PsiElement element = getPsiElement(actionOrGroup);
      if (element == null) {
        throw new IllegalStateException(action.getId().getStringValue() + " in " + DomUtil.getFile(action) + " " + action.isValid() + " ");
      }

      LookupElementBuilder builder =
        LookupElementBuilder.create(ObjectUtils.assertNotNull(element),
                                    ObjectUtils.assertNotNull(getName(action)));

      final String text = action.getText().getStringValue();
      if (StringUtil.isNotEmpty(text)) {
        String withoutMnemonic = StringUtil.replace(text, "_", "");
        builder = builder.withTailText(" \"" + withoutMnemonic + "\"", true);
      }

      return builder;
    }

    return super.createLookupElement(actionOrGroup);
  }

  private static boolean processActionOrGroup(ConvertContext context, PairProcessor<String, ActionOrGroup> processor) {
    final Project project = context.getProject();

    Module module = context.getModule();
    if (module == null) {
      final Collection<IdeaPlugin> plugins = IdeaPluginConverter.getAllPlugins(project);
      return processPlugins(plugins, processor);
    }

    final Collection<IdeaPlugin> modulePlugins = IdeaPluginConverter.getPlugins(project, module.getModuleScope(false));
    if (!processPlugins(modulePlugins, processor)) return false;

    final Collection<IdeaPlugin> dependenciesAndLibs =
      IdeaPluginConverter.getPlugins(project, module.getModuleWithDependenciesAndLibrariesScope(false));
    return processPlugins(dependenciesAndLibs, processor);
  }

  private static boolean processPlugins(Collection<IdeaPlugin> plugins, PairProcessor<String, ActionOrGroup> processor) {
    for (IdeaPlugin plugin : plugins) {
      final Map<String, ActionOrGroup> forFile = collectForFile(plugin);
      for (Map.Entry<String, ActionOrGroup> entry : forFile.entrySet()) {
        if (!processor.process(entry.getKey(), entry.getValue())) return false;
      }
    }
    return true;
  }

  private static Map<String, ActionOrGroup> collectForFile(final IdeaPlugin plugin) {
    final XmlFile xmlFile = DomUtil.getFile(plugin);
    return CachedValuesManager.getCachedValue(xmlFile, new CachedValueProvider<Map<String, ActionOrGroup>>() {
      @Nullable
      @Override
      public Result<Map<String, ActionOrGroup>> compute() {
        Map<String, ActionOrGroup> result = new HashMap<String, ActionOrGroup>();
        for (Actions actions : plugin.getActions()) {
          collectRecursive(result, actions);
        }<commit_msg>ActionOrGroupResolveConverter: walk module dependencies (IDEA-143835)<commit_after>/*
 * Copyright 2000-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jetbrains.idea.devkit.dom.impl;

import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.ObjectUtils;
import com.intellij.util.PairProcessor;
import com.intellij.util.xml.ConvertContext;
import com.intellij.util.xml.DomUtil;
import com.intellij.util.xml.ElementPresentationManager;
import com.intellij.util.xml.ResolvingConverter;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.dom.*;

import java.util.*;

public class ActionOrGroupResolveConverter extends ResolvingConverter<ActionOrGroup> {

  @NotNull
  @Override
  public Collection<? extends ActionOrGroup> getVariants(ConvertContext context) {
    final List<ActionOrGroup> variants = new ArrayList<ActionOrGroup>();
    PairProcessor<String, ActionOrGroup> collectProcessor = new PairProcessor<String, ActionOrGroup>() {
      @Override
      public boolean process(String s, ActionOrGroup actionOrGroup) {
        variants.add(actionOrGroup);
        return true;
      }
    };
    processActionOrGroup(context, collectProcessor);
    return variants;
  }

  @Nullable
  @Override
  public ActionOrGroup fromString(@Nullable @NonNls final String value, ConvertContext context) {
    if (StringUtil.isEmptyOrSpaces(value)) return null;

    final ActionOrGroup[] result = {null};
    PairProcessor<String, ActionOrGroup> findProcessor = new PairProcessor<String, ActionOrGroup>() {
      @Override
      public boolean process(String s, ActionOrGroup actionOrGroup) {
        if (Comparing.strEqual(value, s)) {
          result[0] = actionOrGroup;
          return false;
        }
        return true;
      }
    };
    processActionOrGroup(context, findProcessor);
    return result[0];
  }

  @Nullable
  @Override
  public String toString(@Nullable ActionOrGroup group, ConvertContext context) {
    return group == null ? null : getName(group);
  }

  @Override
  public String getErrorMessage(@Nullable String s, ConvertContext context) {
    return "Cannot resolve action or group '" + s + "'";
  }

  @Nullable
  @Override
  public LookupElement createLookupElement(ActionOrGroup actionOrGroup) {
    if (actionOrGroup instanceof Action) {
      Action action = (Action)actionOrGroup;
      final PsiElement element = getPsiElement(actionOrGroup);
      if (element == null) {
        throw new IllegalStateException(action.getId().getStringValue() + " in " + DomUtil.getFile(action) + " " + action.isValid() + " ");
      }

      LookupElementBuilder builder =
        LookupElementBuilder.create(ObjectUtils.assertNotNull(element),
                                    ObjectUtils.assertNotNull(getName(action)));

      final String text = action.getText().getStringValue();
      if (StringUtil.isNotEmpty(text)) {
        String withoutMnemonic = StringUtil.replace(text, "_", "");
        builder = builder.withTailText(" \"" + withoutMnemonic + "\"", true);
      }

      return builder;
    }

    return super.createLookupElement(actionOrGroup);
  }

  private static boolean processActionOrGroup(ConvertContext context, final PairProcessor<String, ActionOrGroup> processor) {
    final Project project = context.getProject();

    Module module = context.getModule();
    if (module == null) {
      final Collection<IdeaPlugin> plugins = IdeaPluginConverter.getAllPlugins(project);
      return processPlugins(plugins, processor);
    }

    return ModuleUtilCore.visitMeAndDependentModules(module, new ModuleUtilCore.ModuleVisitor() {
      @Override
      public boolean visit(Module module) {
        final Collection<IdeaPlugin> dependenciesAndLibs =
          IdeaPluginConverter.getPlugins(project, module.getModuleRuntimeScope(false));
        return processPlugins(dependenciesAndLibs, processor);
      }
    });
  }

  private static boolean processPlugins(Collection<IdeaPlugin> plugins, PairProcessor<String, ActionOrGroup> processor) {
    for (IdeaPlugin plugin : plugins) {
      final Map<String, ActionOrGroup> forFile = collectForFile(plugin);
      for (Map.Entry<String, ActionOrGroup> entry : forFile.entrySet()) {
        if (!processor.process(entry.getKey(), entry.getValue())) return false;
      }
    }
    return true;
  }

  private static Map<String, ActionOrGroup> collectForFile(final IdeaPlugin plugin) {
    final XmlFile xmlFile = DomUtil.getFile(plugin);
    return CachedValuesManager.getCachedValue(xmlFile, new CachedValueProvider<Map<String, ActionOrGroup>>() {
      @Nullable
      @Override
      public Result<Map<String, ActionOrGroup>> compute() {
        Map<String, ActionOrGroup> result = new HashMap<String, ActionOrGroup>();
        for (Actions actions : plugin.getActions()) {
          collectRecursive(result, actions);
        }
{'fraction_non_alphanumeric': 0.0978536420483353, 'fraction_numerical': 0.0028730775730944737, 'mean_word_length': 4.092512908777969, 'pattern_counts': {'":': 0, '<': 48, '<?xml version=': 0, '>': 48, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 14}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4626070', 'n_tokens_mistral': 3309, 'n_tokens_neox': 3222, 'n_words': 792}