FileDocCategorySizeDatePackage
NewXmlFileCreationPage.javaAPI DocAndroid 1.5 API48700Wed May 06 22:41:10 BST 2009com.android.ide.eclipse.adt.wizards.newxmlfile

NewXmlFileCreationPage

public class NewXmlFileCreationPage extends org.eclipse.jface.wizard.WizardPage
This is the single page of the {@link NewXmlFileWizard} which provides the ability to create skeleton XML resources files for Android projects.

This page is used to select the project, the resource folder, resource type and file name.

Fields Summary
private static final TypeInfo[]
sTypes
TypeInfo, information for each "type" of file that can be created.
static final int
NUM_COL
Number of columns in the grid layout
private static final String
RES_FOLDER_ABS
Absolute destination folder root, e.g. "/res/"
private static final String
RES_FOLDER_REL
Relative destination folder root, e.g. "res/"
private org.eclipse.core.resources.IProject
mProject
private org.eclipse.swt.widgets.Text
mProjectTextField
private org.eclipse.swt.widgets.Button
mProjectBrowseButton
private org.eclipse.swt.widgets.Text
mFileNameTextField
private org.eclipse.swt.widgets.Text
mWsFolderPathTextField
private org.eclipse.swt.widgets.Combo
mRootElementCombo
private org.eclipse.jface.viewers.IStructuredSelection
mInitialSelection
private com.android.ide.eclipse.adt.ui.ConfigurationSelector
mConfigSelector
private com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration
mTempConfig
private boolean
mInternalWsFolderPathUpdate
private boolean
mInternalTypeUpdate
private boolean
mInternalConfigSelectorUpdate
private com.android.ide.eclipse.common.project.ProjectChooserHelper
mProjectChooserHelper
private com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener
mSdkTargetChangeListener
private TypeInfo
mCurrentTypeInfo
Constructors Summary
protected NewXmlFileCreationPage(String pageName)
Constructs a new {@link NewXmlFileCreationPage}.

Called by {@link NewXmlFileWizard#createMainPage()}.


    // --- UI creation ---
    
                   
       
        super(pageName);
        setPageComplete(false);
    
Methods Summary
private voidchangeProject(org.eclipse.core.resources.IProject newProject)
Changes mProject to the given new project and update the UI accordingly.

Note that this does not check if the new project is the same as the current one on purpose, which allows a project to be updated when its target has changed or when targets are loaded in the background.

        mProject = newProject;

        // enable types based on new API level
        enableTypesBasedOnApi();
        
        // update the Type with the new descriptors.
        initializeRootValues();
        
        // update the combo
        updateRootCombo(getSelectedType());
        
        validatePage();
    
public voidcreateControl(org.eclipse.swt.widgets.Composite parent)
Called by the parent Wizard to create the UI for this Wizard Page. {@inheritDoc}

see
org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)

        Composite composite = new Composite(parent, SWT.NULL);
        composite.setFont(parent.getFont());

        initializeDialogUnits(parent);

        composite.setLayout(new GridLayout(NUM_COL, false /*makeColumnsEqualWidth*/));
        composite.setLayoutData(new GridData(GridData.FILL_BOTH));

        createProjectGroup(composite);
        createTypeGroup(composite);
        createRootGroup(composite);

        // Show description the first time
        setErrorMessage(null);
        setMessage(null);
        setControl(composite);

        // Update state the first time
        initializeFromSelection(mInitialSelection);
        initializeRootValues();
        enableTypesBasedOnApi();
        if (mCurrentTypeInfo != null) {
            updateRootCombo(mCurrentTypeInfo);
        }
        installTargetChangeListener();
        validatePage();
    
private voidcreateProjectGroup(org.eclipse.swt.widgets.Composite parent)
Creates the project & filename fields.

