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; }
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; }
/** @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; }