Exemplo n.º 1
0
  protected DfaMemoryStateImpl(DfaMemoryStateImpl toCopy) {
    myFactory = toCopy.myFactory;
    myEphemeral = toCopy.myEphemeral;
    myDefaultVariableStates = toCopy.myDefaultVariableStates; // shared between all states

    myStack = new Stack<>(toCopy.myStack);
    myDistinctClasses = new TLongHashSet(toCopy.myDistinctClasses.toArray());
    myUnknownVariables = ContainerUtil.newLinkedHashSet(toCopy.myUnknownVariables);

    myEqClasses = ContainerUtil.newArrayList(toCopy.myEqClasses);
    myIdToEqClassesIndices = new MyIdMap(toCopy.myIdToEqClassesIndices.size());
    toCopy.myIdToEqClassesIndices.forEachEntry(
        new TIntObjectProcedure<int[]>() {
          @Override
          public boolean execute(int id, int[] set) {
            myIdToEqClassesIndices.put(id, set);
            return true;
          }
        });
    myVariableStates = ContainerUtil.newLinkedHashMap(toCopy.myVariableStates);

    myCachedDistinctClassPairs = toCopy.myCachedDistinctClassPairs;
    myCachedNonTrivialEqClasses = toCopy.myCachedNonTrivialEqClasses;
    myCachedHash = toCopy.myCachedHash;
  }
Exemplo n.º 2
0
 protected DfaMemoryStateImpl(final DfaValueFactory factory) {
   myFactory = factory;
   myDefaultVariableStates = ContainerUtil.newTroveMap();
   myEqClasses = ContainerUtil.newArrayList();
   myUnknownVariables = ContainerUtil.newLinkedHashSet();
   myVariableStates = ContainerUtil.newLinkedHashMap();
   myDistinctClasses = new TLongHashSet();
   myStack = new Stack<>();
   myIdToEqClassesIndices = new MyIdMap(20);
 }
  /** Like {@link Properties#load(java.io.Reader)}, but preserves the order of key/value pairs. */
  @NotNull
  public static Map<String, String> loadProperties(@NotNull Reader reader) throws IOException {
    final Map<String, String> map = ContainerUtil.newLinkedHashMap();

    new Properties() {
      @Override
      public synchronized Object put(Object key, Object value) {
        map.put(String.valueOf(key), String.valueOf(value));
        //noinspection UseOfPropertiesAsHashtable
        return super.put(key, value);
      }
    }.load(reader);

    return map;
  }
Exemplo n.º 4
0
/** @author Eugene Zhuravlev Date: Dec 6, 2004 */
public class ExpandMacroToPathMap extends PathMacroMap {
  private final Map<String, String> myPlainMap = ContainerUtilRt.newLinkedHashMap();
  private final Map<String, String> myMacroExpands = ContainerUtil.newLinkedHashMap();

  public void addMacroExpand(String macroName, String path) {
    myMacroExpands.put(macroName, PathMacroMap.quotePath(path));
  }

  public void put(String fromText, String toText) {
    myPlainMap.put(fromText, toText);
  }

  public void putAll(ExpandMacroToPathMap another) {
    myPlainMap.putAll(another.myPlainMap);
    myMacroExpands.putAll(another.myMacroExpands);
  }

  public String substitute(String text, boolean caseSensitive) {
    if (text == null) {
      //noinspection ConstantConditions
      return null;
    }

    for (Map.Entry<String, String> entry : myPlainMap.entrySet()) {
      // when replacing macros with actual paths the replace utility may be used as always
      // 'case-sensitive'
      // for case-insensitive file systems there will be no unnecessary toLowerCase() transforms.
      text = StringUtil.replace(text, entry.getKey(), entry.getValue(), false);
    }

    for (Map.Entry<String, String> entry : myMacroExpands.entrySet()) {
      text = replaceMacro(text, entry.getKey(), entry.getValue());
    }

    return text;
  }

  private static String replaceMacro(String text, String macroName, String replacement) {
    while (true) {
      int start = findMacroIndex(text, macroName);
      if (start < 0) {
        break;
      }

      int end = start + macroName.length() + 2;
      int slashCount = getSlashCount(text, end);
      String actualReplacement =
          slashCount > 0 && !replacement.endsWith("/") ? replacement + "/" : replacement;
      text =
          StringUtil.replaceSubstring(
              text, new TextRange(start, end + slashCount), actualReplacement);
    }
    return text;
  }

  private static int getSlashCount(String text, int pos) {
    return StringUtil.isChar(text, pos, '/') ? StringUtil.isChar(text, pos + 1, '/') ? 2 : 1 : 0;
  }

  private static int findMacroIndex(String text, String macroName) {
    int i = -1;
    while (true) {
      i = text.indexOf('$', i + 1);
      if (i < 0) {
        return -1;
      }
      if (StringUtil.startsWith(text, i + 1, macroName)
          && StringUtil.isChar(text, i + macroName.length() + 1, '$')) {
        return i;
      }
    }
  }

  @Override
  public int hashCode() {
    return myPlainMap.hashCode() + myMacroExpands.hashCode();
  }
}
public class TrafficLightRenderer implements ErrorStripeRenderer, Disposable {
  private final Project myProject;
  private final Document myDocument;
  private final PsiFile myFile;
  private final DaemonCodeAnalyzerImpl myDaemonCodeAnalyzer;
  private final SeverityRegistrar mySeverityRegistrar;
  private Icon icon;
  String statistics;
  String statusLabel;
  String statusExtraLine;
  boolean passStatusesVisible;
  final Map<ProgressableTextEditorHighlightingPass, Pair<JProgressBar, JLabel>> passes =
      ContainerUtil.newLinkedHashMap();
  static final int MAX = 100;
  boolean progressBarsEnabled;
  Boolean progressBarsCompleted;

  /**
   * array filled with number of highlighters with a given severity. errorCount[idx] == number of
   * highlighters of severity with index idx in this markup model. severity index can be obtained
   * via
   * com.intellij.codeInsight.daemon.impl.SeverityRegistrar#getSeverityIdx(com.intellij.lang.annotation.HighlightSeverity)
   */
  private int[] errorCount;