The parent must be a GridLayout with NUM_COL colums.

        int col = 0;
        
        // project name
        String tooltip = "The Android Project where the new resource file will be created.";
        Label label = new Label(parent, SWT.NONE);
        label.setText("Project");
        label.setToolTipText(tooltip);
        ++col;

        mProjectTextField = new Text(parent, SWT.BORDER);
        mProjectTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        mProjectTextField.setToolTipText(tooltip);
        mProjectTextField.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                onProjectFieldUpdated();
            }
        });
        ++col;

        mProjectBrowseButton = new Button(parent, SWT.NONE);
        mProjectBrowseButton.setText("Browse...");
        mProjectBrowseButton.setToolTipText("Allows you to select the Android project to modify.");
        mProjectBrowseButton.addSelectionListener(new SelectionAdapter() {
           @Override
            public void widgetSelected(SelectionEvent e) {
               onProjectBrowse();
            }
        });
        mProjectChooserHelper = new ProjectChooserHelper(parent.getShell());
        ++col;

        col = padWithEmptyCells(parent, col);
        
        // file name
        tooltip = "The name of the resource file to create.";
        label = new Label(parent, SWT.NONE);
        label.setText("File");
        label.setToolTipText(tooltip);
        ++col;

        mFileNameTextField = new Text(parent, SWT.BORDER);
        mFileNameTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        mFileNameTextField.setToolTipText(tooltip);
        mFileNameTextField.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                validatePage();
            }
        });
        ++col;

        padWithEmptyCells(parent, col);
    
private voidcreateRootGroup(org.eclipse.swt.widgets.Composite parent)
Creates the root element combo.

The parent must be a GridLayout with NUM_COL colums.

        // separator
        Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
        label.setLayoutData(newGridData(NUM_COL, GridData.GRAB_HORIZONTAL));

        // label before the root combo
        String tooltip = "The root element to create in the XML file.";
        label = new Label(parent, SWT.NONE);
        label.setText("Select the root element for the XML file:");
        label.setLayoutData(newGridData(NUM_COL));
        label.setToolTipText(tooltip);

        // root combo
        emptyCell(parent);

        mRootElementCombo = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY);
        mRootElementCombo.setEnabled(false);
        mRootElementCombo.select(0);
        mRootElementCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        mRootElementCombo.setToolTipText(tooltip);
        
        padWithEmptyCells(parent, 2);
    
private voidcreateTypeGroup(org.eclipse.swt.widgets.Composite parent)
Creates the type field, {@link ConfigurationSelector} and the folder field.

The parent must be a GridLayout with NUM_COL colums.

        // separator
        Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
        label.setLayoutData(newGridData(NUM_COL, GridData.GRAB_HORIZONTAL));
        
        // label before type radios
        label = new Label(parent, SWT.NONE);
        label.setText("What type of resource would you like to create?");
        label.setLayoutData(newGridData(NUM_COL));

        // display the types on three columns of radio buttons.
        emptyCell(parent);
        Composite grid = new Composite(parent, SWT.NONE);
        padWithEmptyCells(parent, 2);

        grid.setLayout(new GridLayout(NUM_COL, true /*makeColumnsEqualWidth*/));
        
        SelectionListener radioListener = new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                // single-click. Only do something if activated.
                if (e.getSource() instanceof Button) {
                    onRadioTypeUpdated((Button) e.getSource());
                }
            }
        };
        
        int n = sTypes.length;
        int num_lines = (n + NUM_COL/2) / NUM_COL;
        for (int line = 0, k = 0; line < num_lines; line++) {
            for (int i = 0; i < NUM_COL; i++, k++) {
                if (k < n) {
                    TypeInfo type = sTypes[k];
                    Button radio = new Button(grid, SWT.RADIO);
                    type.setWidget(radio);
                    radio.setSelection(false);
                    radio.setText(type.getUiName());
                    radio.setToolTipText(type.getTooltip());
                    radio.addSelectionListener(radioListener);
                } else {
                    emptyCell(grid);
                }
            }
        }

        // label before configuration selector
        label = new Label(parent, SWT.NONE);
        label.setText("What type of resource configuration would you like?");
        label.setLayoutData(newGridData(NUM_COL));

        // configuration selector
        emptyCell(parent);
        mConfigSelector = new ConfigurationSelector(parent);
        GridData gd = newGridData(2, GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
        gd.widthHint = ConfigurationSelector.WIDTH_HINT;
        gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
        mConfigSelector.setLayoutData(gd);
        mConfigSelector.setOnChangeListener(new onConfigSelectorUpdated());
        emptyCell(parent);
        
        // folder name
        String tooltip = "The folder where the file will be generated, relative to the project.";
        label = new Label(parent, SWT.NONE);
        label.setText("Folder");
        label.setToolTipText(tooltip);

        mWsFolderPathTextField = new Text(parent, SWT.BORDER);
        mWsFolderPathTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        mWsFolderPathTextField.setToolTipText(tooltip);
        mWsFolderPathTextField.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                onWsFolderPathUpdated();
            }
        });
    
