Home | History | Annotate | Download | only in lint
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.ide.eclipse.adt.internal.lint;
     17 
     18 import com.android.annotations.NonNull;
     19 import com.android.annotations.Nullable;
     20 import com.android.ide.eclipse.adt.AdtPlugin;
     21 import com.android.ide.eclipse.adt.AdtUtils;
     22 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
     23 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
     24 
     25 import org.eclipse.core.resources.IFile;
     26 import org.eclipse.core.resources.IMarker;
     27 import org.eclipse.core.resources.IResource;
     28 import org.eclipse.core.runtime.IStatus;
     29 import org.eclipse.jface.dialogs.IDialogConstants;
     30 import org.eclipse.jface.dialogs.TitleAreaDialog;
     31 import org.eclipse.swt.SWT;
     32 import org.eclipse.swt.events.SelectionEvent;
     33 import org.eclipse.swt.events.SelectionListener;
     34 import org.eclipse.swt.graphics.Point;
     35 import org.eclipse.swt.layout.GridData;
     36 import org.eclipse.swt.layout.GridLayout;
     37 import org.eclipse.swt.widgets.Button;
     38 import org.eclipse.swt.widgets.Composite;
     39 import org.eclipse.swt.widgets.Control;
     40 import org.eclipse.swt.widgets.Display;
     41 import org.eclipse.swt.widgets.Label;
     42 import org.eclipse.swt.widgets.Shell;
     43 import org.eclipse.swt.widgets.Text;
     44 import org.eclipse.ui.IEditorPart;
     45 import org.eclipse.ui.IWorkbenchPage;
     46 import org.eclipse.ui.IWorkbenchPartSite;
     47 import org.eclipse.ui.PlatformUI;
     48 import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
     49 
     50 import java.util.Collections;
     51 import java.util.HashSet;
     52 import java.util.List;
     53 import java.util.Set;
     54 
     55 @SuppressWarnings("restriction") // WST DOM access
     56 class LintListDialog extends TitleAreaDialog implements SelectionListener {
     57     private static final String PROJECT_LOGO_LARGE = "android-64"; //$NON-NLS-1$
     58     private final IFile mFile;
     59     private final IEditorPart mEditor;
     60     private Button mFixButton;
     61     private Button mIgnoreButton;
     62     private Button mIgnoreAllButton;
     63     private Button mShowButton;
     64     private Text mDetailsText;
     65     private Button mIgnoreTypeButton;
     66     private LintList mList;
     67 
     68     LintListDialog(
     69             @NonNull Shell parentShell,
     70             @NonNull IFile file,
     71             @Nullable IEditorPart editor) {
     72         super(parentShell);
     73         mFile = file;
     74         mEditor = editor;
     75         setHelpAvailable(false);
     76     }
     77 
     78     @Override
     79     protected void setShellStyle(int newShellStyle) {
     80         // Allow resize
     81         super.setShellStyle(newShellStyle | SWT.TITLE | SWT.MODELESS | SWT.RESIZE);
     82     }
     83 
     84     @Override
     85     public boolean close() {
     86         mList.dispose();
     87         return super.close();
     88     }
     89 
     90     @Override
     91     protected Control createContents(Composite parent) {
     92       Control contents = super.createContents(parent);
     93       setTitle("Lint Warnings in Layout");
     94       setMessage("Lint Errors found for the current layout:");
     95       setTitleImage(IconFactory.getInstance().getIcon(PROJECT_LOGO_LARGE));
     96 
     97       return contents;
     98     }
     99 
    100     @SuppressWarnings("unused") // SWT constructors have side effects, they are not unused
    101     @Override
    102     protected Control createDialogArea(Composite parent) {
    103         Composite area = (Composite) super.createDialogArea(parent);
    104         Composite container = new Composite(area, SWT.NONE);
    105         container.setLayoutData(new GridData(GridData.FILL_BOTH));
    106 
    107         container.setLayout(new GridLayout(2, false));
    108         IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
    109         IWorkbenchPartSite site = null;
    110         if (page.getActivePart() != null) {
    111             site = page.getActivePart().getSite();
    112         }
    113 
    114         mList = new LintList(site, container, null /*memento*/, true /*singleFile*/);
    115         mList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 6));
    116 
    117         mShowButton = new Button(container, SWT.NONE);
    118         mShowButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
    119         mShowButton.setText("Show");
    120         mShowButton.setToolTipText("Opens the editor to reveal the XML with the issue");
    121         mShowButton.addSelectionListener(this);
    122 
    123         mFixButton = new Button(container, SWT.NONE);
    124         mFixButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
    125         mFixButton.setText("Fix");
    126         mFixButton.setToolTipText("Automatically corrects the problem, if possible");
    127         mFixButton.setEnabled(false);
    128         mFixButton.addSelectionListener(this);
    129 
    130         mIgnoreButton = new Button(container, SWT.NONE);
    131         mIgnoreButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
    132         mIgnoreButton.setText("Suppress Issue");
    133         mIgnoreButton.setToolTipText("Adds a special attribute in the layout to suppress this specific warning");
    134         mIgnoreButton.addSelectionListener(this);
    135 
    136         mIgnoreAllButton = new Button(container, SWT.NONE);
    137         mIgnoreAllButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
    138         mIgnoreAllButton.setText("Suppress in Layout");
    139         mIgnoreAllButton.setEnabled(mEditor instanceof AndroidXmlEditor);
    140         mIgnoreAllButton.setToolTipText("Adds an attribute on the root element to suppress all issues of this type in this layout");
    141         mIgnoreAllButton.addSelectionListener(this);
    142 
    143         mIgnoreTypeButton = new Button(container, SWT.NONE);
    144         mIgnoreTypeButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
    145         mIgnoreTypeButton.setText("Disable Issue Type");
    146         mIgnoreTypeButton.setToolTipText("Turns off checking for this type of error everywhere");
    147         mIgnoreTypeButton.addSelectionListener(this);
    148 
    149         new Label(container, SWT.NONE);
    150 
    151         mDetailsText = new Text(container, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP
    152                 | SWT.V_SCROLL | SWT.MULTI);
    153         Display display = parent.getDisplay();
    154         mDetailsText.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
    155         mDetailsText.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
    156         GridData gdText = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
    157         gdText.heightHint = 80;
    158         mDetailsText.setLayoutData(gdText);
    159 
    160         new Label(container, SWT.NONE);
    161 
    162         mList.addSelectionListener(this);
    163 
    164         mList.setResources(Collections.<IResource>singletonList(mFile));
    165         mList.selectFirst();
    166         if (mList.getSelectedMarkers().size() > 0) {
    167             updateSelectionState();
    168         }
    169 
    170         return area;
    171     }
    172 
    173     /**
    174      * Create contents of the button bar.
    175      */
    176     @Override
    177     protected void createButtonsForButtonBar(Composite parent) {
    178         createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
    179     }
    180 
    181     /**
    182      * Return the initial size of the dialog.
    183      */
    184     @Override
    185     protected Point getInitialSize() {
    186         return new Point(600, 400);
    187     }
    188 
    189     private void selectMarker(IMarker marker) {
    190         if (marker == null) {
    191             mDetailsText.setText(""); //$NON-NLS-1$
    192             return;
    193         }
    194 
    195         mDetailsText.setText(EclipseLintClient.describe(marker));
    196     }
    197 
    198     // ---- Implements SelectionListener ----
    199 
    200     @Override
    201     public void widgetSelected(SelectionEvent e) {
    202         Object source = e.getSource();
    203         if (source == mList.getTreeViewer().getControl()) {
    204             // Enable/disable buttons
    205             updateSelectionState();
    206         } else if (source == mShowButton) {
    207             List<IMarker> selection = mList.getSelectedMarkers();
    208             if (selection.size() > 0) {
    209                 EclipseLintClient.showMarker(selection.get(0));
    210             }
    211         } else if (source == mFixButton) {
    212             List<IMarker> selection = mList.getSelectedMarkers();
    213             for (IMarker marker : selection) {
    214                 List<LintFix> fixes = LintFix.getFixes(EclipseLintClient.getId(marker), marker);
    215                 if (fixes == null) {
    216                     continue;
    217                 }
    218                 LintFix fix = fixes.get(0);
    219                 IEditorPart editor = AdtUtils.getActiveEditor();
    220                 if (editor instanceof AndroidXmlEditor) {
    221                     IStructuredDocument doc = ((AndroidXmlEditor) editor).getStructuredDocument();
    222                     fix.apply(doc);
    223                     if (fix.needsFocus()) {
    224                         close();
    225                     }
    226                 } else {
    227                     AdtPlugin.log(IStatus.ERROR, "Did not find associated editor to apply fix");
    228                 }
    229             }
    230         } else if (source == mIgnoreTypeButton) {
    231             for (IMarker marker : mList.getSelectedMarkers()) {
    232                 String id = EclipseLintClient.getId(marker);
    233                 if (id != null) {
    234                     LintFixGenerator.suppressDetector(id, true, mFile, true /*all*/);
    235                 }
    236             }
    237         } else if (source == mIgnoreButton) {
    238             for (IMarker marker : mList.getSelectedMarkers()) {
    239                 LintFixGenerator.addSuppressAnnotation(marker);
    240             }
    241         } else if (source == mIgnoreAllButton) {
    242             Set<String> ids = new HashSet<String>();
    243             for (IMarker marker : mList.getSelectedMarkers()) {
    244                 String id = EclipseLintClient.getId(marker);
    245                 if (id != null && !ids.contains(id)) {
    246                     ids.add(id);
    247                     if (mEditor instanceof AndroidXmlEditor) {
    248                         AndroidXmlEditor editor = (AndroidXmlEditor) mEditor;
    249                         AddSuppressAttribute fix = AddSuppressAttribute.createFixForAll(editor,
    250                                 marker, id);
    251                         if (fix != null) {
    252                             IStructuredDocument document = editor.getStructuredDocument();
    253                             fix.apply(document);
    254                         }
    255                     }
    256                 }
    257             }
    258             mList.refresh();
    259         }
    260     }
    261 
    262     private void updateSelectionState() {
    263         List<IMarker> selection = mList.getSelectedMarkers();
    264 
    265         if (selection.size() == 1) {
    266             selectMarker(selection.get(0));
    267         } else {
    268             selectMarker(null);
    269         }
    270 
    271         boolean canFix = selection.size() > 0;
    272         for (IMarker marker : selection) {
    273             if (!LintFix.hasFix(EclipseLintClient.getId(marker))) {
    274                 canFix = false;
    275                 break;
    276             }
    277 
    278             // Some fixes cannot be run in bulk
    279             if (selection.size() > 1) {
    280                 List<LintFix> fixes = LintFix.getFixes(EclipseLintClient.getId(marker), marker);
    281                 if (fixes == null || !fixes.get(0).isBulkCapable()) {
    282                     canFix = false;
    283                     break;
    284                 }
    285             }
    286         }
    287 
    288         mFixButton.setEnabled(canFix);
    289     }
    290 
    291     @Override
    292     public void widgetDefaultSelected(SelectionEvent e) {
    293         Object source = e.getSource();
    294         if (source == mList.getTreeViewer().getControl()) {
    295             // Jump to editor
    296             List<IMarker> selection = mList.getSelectedMarkers();
    297             if (selection.size() > 0) {
    298                 EclipseLintClient.showMarker(selection.get(0));
    299                 close();
    300             }
    301         }
    302     }
    303 }
    304