  public TrafficLightRenderer(@Nullable Project project, Document document, PsiFile file) {
    myProject = project;
    myDaemonCodeAnalyzer =
        project == null ? null : (DaemonCodeAnalyzerImpl) DaemonCodeAnalyzer.getInstance(project);
    myDocument = document;
    myFile = file;
    mySeverityRegistrar = SeverityRegistrar.getSeverityRegistrar(myProject);
    refresh();

    if (project != null) {
      final MarkupModelEx model =
          (MarkupModelEx) DocumentMarkupModel.forDocument(document, project, true);
      model.addMarkupModelListener(
          this,
          new MarkupModelListener.Adapter() {
            @Override
            public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
              incErrorCount(highlighter, 1);
            }

            @Override
            public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
              incErrorCount(highlighter, -1);
            }
          });
      UIUtil.invokeLaterIfNeeded(
          () -> {
            for (RangeHighlighter rangeHighlighter : model.getAllHighlighters()) {
              incErrorCount(rangeHighlighter, 1);
            }
          });
    }
  }

  private void refresh() {
    int maxIndex = mySeverityRegistrar.getSeverityMaxIndex();
    if (errorCount != null && maxIndex == errorCount.length) return;
    int[] newErrors = new int[maxIndex + 1];
    if (errorCount != null) {
      System.arraycopy(errorCount, 0, newErrors, 0, Math.min(errorCount.length, newErrors.length));
    }
    errorCount = newErrors;
  }

  static void setOrRefreshErrorStripeRenderer(
      @NotNull EditorMarkupModel editorMarkupModel,
      @NotNull Project project,
      @NotNull Document document,
      PsiFile file) {
    ApplicationManager.getApplication().assertIsDispatchThread();
    if (!editorMarkupModel.isErrorStripeVisible()
        || !DaemonCodeAnalyzer.getInstance(project).isHighlightingAvailable(file)) {
      return;
    }
    ErrorStripeRenderer renderer = editorMarkupModel.getErrorStripeRenderer();
    if (renderer instanceof TrafficLightRenderer) {
      TrafficLightRenderer tlr = (TrafficLightRenderer) renderer;
      tlr.refresh();
      ((EditorMarkupModelImpl) editorMarkupModel).repaintVerticalScrollBar();
      if (tlr.myFile == null || tlr.myFile.isValid()) return;
      Disposer.dispose(tlr);
    }
    EditorImpl editor = (EditorImpl) editorMarkupModel.getEditor();

    if (!editor.isDisposed()) {
      renderer = new TrafficLightRenderer(project, document, file);
      Disposer.register(editor.getDisposable(), (Disposable) renderer);
      editorMarkupModel.setErrorStripeRenderer(renderer);
    }
  }

  @Override
  public void dispose() {}

  private void incErrorCount(RangeHighlighter highlighter, int delta) {
    Object o = highlighter.getErrorStripeTooltip();
    if (!(o instanceof HighlightInfo)) return;
    HighlightInfo info = (HighlightInfo) o;
    HighlightSeverity infoSeverity = info.getSeverity();
    if (infoSeverity.myVal <= HighlightSeverity.INFORMATION.myVal) return;
    final int severityIdx = mySeverityRegistrar.getSeverityIdx(infoSeverity);
    if (severityIdx != -1) {
      errorCount[severityIdx] += delta;
    }
  }

  protected static class DaemonCodeAnalyzerStatus {
    public boolean errorAnalyzingFinished; // all passes done
    List<ProgressableTextEditorHighlightingPass> passStati = Collections.emptyList();
    public int[] errorCount = ArrayUtil.EMPTY_INT_ARRAY;
    private String reasonWhyDisabled;
    private String reasonWhySuspended;

    public DaemonCodeAnalyzerStatus() {}

    @Override
    public String toString() {
      @NonNls String s = "DS: finished=" + errorAnalyzingFinished;
      s += "; pass statuses: " + passStati.size() + "; ";
      for (ProgressableTextEditorHighlightingPass passStatus : passStati) {
        s +=
            String.format(
                "(%s %2.0f%% %b)",
                passStatus.getPresentableName(),
                passStatus.getProgress() * 100,
                passStatus.isFinished());
      }
      s += "; error count: " + errorCount.length + ": " + new TIntArrayList(errorCount);
      return s;
    }
  }

  @NotNull
  protected DaemonCodeAnalyzerStatus getDaemonCodeAnalyzerStatus(
      @NotNull SeverityRegistrar severityRegistrar) {
    DaemonCodeAnalyzerStatus status = new DaemonCodeAnalyzerStatus();
    if (myFile == null) {
      status.reasonWhyDisabled = "No file";
      status.errorAnalyzingFinished = true;
      return status;
    }
    if (myProject != null && myProject.isDisposed()) {
      status.reasonWhyDisabled = "Project is disposed";
      status.errorAnalyzingFinished = true;
      return status;
    }
    if (!myDaemonCodeAnalyzer.isHighlightingAvailable(myFile)) {
      if (!myFile.isPhysical()) {
        status.reasonWhyDisabled = "File is generated";
        status.errorAnalyzingFinished = true;
        return status;
      } else if (myFile instanceof PsiCompiledElement) {
        status.reasonWhyDisabled = "File is decompiled";
        status.errorAnalyzingFinished = true;
        return status;
      }
      final FileType fileType = myFile.getFileType();
      if (fileType.isBinary()) {
        status.reasonWhyDisabled = "File is binary";
        status.errorAnalyzingFinished = true;
        return status;
      }
      status.reasonWhyDisabled = "Highlighting is disabled for this file";
      status.errorAnalyzingFinished = true;
      return status;
    }

    FileViewProvider provider = myFile.getViewProvider();
    Set<Language> languages = provider.getLanguages();
    HighlightingSettingsPerFile levelSettings = HighlightingSettingsPerFile.getInstance(myProject);
    boolean shouldHighlight = languages.isEmpty();
    for (Language language : languages) {
      PsiFile root = provider.getPsi(language);
      FileHighlightingSetting level = levelSettings.getHighlightingSettingForRoot(root);
      shouldHighlight |= level != FileHighlightingSetting.SKIP_HIGHLIGHTING;
    }
    if (!shouldHighlight) {
      status.reasonWhyDisabled = "Highlighting level is None";
      status.errorAnalyzingFinished = true;
      return status;
    }

    if (HeavyProcessLatch.INSTANCE.isRunning()) {
      status.reasonWhySuspended =
          StringUtil.defaultIfEmpty(
              HeavyProcessLatch.INSTANCE.getRunningOperationName(), "Heavy operation is running");
      status.errorAnalyzingFinished = true;
      return status;
    }

    status.errorCount = errorCount.clone();
    fillDaemonCodeAnalyzerErrorsStatus(status, severityRegistrar);
    List<TextEditorHighlightingPass> passes =
        myDaemonCodeAnalyzer.getPassesToShowProgressFor(myDocument);
    status.passStati =
        passes.isEmpty()
            ? Collections.<ProgressableTextEditorHighlightingPass>emptyList()
            : new ArrayList<>(passes.size());
    //noinspection ForLoopReplaceableByForEach
    for (int i = 0; i < passes.size(); i++) {
      TextEditorHighlightingPass tepass = passes.get(i);
      if (!(tepass instanceof ProgressableTextEditorHighlightingPass)) continue;
      ProgressableTextEditorHighlightingPass pass = (ProgressableTextEditorHighlightingPass) tepass;

      if (pass.getProgress() < 0) continue;
      status.passStati.add(pass);
    }
    status.errorAnalyzingFinished = myDaemonCodeAnalyzer.isAllAnalysisFinished(myFile);
    status.reasonWhySuspended =
        myDaemonCodeAnalyzer.isUpdateByTimerEnabled() ? null : "Highlighting is paused temporarily";

    return status;
  }

  protected void fillDaemonCodeAnalyzerErrorsStatus(
      @NotNull DaemonCodeAnalyzerStatus status, @NotNull SeverityRegistrar severityRegistrar) {}

  protected final Project getProject() {
    return myProject;
  }

  @Override
  public String getTooltipMessage() {
    // see TrafficProgressPanel
    return null;
  }

  @Override
  public void paint(Component c, Graphics g, Rectangle r) {
    DaemonCodeAnalyzerStatus status = getDaemonCodeAnalyzerStatus(mySeverityRegistrar);
    Icon icon = getIcon(status);
    icon.paintIcon(c, g, r.x, r.y);
  }

  @NotNull
  private Icon getIcon(@NotNull DaemonCodeAnalyzerStatus status) {
    updatePanel(status, getProject());
    Icon icon = this.icon;
    if (PowerSaveMode.isEnabled()
        || status.reasonWhySuspended != null
        || status.reasonWhyDisabled != null
        || status.errorAnalyzingFinished) {
      return icon;
    }
    return AllIcons.General.InspectionsEye;
  }

  // return true if panel needs to be rebuilt
  boolean updatePanel(@NotNull DaemonCodeAnalyzerStatus status, Project project) {
    progressBarsEnabled = false;
    progressBarsCompleted = null;
    statistics = "";
    passStatusesVisible = false;
    statusLabel = null;
    statusExtraLine = null;

    boolean result = false;
    if (!status.passStati.equals(new ArrayList<>(passes.keySet()))) {
      // passes set has changed
      rebuildPassesMap(status);
      result = true;
    }

    if (PowerSaveMode.isEnabled()) {
      statusLabel = "Code analysis is disabled in power save mode";
      status.errorAnalyzingFinished = true;
      icon = AllIcons.General.SafeMode;
      return result;
    }
    if (status.reasonWhyDisabled != null) {
      statusLabel = "No analysis has been performed";
      statusExtraLine = "(" + status.reasonWhyDisabled + ")";
      passStatusesVisible = true;
      progressBarsCompleted = Boolean.FALSE;
      icon = AllIcons.General.InspectionsTrafficOff;
      return result;
    }
    if (status.reasonWhySuspended != null) {
      statusLabel = "Code analysis has been suspended";
      statusExtraLine = "(" + status.reasonWhySuspended + ")";
      passStatusesVisible = true;
      progressBarsCompleted = Boolean.FALSE;
      icon = AllIcons.General.InspectionsPause;
      return result;
    }

    Icon icon = AllIcons.General.InspectionsOK;
    for (int i = status.errorCount.length - 1; i >= 0; i--) {
      if (status.errorCount[i] != 0) {
        icon = SeverityRegistrar.getSeverityRegistrar(project).getRendererIconByIndex(i);
        break;
      }
    }

    if (status.errorAnalyzingFinished) {
      boolean isDumb = project != null && DumbService.isDumb(project);
      if (isDumb) {
        statusLabel = "Shallow analysis completed";
        statusExtraLine = "Complete results will be available after indexing";
      } else {
        statusLabel = DaemonBundle.message("analysis.completed");
      }
      progressBarsCompleted = Boolean.TRUE;
    } else {
      statusLabel = DaemonBundle.message("performing.code.analysis");
      passStatusesVisible = true;
      progressBarsEnabled = true;
      progressBarsCompleted = null;
    }

    int currentSeverityErrors = 0;
    @org.intellij.lang.annotations.Language("HTML")
    String text = "";
    for (int i = status.errorCount.length - 1; i >= 0; i--) {
      if (status.errorCount[i] > 0) {
        final HighlightSeverity severity =
            SeverityRegistrar.getSeverityRegistrar(project).getSeverityByIndex(i);
        String name =
            status.errorCount[i] > 1
                ? StringUtil.pluralize(severity.getName().toLowerCase())
                : severity.getName().toLowerCase();
        text +=
            status.errorAnalyzingFinished
                ? DaemonBundle.message("errors.found", status.errorCount[i], name)
                : DaemonBundle.message("errors.found.so.far", status.errorCount[i], name);
        text += "<br>";
        currentSeverityErrors += status.errorCount[i];
      }
    }
    if (currentSeverityErrors == 0) {
      text +=
          status.errorAnalyzingFinished
              ? DaemonBundle.message("no.errors.or.warnings.found")
              : DaemonBundle.message("no.errors.or.warnings.found.so.far") + "<br>";
    }
    statistics = XmlStringUtil.wrapInHtml(text);

    this.icon = icon;
    return result;
  }

  private void rebuildPassesMap(@NotNull DaemonCodeAnalyzerStatus status) {
    passes.clear();
    for (ProgressableTextEditorHighlightingPass pass : status.passStati) {
      JProgressBar progressBar = new JProgressBar(0, MAX);
      progressBar.setMaximum(MAX);
      progressBar.putClientProperty("JComponent.sizeVariant", "mini");
      JLabel percLabel = new JLabel();
      percLabel.setText(TrafficProgressPanel.MAX_TEXT);
      passes.put(pass, Pair.create(progressBar, percLabel));
    }
  }
}
/** @author Gregory.Shrago */
public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Configurable.NoScroll {

  private final Project myProject;
  private final CfgInfo[] myInfos;

  private final JPanel myRoot;
  private final InjectionsTable myInjectionsTable;
  private final Map<String, LanguageInjectionSupport> mySupports = ContainerUtil.newLinkedHashMap();
  private final Map<String, AnAction> myEditActions = ContainerUtil.newLinkedHashMap();
  private final List<AnAction> myAddActions = ContainerUtil.newArrayList();
  private final JLabel myCountLabel;

  private Configurable[] myConfigurables;
  private Configuration myConfiguration;

  public InjectionsSettingsUI(final Project project, final Configuration configuration) {
    myProject = project;
    myConfiguration = configuration;

    final CfgInfo currentInfo = new CfgInfo(configuration, "Project");
    myInfos =
        configuration instanceof Configuration.Prj
            ? new CfgInfo[] {
              new CfgInfo(((Configuration.Prj) configuration).getParentConfiguration(), "IDE"),
              currentInfo
            }
            : new CfgInfo[] {currentInfo};

    myRoot = new JPanel(new BorderLayout());

    myInjectionsTable = new InjectionsTable(getInjInfoList(myInfos));
    myInjectionsTable.getEmptyText().setText("No injections configured");

    ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myInjectionsTable);
    createActions(decorator);

    // myRoot.add(new TitledSeparator("Languages injection places"), BorderLayout.NORTH);
    myRoot.add(decorator.createPanel(), BorderLayout.CENTER);
    myCountLabel = new JLabel();
    myCountLabel.setHorizontalAlignment(SwingConstants.RIGHT);
    myCountLabel.setForeground(SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES.getFgColor());
    myRoot.add(myCountLabel, BorderLayout.SOUTH);
    updateCountLabel();
  }

  private void createActions(ToolbarDecorator decorator) {
    final Consumer<BaseInjection> consumer =
        new Consumer<BaseInjection>() {
          public void consume(final BaseInjection injection) {
            addInjection(injection);
          }
        };
    final Factory<BaseInjection> producer =
        new NullableFactory<BaseInjection>() {
          public BaseInjection create() {
            final InjInfo info = getSelectedInjection();
            return info == null ? null : info.injection;
          }
        };
    for (LanguageInjectionSupport support : InjectorUtils.getActiveInjectionSupports()) {
      ContainerUtil.addAll(myAddActions, support.createAddActions(myProject, consumer));
      final AnAction action = support.createEditAction(myProject, producer);
      myEditActions.put(
          support.getId(),
          action == null
              ? AbstractLanguageInjectionSupport.createDefaultEditAction(myProject, producer)
              : action);
      mySupports.put(support.getId(), support);
    }
    Collections.sort(
        myAddActions,
        new Comparator<AnAction>() {
          public int compare(final AnAction o1, final AnAction o2) {
            return Comparing.compare(
                o1.getTemplatePresentation().getText(), o2.getTemplatePresentation().getText());
          }
        });
    decorator.disableUpDownActions();
    decorator.setAddActionUpdater(
        new AnActionButtonUpdater() {
          @Override
          public boolean isEnabled(AnActionEvent e) {
            return !myAddActions.isEmpty();
          }
        });
    decorator.setAddAction(
        new AnActionButtonRunnable() {
          @Override
          public void run(AnActionButton button) {
            performAdd(button);
          }
        });
    decorator.setRemoveActionUpdater(
        new AnActionButtonUpdater() {
          @Override
          public boolean isEnabled(AnActionEvent e) {
            boolean enabled = false;
            for (InjInfo info : getSelectedInjections()) {
              if (!info.bundled) {
                enabled = true;
                break;
              }
            }
            return enabled;
          }
        });
    decorator.setRemoveAction(
        new AnActionButtonRunnable() {
          @Override
          public void run(AnActionButton button) {
            performRemove();
          }
        });

    decorator.setEditActionUpdater(
        new AnActionButtonUpdater() {
          @Override
          public boolean isEnabled(AnActionEvent e) {
            AnAction edit = getEditAction();
            if (edit != null) edit.update(e);
            return edit != null && edit.getTemplatePresentation().isEnabled();
          }
        });
    decorator.setEditAction(
        new AnActionButtonRunnable() {
          @Override
          public void run(AnActionButton button) {
            performEditAction();
          }
        });
    decorator.addExtraAction(
        new DumbAwareActionButton("Duplicate", "Duplicate", PlatformIcons.COPY_ICON) {

          @Override
          public boolean isEnabled() {
            return getEditAction() != null;
          }

          @Override
          public void actionPerformed(@NotNull AnActionEvent e) {
            final InjInfo injection = getSelectedInjection();
            if (injection != null) {
              addInjection(injection.injection.copy());
              // performEditAction(e);
            }
          }
        });

    decorator.addExtraAction(
        new DumbAwareActionButton(
            "Enable Selected Injections",
            "Enable Selected Injections",
            PlatformIcons.SELECT_ALL_ICON) {

          @Override
          public void actionPerformed(@NotNull final AnActionEvent e) {
            performSelectedInjectionsEnabled(true);
          }
        });
    decorator.addExtraAction(
        new DumbAwareActionButton(
            "Disable Selected Injections",
            "Disable Selected Injections",
            PlatformIcons.UNSELECT_ALL_ICON) {

          @Override
          public void actionPerformed(@NotNull final AnActionEvent e) {
            performSelectedInjectionsEnabled(false);
          }
        });

    new DumbAwareAction("Toggle") {
      @Override
      public void update(@NotNull AnActionEvent e) {
        SpeedSearchSupply supply = SpeedSearchSupply.getSupply(myInjectionsTable);
        e.getPresentation().setEnabled(supply == null || !supply.isPopupActive());
      }

      @Override
      public void actionPerformed(@NotNull final AnActionEvent e) {
        performToggleAction();
      }
    }.registerCustomShortcutSet(
        new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0)), myInjectionsTable);

    if (myInfos.length > 1) {
      AnActionButton shareAction =
          new DumbAwareActionButton("Move to IDE Scope", null, PlatformIcons.IMPORT_ICON) {
            {
              addCustomUpdater(
                  new AnActionButtonUpdater() {
                    @Override
                    public boolean isEnabled(AnActionEvent e) {
                      CfgInfo cfg = getTargetCfgInfo(getSelectedInjections());
                      e.getPresentation()
                          .setText(
                              cfg == getDefaultCfgInfo()
                                  ? "Move to IDE Scope"
                                  : "Move to Project Scope");
                      return cfg != null;
                    }
                  });
            }

            @Override
            public void actionPerformed(@NotNull final AnActionEvent e) {
              final List<InjInfo> injections = getSelectedInjections();
              final CfgInfo cfg = getTargetCfgInfo(injections);
              if (cfg == null) return;
              for (InjInfo info : injections) {
                if (info.cfgInfo == cfg) continue;
                if (info.bundled) continue;
                info.cfgInfo.injectionInfos.remove(info);
                cfg.addInjection(info.injection);
              }
              final int[] selectedRows = myInjectionsTable.getSelectedRows();
              myInjectionsTable.getListTableModel().setItems(getInjInfoList(myInfos));
              TableUtil.selectRows(myInjectionsTable, selectedRows);
            }

            @Nullable
            private CfgInfo getTargetCfgInfo(final List<InjInfo> injections) {
              CfgInfo cfg = null;
              for (InjInfo info : injections) {
                if (info.bundled) {
                  continue;
                }
                if (cfg == null) cfg = info.cfgInfo;
                else if (cfg != info.cfgInfo) return info.cfgInfo;
              }
              if (cfg == null) return null;
              for (CfgInfo info : myInfos) {
                if (info != cfg) return info;
              }
              throw new AssertionError();
            }
          };
      shareAction.setShortcut(
          new CustomShortcutSet(
              KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK)));
      decorator.addExtraAction(shareAction);
    }
    decorator.addExtraAction(
        new DumbAwareActionButton("Import", "Import", AllIcons.Actions.Install) {

          @Override
          public void actionPerformed(@NotNull final AnActionEvent e) {
            doImportAction(e.getDataContext());
            updateCountLabel();
          }
        });
    decorator.addExtraAction(
        new DumbAwareActionButton("Export", "Export", AllIcons.Actions.Export) {

          @Override
          public void actionPerformed(@NotNull final AnActionEvent e) {
            final List<BaseInjection> injections = getInjectionList(getSelectedInjections());
            final VirtualFileWrapper wrapper =
                FileChooserFactory.getInstance()
                    .createSaveFileDialog(
                        new FileSaverDescriptor("Export Selected Injections to File...", "", "xml"),
                        myProject)
                    .save(null, null);
            if (wrapper == null) return;
            final Configuration configuration = new Configuration();
            configuration.setInjections(injections);
            final Document document = new Document(configuration.getState());
            try {
              JDOMUtil.writeDocument(document, wrapper.getFile(), "\n");
            } catch (IOException ex) {
              final String msg = ex.getLocalizedMessage();
              Messages.showErrorDialog(
                  myProject,
                  msg != null && msg.length() > 0 ? msg : ex.toString(),
                  "Export Failed");
            }
          }

          @Override
          public boolean isEnabled() {
            return !getSelectedInjections().isEmpty();
          }
        });
  }

  private void performEditAction() {
    final AnAction action = getEditAction();
    if (action != null) {
      final int row = myInjectionsTable.getSelectedRow();
      action.actionPerformed(
          new AnActionEvent(
              null,
              DataManager.getInstance().getDataContext(myInjectionsTable),
              ActionPlaces.UNKNOWN,
              new Presentation(""),
              ActionManager.getInstance(),
              0));
      myInjectionsTable.getListTableModel().fireTableDataChanged();
      myInjectionsTable.getSelectionModel().setSelectionInterval(row, row);
      updateCountLabel();
    }
  }

  private void updateCountLabel() {
    int placesCount = 0;
    int enablePlacesCount = 0;
    final List<InjInfo> items = myInjectionsTable.getListTableModel().getItems();
    if (!items.isEmpty()) {
      for (InjInfo injection : items) {
        for (InjectionPlace place : injection.injection.getInjectionPlaces()) {
          placesCount++;
          if (place.isEnabled()) enablePlacesCount++;
        }
      }
      myCountLabel.setText(
          items.size()
              + " injection"
              + (items.size() > 1 ? "s" : "")
              + " ("
              + enablePlacesCount
              + " of "
              + placesCount
              + " place"
              + (placesCount > 1 ? "s" : "")
              + " enabled) ");
    } else {
      myCountLabel.setText("no injections configured ");
    }
  }

  @Nullable
  private AnAction getEditAction() {
    final InjInfo info = getSelectedInjection();
    final String supportId = info == null ? null : info.injection.getSupportId();
    return supportId == null ? null : myEditActions.get(supportId);
  }

  private void addInjection(final BaseInjection injection) {
    final InjInfo info = getDefaultCfgInfo().addInjection(injection);
    myInjectionsTable.getListTableModel().setItems(getInjInfoList(myInfos));
    final int index =
        myInjectionsTable.convertRowIndexToView(
            myInjectionsTable.getListTableModel().getItems().indexOf(info));
    myInjectionsTable.getSelectionModel().setSelectionInterval(index, index);
    TableUtil.scrollSelectionToVisible(myInjectionsTable);
  }

  private CfgInfo getDefaultCfgInfo() {
    return myInfos[0];
  }

  @Override
  public boolean hasOwnContent() {
    return true;
  }

  @Override
  public boolean isVisible() {
    return true;
  }

  @Override
  public Configurable[] getConfigurables() {
    if (myConfigurables == null) {
      final ArrayList<Configurable> configurables = new ArrayList<Configurable>();
      for (LanguageInjectionSupport support : InjectorUtils.getActiveInjectionSupports()) {
        ContainerUtil.addAll(configurables, support.createSettings(myProject, myConfiguration));
      }
      Collections.sort(
          configurables,
          new Comparator<Configurable>() {
            public int compare(final Configurable o1, final Configurable o2) {
              return Comparing.compare(o1.getDisplayName(), o2.getDisplayName());
            }
          });
      myConfigurables = configurables.toArray(new Configurable[configurables.size()]);
    }

    return myConfigurables;
  }

  @NotNull
  @Override
  public String getId() {
    return "IntelliLang.Configuration";
  }

  @Override
  public Runnable enableSearch(String option) {
    return null;
  }

  private static void sortInjections(final List<BaseInjection> injections) {
    Collections.sort(
        injections,
        new Comparator<BaseInjection>() {
          public int compare(final BaseInjection o1, final BaseInjection o2) {
            final int support = Comparing.compare(o1.getSupportId(), o2.getSupportId());
            if (support != 0) return support;
            final int lang =
                Comparing.compare(o1.getInjectedLanguageId(), o2.getInjectedLanguageId());
            if (lang != 0) return lang;
            return Comparing.compare(o1.getDisplayName(), o2.getDisplayName());
          }
        });
  }

  public JComponent createComponent() {
    return myRoot;
  }

  public void reset() {
    for (CfgInfo info : myInfos) {
      info.reset();
    }
    myInjectionsTable.getListTableModel().setItems(getInjInfoList(myInfos));
    updateCountLabel();
  }

  public void disposeUIResources() {}

  public void apply() {
    for (CfgInfo info : myInfos) {
      info.apply();
    }
    reset();
  }

  public boolean isModified() {
    for (CfgInfo info : myInfos) {
      if (info.isModified()) return true;
    }
    return false;
  }

  private void performSelectedInjectionsEnabled(final boolean enabled) {
    for (InjInfo info : getSelectedInjections()) {
      info.injection.setPlaceEnabled(null, enabled);
    }
    myInjectionsTable.updateUI();
    updateCountLabel();
  }

  private void performToggleAction() {
    final List<InjInfo> selectedInjections = getSelectedInjections();
    boolean enabledExists = false;
    boolean disabledExists = false;
    for (InjInfo info : selectedInjections) {
      if (info.injection.isEnabled()) enabledExists = true;
      else disabledExists = true;
      if (enabledExists && disabledExists) break;
    }
    boolean allEnabled = !enabledExists && disabledExists;
    performSelectedInjectionsEnabled(allEnabled);
  }

  private void performRemove() {
    final int selectedRow = myInjectionsTable.getSelectedRow();
    if (selectedRow < 0) return;
    final List<InjInfo> selected = getSelectedInjections();
    for (InjInfo info : selected) {
      if (info.bundled) continue;
      info.cfgInfo.injectionInfos.remove(info);
    }
    myInjectionsTable.getListTableModel().setItems(getInjInfoList(myInfos));
    final int index =
        Math.min(myInjectionsTable.getListTableModel().getRowCount() - 1, selectedRow);
    myInjectionsTable.getSelectionModel().setSelectionInterval(index, index);
    TableUtil.scrollSelectionToVisible(myInjectionsTable);
    updateCountLabel();
  }

  private List<InjInfo> getSelectedInjections() {
    final ArrayList<InjInfo> toRemove = new ArrayList<InjInfo>();
    for (int row : myInjectionsTable.getSelectedRows()) {
      toRemove.add(myInjectionsTable.getItems().get(myInjectionsTable.convertRowIndexToModel(row)));
    }
    return toRemove;
  }

  @Nullable
  private InjInfo getSelectedInjection() {
    final int row = myInjectionsTable.getSelectedRow();
    return row < 0
        ? null
        : myInjectionsTable.getItems().get(myInjectionsTable.convertRowIndexToModel(row));
  }

  private void performAdd(AnActionButton e) {
    DefaultActionGroup group = new DefaultActionGroup(myAddActions);

    JBPopupFactory.getInstance()
        .createActionGroupPopup(
            null,
            group,
            e.getDataContext(),
            JBPopupFactory.ActionSelectionAid.NUMBERING,
            true,
            new Runnable() {
              public void run() {
                updateCountLabel();
              }
            },
            -1)
        .show(e.getPreferredPopupPoint());
  }

  @Nls
  public String getDisplayName() {
    return "Language Injections";
  }

  public String getHelpTopic() {
    return "reference.settings.injection.language.injection.settings";
  }

  private class InjectionsTable extends TableView<InjInfo> {
    private InjectionsTable(final List<InjInfo> injections) {
      super(new ListTableModel<InjInfo>(createInjectionColumnInfos(), injections, 1));
      setAutoResizeMode(AUTO_RESIZE_LAST_COLUMN);
      getColumnModel().getColumn(2).setCellRenderer(createLanguageCellRenderer());
      getColumnModel().getColumn(1).setCellRenderer(createDisplayNameCellRenderer());
      getColumnModel().getColumn(0).setResizable(false);
      setShowGrid(false);
      setShowVerticalLines(false);
      setGridColor(getForeground());
      TableUtil.setupCheckboxColumn(getColumnModel().getColumn(0));

      new DoubleClickListener() {
        @Override
        protected boolean onDoubleClick(MouseEvent e) {
          final int row = rowAtPoint(e.getPoint());
          if (row < 0) return false;
          if (columnAtPoint(e.getPoint()) <= 0) return false;
          myInjectionsTable.getSelectionModel().setSelectionInterval(row, row);
          performEditAction();
          return true;
        }
      }.installOn(this);

      final String[] maxName = new String[] {""};
      ContainerUtil.process(
          injections,
          new Processor<InjInfo>() {
            public boolean process(final InjInfo injection) {
              String languageId = injection.injection.getInjectedLanguageId();
              Language language = InjectedLanguage.findLanguageById(languageId);
              String displayName = language == null ? languageId : language.getDisplayName();
              if (maxName[0].length() < displayName.length()) maxName[0] = displayName;
              return true;
            }
          });
      ContainerUtil.process(
          InjectedLanguage.getAvailableLanguages(),
          new Processor<Language>() {
            public boolean process(final Language language) {
              String displayName = language.getDisplayName();
              if (maxName[0].length() < displayName.length()) maxName[0] = displayName;
              return true;
            }
          });
      Icon icon = FileTypes.PLAIN_TEXT.getIcon();
      int preferred =
          (int) (new JLabel(maxName[0], icon, SwingConstants.LEFT).getPreferredSize().width * 1.1);
      getColumnModel().getColumn(2).setMinWidth(preferred);
      getColumnModel().getColumn(2).setPreferredWidth(preferred);
      getColumnModel().getColumn(2).setMaxWidth(preferred);
      new TableViewSpeedSearch<InjInfo>(this) {
        @Override
        protected String getItemText(@NotNull InjInfo element) {
          final BaseInjection injection = element.injection;
          return injection.getSupportId()
              + " "
              + injection.getInjectedLanguageId()
              + " "
              + injection.getDisplayName();
        }
      };
    }
  }

  private ColumnInfo[] createInjectionColumnInfos() {
    final TableCellRenderer booleanCellRenderer = createBooleanCellRenderer();
    final TableCellRenderer displayNameCellRenderer = createDisplayNameCellRenderer();
    final TableCellRenderer languageCellRenderer = createLanguageCellRenderer();
    final Comparator<InjInfo> languageComparator =
        new Comparator<InjInfo>() {
          public int compare(final InjInfo o1, final InjInfo o2) {
            return Comparing.compare(
                o1.injection.getInjectedLanguageId(), o2.injection.getInjectedLanguageId());
          }
        };
    final Comparator<InjInfo> displayNameComparator =
        new Comparator<InjInfo>() {
          public int compare(final InjInfo o1, final InjInfo o2) {
            final int support =
                Comparing.compare(o1.injection.getSupportId(), o2.injection.getSupportId());
            if (support != 0) return support;
            return Comparing.compare(o1.injection.getDisplayName(), o2.injection.getDisplayName());
          }
        };
    final ColumnInfo[] columnInfos = {
      new ColumnInfo<InjInfo, Boolean>(" ") {
        @Override
        public Class getColumnClass() {
          return Boolean.class;
        }

        @Override
        public Boolean valueOf(final InjInfo o) {
          return o.injection.isEnabled();
        }

        @Override
        public boolean isCellEditable(final InjInfo injection) {
          return true;
        }

        @Override
        public void setValue(final InjInfo injection, final Boolean value) {
          injection.injection.setPlaceEnabled(null, value.booleanValue());
        }

        @Override
        public TableCellRenderer getRenderer(final InjInfo injection) {
          return booleanCellRenderer;
        }
      },
      new ColumnInfo<InjInfo, InjInfo>("Name") {
        @Override
        public InjInfo valueOf(final InjInfo info) {
          return info;
        }

        @Override
        public Comparator<InjInfo> getComparator() {
          return displayNameComparator;
        }

        @Override
        public TableCellRenderer getRenderer(final InjInfo injection) {
          return displayNameCellRenderer;
        }
      },
      new ColumnInfo<InjInfo, InjInfo>("Language") {
        @Override
        public InjInfo valueOf(final InjInfo info) {
          return info;
        }

        @Override
        public Comparator<InjInfo> getComparator() {
          return languageComparator;
        }

        @Override
        public TableCellRenderer getRenderer(final InjInfo info) {
          return languageCellRenderer;
        }
      }
    };
    if (myInfos.length > 1) {
      final TableCellRenderer typeRenderer = createTypeRenderer();
      return ArrayUtil.append(
          columnInfos,
          new ColumnInfo<InjInfo, String>("Scope") {
            @Override
            public String valueOf(final InjInfo info) {
              return info.bundled ? "Built-in" : info.cfgInfo.title;
            }

            @Override
            public TableCellRenderer getRenderer(final InjInfo injInfo) {
              return typeRenderer;
            }

            @Override
            public int getWidth(final JTable table) {
              return table
                  .getFontMetrics(table.getFont())
                  .stringWidth(StringUtil.repeatSymbol('m', 6));
            }

            @Override
            public Comparator<InjInfo> getComparator() {
              return new Comparator<InjInfo>() {
                @Override
                public int compare(final InjInfo o1, final InjInfo o2) {
                  return Comparing.compare(valueOf(o1), valueOf(o2));
                }
              };
            }
          });
    }
    return columnInfos;
  }

  private static BooleanTableCellRenderer createBooleanCellRenderer() {
    return new BooleanTableCellRenderer() {
      @Override
      public Component getTableCellRendererComponent(
          final JTable table,
          final Object value,
          final boolean isSelected,
          final boolean hasFocus,
          final int row,
          final int column) {
        return setLabelColors(
            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column),
            table,
            isSelected,
            row);
      }
    };
  }

  private static TableCellRenderer createLanguageCellRenderer() {
    return new TableCellRenderer() {
      final JLabel myLabel = new JLabel();

      public Component getTableCellRendererComponent(
          final JTable table,
          final Object value,
          final boolean isSelected,
          final boolean hasFocus,
          final int row,
          final int column) {
        final InjInfo injection = (InjInfo) value;
        // fix for a marvellous Swing peculiarity: AccessibleJTable likes to pass null here
        if (injection == null) return myLabel;
        final String languageId = injection.injection.getInjectedLanguageId();
        final Language language = InjectedLanguage.findLanguageById(languageId);
        final FileType fileType = language == null ? null : language.getAssociatedFileType();
        myLabel.setIcon(fileType == null ? null : fileType.getIcon());
        myLabel.setText(language == null ? languageId : language.getDisplayName());
        setLabelColors(myLabel, table, isSelected, row);
        return myLabel;
      }
    };
  }

  private TableCellRenderer createDisplayNameCellRenderer() {
    return new TableCellRenderer() {
      final SimpleColoredComponent myLabel = new SimpleColoredComponent();
      final SimpleColoredText myText = new SimpleColoredText();

      public Component getTableCellRendererComponent(
          final JTable table,
          final Object value,
          final boolean isSelected,
          final boolean hasFocus,
          final int row,
          final int column) {
        myLabel.clear();
        final InjInfo info = (InjInfo) value;
        // fix for a marvellous Swing peculiarity: AccessibleJTable likes to pass null here
        if (info == null) return myLabel;
        final SimpleTextAttributes grayAttrs =
            isSelected
                ? SimpleTextAttributes.REGULAR_ATTRIBUTES
                : SimpleTextAttributes.GRAY_ATTRIBUTES;
        final String supportId = info.injection.getSupportId();
        myText.append(supportId + ": ", grayAttrs);
        mySupports.get(supportId).setupPresentation(info.injection, myText, isSelected);
        myText.appendToComponent(myLabel);
        myText.clear();
        setLabelColors(myLabel, table, isSelected, row);
        return myLabel;
      }
    };
  }

  private static TableCellRenderer createTypeRenderer() {
    return new TableCellRenderer() {
      final SimpleColoredComponent myLabel = new SimpleColoredComponent();

      public Component getTableCellRendererComponent(
          final JTable table,
          final Object value,
          final boolean isSelected,
          final boolean hasFocus,
          final int row,
          final int column) {
        myLabel.clear();
        final String info = (String) value;
        if (info == null) return myLabel;
        final SimpleTextAttributes grayAttrs =
            isSelected
                ? SimpleTextAttributes.REGULAR_ATTRIBUTES
                : SimpleTextAttributes.GRAY_ATTRIBUTES;
        myLabel.append(info, grayAttrs);
        setLabelColors(myLabel, table, isSelected, row);
        return myLabel;
      }
    };
  }

  private static Component setLabelColors(
      final Component label, final JTable table, final boolean isSelected, final int row) {
    if (label instanceof JComponent) {
      ((JComponent) label).setOpaque(true);
    }
    label.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
    label.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
    return label;
  }

  private void doImportAction(final DataContext dataContext) {
    final FileChooserDescriptor descriptor =
        new FileChooserDescriptor(true, false, true, false, true, false) {
          @Override
          public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
            return super.isFileVisible(file, showHiddenFiles)
                && (file.isDirectory()
                    || "xml".equals(file.getExtension())
                    || file.getFileType() == FileTypes.ARCHIVE);
          }

          @Override
          public boolean isFileSelectable(VirtualFile file) {
            return file.getFileType() == StdFileTypes.XML;
          }
        };
    descriptor.setDescription(
        "Please select the configuration file (usually named IntelliLang.xml) to import.");
    descriptor.setTitle("Import Configuration");

    descriptor.putUserData(LangDataKeys.MODULE_CONTEXT, LangDataKeys.MODULE.getData(dataContext));

    final SplitterProportionsData splitterData = new SplitterProportionsDataImpl();
    splitterData.externalizeFromDimensionService(
        "IntelliLang.ImportSettingsKey.SplitterProportions");

    final VirtualFile file = FileChooser.chooseFile(descriptor, myProject, null);
    if (file == null) return;
    try {
      final Configuration cfg = Configuration.load(file.getInputStream());
      if (cfg == null) {
        Messages.showWarningDialog(
            myProject,
            "The selected file does not contain any importable configuration.",
            "Nothing to Import");
        return;
      }
      final CfgInfo info = getDefaultCfgInfo();
      final Map<String, Set<InjInfo>> currentMap =
          ContainerUtil.classify(
              info.injectionInfos.iterator(),
              new Convertor<InjInfo, String>() {
                public String convert(final InjInfo o) {
                  return o.injection.getSupportId();
                }
              });
      final List<BaseInjection> originalInjections = new ArrayList<BaseInjection>();
      final List<BaseInjection> newInjections = new ArrayList<BaseInjection>();
      //// remove duplicates
      // for (String supportId : InjectorUtils.getActiveInjectionSupportIds()) {
      //  final Set<BaseInjection> currentInjections = currentMap.get(supportId);
      //  if (currentInjections == null) continue;
      //  for (BaseInjection injection : currentInjections) {
      //    Configuration.importInjections(newInjections, Collections.singleton(injection),
      // originalInjections, newInjections);
      //  }
      // }
      // myInjections.clear();
      // myInjections.addAll(newInjections);

      for (String supportId : InjectorUtils.getActiveInjectionSupportIds()) {
        ArrayList<InjInfo> list =
            new ArrayList<InjInfo>(
                ObjectUtils.notNull(currentMap.get(supportId), Collections.<InjInfo>emptyList()));
        final List<BaseInjection> currentInjections = getInjectionList(list);
        final List<BaseInjection> importingInjections = cfg.getInjections(supportId);
        if (currentInjections == null) {
          newInjections.addAll(importingInjections);
        } else {
          Configuration.importInjections(
              currentInjections, importingInjections, originalInjections, newInjections);
        }
      }
      info.replace(originalInjections, newInjections);
      myInjectionsTable.getListTableModel().setItems(getInjInfoList(myInfos));
      final int n = newInjections.size();
      if (n > 1) {
        Messages.showInfoMessage(
            myProject, n + " entries have been successfully imported", "Import Successful");
      } else if (n == 1) {
        Messages.showInfoMessage(
            myProject, "One entry has been successfully imported", "Import Successful");
      } else {
        Messages.showInfoMessage(myProject, "No new entries have been imported", "Import");
      }
    } catch (Exception ex) {
      Configuration.LOG.error(ex);

      final String msg = ex.getLocalizedMessage();
      Messages.showErrorDialog(
          myProject, msg != null && msg.length() > 0 ? msg : ex.toString(), "Import Failed");
    }
  }

  private static class CfgInfo {
    final Configuration cfg;
    final List<BaseInjection> originalInjections;
    final List<InjInfo> injectionInfos = new ArrayList<InjInfo>();
    final THashSet<BaseInjection> bundledInjections =
        new THashSet<BaseInjection>(new SameParamsAndPlacesStrategy());
    final String title;

    public CfgInfo(Configuration cfg, final String title) {
      this.cfg = cfg;
      this.title = title;
      bundledInjections.addAll(cfg.getDefaultInjections());
      originalInjections =
          new ArrayList<BaseInjection>(
              ContainerUtil.concat(
                  InjectorUtils.getActiveInjectionSupportIds(),
                  new Function<String, Collection<? extends BaseInjection>>() {
                    public Collection<? extends BaseInjection> fun(final String s) {
                      List<BaseInjection> injections =
                          CfgInfo.this.cfg instanceof Configuration.Prj
                              ? ((Configuration.Prj) CfgInfo.this.cfg).getOwnInjections(s)
                              : CfgInfo.this.cfg.getInjections(s);
                      return ContainerUtil.findAll(
                          injections,
                          new Condition<BaseInjection>() {
                            public boolean value(final BaseInjection injection) {
                              String id = injection.getInjectedLanguageId();
                              return InjectedLanguage.findLanguageById(id) != null
                                  || ReferenceInjector.findById(id) != null;
                            }
                          });
                    }
                  }));
      sortInjections(originalInjections);
      reset();
    }

    public void apply() {
      final List<BaseInjection> injectionList = getInjectionList(injectionInfos);
      cfg.replaceInjections(injectionList, originalInjections, true);
      originalInjections.clear();
      originalInjections.addAll(injectionList);
      sortInjections(originalInjections);
      FileContentUtil.reparseOpenedFiles();
    }

    public void reset() {
      injectionInfos.clear();
      for (BaseInjection injection : originalInjections) {
        injectionInfos.add(new InjInfo(injection.copy(), this));
      }
    }

    public InjInfo addInjection(final BaseInjection injection) {
      final InjInfo info = new InjInfo(injection, this);
      injectionInfos.add(info);
      return info;
    }

    public boolean isModified() {
      final List<BaseInjection> copy =
          new ArrayList<BaseInjection>(getInjectionList(injectionInfos));
      sortInjections(copy);
      return !originalInjections.equals(copy);
    }

    public void replace(
        final List<BaseInjection> originalInjections, final List<BaseInjection> newInjections) {
      for (Iterator<InjInfo> it = injectionInfos.iterator(); it.hasNext(); ) {
        final InjInfo info = it.next();
        if (originalInjections.contains(info.injection)) it.remove();
      }
      for (BaseInjection newInjection : newInjections) {
        injectionInfos.add(new InjInfo(newInjection, this));
      }
    }
  }

  private static class SameParamsAndPlacesStrategy
      implements TObjectHashingStrategy<BaseInjection> {
    @Override
    public int computeHashCode(final BaseInjection object) {
      return object.hashCode();
    }

    @Override
    public boolean equals(final BaseInjection o1, final BaseInjection o2) {
      return o1.sameLanguageParameters(o2)
          && Arrays.equals(o1.getInjectionPlaces(), o2.getInjectionPlaces());
    }
  }

  private static class InjInfo {
    final BaseInjection injection;
    final CfgInfo cfgInfo;
    final boolean bundled;

    private InjInfo(final BaseInjection injection, final CfgInfo cfgInfo) {
      this.injection = injection;
      this.cfgInfo = cfgInfo;
      bundled = cfgInfo.bundledInjections.contains(injection);
    }
  }

  private static List<InjInfo> getInjInfoList(final CfgInfo[] infos) {
    return ContainerUtil.concat(
        infos,
        new Function<CfgInfo, Collection<? extends InjInfo>>() {
          @Override
          public Collection<InjInfo> fun(final CfgInfo cfgInfo) {
            return cfgInfo.injectionInfos;
          }
        });
  }

  private static List<BaseInjection> getInjectionList(final List<InjInfo> list) {
    return new AbstractList<BaseInjection>() {
      @Override
      public BaseInjection get(final int index) {
        return list.get(index).injection;
      }

      @Override
      public int size() {
        return list.size();
      }
    };
  }
}
  @Override
  protected Map<OrderEntry, OrderAware> importData(
      @NotNull final Collection<DataNode<LibraryDependencyData>> nodesToImport,
      @NotNull final Module module,
      @NotNull final IdeModifiableModelsProvider modelsProvider) {
    // The general idea is to import all external project library dependencies and module libraries
    // which don't present at the
    // ide side yet and remove all project library dependencies and module libraries which present
    // at the ide but not at
    // the given collection.
    // The trick is that we should perform module settings modification inside try/finally block
    // against target root model.
    // That means that we need to prepare all necessary data, obtain a model and modify it as
    // necessary.
    final Map<Set<String> /* library paths */, LibraryDependencyData> moduleLibrariesToImport =
        ContainerUtilRt.newHashMap();
    final Map<String /* library name + scope */, LibraryDependencyData> projectLibrariesToImport =
        ContainerUtilRt.newHashMap();
    final Set<LibraryDependencyData> toImport = ContainerUtilRt.newLinkedHashSet();
    final Map<OrderEntry, OrderAware> orderEntryDataMap = ContainerUtil.newLinkedHashMap();

    boolean hasUnresolved = false;
    for (DataNode<LibraryDependencyData> dependencyNode : nodesToImport) {
      LibraryDependencyData dependencyData = dependencyNode.getData();
      LibraryData libraryData = dependencyData.getTarget();
      hasUnresolved |= libraryData.isUnresolved();
      switch (dependencyData.getLevel()) {
        case MODULE:
          Set<String> paths = ContainerUtilRt.newHashSet();
          for (String path : libraryData.getPaths(LibraryPathType.BINARY)) {
            paths.add(
                ExternalSystemApiUtil.toCanonicalPath(path) + dependencyData.getScope().name());
          }
          moduleLibrariesToImport.put(paths, dependencyData);
          toImport.add(dependencyData);
          break;
        case PROJECT:
          projectLibrariesToImport.put(
              libraryData.getInternalName() + dependencyData.getScope().name(), dependencyData);
          toImport.add(dependencyData);
      }
    }

    final boolean finalHasUnresolved = hasUnresolved;

    final ModifiableRootModel modifiableRootModel = modelsProvider.getModifiableRootModel(module);
    LibraryTable moduleLibraryTable = modifiableRootModel.getModuleLibraryTable();
    syncExistingAndRemoveObsolete(
        modelsProvider,
        moduleLibrariesToImport,
        projectLibrariesToImport,
        toImport,
        orderEntryDataMap,
        modifiableRootModel,
        finalHasUnresolved);
    // Import missing library dependencies.
    if (!toImport.isEmpty()) {
      importMissing(
          modelsProvider,
          toImport,
          orderEntryDataMap,
          modifiableRootModel,
          moduleLibraryTable,
          module);
    }
    return orderEntryDataMap;
  }