public voiddispose()

        
        if (mSdkTargetChangeListener != null) {
            AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener);
            mSdkTargetChangeListener = null;
        }
        
        super.dispose();
    
private voidemptyCell(org.eclipse.swt.widgets.Composite parent)
Helper method that creates an empty cell in the parent composite.

param
parent The parent composite.

        new Label(parent, SWT.NONE);
    
private voidenableTypesBasedOnApi()
Helper method to enable the type radio buttons depending on the current API level.

A type radio button is enabled either if: - if mProject is null, API level 1 is considered valid - if mProject is !null, the project->target->API must be >= to the type's API level.


        IAndroidTarget target = mProject != null ? Sdk.getCurrent().getTarget(mProject) : null;
        int currentApiLevel = 1;
        if (target != null) {
            currentApiLevel = target.getApiVersionNumber();
        }
        
        for (TypeInfo type : sTypes) {
            type.getWidget().setEnabled(type.getTargetApiLevel() <= currentApiLevel);
        }
    
public org.eclipse.core.resources.IFilegetDestinationFile()
Returns an {@link IFile} on the destination file.

Uses {@link #getProject()}, {@link #getWsFolderPath()} and {@link #getFileName()}.

Returns null if the project, filename or folder are invalid and the destination file cannot be determined.

The {@link IFile} is a resource. There might or might not be an actual real file.

        IProject project = getProject();
        String wsFolderPath = getWsFolderPath();
        String fileName = getFileName();
        if (project != null && wsFolderPath.length() > 0 && fileName.length() > 0) {
            IPath dest = new Path(wsFolderPath).append(fileName);
            IFile file = project.getFile(dest);
            return file;
        }
        return null;
    
public java.lang.StringgetFileName()
Returns the destination filename or an empty string.

        return mFileNameTextField == null ? "" : mFileNameTextField.getText();         //$NON-NLS-1$
    
public org.eclipse.core.resources.IProjectgetProject()
Returns the target project or null.

        return mProject;
    
public java.lang.StringgetRootElement()
Returns the selected root element string, if any.

return
The selected root element string or null.

        int index = mRootElementCombo.getSelectionIndex();
        if (index >= 0) {
            return mRootElementCombo.getItem(index);
        }
        return null;
    
public com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileCreationPage$TypeInfogetSelectedType()
Returns the {@link TypeInfo} for the currently selected type radio button. Returns null if no radio button is selected.

return
A {@link TypeInfo} or null.

        TypeInfo type = null;
        for (TypeInfo ti : sTypes) {
            if (ti.getWidget().getSelection()) {
                type = ti;
                break;
            }
        }
        return type;
    
public java.lang.StringgetWsFolderPath()
Returns the destination folder path relative to the project or an empty string.

        return mWsFolderPathTextField == null ? "" : mWsFolderPathTextField.getText(); //$NON-NLS-1$
    
private voidinitRootElementDescriptor(java.util.ArrayList roots, com.android.ide.eclipse.editors.descriptors.ElementDescriptor desc, java.util.HashSet visited)
Helper method to recursively insert all XML names for the given {@link ElementDescriptor} into the roots array list. Keeps track of visited nodes to avoid infinite recursion. Also avoids inserting the top {@link DocumentDescriptor} which is generally synthetic and not a valid root element.

        if (!(desc instanceof DocumentDescriptor)) {
            String xmlName = desc.getXmlName();
            if (xmlName != null && xmlName.length() > 0) {
                roots.add(xmlName);
            }
        }
        
        visited.add(desc);
        
        for (ElementDescriptor child : desc.getChildren()) {
            if (!visited.contains(child)) {
                initRootElementDescriptor(roots, child, visited);
            }
        }
    
private voidinitializeFromSelection(org.eclipse.jface.viewers.IStructuredSelection selection)
Called by {@link NewXmlFileWizard} to initialize the page with the selection received by the wizard -- typically the current user workbench selection.

Things we expect to find out from the selection:

  • The project name, valid if it's an android nature.
  • The current folder, valid if it's a folder under /res
  • An existing filename, in which case the user will be asked whether to override it.
    • param
      selection The selection when the wizard was initiated.

              if (selection == null) {
                  return;
              }
      
              // Find the best match in the element list. In case there are multiple selected elements
              // select the one that provides the most information and assign them a score,
              // e.g. project=1 + folder=2 + file=4.
              IProject targetProject = null;
              String targetWsFolderPath = null;
              String targetFileName = null;
              int targetScore = 0;
              for (Object element : selection.toList()) {
                  if (element instanceof IAdaptable) {
                      IResource res = (IResource) ((IAdaptable) element).getAdapter(IResource.class);
                      IProject project = res != null ? res.getProject() : null;
                      
                      // Is this an Android project?
                      try {
                          if (project == null || !project.hasNature(AndroidConstants.NATURE)) {
                              continue;
                          }
                      } catch (CoreException e) {
                          // checking the nature failed, ignore this resource
                          continue;
                      }
                      
                      int score = 1; // we have a valid project at least
      
                      IPath wsFolderPath = null;
                      String fileName = null;
                      if (res.getType() == IResource.FOLDER) {
                          wsFolderPath = res.getProjectRelativePath();                    
                      } else if (res.getType() == IResource.FILE) {
                          fileName = res.getName();
                          wsFolderPath = res.getParent().getProjectRelativePath();
                      }
                      
                      // Disregard this folder selection if it doesn't point to /res/something
                      if (wsFolderPath != null &&
                              wsFolderPath.segmentCount() > 1 &&
                              SdkConstants.FD_RESOURCES.equals(wsFolderPath.segment(0))) {
                          score += 2;
                      } else {
                          wsFolderPath = null;
                          fileName = null;
                      }
      
                      score += fileName != null ? 4 : 0;
                      
                      if (score > targetScore) {
                          targetScore = score;
                          targetProject = project;
                          targetWsFolderPath = wsFolderPath != null ? wsFolderPath.toString() : null;
                          targetFileName = fileName;
                      }
                  }
              }
              
              // Now set the UI accordingly
              if (targetScore > 0) {
                  mProject = targetProject;
                  mProjectTextField.setText(targetProject != null ? targetProject.getName() : ""); //$NON-NLS-1$
                  mFileNameTextField.setText(targetFileName != null ? targetFileName : ""); //$NON-NLS-1$
                  mWsFolderPathTextField.setText(targetWsFolderPath != null ? targetWsFolderPath : ""); //$NON-NLS-1$
              }
          
private voidinitializeRootValues()
Initialize the root values of the type infos based on the current framework values.

        for (TypeInfo type : sTypes) {
            // Clear all the roots for this type
            ArrayList<String> roots = type.getRoots();
            if (roots.size() > 0) {
                roots.clear();
            }
            
            // depending of the type of the seed, initialize the root in different ways
            Object rootSeed = type.getRootSeed();

            if (rootSeed instanceof String) {
                // The seed is a single string, Add it as-is.
                roots.add((String) rootSeed);
            } else if (rootSeed instanceof String[]) {
                // The seed is an array of strings. Add them as-is.
                for (String value : (String[]) rootSeed) {
                    roots.add(value);
                }
            } else if (rootSeed instanceof Integer && mProject != null) {
                // The seed is a descriptor reference defined in AndroidTargetData.DESCRIPTOR_*
                // In this case add all the children element descriptors defined, recursively,
                // and avoid infinite recursion by keeping track of what has already been added.

                // Note: if project is null, the root list will be empty since it has been
                // cleared above.
                
                // get the AndroidTargetData from the project
                IAndroidTarget target = null;
                AndroidTargetData data = null;

                target = Sdk.getCurrent().getTarget(mProject);
                if (target == null) {
                    // A project should have a target. The target can be missing if the project
                    // is an old project for which a target hasn't been affected or if the
                    // target no longer exists in this SDK. Simply log the error and dismiss.
                    
                    AdtPlugin.log(IStatus.INFO,
                            "NewXmlFile wizard: no platform target for project %s",  //$NON-NLS-1$
                            mProject.getName());
                    continue;
                } else {
                    data = Sdk.getCurrent().getTargetData(target);

                    if (data == null) {
                        // We should have both a target and its data.
                        // However if the wizard is invoked whilst the platform is still being
                        // loaded we can end up in a weird case where we have a target but it
                        // doesn't have any data yet.
                        // Lets log a warning and silently ignore this root.
                        
                        AdtPlugin.log(IStatus.INFO,
                              "NewXmlFile wizard: no data for target %s, project %s",  //$NON-NLS-1$
                              target.getName(), mProject.getName());
                        continue;
                    }
                }
                
                IDescriptorProvider provider = data.getDescriptorProvider((Integer)rootSeed);
                ElementDescriptor descriptor = provider.getDescriptor();
                if (descriptor != null) {
                    HashSet<ElementDescriptor> visited = new HashSet<ElementDescriptor>();
                    initRootElementDescriptor(roots, descriptor, visited);
                }

                // Sort alphabetically.
                Collections.sort(roots);
            }
        }
    
private voidinstallTargetChangeListener()

        mSdkTargetChangeListener = new ITargetChangeListener() {
            public void onProjectTargetChange(IProject changedProject) {
                // If this is the current project, force it to reload its data
                if (changedProject != null && changedProject == mProject) {
                    changeProject(mProject);
                }
            }

            public void onTargetsLoaded() {
                // Reload the current project, if any, in case its target has changed.
                if (mProject != null) {
                    changeProject(mProject);
                }
            }
        };
        
        AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener);
    
