| /* |
| * Copyright 2000-2012 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.jetbrains.android.logcat; |
| |
| import com.android.ddmlib.Log; |
| import com.intellij.CommonBundle; |
| import com.intellij.codeInsight.completion.CompletionParameters; |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.editor.event.*; |
| import com.intellij.openapi.editor.event.DocumentAdapter; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.DialogWrapper; |
| import com.intellij.openapi.ui.Splitter; |
| import com.intellij.openapi.ui.ValidationInfo; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.ui.*; |
| import com.intellij.ui.components.JBLabel; |
| import com.intellij.ui.components.JBList; |
| import com.intellij.util.IconUtil; |
| import com.intellij.util.containers.HashSet; |
| import org.jetbrains.android.logcat.AndroidLogcatReceiver.LogMessageHeader; |
| import org.jetbrains.android.util.AndroidBundle; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.ListSelectionEvent; |
| import javax.swing.event.ListSelectionListener; |
| import java.awt.*; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.util.*; |
| import java.util.List; |
| import java.util.regex.Pattern; |
| import java.util.regex.PatternSyntaxException; |
| |
| /** |
| * @author Eugene.Kudelevsky |
| */ |
| class EditLogFilterDialog extends DialogWrapper { |
| @NonNls private static final Key<JComponent> OWNER = new Key<JComponent>("Owner"); |
| private static final String NEW_FILTER_NAME_PREFIX = "Unnamed-"; |
| @NonNls private static final String EDIT_FILTER_DIALOG_DIMENSIONS_KEY = |
| "edit.logcat.filter.dialog.dimensions"; |
| |
| private final Splitter mySplitter; |
| private JPanel myContentPanel; |
| private JPanel myLeftPanel; |
| |
| private EditorTextField myFilterNameField; |
| private EditorTextField myLogMessageField; |
| private TextFieldWithAutoCompletion myTagField; |
| private TextFieldWithAutoCompletion myPidField; |
| private TextFieldWithAutoCompletion myPackageNameField; |
| private JComboBox myLogLevelCombo; |
| |
| private JPanel myTagFieldWrapper; |
| private JPanel myPidFieldWrapper; |
| private JPanel myPackageNameFieldWrapper; |
| |
| private JBLabel myNameFieldLabel; |
| private JBLabel myLogTagLabel; |
| private JBLabel myLogMessageLabel; |
| private JBLabel myPidLabel; |
| private JBLabel myPackageNameLabel; |
| |
| private JBList myFiltersList; |
| private CollectionListModel<String> myFiltersListModel; |
| |
| private AndroidConfiguredLogFilters.MyFilterEntry mySelectedEntry; |
| private final List<AndroidConfiguredLogFilters.MyFilterEntry> myFilterEntries; |
| |
| private JPanel myFiltersToolbarPanel; |
| |
| private final AndroidLogcatView myView; |
| private final Project myProject; |
| |
| private boolean myExistingMessagesParsed = false; |
| |
| private String[] myUsedTags; |
| private String[] myUsedPids; |
| private String[] myUsedPackageNames; |
| |
| protected EditLogFilterDialog(@NotNull final AndroidLogcatView view, |
| @Nullable String selectedFilter) { |
| super(view.getProject(), false); |
| |
| myView = view; |
| myProject = view.getProject(); |
| |
| mySplitter = new Splitter(false, 0.25f); |
| mySplitter.setFirstComponent(myLeftPanel); |
| mySplitter.setSecondComponent(myContentPanel); |
| |
| myFilterEntries = AndroidConfiguredLogFilters.getInstance(myProject).getFilterEntries(); |
| |
| if (selectedFilter != null) { |
| for (AndroidConfiguredLogFilters.MyFilterEntry fe: myFilterEntries) { |
| if (selectedFilter.equals(fe.getName())) { |
| mySelectedEntry = fe; |
| } |
| } |
| } |
| |
| if (mySelectedEntry == null) { |
| mySelectedEntry = myFilterEntries.isEmpty() ? createNewFilterEntry() : myFilterEntries.get(0); |
| } |
| |
| createEditorFields(); |
| initFiltersToolbar(); |
| initFiltersList(); |
| updateConfiguredFilters(); |
| |
| init(); |
| } |
| |
| @Override |
| @Nullable |
| @NonNls |
| protected String getDimensionServiceKey() { |
| return EDIT_FILTER_DIALOG_DIMENSIONS_KEY; |
| } |
| |
| private void createEditorFields() { |
| myNameFieldLabel.setLabelFor(myFilterNameField); |
| myLogMessageLabel.setLabelFor(myLogMessageField); |
| |
| myTagField = new TextFieldWithAutoCompletion<String>(myProject, new TextFieldWithAutoCompletion.StringsCompletionProvider(null, null) { |
| @NotNull |
| @Override |
| public Collection<String> getItems(String prefix, boolean cached, CompletionParameters parameters) { |
| parseExistingMessagesIfNecessary(); |
| setItems(Arrays.asList(myUsedTags)); |
| return super.getItems(prefix, cached, parameters); |
| } |
| }, true, null); |
| myTagFieldWrapper.add(myTagField); |
| myLogTagLabel.setLabelFor(myTagField); |
| |
| myPidField = new TextFieldWithAutoCompletion<String>(myProject, new TextFieldWithAutoCompletion.StringsCompletionProvider(null, null) { |
| @NotNull |
| @Override |
| public Collection<String> getItems(String prefix, boolean cached, CompletionParameters parameters) { |
| parseExistingMessagesIfNecessary(); |
| setItems(Arrays.asList(myUsedPids)); |
| return super.getItems(prefix, cached, parameters); |
| } |
| |
| @Override |
| public int compare(String item1, String item2) { |
| final int pid1 = Integer.parseInt(item1); |
| final int pid2 = Integer.parseInt(item2); |
| return Comparing.compare(pid1, pid2); |
| } |
| }, true, null); |
| myPidFieldWrapper.add(myPidField); |
| myPidLabel.setLabelFor(myPidField); |
| |
| myPackageNameField = new TextFieldWithAutoCompletion<String>( |
| myProject, new TextFieldWithAutoCompletion.StringsCompletionProvider(null, null) { |
| @NotNull |
| @Override |
| public Collection<String> getItems(String prefix, boolean cached, |
| CompletionParameters parameters) { |
| parseExistingMessagesIfNecessary(); |
| setItems(Arrays.asList(myUsedPackageNames)); |
| return super.getItems(prefix, cached, parameters); |
| } |
| }, true, null); |
| myPackageNameFieldWrapper.add(myPackageNameField); |
| myPackageNameLabel.setLabelFor(myPackageNameField); |
| |
| myLogLevelCombo.setModel(new EnumComboBoxModel<Log.LogLevel>(Log.LogLevel.class)); |
| myLogLevelCombo.setRenderer(new ListCellRendererWrapper() { |
| @Override |
| public void customize(JList list, |
| Object value, |
| int index, |
| boolean selected, |
| boolean hasFocus) { |
| if (value != null) { |
| setText(StringUtil.capitalize(((Log.LogLevel)value).getStringValue().toLowerCase())); |
| } |
| } |
| }); |
| myLogLevelCombo.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| if (mySelectedEntry == null) { |
| return; |
| } |
| |
| Log.LogLevel selectedItem = (Log.LogLevel)myLogLevelCombo.getSelectedItem(); |
| mySelectedEntry.setLogLevel(selectedItem.getStringValue()); |
| } |
| }); |
| |
| myFilterNameField.getDocument().putUserData(OWNER, myFilterNameField); |
| myTagField.getDocument().putUserData(OWNER, myTagField); |
| myLogMessageField.getDocument().putUserData(OWNER, myLogMessageField); |
| myPidField.getDocument().putUserData(OWNER, myPidField); |
| myPackageNameField.getDocument().putUserData(OWNER, myPackageNameField); |
| |
| DocumentListener l = new DocumentAdapter() { |
| @Override |
| public void documentChanged(DocumentEvent e) { |
| if (mySelectedEntry == null) { |
| return; |
| } |
| |
| String text = e.getDocument().getText().trim(); |
| JComponent src = e.getDocument().getUserData(OWNER); |
| |
| if (src == myTagField) { |
| mySelectedEntry.setLogTagPattern(text); |
| } else if (src == myPidField) { |
| mySelectedEntry.setPid(text); |
| } else if (src == myPackageNameField) { |
| mySelectedEntry.setPackageNamePattern(text); |
| } else if (src == myLogMessageField) { |
| mySelectedEntry.setLogMessagePattern(text); |
| } else if (src == myFilterNameField) { |
| int index = myFiltersList.getSelectedIndex(); |
| if (index != -1) { |
| myFiltersListModel.setElementAt(text, index); |
| } |
| mySelectedEntry.setName(text); |
| } |
| } |
| }; |
| |
| myFilterNameField.getDocument().addDocumentListener(l); |
| myTagField.getDocument().addDocumentListener(l); |
| myLogMessageField.getDocument().addDocumentListener(l); |
| myPidField.getDocument().addDocumentListener(l); |
| myPackageNameField.getDocument().addDocumentListener(l); |
| } |
| |
| private void initFiltersList() { |
| myFiltersList.addListSelectionListener(new ListSelectionListener() { |
| @Override |
| public void valueChanged(ListSelectionEvent e) { |
| if (e.getValueIsAdjusting()) { |
| return; |
| } |
| |
| int i = myFiltersList.getSelectedIndex(); |
| mySelectedEntry = (i == -1) ? null : myFilterEntries.get(i); |
| resetFieldEditors(); |
| } |
| }); |
| |
| myFiltersListModel = new CollectionListModel<String>(); |
| for (AndroidConfiguredLogFilters.MyFilterEntry entry : myFilterEntries) { |
| myFiltersListModel.add(entry.getName()); |
| } |
| myFiltersList.setModel(myFiltersListModel); |
| myFiltersList.setEmptyText( |
| AndroidBundle.message("android.logcat.edit.filter.dialog.no.filters")); |
| } |
| |
| private void initFiltersToolbar() { |
| final DefaultActionGroup group = new DefaultActionGroup(); |
| group.add(new MyAddFilterAction()); |
| group.add(new MyRemoveFilterAction()); |
| final JComponent component = |
| ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true).getComponent(); |
| myFiltersToolbarPanel.add(component, BorderLayout.CENTER); |
| } |
| |
| private void parseExistingMessagesIfNecessary() { |
| if (myExistingMessagesParsed) { |
| return; |
| } |
| myExistingMessagesParsed = true; |
| |
| final StringBuffer document = myView.getLogConsole().getOriginalDocument(); |
| if (document == null) { |
| return; |
| } |
| |
| final Set<String> tagSet = new HashSet<String>(); |
| final Set<String> pidSet = new HashSet<String>(); |
| final Set<String> pkgSet = new HashSet<String>(); |
| |
| final String[] lines = StringUtil.splitByLines(document.toString()); |
| for (String line : lines) { |
| Pair<LogMessageHeader,String> result = AndroidLogcatFormatter.parseMessage(line); |
| if (result.getFirst() == null) { |
| continue; |
| } |
| |
| final String tag = result.getFirst().myTag; |
| if (StringUtil.isNotEmpty(tag)) { |
| tagSet.add(tag); |
| } |
| |
| final String pkg = result.getFirst().myAppPackage; |
| if (StringUtil.isNotEmpty(pkg)) { |
| pkgSet.add(pkg); |
| } |
| |
| pidSet.add(Integer.toString(result.getFirst().myPid)); |
| } |
| |
| myUsedTags = tagSet.toArray(new String[tagSet.size()]); |
| myUsedPids = pidSet.toArray(new String[pidSet.size()]); |
| myUsedPackageNames = pkgSet.toArray(new String[pkgSet.size()]); |
| } |
| |
| private void resetFieldEditors() { |
| boolean enabled = mySelectedEntry != null; |
| myFilterNameField.setEnabled(enabled); |
| myTagField.setEnabled(enabled); |
| myLogMessageField.setEnabled(enabled); |
| myPidField.setEnabled(enabled); |
| myPackageNameField.setEnabled(enabled); |
| myLogLevelCombo.setEnabled(enabled); |
| |
| String name = enabled ? mySelectedEntry.getName() : ""; |
| String tag = enabled ? mySelectedEntry.getLogTagPattern() : ""; |
| String msg = enabled ? mySelectedEntry.getLogMessagePattern() : ""; |
| String pid = enabled ? mySelectedEntry.getPid() : ""; |
| String pkg = enabled ? mySelectedEntry.getPackageNamePattern() : ""; |
| Log.LogLevel logLevel = enabled ? Log.LogLevel.getByString(mySelectedEntry.getLogLevel()) |
| : Log.LogLevel.VERBOSE; |
| |
| myFilterNameField.setText(name != null ? name : ""); |
| myTagField.setText(tag != null ? tag : ""); |
| myLogMessageField.setText(msg != null ? msg : ""); |
| myPidField.setText(pid != null ? pid : ""); |
| myPackageNameField.setText(pkg != null ? pkg : ""); |
| myLogLevelCombo.setSelectedItem(logLevel != null ? logLevel : Log.LogLevel.VERBOSE); |
| } |
| |
| @Override |
| protected JComponent createCenterPanel() { |
| return mySplitter; |
| } |
| |
| @Override |
| public JComponent getPreferredFocusedComponent() { |
| return myFilterNameField.getText().length() == 0 ? myFilterNameField : myTagField; |
| } |
| |
| @Override |
| protected void doOKAction() { |
| AndroidConfiguredLogFilters.getInstance(myProject).setFilterEntries(myFilterEntries); |
| |
| super.doOKAction(); |
| } |
| |
| @Override |
| protected ValidationInfo doValidate() { |
| if (!myFilterNameField.isEnabled()) { |
| return null; |
| } |
| |
| final String name = myFilterNameField.getText().trim(); |
| |
| if (name.isEmpty()) { |
| return new ValidationInfo(AndroidBundle.message("android.logcat.new.filter.dialog.name.not.specified.error"), |
| myFilterNameField); |
| } |
| |
| if (name.equals(AndroidLogcatView.NO_FILTERS) |
| || name.equals(AndroidLogcatView.SELECTED_APP_FILTER) |
| || name.equals(AndroidLogcatView.EDIT_FILTER_CONFIGURATION)) { |
| return new ValidationInfo(AndroidBundle.message("android.logcat.new.filter.dialog.name.busy.error", name)); |
| } |
| |
| final AndroidConfiguredLogFilters.MyFilterEntry entry = |
| AndroidConfiguredLogFilters.getInstance(myView.getProject()).findFilterEntryByName(name); |
| if (entry != null && entry != mySelectedEntry) { |
| return new ValidationInfo(AndroidBundle.message("android.logcat.new.filter.dialog.name.busy.error", name)); |
| } |
| |
| ValidationInfo info = validatePattern( |
| myTagField.getText().trim(), |
| AndroidBundle.message("android.logcat.new.filter.dialog.incorrect.log.tag.pattern.error")); |
| if (info != null) { |
| return info; |
| } |
| |
| info = validatePattern( |
| myLogMessageField.getText().trim(), |
| AndroidBundle.message("android.logcat.new.filter.dialog.incorrect.message.pattern.error")); |
| if (info != null) { |
| return info; |
| } |
| |
| info = validatePattern( |
| myPackageNameField.getText().trim(), |
| AndroidBundle.message("android.logcat.new.filter.dialog.incorrect.application.name.pattern.error")); |
| if (info != null) { |
| return info; |
| } |
| |
| boolean validPid = false; |
| try { |
| final String pidStr = myPidField.getText().trim(); |
| final Integer pid = pidStr.length() > 0 ? Integer.parseInt(pidStr) : null; |
| |
| if (pid == null || pid.intValue() >= 0) { |
| validPid = true; |
| } |
| } |
| catch (NumberFormatException ignored) { |
| } |
| if (!validPid) { |
| return new ValidationInfo(AndroidBundle.message("android.logcat.new.filter.dialog.incorrect.pid.error")); |
| } |
| |
| return null; |
| } |
| |
| private static ValidationInfo validatePattern(String pattern, String errorMessage) { |
| try { |
| if (!pattern.isEmpty()) { |
| Pattern.compile(pattern, AndroidConfiguredLogFilters.getPatternCompileFlags(pattern)); |
| } |
| return null; |
| } |
| catch (PatternSyntaxException e) { |
| String message = e.getMessage(); |
| return new ValidationInfo(errorMessage + (message != null ? ('\n' + message) : "")); |
| } |
| } |
| |
| @Nullable |
| public AndroidConfiguredLogFilters.MyFilterEntry getCustomLogFiltersEntry() { |
| return mySelectedEntry; |
| } |
| |
| private void updateConfiguredFilters() { |
| myFiltersList.setEnabled(myFilterEntries.size() > 0); |
| if (mySelectedEntry != null) { |
| myFiltersList.setSelectedValue(mySelectedEntry.getName(), true); |
| } else if (!myFilterEntries.isEmpty()) { |
| myFiltersList.setSelectedIndex(0); |
| } |
| |
| resetFieldEditors(); |
| } |
| |
| private AndroidConfiguredLogFilters.MyFilterEntry createNewFilterEntry() { |
| AndroidConfiguredLogFilters.MyFilterEntry entry = |
| new AndroidConfiguredLogFilters.MyFilterEntry(); |
| myFilterEntries.add(entry); |
| entry.setName(getUniqueName()); |
| return entry; |
| } |
| |
| private String getUniqueName() { |
| Set<String> names = new HashSet<String>(myFilterEntries.size()); |
| for (AndroidConfiguredLogFilters.MyFilterEntry fe : myFilterEntries) { |
| names.add(fe.getName()); |
| } |
| |
| for (int i = 0; ; i++){ |
| String n = NEW_FILTER_NAME_PREFIX + i; |
| if (!names.contains(n)) { |
| return n; |
| } |
| } |
| } |
| |
| private class MyAddFilterAction extends AnAction { |
| private MyAddFilterAction() { |
| super(CommonBundle.message("button.add"), |
| AndroidBundle.message("android.logcat.add.logcat.filter.button"), |
| IconUtil.getAddIcon()); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| mySelectedEntry = createNewFilterEntry(); |
| myFiltersListModel.add(mySelectedEntry.getName()); |
| updateConfiguredFilters(); |
| } |
| } |
| |
| private class MyRemoveFilterAction extends AnAction { |
| private MyRemoveFilterAction() { |
| super(CommonBundle.message("button.delete"), |
| AndroidBundle.message("android.logcat.remove.logcat.filter.button"), |
| IconUtil.getRemoveIcon()); |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| super.update(e); |
| e.getPresentation().setEnabled(getSelectedFilterName() != null); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| int i = myFiltersList.getSelectedIndex(); |
| |
| // remove the entry from the model and from the displayed list |
| myFilterEntries.remove(i); |
| myFiltersListModel.remove(i); |
| |
| // select another entry |
| mySelectedEntry = null; |
| if (myFilterEntries.size() > 0) { |
| if (i >= myFilterEntries.size()) { |
| i = myFilterEntries.size() - 1; |
| } |
| mySelectedEntry = myFilterEntries.get(i); |
| } |
| |
| updateConfiguredFilters(); |
| } |
| } |
| |
| private String getSelectedFilterName() { |
| return (String)myFiltersList.getSelectedValue(); |
| } |
| } |