Home | History | Annotate | Download | only in templates
      1 /*
      2  * Copyright (C) 2012 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.wizards.templates;
     17 
     18 import static com.android.SdkConstants.CLASS_ACTIVITY;
     19 import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_API;
     20 import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_BUILD_API;
     21 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_DEFAULT;
     22 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_ID;
     23 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_NAME;
     24 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_PADDING;
     25 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.PREVIEW_WIDTH;
     26 
     27 import com.android.annotations.NonNull;
     28 import com.android.annotations.Nullable;
     29 import com.android.ide.eclipse.adt.AdtPlugin;
     30 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
     31 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageControl;
     32 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
     33 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper;
     34 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.ProjectCombo;
     35 import com.android.ide.eclipse.adt.internal.wizards.templates.Parameter.Constraint;
     36 import com.android.ide.eclipse.adt.internal.wizards.templates.Parameter.Type;
     37 import com.android.tools.lint.detector.api.LintUtils;
     38 import com.google.common.collect.Lists;
     39 
     40 import org.eclipse.core.resources.IProject;
     41 import org.eclipse.core.runtime.CoreException;
     42 import org.eclipse.core.runtime.IStatus;
     43 import org.eclipse.core.runtime.NullProgressMonitor;
     44 import org.eclipse.core.runtime.Status;
     45 import org.eclipse.jdt.core.Flags;
     46 import org.eclipse.jdt.core.IJavaProject;
     47 import org.eclipse.jdt.core.IType;
     48 import org.eclipse.jdt.core.ITypeHierarchy;
     49 import org.eclipse.jdt.core.JavaModelException;
     50 import org.eclipse.jdt.core.search.IJavaSearchScope;
     51 import org.eclipse.jdt.core.search.SearchEngine;
     52 import org.eclipse.jdt.ui.IJavaElementSearchConstants;
     53 import org.eclipse.jdt.ui.JavaUI;
     54 import org.eclipse.jdt.ui.dialogs.ITypeInfoFilterExtension;
     55 import org.eclipse.jdt.ui.dialogs.ITypeInfoRequestor;
     56 import org.eclipse.jdt.ui.dialogs.TypeSelectionExtension;
     57 import org.eclipse.jface.dialogs.IDialogConstants;
     58 import org.eclipse.jface.dialogs.IInputValidator;
     59 import org.eclipse.jface.dialogs.IMessageProvider;
     60 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
     61 import org.eclipse.jface.fieldassist.ControlDecoration;
     62 import org.eclipse.jface.fieldassist.FieldDecoration;
     63 import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
     64 import org.eclipse.jface.wizard.WizardPage;
     65 import org.eclipse.swt.SWT;
     66 import org.eclipse.swt.events.FocusEvent;
     67 import org.eclipse.swt.events.FocusListener;
     68 import org.eclipse.swt.events.ModifyEvent;
     69 import org.eclipse.swt.events.ModifyListener;
     70 import org.eclipse.swt.events.SelectionEvent;
     71 import org.eclipse.swt.events.SelectionListener;
     72 import org.eclipse.swt.graphics.Image;
     73 import org.eclipse.swt.layout.GridData;
     74 import org.eclipse.swt.layout.GridLayout;
     75 import org.eclipse.swt.widgets.Button;
     76 import org.eclipse.swt.widgets.Combo;
     77 import org.eclipse.swt.widgets.Composite;
     78 import org.eclipse.swt.widgets.Control;
     79 import org.eclipse.swt.widgets.Label;
     80 import org.eclipse.swt.widgets.Shell;
     81 import org.eclipse.swt.widgets.Text;
     82 import org.eclipse.ui.dialogs.SelectionDialog;
     83 import org.w3c.dom.Element;
     84 import org.w3c.dom.Node;
     85 import org.w3c.dom.NodeList;
     86 
     87 import java.io.ByteArrayInputStream;
     88 import java.util.HashMap;
     89 import java.util.HashSet;
     90 import java.util.List;
     91 import java.util.Map;
     92 import java.util.Set;
     93 
     94 /**
     95  * First wizard page in the "New Project From Template" wizard (which is parameterized
     96  * via template.xml files)
     97  */
     98 public class NewTemplatePage extends WizardPage
     99         implements ModifyListener, SelectionListener, FocusListener {
    100     /** The default width to use for the wizard page */
    101     static final int WIZARD_PAGE_WIDTH = 600;
    102 
    103     private final NewTemplateWizardState mValues;
    104     private final boolean mChooseProject;
    105     private int mCustomMinSdk = -1;
    106     private int mCustomBuildApi = -1;
    107     private boolean mIgnore;
    108     private boolean mShown;
    109     private Control mFirst;
    110     // TODO: Move decorators to the Parameter objects?
    111     private Map<String, ControlDecoration> mDecorations = new HashMap<String, ControlDecoration>();
    112     private Label mHelpIcon;
    113     private Label mTipLabel;
    114     private ImageControl mPreview;
    115     private Image mPreviewImage;
    116     private boolean mDisposePreviewImage;
    117     private ProjectCombo mProjectButton;
    118     private StringEvaluator mEvaluator;
    119 
    120     private TemplateMetadata mShowingTemplate;
    121 
    122     /**
    123      * Creates a new {@link NewTemplatePage}
    124      *
    125      * @param values the wizard state
    126      * @param chooseProject whether the wizard should present a project chooser,
    127      *            and update {@code values}' project field
    128      */
    129     NewTemplatePage(NewTemplateWizardState values, boolean chooseProject) {
    130         super("newTemplatePage"); //$NON-NLS-1$
    131         mValues = values;
    132         mChooseProject = chooseProject;
    133     }
    134 
    135     /**
    136      * @param minSdk a minimum SDK to use, provided chooseProject is false. If
    137      *            it is true, then the minimum SDK used for validation will be
    138      *            the one of the project
    139      * @param buildApi the build API to use
    140      */
    141     void setCustomMinSdk(int minSdk, int buildApi) {
    142         assert !mChooseProject;
    143         //assert buildApi >= minSdk;
    144         mCustomMinSdk = minSdk;
    145         mCustomBuildApi = buildApi;
    146     }
    147 
    148     @Override
    149     public void createControl(Composite parent2) {
    150         Composite parent = new Composite(parent2, SWT.NULL);
    151         setControl(parent);
    152         GridLayout parentLayout = new GridLayout(3, false);
    153         parentLayout.verticalSpacing = 0;
    154         parentLayout.marginWidth = 0;
    155         parentLayout.marginHeight = 0;
    156         parentLayout.horizontalSpacing = 0;
    157         parent.setLayout(parentLayout);
    158 
    159         // Reserve enough width (since the panel is created lazily later)
    160         Label label = new Label(parent, SWT.NONE);
    161         GridData data = new GridData();
    162         data.widthHint = WIZARD_PAGE_WIDTH;
    163         label.setLayoutData(data);
    164     }
    165 
    166     @SuppressWarnings("unused") // SWT constructors have side effects and aren't unused
    167     private void onEnter() {
    168         TemplateMetadata template = mValues.getTemplateHandler().getTemplate();
    169         if (template == mShowingTemplate) {
    170             return;
    171         }
    172         mShowingTemplate = template;
    173 
    174         Composite parent = (Composite) getControl();
    175 
    176         Control[] children = parent.getChildren();
    177         if (children.length > 0) {
    178             for (Control c : parent.getChildren()) {
    179                 c.dispose();
    180             }
    181             for (ControlDecoration decoration : mDecorations.values()) {
    182                 decoration.dispose();
    183             }
    184             mDecorations.clear();
    185         }
    186 
    187         Composite container = new Composite(parent, SWT.NULL);
    188         container.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
    189         GridLayout gl_container = new GridLayout(3, false);
    190         gl_container.horizontalSpacing = 10;
    191         container.setLayout(gl_container);
    192 
    193         if (mChooseProject) {
    194             // Project: [button]
    195             String tooltip = "The Android Project where the new resource will be created.";
    196             Label projectLabel = new Label(container, SWT.NONE);
    197             projectLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
    198             projectLabel.setText("Project:");
    199             projectLabel.setToolTipText(tooltip);
    200 
    201             ProjectChooserHelper helper =
    202                     new ProjectChooserHelper(getShell(), null /* filter */);
    203             mProjectButton = new ProjectCombo(helper, container, mValues.project);
    204             mProjectButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
    205             mProjectButton.setToolTipText(tooltip);
    206             mProjectButton.addSelectionListener(this);
    207 
    208             //Label projectSeparator = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
    209             //projectSeparator.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1));
    210         }
    211 
    212         // Add parameters
    213         mFirst = null;
    214         String thumb = null;
    215         if (template != null) {
    216             thumb = template.getThumbnailPath();
    217             String title = template.getTitle();
    218             if (title != null && !title.isEmpty()) {
    219                 setTitle(title);
    220             }
    221             String description = template.getDescription();
    222             if (description != null && !description.isEmpty()) {
    223                 setDescription(description);
    224             }
    225 
    226             Map<String, String> defaults = mValues.defaults;
    227             Set<String> seen = null;
    228             if (LintUtils.assertionsEnabled()) {
    229                 seen = new HashSet<String>();
    230             }
    231 
    232             List<Parameter> parameters = template.getParameters();
    233             for (Parameter parameter : parameters) {
    234                 Parameter.Type type = parameter.type;
    235 
    236                 if (type == Parameter.Type.SEPARATOR) {
    237                     Label separator = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
    238                     separator.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1));
    239                     continue;
    240                 }
    241 
    242                 String id = parameter.id;
    243                 assert id != null && !id.isEmpty() : ATTR_ID;
    244                 Object value = defaults.get(id);
    245                 if (value == null) {
    246                     value = parameter.value;
    247                 }
    248 
    249                 String name = parameter.name;
    250                 String help = parameter.help;
    251 
    252                 // Required
    253                 assert name != null && !name.isEmpty() : ATTR_NAME;
    254                 // Ensure id's are unique:
    255                 assert seen != null && seen.add(id) : id;
    256 
    257                 // Skip attributes that were already provided by the surrounding
    258                 // context. For example, when adding into an existing project,
    259                 // provide the minimum SDK automatically from the project.
    260                 if (mValues.hidden != null && mValues.hidden.contains(id)) {
    261                     continue;
    262                 }
    263 
    264                 switch (type) {
    265                     case STRING: {
    266                         // TODO: Look at the constraints to add validators here
    267                         // TODO: If I type.equals("layout") add resource validator for layout
    268                         // names
    269                         // TODO: If I type.equals("class") make class validator
    270 
    271                         // TODO: Handle package and id better later
    272                         Label label = new Label(container, SWT.NONE);
    273                         label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false,
    274                                 1, 1));
    275                         label.setText(name);
    276 
    277                         Text text = new Text(container, SWT.BORDER);
    278                         text.setData(parameter);
    279                         parameter.control = text;
    280 
    281                         if (parameter.constraints.contains(Constraint.EXISTS)) {
    282                             text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false,
    283                                     1, 1));
    284 
    285                             Button button = new Button(container, SWT.FLAT);
    286                             button.setData(parameter);
    287                             button.setText("...");
    288                             button.addSelectionListener(this);
    289                         } else {
    290                             text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false,
    291                                     2, 1));
    292                         }
    293 
    294                         boolean hasValue = false;
    295                         if (value instanceof String) {
    296                             String stringValue = (String) value;
    297                             hasValue = !stringValue.isEmpty();
    298                             text.setText(stringValue);
    299                             mValues.parameters.put(id, value);
    300                         }
    301 
    302                         if (!hasValue) {
    303                             if (parameter.constraints.contains(Constraint.EMPTY)) {
    304                                 text.setMessage("Optional");
    305                             } else if (parameter.constraints.contains(Constraint.NONEMPTY)) {
    306                                 text.setMessage("Required");
    307                             }
    308                         }
    309 
    310                         text.addModifyListener(this);
    311                         text.addFocusListener(this);
    312 
    313                         if (mFirst == null) {
    314                             mFirst = text;
    315                         }
    316 
    317                         if (help != null && !help.isEmpty()) {
    318                             text.setToolTipText(help);
    319                             ControlDecoration decoration = createFieldDecoration(id, text, help);
    320                         }
    321                         break;
    322                     }
    323                     case BOOLEAN: {
    324                         Label label = new Label(container, SWT.NONE);
    325                         label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false,
    326                                 1, 1));
    327 
    328                         Button checkBox = new Button(container, SWT.CHECK);
    329                         checkBox.setText(name);
    330                         checkBox.setData(parameter);
    331                         parameter.control = checkBox;
    332                         checkBox.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false,
    333                                 2, 1));
    334 
    335                         if (value instanceof Boolean) {
    336                             Boolean selected = (Boolean) value;
    337                             checkBox.setSelection(selected);
    338                             mValues.parameters.put(id, value);
    339                         }
    340 
    341                         checkBox.addSelectionListener(this);
    342                         checkBox.addFocusListener(this);
    343 
    344                         if (mFirst == null) {
    345                             mFirst = checkBox;
    346                         }
    347 
    348                         if (help != null && !help.isEmpty()) {
    349                             checkBox.setToolTipText(help);
    350                             ControlDecoration decoration = createFieldDecoration(id, checkBox,
    351                                     help);
    352                         }
    353                         break;
    354                     }
    355                     case ENUM: {
    356                         Label label = new Label(container, SWT.NONE);
    357                         label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false,
    358                                 1, 1));
    359                         label.setText(name);
    360 
    361                         Combo combo = createOptionCombo(parameter, container, mValues.parameters,
    362                                 this, this);
    363                         combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false,
    364                                 2, 1));
    365 
    366                         if (mFirst == null) {
    367                             mFirst = combo;
    368                         }
    369 
    370                         if (help != null && !help.isEmpty()) {
    371                             ControlDecoration decoration = createFieldDecoration(id, combo, help);
    372                         }
    373                         break;
    374                     }
    375                     case SEPARATOR:
    376                         // Already handled above
    377                         assert false : type;
    378                         break;
    379                     default:
    380                         assert false : type;
    381                 }
    382             }
    383         }
    384 
    385         // Preview
    386         mPreview = new ImageControl(parent, SWT.NONE, null);
    387         mPreview.setDisposeImage(false); // Handled manually in this class
    388         GridData gd_mImage = new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1);
    389         gd_mImage.widthHint = PREVIEW_WIDTH + 2 * PREVIEW_PADDING;
    390         mPreview.setLayoutData(gd_mImage);
    391 
    392         Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
    393         GridData separatorData = new GridData(SWT.FILL, SWT.TOP, true, false, 3, 1);
    394         separatorData.heightHint = 16;
    395         separator.setLayoutData(separatorData);
    396 
    397         // Generic help
    398         mHelpIcon = new Label(parent, SWT.NONE);
    399         mHelpIcon.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false, 1, 1));
    400         Image icon = IconFactory.getInstance().getIcon("quickfix");
    401         mHelpIcon.setImage(icon);
    402         mHelpIcon.setVisible(false);
    403         mTipLabel = new Label(parent, SWT.WRAP);
    404         mTipLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
    405 
    406         setPreview(thumb);
    407 
    408         parent.layout(true, true);
    409         // TODO: This is a workaround for the fact that (at least on OSX) you end up
    410         // with some visual artifacts from the control decorations in the upper left corner
    411         // (outside the parent widget itself) from the initial control decoration placement
    412         // prior to layout. Therefore, perform a redraw. A better solution would be to
    413         // delay creation of the control decorations until layout has been performed.
    414         // Let's do that soon.
    415         parent.getParent().redraw();
    416     }
    417 
    418     @NonNull
    419     static Combo createOptionCombo(
    420             @NonNull Parameter parameter,
    421             @NonNull Composite container,
    422             @NonNull Map<String, Object> valueMap,
    423             @NonNull SelectionListener selectionListener,
    424             @NonNull FocusListener focusListener) {
    425         Combo combo = new Combo(container, SWT.READ_ONLY);
    426 
    427         List<Element> options = parameter.getOptions();
    428         assert options.size() > 0;
    429         int selected = 0;
    430         List<String> ids = Lists.newArrayList();
    431         List<Integer> minSdks = Lists.newArrayList();
    432         List<Integer> minBuildApis = Lists.newArrayList();
    433         List<String> labels = Lists.newArrayList();
    434         for (int i = 0, n = options.size(); i < n; i++) {
    435             Element option = options.get(i);
    436             String optionId = option.getAttribute(ATTR_ID);
    437             assert optionId != null && !optionId.isEmpty() : ATTR_ID;
    438             String isDefault = option.getAttribute(ATTR_DEFAULT);
    439             if (isDefault != null && !isDefault.isEmpty() &&
    440                     Boolean.valueOf(isDefault)) {
    441                 selected = i;
    442             }
    443             NodeList childNodes = option.getChildNodes();
    444             assert childNodes.getLength() == 1 &&
    445                     childNodes.item(0).getNodeType() == Node.TEXT_NODE;
    446             String optionLabel = childNodes.item(0).getNodeValue().trim();
    447 
    448             String minApiString = option.getAttribute(ATTR_MIN_API);
    449             int minSdk = 1;
    450             if (minApiString != null && !minApiString.isEmpty()) {
    451                 try {
    452                     minSdk = Integer.parseInt(minApiString);
    453                 } catch (NumberFormatException nufe) {
    454                     // Templates aren't allowed to contain codenames, should
    455                     // always be an integer
    456                     AdtPlugin.log(nufe, null);
    457                     minSdk = 1;
    458                 }
    459             }
    460             String minBuildApiString = option.getAttribute(ATTR_MIN_BUILD_API);
    461             int minBuildApi = 1;
    462             if (minBuildApiString != null && !minBuildApiString.isEmpty()) {
    463                 try {
    464                     minBuildApi = Integer.parseInt(minBuildApiString);
    465                 } catch (NumberFormatException nufe) {
    466                     // Templates aren't allowed to contain codenames, should
    467                     // always be an integer
    468                     AdtPlugin.log(nufe, null);
    469                     minBuildApi = 1;
    470                 }
    471             }
    472             minSdks.add(minSdk);
    473             minBuildApis.add(minBuildApi);
    474             ids.add(optionId);
    475             labels.add(optionLabel);
    476         }
    477         combo.setData(parameter);
    478         parameter.control = combo;
    479         combo.setData(ATTR_ID, ids.toArray(new String[ids.size()]));
    480         combo.setData(ATTR_MIN_API, minSdks.toArray(new Integer[minSdks.size()]));
    481         combo.setData(ATTR_MIN_BUILD_API, minBuildApis.toArray(
    482                 new Integer[minBuildApis.size()]));
    483         assert labels.size() > 0;
    484         combo.setItems(labels.toArray(new String[labels.size()]));
    485         combo.select(selected);
    486 
    487         combo.addSelectionListener(selectionListener);
    488         combo.addFocusListener(focusListener);
    489 
    490         valueMap.put(parameter.id, ids.get(selected));
    491 
    492         if (parameter.help != null && !parameter.help.isEmpty()) {
    493             combo.setToolTipText(parameter.help);
    494         }
    495 
    496         return  combo;
    497     }
    498 
    499     private void setPreview(String thumb) {
    500         Image oldImage = mPreviewImage;
    501         boolean dispose = mDisposePreviewImage;
    502         mPreviewImage = null;
    503 
    504         if (thumb == null || thumb.isEmpty()) {
    505             mPreviewImage = TemplateMetadata.getDefaultTemplateIcon();
    506             mDisposePreviewImage = false;
    507         } else {
    508             byte[] data = mValues.getTemplateHandler().readTemplateResource(thumb);
    509             if (data != null) {
    510                 try {
    511                     mPreviewImage = new Image(getControl().getDisplay(),
    512                             new ByteArrayInputStream(data));
    513                     mDisposePreviewImage = true;
    514                 } catch (Exception e) {
    515                     AdtPlugin.log(e, null);
    516                 }
    517             }
    518             if (mPreviewImage == null) {
    519                 return;
    520             }
    521         }
    522 
    523         mPreview.setImage(mPreviewImage);
    524         mPreview.fitToWidth(PREVIEW_WIDTH);
    525 
    526         if (oldImage != null && dispose) {
    527             oldImage.dispose();
    528         }
    529     }
    530 
    531     @Override
    532     public void dispose() {
    533         super.dispose();
    534 
    535         if (mPreviewImage != null && mDisposePreviewImage) {
    536             mDisposePreviewImage = false;
    537             mPreviewImage.dispose();
    538             mPreviewImage = null;
    539         }
    540     }
    541 
    542     private ControlDecoration createFieldDecoration(String id, Control control,
    543             String description) {
    544         ControlDecoration decoration = new ControlDecoration(control, SWT.LEFT);
    545         decoration.setMarginWidth(2);
    546         FieldDecoration errorFieldIndicator = FieldDecorationRegistry.getDefault().
    547            getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION);
    548         decoration.setImage(errorFieldIndicator.getImage());
    549         decoration.setDescriptionText(description);
    550         control.setToolTipText(description);
    551         mDecorations.put(id, decoration);
    552 
    553         return decoration;
    554     }
    555 
    556     @Override
    557     public boolean isPageComplete() {
    558         // Force user to reach this page before hitting Finish
    559         return mShown && super.isPageComplete();
    560     }
    561 
    562     @Override
    563     public void setVisible(boolean visible) {
    564         if (visible) {
    565             onEnter();
    566         }
    567 
    568         super.setVisible(visible);
    569 
    570         if (mFirst != null) {
    571             mFirst.setFocus();
    572         }
    573 
    574         if (visible) {
    575             mShown = true;
    576         }
    577 
    578         validatePage();
    579     }
    580 
    581     /** Returns the parameter associated with the given control */
    582     @Nullable
    583     static Parameter getParameter(Control control) {
    584         return (Parameter) control.getData();
    585     }
    586 
    587     /**
    588      * Returns the current string evaluator, if any
    589      *
    590      * @return the evaluator or null
    591      */
    592     @Nullable
    593     public StringEvaluator getEvaluator() {
    594         return mEvaluator;
    595     }
    596 
    597     // ---- Validation ----
    598 
    599     private void validatePage() {
    600         int minSdk = getMinSdk();
    601         int buildApi = getBuildApi();
    602         IStatus status = mValues.getTemplateHandler().validateTemplate(minSdk, buildApi);
    603 
    604         if (status == null || status.isOK()) {
    605             if (mChooseProject && mValues.project == null) {
    606                 status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    607                         "Please select an Android project.");
    608             }
    609         }
    610 
    611         for (Parameter parameter : mShowingTemplate.getParameters()) {
    612             if (parameter.type == Parameter.Type.SEPARATOR) {
    613                 continue;
    614             }
    615             IInputValidator validator = parameter.getValidator(mValues.project);
    616             if (validator != null) {
    617                ControlDecoration decoration = mDecorations.get(parameter.id);
    618                String value = parameter.value == null ? "" : parameter.value.toString();
    619                String error = validator.isValid(value);
    620                if (error != null) {
    621                    IStatus s = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, error);
    622                    if (decoration != null) {
    623                        updateDecorator(decoration, s, parameter.help);
    624                    }
    625                    if (status == null || status.isOK()) {
    626                        status = s;
    627                    }
    628                } else if (decoration != null) {
    629                    updateDecorator(decoration, null, parameter.help);
    630                }
    631             }
    632 
    633             if (status == null || status.isOK()) {
    634                 if (parameter.control instanceof Combo) {
    635                     status = validateCombo(status, parameter, minSdk, buildApi);
    636                 }
    637             }
    638         }
    639 
    640         setPageComplete(status == null || status.getSeverity() != IStatus.ERROR);
    641         if (status != null) {
    642             setMessage(status.getMessage(),
    643                     status.getSeverity() == IStatus.ERROR
    644                         ? IMessageProvider.ERROR : IMessageProvider.WARNING);
    645         } else {
    646             setErrorMessage(null);
    647             setMessage(null);
    648         }
    649     }
    650 
    651     /** Validates the given combo */
    652     static IStatus validateCombo(IStatus status, Parameter parameter, int minSdk, int buildApi) {
    653         Combo combo = (Combo) parameter.control;
    654         int index = combo.getSelectionIndex();
    655         return validateCombo(status, parameter, index, minSdk, buildApi);
    656     }
    657 
    658     /** Validates the given combo assuming the value at the given index is chosen */
    659     static IStatus validateCombo(IStatus status, Parameter parameter, int index,
    660             int minSdk, int buildApi) {
    661         Combo combo = (Combo) parameter.control;
    662         Integer[] optionIds = (Integer[]) combo.getData(ATTR_MIN_API);
    663         // Check minSdk
    664         if (index != -1 && index < optionIds.length) {
    665             Integer requiredMinSdk = optionIds[index];
    666             if (requiredMinSdk > minSdk) {
    667                 status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    668                     String.format(
    669                             "%1$s \"%2$s\" requires a minimum SDK version of at " +
    670                             "least %3$d, and the current min version is %4$d",
    671                             parameter.name, combo.getItems()[index], requiredMinSdk, minSdk));
    672             }
    673         }
    674 
    675         // Check minimum build target
    676         optionIds = (Integer[]) combo.getData(ATTR_MIN_BUILD_API);
    677         if (index != -1 && index < optionIds.length) {
    678             Integer requiredBuildApi = optionIds[index];
    679             if (requiredBuildApi > buildApi) {
    680                 status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    681                     String.format(
    682                         "%1$s \"%2$s\"  requires a build target API version of at " +
    683                         "least %3$d, and the current version is %4$d",
    684                         parameter.name, combo.getItems()[index], requiredBuildApi, buildApi));
    685             }
    686         }
    687         return status;
    688     }
    689 
    690     private int getMinSdk() {
    691         return mChooseProject ? mValues.getMinSdk() : mCustomMinSdk;
    692     }
    693 
    694     private int getBuildApi() {
    695         return mChooseProject ? mValues.getBuildApi() : mCustomBuildApi;
    696     }
    697 
    698     private void updateDecorator(ControlDecoration decorator, IStatus status, String help) {
    699         if (help != null && !help.isEmpty()) {
    700             decorator.setDescriptionText(status != null ? status.getMessage() : help);
    701 
    702             int severity = status != null ? status.getSeverity() : IStatus.OK;
    703             String id;
    704             if (severity == IStatus.ERROR) {
    705                 id = FieldDecorationRegistry.DEC_ERROR;
    706             } else if (severity == IStatus.WARNING) {
    707                 id = FieldDecorationRegistry.DEC_WARNING;
    708             } else {
    709                 id = FieldDecorationRegistry.DEC_INFORMATION;
    710             }
    711             FieldDecoration errorFieldIndicator = FieldDecorationRegistry.getDefault().
    712                     getFieldDecoration(id);
    713             decorator.setImage(errorFieldIndicator.getImage());
    714         } else {
    715             if (status == null || status.isOK()) {
    716                 decorator.hide();
    717             } else {
    718                 decorator.show();
    719             }
    720         }
    721     }
    722 
    723     // ---- Implements ModifyListener ----
    724 
    725     @Override
    726     public void modifyText(ModifyEvent e) {
    727         if (mIgnore) {
    728             return;
    729         }
    730 
    731         Object source = e.getSource();
    732         if (source instanceof Text) {
    733             Text text = (Text) source;
    734             editParameter(text, text.getText().trim());
    735         }
    736 
    737         validatePage();
    738     }
    739 
    740     // ---- Implements SelectionListener ----
    741 
    742     @Override
    743     public void widgetSelected(SelectionEvent e) {
    744         if (mIgnore) {
    745             return;
    746         }
    747 
    748         Object source = e.getSource();
    749         if (source == mProjectButton) {
    750             mValues.project = mProjectButton.getSelectedProject();
    751         } else if (source instanceof Combo) {
    752             Combo combo = (Combo) source;
    753             String[] optionIds = (String[]) combo.getData(ATTR_ID);
    754             int index = combo.getSelectionIndex();
    755             if (index != -1 && index < optionIds.length) {
    756                 String optionId = optionIds[index];
    757                 editParameter(combo, optionId);
    758                 TemplateMetadata template = mValues.getTemplateHandler().getTemplate();
    759                 if (template != null) {
    760                     setPreview(template.getThumbnailPath());
    761                 }
    762             }
    763         } else if (source instanceof Button) {
    764             Button button = (Button) source;
    765             Parameter parameter = (Parameter) button.getData();
    766             if (parameter.type == Type.BOOLEAN) {
    767                 // Checkbox parameter
    768                 editParameter(button, button.getSelection());
    769 
    770                 TemplateMetadata template = mValues.getTemplateHandler().getTemplate();
    771                 if (template != null) {
    772                     setPreview(template.getThumbnailPath());
    773                 }
    774             } else {
    775                 // Choose button for some other parameter, usually a text
    776                 String activity = chooseActivity();
    777                 if (activity != null) {
    778                     setValue(parameter, activity);
    779                 }
    780             }
    781         }
    782 
    783         validatePage();
    784     }
    785 
    786     private String chooseActivity() {
    787         try {
    788             // Compute a search scope: We need to merge all the subclasses
    789             // android.app.Fragment and android.support.v4.app.Fragment
    790             IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
    791             IProject project = mValues.project;
    792             IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
    793             IType activityType = null;
    794 
    795             if (javaProject != null) {
    796                 activityType = javaProject.findType(CLASS_ACTIVITY);
    797             }
    798             if (activityType == null) {
    799                 IJavaProject[] projects = BaseProjectHelper.getAndroidProjects(null);
    800                 for (IJavaProject p : projects) {
    801                     activityType = p.findType(CLASS_ACTIVITY);
    802                     if (activityType != null) {
    803                         break;
    804                     }
    805                 }
    806             }
    807             if (activityType != null) {
    808                 NullProgressMonitor monitor = new NullProgressMonitor();
    809                 ITypeHierarchy hierarchy = activityType.newTypeHierarchy(monitor);
    810                 IType[] classes = hierarchy.getAllSubtypes(activityType);
    811                 scope = SearchEngine.createJavaSearchScope(classes, IJavaSearchScope.SOURCES);
    812             }
    813 
    814             Shell parent = AdtPlugin.getShell();
    815             final SelectionDialog dialog = JavaUI.createTypeDialog(
    816                     parent,
    817                     new ProgressMonitorDialog(parent),
    818                     scope,
    819                     IJavaElementSearchConstants.CONSIDER_CLASSES, false,
    820                     // Use ? as a default filter to fill dialog with matches
    821                     "?", //$NON-NLS-1$
    822                     new TypeSelectionExtension() {
    823                         @Override
    824                         public ITypeInfoFilterExtension getFilterExtension() {
    825                             return new ITypeInfoFilterExtension() {
    826                                 @Override
    827                                 public boolean select(ITypeInfoRequestor typeInfoRequestor) {
    828                                     int modifiers = typeInfoRequestor.getModifiers();
    829                                     if (!Flags.isPublic(modifiers)
    830                                             || Flags.isInterface(modifiers)
    831                                             || Flags.isEnum(modifiers)) {
    832                                         return false;
    833                                     }
    834                                     return true;
    835                                 }
    836                             };
    837                         }
    838                     });
    839 
    840             dialog.setTitle("Choose Activity Class");
    841             dialog.setMessage("Select an Activity class (? = any character, * = any string):");
    842             if (dialog.open() == IDialogConstants.CANCEL_ID) {
    843                 return null;
    844             }
    845 
    846             Object[] types = dialog.getResult();
    847             if (types != null && types.length > 0) {
    848                 return ((IType) types[0]).getFullyQualifiedName();
    849             }
    850         } catch (JavaModelException e) {
    851             AdtPlugin.log(e, null);
    852         } catch (CoreException e) {
    853             AdtPlugin.log(e, null);
    854         }
    855         return null;
    856     }
    857 
    858     private void editParameter(Control control, Object value) {
    859         Parameter parameter = getParameter(control);
    860         if (parameter != null) {
    861             String id = parameter.id;
    862             parameter.value = value;
    863             parameter.edited = value != null && !value.toString().isEmpty();
    864             mValues.parameters.put(id, value);
    865 
    866             // Update dependent variables, if any
    867             List<Parameter> parameters = mShowingTemplate.getParameters();
    868             for (Parameter p : parameters) {
    869                 if (p == parameter || p.suggest == null || p.edited ||
    870                         p.type == Parameter.Type.SEPARATOR) {
    871                     continue;
    872                 }
    873                 if (!p.suggest.contains(id)) {
    874                     continue;
    875                 }
    876 
    877                 try {
    878                     if (mEvaluator == null) {
    879                         mEvaluator = new StringEvaluator();
    880                     }
    881                     String updated = mEvaluator.evaluate(p.suggest, parameters);
    882                     if (updated != null && !updated.equals(p.value)) {
    883                         setValue(p, updated);
    884                     }
    885                 } catch (Throwable t) {
    886                     // Pass: Ignore updating if something wrong happens
    887                     t.printStackTrace(); // during development only
    888                 }
    889             }
    890         }
    891     }
    892 
    893     private void setValue(Parameter p, String value) {
    894         p.value = value;
    895         mValues.parameters.put(p.id, value);
    896 
    897         // Update form widgets
    898         boolean prevIgnore = mIgnore;
    899         try {
    900             mIgnore = true;
    901             if (p.control instanceof Text) {
    902                 ((Text) p.control).setText(value);
    903             } else if (p.control instanceof Button) {
    904                 // TODO: Handle
    905             } else if (p.control instanceof Combo) {
    906                 // TODO: Handle
    907             } else if (p.control != null) {
    908                 assert false : p.control;
    909             }
    910         } finally {
    911             mIgnore = prevIgnore;
    912         }
    913     }
    914 
    915     @Override
    916     public void widgetDefaultSelected(SelectionEvent e) {
    917     }
    918 
    919     // ---- Implements FocusListener ----
    920 
    921     @Override
    922     public void focusGained(FocusEvent e) {
    923         Object source = e.getSource();
    924         String tip = "";
    925 
    926         if (source instanceof Control) {
    927             Control control = (Control) source;
    928             Parameter parameter = getParameter(control);
    929             if (parameter != null) {
    930                 ControlDecoration decoration = mDecorations.get(parameter.id);
    931                 if (decoration != null) {
    932                     tip = decoration.getDescriptionText();
    933                 }
    934             }
    935         }
    936 
    937         mTipLabel.setText(tip);
    938         mHelpIcon.setVisible(tip.length() > 0);
    939     }
    940 
    941     @Override
    942     public void focusLost(FocusEvent e) {
    943         mTipLabel.setText("");
    944         mHelpIcon.setVisible(false);
    945     }
    946 }
    947