private org.eclipse.swt.layout.GridDatanewGridData(int horizSpan)
Helper method to create a new GridData with an horizontal span.

param
horizSpan The number of cells for the horizontal span.
return
A new GridData with the horizontal span.

        GridData gd = new GridData();
        gd.horizontalSpan = horizSpan;
        return gd;
    
private org.eclipse.swt.layout.GridDatanewGridData(int horizSpan, int style)
Helper method to create a new GridData with an horizontal span and a style.

param
horizSpan The number of cells for the horizontal span.
param
style The style, e.g. {@link GridData#FILL_HORIZONTAL}
return
A new GridData with the horizontal span and the style.

        GridData gd = new GridData(style);
        gd.horizontalSpan = horizSpan;
        return gd;
    
private voidonProjectBrowse()
Callback called when the user uses the "Browse Projects" button.

        IJavaProject p = mProjectChooserHelper.chooseJavaProject(mProjectTextField.getText());
        if (p != null) {
            changeProject(p.getProject());
            mProjectTextField.setText(mProject.getName());
        }
    
private voidonProjectFieldUpdated()
Callback called when the user edits the project text field.

        String project = mProjectTextField.getText();
        
        // Is this a valid project?
        IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null /*javaModel*/);
        IProject found = null;
        for (IJavaProject p : projects) {
            if (p.getProject().getName().equals(project)) {
                found = p.getProject();
                break;
            }
        }

        if (found != mProject) {
            changeProject(found);
        }
    
private voidonRadioTypeUpdated(org.eclipse.swt.widgets.Button typeWidget)
Callback called when one of the type radio button is changed.

param
typeWidget The type radio button that changed.

        // Do nothing if this is an internal modification or if the widget has been
        // de-selected.
        if (mInternalTypeUpdate || !typeWidget.getSelection()) {
            return;
        }

        // Find type info that has just been enabled.
        TypeInfo type = null;
        for (TypeInfo ti : sTypes) {
            if (ti.getWidget() == typeWidget) {
                type = ti;
                break;
            }
        }
        
        if (type == null) {
            return;
        }

        // update the combo
        
        updateRootCombo(type);

        // update the folder path

        String wsFolderPath = mWsFolderPathTextField.getText();
        String newPath = null;

        mConfigSelector.getConfiguration(mTempConfig);
        ResourceQualifier qual = mTempConfig.getInvalidQualifier();
        if (qual == null) {
            // The configuration is valid. Reformat the folder path using the canonical
            // value from the configuration.
            
            newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType());
        } else {
            // The configuration is invalid. We still update the path but this time
            // do it manually on the string.
            if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
                wsFolderPath.replaceFirst(
                        "^(" + RES_FOLDER_ABS +")[^-]*(.*)",         //$NON-NLS-1$ //$NON-NLS-2$
                        "\\1" + type.getResFolderName() + "\\2");   //$NON-NLS-1$ //$NON-NLS-2$
            } else {
                newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType());
            }
        }

        if (newPath != null && !newPath.equals(wsFolderPath)) {
            mInternalWsFolderPathUpdate = true;
            mWsFolderPathTextField.setText(newPath);
            mInternalWsFolderPathUpdate = false;
        }

        validatePage();
    
private voidonWsFolderPathUpdated()
Callback called when the Folder text field is changed, either programmatically or by the user.

        if (mInternalWsFolderPathUpdate) {
            return;
        }

        String wsFolderPath = mWsFolderPathTextField.getText();

        // This is a custom path, we need to sanitize it.
        // First it should start with "/res/". Then we need to make sure there are no
        // relative paths, things like "../" or "./" or even "//".
        wsFolderPath = wsFolderPath.replaceAll("/+\\.\\./+|/+\\./+|//+|\\\\+|^/+", "/");  //$NON-NLS-1$ //$NON-NLS-2$
        wsFolderPath = wsFolderPath.replaceAll("^\\.\\./+|^\\./+", "");                   //$NON-NLS-1$ //$NON-NLS-2$
        wsFolderPath = wsFolderPath.replaceAll("/+\\.\\.$|/+\\.$|/+$", "");               //$NON-NLS-1$ //$NON-NLS-2$

        ArrayList<TypeInfo> matches = new ArrayList<TypeInfo>();

        // We get "res/foo" from selections relative to the project when we want a "/res/foo" path.
        if (wsFolderPath.startsWith(RES_FOLDER_REL)) {
            wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length());
            
            mInternalWsFolderPathUpdate = true;
            mWsFolderPathTextField.setText(wsFolderPath);
            mInternalWsFolderPathUpdate = false;
        }

        if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
            wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
            
            int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR);
            if (pos >= 0) {
                wsFolderPath = wsFolderPath.substring(0, pos);
            }

            String[] folderSegments = wsFolderPath.split(FolderConfiguration.QUALIFIER_SEP);

            if (folderSegments.length > 0) {
                String folderName = folderSegments[0];

                // update config selector
                mInternalConfigSelectorUpdate = true;
                mConfigSelector.setConfiguration(folderSegments);
                mInternalConfigSelectorUpdate = false;

                boolean selected = false;
                for (TypeInfo type : sTypes) {
                    if (type.getResFolderName().equals(folderName)) {
                        matches.add(type);
                        selected |= type.getWidget().getSelection();
                    }
                }

                if (matches.size() == 1) {
                    // If there's only one match, select it if it's not already selected
                    if (!selected) {
                        selectType(matches.get(0));
                    }
                } else if (matches.size() > 1) {
                    // There are multiple type candidates for this folder. This can happen
                    // for /res/xml for example. Check to see if one of them is currently
                    // selected. If yes, leave the selection unchanged. If not, deselect all type.
                    if (!selected) {
                        selectType(null);
                    }
                } else {
                    // Nothing valid was selected.
                    selectType(null);
                }
            }
        }

        validatePage();
    
private intpadWithEmptyCells(org.eclipse.swt.widgets.Composite parent, int col)
Pads the parent with empty cells to match the number of columns of the parent grid.

param
parent A grid layout with NUM_COL columns
param
col The current number of columns used.
return
0, the new number of columns used, for convenience.

        for (; col < NUM_COL; ++col) {
            emptyCell(parent);
        }
        col = 0;
        return col;
    
private voidselectType(com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileCreationPage$TypeInfo type)
Helper method to select on of the type radio buttons.

param
type The TypeInfo matching the radio button to selected or null to deselect them all.

        if (type == null || !type.getWidget().getSelection()) {
            mInternalTypeUpdate = true;
            mCurrentTypeInfo = type;
            for (TypeInfo type2 : sTypes) {
                type2.getWidget().setSelection(type2 == type);
            }
            updateRootCombo(type);
            mInternalTypeUpdate = false;
        }
    
public voidsetInitialSelection(org.eclipse.jface.viewers.IStructuredSelection initialSelection)

        mInitialSelection = initialSelection;
    
private voidupdateRootCombo(com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileCreationPage$TypeInfo type)
Helper method that fills the values of the "root element" combo box based on the currently selected type radio button. Also disables the combo is there's only one choice. Always select the first root element for the given type.

param
type The currently selected {@link TypeInfo}. Cannot be null.

        // reset all the values in the combo
        mRootElementCombo.removeAll();

        if (type != null) {
            // get the list of roots. The list can be empty but not null.
            ArrayList<String> roots = type.getRoots();
            
            // enable the combo if there's more than one choice
            mRootElementCombo.setEnabled(roots != null && roots.size() > 1);
            
            for (String root : roots) {
                mRootElementCombo.add(root);
            }
            
            int index = 0; // default is to select the first one
            String defaultRoot = type.getDefaultRoot();
            if (defaultRoot != null) {
                index = roots.indexOf(defaultRoot);
            }
            mRootElementCombo.select(index < 0 ? 0 : index);
        }
    
private voidvalidatePage()
Validates the fields, displays errors and warnings. Enables the finish button if there are no errors.

        String error = null;
        String warning = null;

        // -- validate project
        if (getProject() == null) {
            error = "Please select an Android project.";
        }

        // -- validate filename
        if (error == null) {
            String fileName = getFileName();
            if (fileName == null || fileName.length() == 0) {
                error = "A destination file name is required.";
            } else if (!fileName.endsWith(AndroidConstants.DOT_XML)) {
                error = String.format("The filename must end with %1$s.", AndroidConstants.DOT_XML);
            }
        }

        // -- validate type
        if (error == null) {
            TypeInfo type = getSelectedType();

            if (type == null) {
                error = "One of the types must be selected (e.g. layout, values, etc.)";
            }
        }

        // -- validate type API level
        if (error == null) {
            IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
            int currentApiLevel = 1;
            if (target != null) {
                currentApiLevel = target.getApiVersionNumber();
            }

            TypeInfo type = getSelectedType();

            if (type.getTargetApiLevel() > currentApiLevel) {
                error = "The API level of the selected type (e.g. AppWidget, etc.) is not " +
                        "compatible with the API level of the project.";
            }
        }

        // -- validate folder configuration
        if (error == null) {
            ConfigurationState state = mConfigSelector.getState();
            if (state == ConfigurationState.INVALID_CONFIG) {
                ResourceQualifier qual = mConfigSelector.getInvalidQualifier();
                if (qual != null) {
                    error = String.format("The qualifier '%1$s' is invalid in the folder configuration.",
                            qual.getName());
                }
            } else if (state == ConfigurationState.REGION_WITHOUT_LANGUAGE) {
                error = "The Region qualifier requires the Language qualifier.";
            }
        }

        // -- validate generated path
        if (error == null) {
            String wsFolderPath = getWsFolderPath();
            if (!wsFolderPath.startsWith(RES_FOLDER_ABS)) {
                error = String.format("Target folder must start with %1$s.", RES_FOLDER_ABS);
            }
        }

        // -- validate destination file doesn't exist
        if (error == null) {
            IFile file = getDestinationFile();
            if (file != null && file.exists()) {
                warning = "The destination file already exists";
            }
        }

        // -- update UI & enable finish if there's no error
        setPageComplete(error == null);
        if (error != null) {
            setMessage(error, WizardPage.ERROR);
        } else if (warning != null) {
            setMessage(warning, WizardPage.WARNING);
        } else {
            setErrorMessage(null);
            setMessage(null);
        }