1 /* 2 * Copyright (C) 2007 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 17 /* 18 * References: 19 * org.eclipse.jdt.internal.ui.wizards.JavaProjectWizard 20 * org.eclipse.jdt.internal.ui.wizards.JavaProjectWizardFirstPage 21 */ 22 23 package com.android.ide.eclipse.adt.internal.wizards.newproject; 24 25 import com.android.ide.eclipse.adt.AdtPlugin; 26 import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper; 27 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper; 28 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 29 import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; 30 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreationPage.IMainInfo; 31 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreationPage.MainInfo; 32 import com.android.sdklib.IAndroidTarget; 33 import com.android.sdklib.SdkConstants; 34 import com.android.sdklib.xml.ManifestData; 35 import com.android.sdkuilib.internal.widgets.SdkTargetSelector; 36 37 import org.eclipse.core.filesystem.URIUtil; 38 import org.eclipse.core.resources.IProject; 39 import org.eclipse.core.resources.IResource; 40 import org.eclipse.core.resources.IWorkspace; 41 import org.eclipse.core.resources.ResourcesPlugin; 42 import org.eclipse.core.runtime.IPath; 43 import org.eclipse.core.runtime.IStatus; 44 import org.eclipse.core.runtime.Path; 45 import org.eclipse.core.runtime.Platform; 46 import org.eclipse.jdt.core.IJavaProject; 47 import org.eclipse.jdt.core.JavaConventions; 48 import org.eclipse.jface.wizard.WizardPage; 49 import org.eclipse.osgi.util.TextProcessor; 50 import org.eclipse.swt.SWT; 51 import org.eclipse.swt.custom.ScrolledComposite; 52 import org.eclipse.swt.events.ControlAdapter; 53 import org.eclipse.swt.events.ControlEvent; 54 import org.eclipse.swt.events.ModifyEvent; 55 import org.eclipse.swt.events.ModifyListener; 56 import org.eclipse.swt.events.SelectionAdapter; 57 import org.eclipse.swt.events.SelectionEvent; 58 import org.eclipse.swt.graphics.Rectangle; 59 import org.eclipse.swt.layout.GridData; 60 import org.eclipse.swt.layout.GridLayout; 61 import org.eclipse.swt.widgets.Button; 62 import org.eclipse.swt.widgets.Composite; 63 import org.eclipse.swt.widgets.Control; 64 import org.eclipse.swt.widgets.DirectoryDialog; 65 import org.eclipse.swt.widgets.Event; 66 import org.eclipse.swt.widgets.Group; 67 import org.eclipse.swt.widgets.Label; 68 import org.eclipse.swt.widgets.Listener; 69 import org.eclipse.swt.widgets.Text; 70 71 import java.io.File; 72 import java.net.URI; 73 import java.util.ArrayList; 74 import java.util.regex.Pattern; 75 76 /** 77 * NewAndroidProjectCreationPage is a project creation page that provides the 78 * following fields: 79 * <ul> 80 * <li> Project name 81 * <li> SDK Target 82 * <li> Application name 83 * <li> Package name 84 * <li> Activity name 85 * </ul> 86 * Note: this class is public so that it can be accessed from unit tests. 87 * It is however an internal class. Its API may change without notice. 88 * It should semantically be considered as a private final class. 89 * Do not derive from this class. 90 */ 91 public class NewTestProjectCreationPage extends WizardPage { 92 93 // constants 94 static final String TEST_PAGE_NAME = "newAndroidTestProjectPage"; //$NON-NLS-1$ 95 96 /** Initial value for all name fields (project, activity, application, package). Used 97 * whenever a value is requested before controls are created. */ 98 private static final String INITIAL_NAME = ""; //$NON-NLS-1$ 99 /** Initial value for the Use Default Location check box. */ 100 private static final boolean INITIAL_USE_DEFAULT_LOCATION = true; 101 /** Initial value for the Create Test Project check box. */ 102 private static final boolean INITIAL_CREATE_TEST_PROJECT = false; 103 104 105 /** Pattern for characters accepted in a project name. Since this will be used as a 106 * directory name, we're being a bit conservative on purpose. It cannot start with a space. */ 107 private static final Pattern sProjectNamePattern = Pattern.compile("^[\\w][\\w. -]*$"); //$NON-NLS-1$ 108 /** Last user-browsed location, static so that it be remembered for the whole session */ 109 private static String sCustomLocationOsPath = ""; //$NON-NLS-1$ 110 111 private final int MSG_NONE = 0; 112 private final int MSG_WARNING = 1; 113 private final int MSG_ERROR = 2; 114 115 /** Structure with the externally visible information from this Test Project page. */ 116 private final TestInfo mInfo = new TestInfo(); 117 /** Structure with the externally visible information from the Main Project page. 118 * This is null if there's no such page, meaning the test project page is standalone. */ 119 private IMainInfo mMainInfo; 120 121 // widgets 122 private Text mProjectNameField; 123 private Text mPackageNameField; 124 private Text mApplicationNameField; 125 private Button mUseDefaultLocation; 126 private Label mLocationLabel; 127 private Text mLocationPathField; 128 private Button mBrowseButton; 129 private Text mMinSdkVersionField; 130 private SdkTargetSelector mSdkTargetSelector; 131 private ITargetChangeListener mSdkTargetChangeListener; 132 private Button mCreateTestProjectField; 133 private Text mTestedProjectNameField; 134 private Button mProjectBrowseButton; 135 private ProjectChooserHelper mProjectChooserHelper; 136 private Button mTestSelfProjectRadio; 137 private Button mTestExistingProjectRadio; 138 139 /** A list of composites that are disabled when the "Create Test Project" toggle is off. */ 140 private ArrayList<Composite> mToggleComposites = new ArrayList<Composite>(); 141 142 private boolean mInternalProjectNameUpdate; 143 private boolean mInternalLocationPathUpdate; 144 private boolean mInternalPackageNameUpdate; 145 private boolean mInternalApplicationNameUpdate; 146 private boolean mInternalMinSdkVersionUpdate; 147 private boolean mInternalSdkTargetUpdate; 148 private IProject mExistingTestedProject; 149 private boolean mProjectNameModifiedByUser; 150 private boolean mApplicationNameModifiedByUser; 151 private boolean mPackageNameModifiedByUser; 152 private boolean mMinSdkVersionModifiedByUser; 153 private boolean mSdkTargetModifiedByUser; 154 155 private Label mTestTargetPackageLabel; 156 157 private String mLastExistingPackageName; 158 159 160 /** 161 * Creates a new project creation wizard page. 162 */ 163 public NewTestProjectCreationPage() { 164 super(TEST_PAGE_NAME); 165 setPageComplete(false); 166 setTitle("New Android Test Project"); 167 setDescription("Creates a new Android Test Project resource."); 168 } 169 170 // --- Getters used by NewProjectWizard --- 171 172 /** 173 * Structure that collects all externally visible information from this page. 174 * This is used by the calling wizard to actually do the work or by other pages. 175 */ 176 public class TestInfo { 177 178 /** Returns true if a new Test Project should be created. */ 179 public boolean getCreateTestProject() { 180 return mCreateTestProjectField == null ? true : mCreateTestProjectField.getSelection(); 181 } 182 183 /** 184 * Returns the current project location path as entered by the user, or its 185 * anticipated initial value. Note that if the default has been returned the 186 * path in a project description used to create a project should not be set. 187 * 188 * @return the project location path or its anticipated initial value. 189 */ 190 public IPath getLocationPath() { 191 return new Path(getProjectLocation()); 192 } 193 194 /** Returns the value of the project name field with leading and trailing spaces removed. */ 195 public String getProjectName() { 196 return mProjectNameField == null ? INITIAL_NAME : mProjectNameField.getText().trim(); 197 } 198 199 /** Returns the value of the package name field with spaces trimmed. */ 200 public String getPackageName() { 201 return mPackageNameField == null ? INITIAL_NAME : mPackageNameField.getText().trim(); 202 } 203 204 /** Returns the value of the test target package name field with spaces trimmed. */ 205 public String getTargetPackageName() { 206 return mTestTargetPackageLabel == null ? INITIAL_NAME 207 : mTestTargetPackageLabel.getText().trim(); 208 } 209 210 /** Returns the value of the min sdk version field with spaces trimmed. */ 211 public String getMinSdkVersion() { 212 return mMinSdkVersionField == null ? "" : mMinSdkVersionField.getText().trim(); //$NON-NLS-1$ 213 } 214 215 /** Returns the value of the application name field with spaces trimmed. */ 216 public String getApplicationName() { 217 // Return the name of the activity as default application name. 218 return mApplicationNameField == null ? "" : mApplicationNameField.getText().trim(); //$NON-NLS-1$ 219 } 220 221 /** Returns the value of the Use Default Location field. */ 222 public boolean useDefaultLocation() { 223 return mUseDefaultLocation == null ? INITIAL_USE_DEFAULT_LOCATION 224 : mUseDefaultLocation.getSelection(); 225 } 226 227 /** Returns the the default "src" constant. */ 228 public String getSourceFolder() { 229 return SdkConstants.FD_SOURCES; 230 } 231 232 /** Returns the current sdk target or null if none has been selected yet. */ 233 public IAndroidTarget getSdkTarget() { 234 return mSdkTargetSelector == null ? null : mSdkTargetSelector.getSelected(); 235 } 236 237 public boolean isTestingSelf() { 238 return mMainInfo == null && 239 (mTestSelfProjectRadio == null ? false : mTestSelfProjectRadio.getSelection()); 240 } 241 242 public boolean isTestingMain() { 243 return mMainInfo != null; 244 } 245 246 public boolean isTestingExisting() { 247 return mMainInfo == null && 248 (mTestExistingProjectRadio == null ? false 249 : mTestExistingProjectRadio.getSelection()); 250 } 251 252 public IProject getExistingTestedProject() { 253 return mExistingTestedProject; 254 } 255 } 256 257 /** 258 * Returns a {@link TestInfo} structure that collects all externally visible information 259 * from this page. This is used by the calling wizard to actually do the work or by other pages. 260 */ 261 public TestInfo getTestInfo() { 262 return mInfo; 263 } 264 265 /** 266 * Grabs the {@link MainInfo} structure with visible parameters from the main project page. 267 * This may be null. 268 */ 269 public void setMainInfo(IMainInfo mainInfo) { 270 mMainInfo = mainInfo; 271 } 272 273 // --- UI creation --- 274 275 /** 276 * Creates the top level control for this dialog page under the given parent 277 * composite. 278 * 279 * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) 280 */ 281 public void createControl(Composite parent) { 282 final ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL); 283 scrolledComposite.setFont(parent.getFont()); 284 scrolledComposite.setExpandHorizontal(true); 285 scrolledComposite.setExpandVertical(true); 286 initializeDialogUnits(parent); 287 288 final Composite composite = new Composite(scrolledComposite, SWT.NULL); 289 composite.setFont(parent.getFont()); 290 scrolledComposite.setContent(composite); 291 292 composite.setLayout(new GridLayout()); 293 composite.setLayoutData(new GridData(GridData.FILL_BOTH)); 294 295 createToggleTestProject(composite); 296 createTestProjectGroup(composite); 297 createLocationGroup(composite); 298 createTestTargetGroup(composite); 299 createTargetGroup(composite); 300 createPropertiesGroup(composite); 301 302 // Update state the first time 303 enableLocationWidgets(); 304 305 scrolledComposite.addControlListener(new ControlAdapter() { 306 @Override 307 public void controlResized(ControlEvent e) { 308 Rectangle r = scrolledComposite.getClientArea(); 309 scrolledComposite.setMinSize(composite.computeSize(r.width, SWT.DEFAULT)); 310 } 311 }); 312 313 // Show description the first time 314 setErrorMessage(null); 315 setMessage(null); 316 setControl(scrolledComposite); 317 318 // Validate. This will complain about the first empty field. 319 validatePageComplete(); 320 } 321 322 /** 323 * Overrides @DialogPage.setVisible(boolean) to put the focus in the project name when 324 * the dialog is made visible and to also update the enabled/disabled state of some 325 * controls (doing so in createControl doesn't always change their state somehow.) 326 */ 327 @Override 328 public void setVisible(boolean visible) { 329 super.setVisible(visible); 330 if (visible) { 331 mProjectNameField.setFocus(); 332 validatePageComplete(); 333 onCreateTestProjectToggle(); 334 onExistingProjectChanged(); 335 } 336 } 337 338 @Override 339 public void dispose() { 340 341 if (mSdkTargetChangeListener != null) { 342 AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener); 343 mSdkTargetChangeListener = null; 344 } 345 346 super.dispose(); 347 } 348 349 /** 350 * Creates the "create test project" checkbox but only if there's a main page in the wizard. 351 * 352 * @param parent the parent composite 353 */ 354 private final void createToggleTestProject(Composite parent) { 355 356 if (mMainInfo != null) { 357 mCreateTestProjectField = new Button(parent, SWT.CHECK); 358 mCreateTestProjectField.setText("Create a Test Project"); 359 mCreateTestProjectField.setToolTipText("Select this if you also want to create a Test Project."); 360 mCreateTestProjectField.setSelection(INITIAL_CREATE_TEST_PROJECT); 361 mCreateTestProjectField.addSelectionListener(new SelectionAdapter() { 362 @Override 363 public void widgetSelected(SelectionEvent e) { 364 onCreateTestProjectToggle(); 365 } 366 }); 367 } 368 } 369 370 /** 371 * Creates the group for the project name: 372 * [label: "Project Name"] [text field] 373 * 374 * @param parent the parent composite 375 */ 376 private final void createTestProjectGroup(Composite parent) { 377 Composite group = new Composite(parent, SWT.NONE); 378 GridLayout layout = new GridLayout(); 379 layout.numColumns = 2; 380 group.setLayout(layout); 381 group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 382 383 mToggleComposites.add(group); 384 385 // --- test project name --- 386 387 // new project label 388 String tooltip = "Name of the Eclipse test project to create. It cannot be empty."; 389 Label label = new Label(group, SWT.NONE); 390 label.setText("Test Project Name:"); 391 label.setFont(parent.getFont()); 392 label.setToolTipText(tooltip); 393 394 // new project name entry field 395 mProjectNameField = new Text(group, SWT.BORDER); 396 GridData data = new GridData(GridData.FILL_HORIZONTAL); 397 mProjectNameField.setToolTipText(tooltip); 398 mProjectNameField.setLayoutData(data); 399 mProjectNameField.setFont(parent.getFont()); 400 mProjectNameField.addListener(SWT.Modify, new Listener() { 401 public void handleEvent(Event event) { 402 if (!mInternalProjectNameUpdate) { 403 mProjectNameModifiedByUser = true; 404 } 405 updateLocationPathField(null); 406 } 407 }); 408 } 409 410 private final void createLocationGroup(Composite parent) { 411 412 // --- project location --- 413 414 Group group = new Group(parent, SWT.SHADOW_ETCHED_IN); 415 group.setLayout(new GridLayout(3, /* num columns */ 416 false /* columns of not equal size */)); 417 group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 418 group.setFont(parent.getFont()); 419 group.setText("Content"); 420 421 mToggleComposites.add(group); 422 423 mUseDefaultLocation = new Button(group, SWT.CHECK); 424 GridData gd = new GridData(GridData.FILL_HORIZONTAL); 425 gd.horizontalSpan = 3; 426 mUseDefaultLocation.setLayoutData(gd); 427 mUseDefaultLocation.setText("Use default location"); 428 mUseDefaultLocation.setSelection(INITIAL_USE_DEFAULT_LOCATION); 429 430 mUseDefaultLocation.addSelectionListener(new SelectionAdapter() { 431 @Override 432 public void widgetSelected(SelectionEvent e) { 433 super.widgetSelected(e); 434 enableLocationWidgets(); 435 validatePageComplete(); 436 } 437 }); 438 439 440 mLocationLabel = new Label(group, SWT.NONE); 441 mLocationLabel.setText("Location:"); 442 443 mLocationPathField = new Text(group, SWT.BORDER); 444 GridData data = new GridData(GridData.FILL, /* horizontal alignment */ 445 GridData.BEGINNING, /* vertical alignment */ 446 true, /* grabExcessHorizontalSpace */ 447 false, /* grabExcessVerticalSpace */ 448 1, /* horizontalSpan */ 449 1); /* verticalSpan */ 450 mLocationPathField.setLayoutData(data); 451 mLocationPathField.setFont(parent.getFont()); 452 mLocationPathField.addListener(SWT.Modify, new Listener() { 453 public void handleEvent(Event event) { 454 onLocationPathFieldModified(); 455 } 456 }); 457 458 mBrowseButton = new Button(group, SWT.PUSH); 459 mBrowseButton.setText("Browse..."); 460 setButtonLayoutData(mBrowseButton); 461 mBrowseButton.addSelectionListener(new SelectionAdapter() { 462 @Override 463 public void widgetSelected(SelectionEvent e) { 464 onOpenDirectoryBrowser(); 465 } 466 }); 467 } 468 469 /** 470 * Creates the group for Test Target options. 471 * 472 * There are two different modes here: 473 * <ul> 474 * <li>When mMainInfo exists, this is part of a new Android Project. In which case 475 * the new test is tied to the soon-to-be main project and there is actually no choice. 476 * <li>When mMainInfo does not exist, this is a standalone new test project. In this case 477 * we offer 2 options for the test target: self test or against an existing Android project. 478 * </ul> 479 * 480 * @param parent the parent composite 481 */ 482 private final void createTestTargetGroup(Composite parent) { 483 484 Group group = new Group(parent, SWT.SHADOW_ETCHED_IN); 485 GridLayout layout = new GridLayout(); 486 layout.numColumns = 3; 487 group.setLayout(layout); 488 group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 489 group.setFont(parent.getFont()); 490 group.setText("Test Target"); 491 492 mToggleComposites.add(group); 493 494 if (mMainInfo == null) { 495 // Standalone mode: choose between self-test and existing-project test 496 497 Label label = new Label(group, SWT.NONE); 498 label.setText("Select the project to test:"); 499 GridData gd = new GridData(GridData.FILL_HORIZONTAL); 500 gd.horizontalSpan = 3; 501 label.setLayoutData(gd); 502 503 mTestSelfProjectRadio = new Button(group, SWT.RADIO); 504 mTestSelfProjectRadio.setText("This project"); 505 mTestSelfProjectRadio.setSelection(false); 506 gd = new GridData(GridData.FILL_HORIZONTAL); 507 gd.horizontalSpan = 3; 508 mTestSelfProjectRadio.setLayoutData(gd); 509 510 mTestExistingProjectRadio = new Button(group, SWT.RADIO); 511 mTestExistingProjectRadio.setText("An existing Android project"); 512 mTestExistingProjectRadio.setSelection(mMainInfo == null); 513 mTestExistingProjectRadio.addSelectionListener(new SelectionAdapter() { 514 @Override 515 public void widgetSelected(SelectionEvent e) { 516 onExistingProjectChanged(); 517 } 518 }); 519 520 String tooltip = "The existing Android Project that is being tested."; 521 522 mTestedProjectNameField = new Text(group, SWT.BORDER); 523 mTestedProjectNameField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 524 mTestedProjectNameField.setToolTipText(tooltip); 525 mTestedProjectNameField.addModifyListener(new ModifyListener() { 526 public void modifyText(ModifyEvent e) { 527 onProjectFieldUpdated(); 528 } 529 }); 530 531 mProjectBrowseButton = new Button(group, SWT.NONE); 532 mProjectBrowseButton.setText("Browse..."); 533 mProjectBrowseButton.setToolTipText("Allows you to select the Android project to test."); 534 mProjectBrowseButton.addSelectionListener(new SelectionAdapter() { 535 @Override 536 public void widgetSelected(SelectionEvent e) { 537 onProjectBrowse(); 538 } 539 }); 540 541 mProjectChooserHelper = new ProjectChooserHelper(parent.getShell(), null /*filter*/); 542 } else { 543 // Part of NPW mode: no selection. 544 545 } 546 547 // package label line 548 549 Label label = new Label(group, SWT.NONE); 550 label.setText("Test Target Package:"); 551 mTestTargetPackageLabel = new Label(group, SWT.NONE); 552 GridData gd = new GridData(GridData.FILL_HORIZONTAL); 553 gd.horizontalSpan = 2; 554 mTestTargetPackageLabel.setLayoutData(gd); 555 } 556 557 /** 558 * Creates the target group. 559 * It only contains an SdkTargetSelector. 560 */ 561 private void createTargetGroup(Composite parent) { 562 Group group = new Group(parent, SWT.SHADOW_ETCHED_IN); 563 // Layout has 1 column 564 group.setLayout(new GridLayout()); 565 group.setLayoutData(new GridData(GridData.FILL_BOTH)); 566 group.setFont(parent.getFont()); 567 group.setText("Build Target"); 568 569 mToggleComposites.add(group); 570 571 // The selector is created without targets. They are added below in the change listener. 572 mSdkTargetSelector = new SdkTargetSelector(group, null); 573 574 mSdkTargetChangeListener = new ITargetChangeListener() { 575 public void onSdkLoaded() { 576 // Update the sdk target selector with the new targets 577 578 // get the targets from the sdk 579 IAndroidTarget[] targets = null; 580 if (Sdk.getCurrent() != null) { 581 targets = Sdk.getCurrent().getTargets(); 582 } 583 mSdkTargetSelector.setTargets(targets); 584 585 // If there's only one target, select it 586 if (targets != null && targets.length == 1) { 587 mSdkTargetSelector.setSelection(targets[0]); 588 } 589 } 590 591 public void onProjectTargetChange(IProject changedProject) { 592 // Ignore 593 } 594 595 public void onTargetLoaded(IAndroidTarget target) { 596 // Ignore 597 } 598 }; 599 600 AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener); 601 602 // Invoke it once to initialize the targets 603 mSdkTargetChangeListener.onSdkLoaded(); 604 605 mSdkTargetSelector.setSelectionListener(new SelectionAdapter() { 606 @Override 607 public void widgetSelected(SelectionEvent e) { 608 onSdkTargetModified(); 609 updateLocationPathField(null); 610 validatePageComplete(); 611 } 612 }); 613 } 614 615 /** 616 * Creates the group for the project properties: 617 * - Package name [text field] 618 * - Activity name [text field] 619 * - Application name [text field] 620 * 621 * @param parent the parent composite 622 */ 623 private final void createPropertiesGroup(Composite parent) { 624 // package specification group 625 Group group = new Group(parent, SWT.SHADOW_ETCHED_IN); 626 GridLayout layout = new GridLayout(); 627 layout.numColumns = 2; 628 group.setLayout(layout); 629 group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 630 group.setFont(parent.getFont()); 631 group.setText("Properties"); 632 633 mToggleComposites.add(group); 634 635 // new application label 636 Label label = new Label(group, SWT.NONE); 637 label.setText("Application name:"); 638 label.setFont(parent.getFont()); 639 label.setToolTipText("Name of the Application. This is a free string. It can be empty."); 640 641 // new application name entry field 642 mApplicationNameField = new Text(group, SWT.BORDER); 643 GridData data = new GridData(GridData.FILL_HORIZONTAL); 644 mApplicationNameField.setToolTipText("Name of the Application. This is a free string. It can be empty."); 645 mApplicationNameField.setLayoutData(data); 646 mApplicationNameField.setFont(parent.getFont()); 647 mApplicationNameField.addListener(SWT.Modify, new Listener() { 648 public void handleEvent(Event event) { 649 if (!mInternalApplicationNameUpdate) { 650 mApplicationNameModifiedByUser = true; 651 } 652 } 653 }); 654 655 // new package label 656 label = new Label(group, SWT.NONE); 657 label.setText("Package name:"); 658 label.setFont(parent.getFont()); 659 label.setToolTipText("Namespace of the Package to create. This must be a Java namespace with at least two components."); 660 661 // new package name entry field 662 mPackageNameField = new Text(group, SWT.BORDER); 663 data = new GridData(GridData.FILL_HORIZONTAL); 664 mPackageNameField.setToolTipText("Namespace of the Package to create. This must be a Java namespace with at least two components."); 665 mPackageNameField.setLayoutData(data); 666 mPackageNameField.setFont(parent.getFont()); 667 mPackageNameField.addListener(SWT.Modify, new Listener() { 668 public void handleEvent(Event event) { 669 if (!mInternalPackageNameUpdate) { 670 mPackageNameModifiedByUser = true; 671 } 672 onPackageNameFieldModified(); 673 } 674 }); 675 676 // min sdk version label 677 label = new Label(group, SWT.NONE); 678 label.setText("Min SDK Version:"); 679 label.setFont(parent.getFont()); 680 label.setToolTipText("The minimum SDK version number that the application requires. Must be an integer > 0. It can be empty."); 681 682 // min sdk version entry field 683 mMinSdkVersionField = new Text(group, SWT.BORDER); 684 data = new GridData(GridData.FILL_HORIZONTAL); 685 label.setToolTipText("The minimum SDK version number that the application requires. Must be an integer > 0. It can be empty."); 686 mMinSdkVersionField.setLayoutData(data); 687 mMinSdkVersionField.setFont(parent.getFont()); 688 mMinSdkVersionField.addListener(SWT.Modify, new Listener() { 689 public void handleEvent(Event event) { 690 onMinSdkVersionFieldModified(); 691 validatePageComplete(); 692 } 693 }); 694 } 695 696 697 //--- Internal getters & setters ------------------ 698 699 /** Returns the location path field value with spaces trimmed. */ 700 private String getLocationPathFieldValue() { 701 return mLocationPathField == null ? "" : mLocationPathField.getText().trim(); //$NON-NLS-1$ 702 } 703 704 /** Returns the current project location, depending on the Use Default Location check box. */ 705 private String getProjectLocation() { 706 if (mInfo.useDefaultLocation()) { 707 return Platform.getLocation().toString(); 708 } else { 709 return getLocationPathFieldValue(); 710 } 711 } 712 713 /** 714 * Creates a project resource handle for the current project name field 715 * value. 716 * <p> 717 * This method does not create the project resource; this is the 718 * responsibility of <code>IProject::create</code> invoked by the new 719 * project resource wizard. 720 * </p> 721 * 722 * @return the new project resource handle 723 */ 724 private IProject getProjectHandle() { 725 return ResourcesPlugin.getWorkspace().getRoot().getProject(mInfo.getProjectName()); 726 } 727 728 // --- UI Callbacks ---- 729 730 /** 731 * Callback invoked when the user toggles the "Test target: Existing Android Project" 732 * checkbox. It enables or disable the UI to select an existing project. 733 */ 734 private void onExistingProjectChanged() { 735 if (mInfo.isTestingExisting()) { 736 boolean enabled = mTestExistingProjectRadio.getSelection(); 737 mTestedProjectNameField.setEnabled(enabled); 738 mProjectBrowseButton.setEnabled(enabled); 739 setExistingProject(mInfo.getExistingTestedProject()); 740 validatePageComplete(); 741 } 742 } 743 744 /** 745 * Tries to load the defaults from the main page if possible. 746 */ 747 private void useMainProjectInformation() { 748 if (mInfo.isTestingMain() && mMainInfo != null) { 749 750 String projName = String.format("%1$sTest", mMainInfo.getProjectName()); 751 String appName = String.format("%1$sTest", mMainInfo.getApplicationName()); 752 753 String packageName = mMainInfo.getPackageName(); 754 if (packageName == null) { 755 packageName = ""; //$NON-NLS-1$ 756 } 757 758 updateTestTargetPackageField(packageName); 759 760 if (!mProjectNameModifiedByUser) { 761 mInternalProjectNameUpdate = true; 762 mProjectNameField.setText(projName); //$NON-NLS-1$ 763 mInternalProjectNameUpdate = false; 764 } 765 766 if (!mApplicationNameModifiedByUser) { 767 mInternalApplicationNameUpdate = true; 768 mApplicationNameField.setText(appName); 769 mInternalApplicationNameUpdate = false; 770 } 771 772 if (!mPackageNameModifiedByUser) { 773 mInternalPackageNameUpdate = true; 774 packageName += ".test"; //$NON-NLS-1$ 775 mPackageNameField.setText(packageName); 776 mInternalPackageNameUpdate = false; 777 } 778 779 if (!mSdkTargetModifiedByUser) { 780 mInternalSdkTargetUpdate = true; 781 mSdkTargetSelector.setSelection(mMainInfo.getSdkTarget()); 782 mInternalSdkTargetUpdate = false; 783 } 784 785 if (!mMinSdkVersionModifiedByUser) { 786 mInternalMinSdkVersionUpdate = true; 787 mMinSdkVersionField.setText(mMainInfo.getMinSdkVersion()); 788 mInternalMinSdkVersionUpdate = false; 789 } 790 } 791 } 792 793 /** 794 * Callback invoked when the user edits the project text field. 795 */ 796 private void onProjectFieldUpdated() { 797 String project = mTestedProjectNameField.getText(); 798 799 // Is this a valid project? 800 IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null /*javaModel*/); 801 for (IJavaProject p : projects) { 802 if (p.getProject().getName().equals(project)) { 803 setExistingProject(p.getProject()); 804 return; 805 } 806 } 807 } 808 809 /** 810 * Callback called when the user uses the "Browse Projects" button. 811 */ 812 private void onProjectBrowse() { 813 IJavaProject p = mProjectChooserHelper.chooseJavaProject(mTestedProjectNameField.getText(), 814 null /*message*/); 815 if (p != null) { 816 setExistingProject(p.getProject()); 817 mTestedProjectNameField.setText(mExistingTestedProject.getName()); 818 } 819 } 820 821 private void setExistingProject(IProject project) { 822 mExistingTestedProject = project; 823 824 // Try to update the application, package, sdk target and minSdkVersion accordingly 825 if (project != null && 826 (!mApplicationNameModifiedByUser || 827 !mPackageNameModifiedByUser || 828 !mSdkTargetModifiedByUser || 829 !mMinSdkVersionModifiedByUser)) { 830 831 ManifestData manifestData = AndroidManifestHelper.parseForData(project); 832 if (manifestData != null) { 833 String appName = String.format("%1$sTest", project.getName()); 834 String packageName = manifestData.getPackage(); 835 String minSdkVersion = manifestData.getMinSdkVersionString(); 836 IAndroidTarget sdkTarget = null; 837 if (Sdk.getCurrent() != null) { 838 sdkTarget = Sdk.getCurrent().getTarget(project); 839 } 840 841 if (packageName == null) { 842 packageName = ""; //$NON-NLS-1$ 843 } 844 mLastExistingPackageName = packageName; 845 846 if (!mProjectNameModifiedByUser) { 847 mInternalProjectNameUpdate = true; 848 mProjectNameField.setText(appName); 849 mInternalProjectNameUpdate = false; 850 } 851 852 if (!mApplicationNameModifiedByUser) { 853 mInternalApplicationNameUpdate = true; 854 mApplicationNameField.setText(appName); 855 mInternalApplicationNameUpdate = false; 856 } 857 858 if (!mPackageNameModifiedByUser) { 859 mInternalPackageNameUpdate = true; 860 packageName += ".test"; //$NON-NLS-1$ 861 mPackageNameField.setText(packageName); //$NON-NLS-1$ 862 mInternalPackageNameUpdate = false; 863 } 864 865 if (!mSdkTargetModifiedByUser && sdkTarget != null) { 866 mInternalSdkTargetUpdate = true; 867 mSdkTargetSelector.setSelection(sdkTarget); 868 mInternalSdkTargetUpdate = false; 869 } 870 871 if (!mMinSdkVersionModifiedByUser) { 872 mInternalMinSdkVersionUpdate = true; 873 if (minSdkVersion != null) { 874 mMinSdkVersionField.setText(minSdkVersion); 875 } 876 if (sdkTarget == null) { 877 updateSdkSelectorToMatchMinSdkVersion(); 878 } 879 mInternalMinSdkVersionUpdate = false; 880 } 881 } 882 } 883 884 updateTestTargetPackageField(mLastExistingPackageName); 885 validatePageComplete(); 886 } 887 888 /** 889 * Display a directory browser and update the location path field with the selected path 890 */ 891 private void onOpenDirectoryBrowser() { 892 893 String existing_dir = getLocationPathFieldValue(); 894 895 // Disable the path if it doesn't exist 896 if (existing_dir.length() == 0) { 897 existing_dir = null; 898 } else { 899 File f = new File(existing_dir); 900 if (!f.exists()) { 901 existing_dir = null; 902 } 903 } 904 905 DirectoryDialog dd = new DirectoryDialog(mLocationPathField.getShell()); 906 dd.setMessage("Browse for folder"); 907 dd.setFilterPath(existing_dir); 908 String abs_dir = dd.open(); 909 910 if (abs_dir != null) { 911 updateLocationPathField(abs_dir); 912 validatePageComplete(); 913 } 914 } 915 916 /** 917 * Callback when the "create test project" checkbox is changed. 918 * It enables or disables all UI groups accordingly. 919 */ 920 private void onCreateTestProjectToggle() { 921 boolean enabled = mInfo.getCreateTestProject(); 922 for (Composite c : mToggleComposites) { 923 enableControl(c, enabled); 924 } 925 mSdkTargetSelector.setEnabled(enabled); 926 927 if (enabled) { 928 useMainProjectInformation(); 929 } 930 validatePageComplete(); 931 } 932 933 /** Enables or disables controls; recursive for composite controls. */ 934 private void enableControl(Control c, boolean enabled) { 935 c.setEnabled(enabled); 936 if (c instanceof Composite) 937 for (Control c2 : ((Composite) c).getChildren()) { 938 enableControl(c2, enabled); 939 } 940 } 941 942 /** 943 * Enables or disable the location widgets depending on the user selection: 944 * the location path is enabled when using the "existing source" mode (i.e. not new project) 945 * or in new project mode with the "use default location" turned off. 946 */ 947 private void enableLocationWidgets() { 948 boolean use_default = mInfo.useDefaultLocation(); 949 boolean location_enabled = !use_default; 950 951 mLocationLabel.setEnabled(location_enabled); 952 mLocationPathField.setEnabled(location_enabled); 953 mBrowseButton.setEnabled(location_enabled); 954 955 updateLocationPathField(null); 956 } 957 958 /** 959 * Updates the location directory path field. 960 * <br/> 961 * When custom user selection is enabled, use the abs_dir argument if not null and also 962 * save it internally. If abs_dir is null, restore the last saved abs_dir. This allows the 963 * user selection to be remembered when the user switches from default to custom. 964 * <br/> 965 * When custom user selection is disabled, use the workspace default location with the 966 * current project name. This does not change the internally cached abs_dir. 967 * 968 * @param abs_dir A new absolute directory path or null to use the default. 969 */ 970 private void updateLocationPathField(String abs_dir) { 971 boolean use_default = mInfo.useDefaultLocation(); 972 boolean custom_location = !use_default; 973 974 if (!mInternalLocationPathUpdate) { 975 mInternalLocationPathUpdate = true; 976 if (custom_location) { 977 if (abs_dir != null) { 978 // We get here if the user selected a directory with the "Browse" button. 979 sCustomLocationOsPath = TextProcessor.process(abs_dir); 980 } 981 if (!mLocationPathField.getText().equals(sCustomLocationOsPath)) { 982 mLocationPathField.setText(sCustomLocationOsPath); 983 } 984 } else { 985 String value = Platform.getLocation().append(mInfo.getProjectName()).toString(); 986 value = TextProcessor.process(value); 987 if (!mLocationPathField.getText().equals(value)) { 988 mLocationPathField.setText(value); 989 } 990 } 991 validatePageComplete(); 992 mInternalLocationPathUpdate = false; 993 } 994 } 995 996 /** 997 * The location path field is either modified internally (from updateLocationPathField) 998 * or manually by the user when the custom_location mode is not set. 999 * 1000 * Ignore the internal modification. When modified by the user, memorize the choice and 1001 * validate the page. 1002 */ 1003 private void onLocationPathFieldModified() { 1004 if (!mInternalLocationPathUpdate) { 1005 // When the updates doesn't come from updateLocationPathField, it must be the user 1006 // editing the field manually, in which case we want to save the value internally 1007 String newPath = getLocationPathFieldValue(); 1008 sCustomLocationOsPath = newPath; 1009 validatePageComplete(); 1010 } 1011 } 1012 1013 /** 1014 * The package name field is either modified internally (from extractNamesFromAndroidManifest) 1015 * or manually by the user when the custom_location mode is not set. 1016 * 1017 * Ignore the internal modification. When modified by the user, memorize the choice and 1018 * validate the page. 1019 */ 1020 private void onPackageNameFieldModified() { 1021 updateTestTargetPackageField(null); 1022 validatePageComplete(); 1023 } 1024 1025 /** 1026 * Changes the {@link #mTestTargetPackageLabel} field. 1027 * 1028 * When using the "self-test" option, the packageName argument is ignored and the 1029 * current value from the project package is used. 1030 * 1031 * Otherwise the packageName is used if it is not null. 1032 */ 1033 private void updateTestTargetPackageField(String packageName) { 1034 if (mInfo.isTestingSelf()) { 1035 mTestTargetPackageLabel.setText(mInfo.getPackageName()); 1036 1037 } else if (packageName != null) { 1038 mTestTargetPackageLabel.setText(packageName); 1039 } 1040 } 1041 1042 /** 1043 * Called when the min sdk version field has been modified. 1044 * 1045 * Ignore the internal modifications. When modified by the user, try to match 1046 * a target with the same API level. 1047 */ 1048 private void onMinSdkVersionFieldModified() { 1049 if (mInternalMinSdkVersionUpdate || mInternalSdkTargetUpdate) { 1050 return; 1051 } 1052 1053 updateSdkSelectorToMatchMinSdkVersion(); 1054 1055 mMinSdkVersionModifiedByUser = true; 1056 } 1057 1058 /** 1059 * Try to find an SDK Target that matches the current MinSdkVersion. 1060 * 1061 * There can be multiple targets with the same sdk api version, so don't change 1062 * it if it's already at the right version. Otherwise pick the first target 1063 * that matches. 1064 */ 1065 private void updateSdkSelectorToMatchMinSdkVersion() { 1066 String minSdkVersion = mInfo.getMinSdkVersion(); 1067 1068 IAndroidTarget curr_target = mInfo.getSdkTarget(); 1069 if (curr_target != null && curr_target.getVersion().equals(minSdkVersion)) { 1070 return; 1071 } 1072 1073 for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { 1074 if (target.getVersion().equals(minSdkVersion)) { 1075 mSdkTargetSelector.setSelection(target); 1076 return; 1077 } 1078 } 1079 } 1080 1081 /** 1082 * Called when an SDK target is modified. 1083 * 1084 * Also changes the minSdkVersion field to reflect the sdk api level that has 1085 * just been selected. 1086 */ 1087 private void onSdkTargetModified() { 1088 if (mInternalMinSdkVersionUpdate || mInternalSdkTargetUpdate) { 1089 return; 1090 } 1091 1092 IAndroidTarget target = mInfo.getSdkTarget(); 1093 1094 if (target != null) { 1095 mInternalMinSdkVersionUpdate = true; 1096 mMinSdkVersionField.setText(target.getVersion().getApiString()); 1097 mInternalMinSdkVersionUpdate = false; 1098 } 1099 1100 mSdkTargetModifiedByUser = true; 1101 } 1102 1103 /** 1104 * Returns whether this page's controls currently all contain valid values. 1105 * 1106 * @return <code>true</code> if all controls are valid, and 1107 * <code>false</code> if at least one is invalid 1108 */ 1109 private boolean validatePage() { 1110 IWorkspace workspace = ResourcesPlugin.getWorkspace(); 1111 1112 int status = MSG_NONE; 1113 1114 // there is nothing to validate if we're not going to create a test project 1115 if (mInfo.getCreateTestProject()) { 1116 status = validateProjectField(workspace); 1117 if ((status & MSG_ERROR) == 0) { 1118 status |= validateLocationPath(workspace); 1119 } 1120 if ((status & MSG_ERROR) == 0) { 1121 status |= validateTestTarget(); 1122 } 1123 if ((status & MSG_ERROR) == 0) { 1124 status |= validateSdkTarget(); 1125 } 1126 if ((status & MSG_ERROR) == 0) { 1127 status |= validatePackageField(); 1128 } 1129 if ((status & MSG_ERROR) == 0) { 1130 status |= validateMinSdkVersionField(); 1131 } 1132 } 1133 if (status == MSG_NONE) { 1134 setStatus(null, MSG_NONE); 1135 } 1136 1137 // Return false if there's an error so that the finish button be disabled. 1138 return (status & MSG_ERROR) == 0; 1139 } 1140 1141 /** 1142 * Validates the page and updates the Next/Finish buttons 1143 */ 1144 private void validatePageComplete() { 1145 setPageComplete(validatePage()); 1146 } 1147 1148 /** 1149 * Validates the test target (self, main project or existing project) 1150 * 1151 * @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE. 1152 */ 1153 private int validateTestTarget() { 1154 if (mInfo.isTestingExisting() && mInfo.getExistingTestedProject() == null) { 1155 return setStatus("Please select an existing Android project as a test target.", 1156 MSG_ERROR); 1157 } 1158 1159 return MSG_NONE; 1160 } 1161 1162 /** 1163 * Validates the project name field. 1164 * 1165 * @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE. 1166 */ 1167 private int validateProjectField(IWorkspace workspace) { 1168 // Validate project field 1169 String projectName = mInfo.getProjectName(); 1170 if (projectName.length() == 0) { 1171 return setStatus("Project name must be specified", MSG_ERROR); 1172 } 1173 1174 // Limit the project name to shell-agnostic characters since it will be used to 1175 // generate the final package 1176 if (!sProjectNamePattern.matcher(projectName).matches()) { 1177 return setStatus("The project name must start with an alphanumeric characters, followed by one or more alphanumerics, digits, dots, dashes, underscores or spaces.", 1178 MSG_ERROR); 1179 } 1180 1181 IStatus nameStatus = workspace.validateName(projectName, IResource.PROJECT); 1182 if (!nameStatus.isOK()) { 1183 return setStatus(nameStatus.getMessage(), MSG_ERROR); 1184 } 1185 1186 if (mMainInfo != null && projectName.equals(mMainInfo.getProjectName())) { 1187 return setStatus("The main project name and the test project name must be different.", 1188 MSG_ERROR); 1189 } 1190 1191 if (getProjectHandle().exists()) { 1192 return setStatus("A project with that name already exists in the workspace", 1193 MSG_ERROR); 1194 } 1195 1196 return MSG_NONE; 1197 } 1198 1199 /** 1200 * Validates the location path field. 1201 * 1202 * @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE. 1203 */ 1204 private int validateLocationPath(IWorkspace workspace) { 1205 Path path = new Path(getProjectLocation()); 1206 if (!mInfo.useDefaultLocation()) { 1207 // If not using the default value validate the location. 1208 URI uri = URIUtil.toURI(path.toOSString()); 1209 IStatus locationStatus = workspace.validateProjectLocationURI(getProjectHandle(), 1210 uri); 1211 if (!locationStatus.isOK()) { 1212 return setStatus(locationStatus.getMessage(), MSG_ERROR); 1213 } else { 1214 // The location is valid as far as Eclipse is concerned (i.e. mostly not 1215 // an existing workspace project.) Check it either doesn't exist or is 1216 // a directory that is empty. 1217 File f = path.toFile(); 1218 if (f.exists() && !f.isDirectory()) { 1219 return setStatus("A directory name must be specified.", MSG_ERROR); 1220 } else if (f.isDirectory()) { 1221 // However if the directory exists, we should put a warning if it is not 1222 // empty. We don't put an error (we'll ask the user again for confirmation 1223 // before using the directory.) 1224 String[] l = f.list(); 1225 if (l.length != 0) { 1226 return setStatus("The selected output directory is not empty.", 1227 MSG_WARNING); 1228 } 1229 } 1230 } 1231 } else { 1232 // Otherwise validate the path string is not empty 1233 if (getProjectLocation().length() == 0) { 1234 return setStatus("A directory name must be specified.", MSG_ERROR); 1235 } 1236 1237 File dest = path.append(mInfo.getProjectName()).toFile(); 1238 if (dest.exists()) { 1239 return setStatus(String.format("There is already a file or directory named \"%1$s\" in the selected location.", 1240 mInfo.getProjectName()), MSG_ERROR); 1241 } 1242 } 1243 1244 return MSG_NONE; 1245 } 1246 1247 /** 1248 * Validates the sdk target choice. 1249 * 1250 * @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE. 1251 */ 1252 private int validateSdkTarget() { 1253 if (mInfo.getSdkTarget() == null) { 1254 return setStatus("An SDK Target must be specified.", MSG_ERROR); 1255 } 1256 return MSG_NONE; 1257 } 1258 1259 /** 1260 * Validates the sdk target choice. 1261 * 1262 * @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE. 1263 */ 1264 private int validateMinSdkVersionField() { 1265 1266 // If the min sdk version is empty, it is always accepted. 1267 if (mInfo.getMinSdkVersion().length() == 0) { 1268 return MSG_NONE; 1269 } 1270 1271 if (mInfo.getSdkTarget() != null && 1272 mInfo.getSdkTarget().getVersion().equals(mInfo.getMinSdkVersion()) == false) { 1273 return setStatus("The API level for the selected SDK target does not match the Min SDK version.", 1274 mInfo.getSdkTarget().getVersion().isPreview() ? MSG_ERROR : MSG_WARNING); 1275 } 1276 1277 return MSG_NONE; 1278 } 1279 1280 /** 1281 * Validates the package name field. 1282 * 1283 * @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE. 1284 */ 1285 private int validatePackageField() { 1286 // Validate package field 1287 String packageName = mInfo.getPackageName(); 1288 if (packageName.length() == 0) { 1289 return setStatus("Project package name must be specified.", MSG_ERROR); 1290 } 1291 1292 // Check it's a valid package string 1293 int result = MSG_NONE; 1294 IStatus status = JavaConventions.validatePackageName(packageName, "1.5", "1.5"); //$NON-NLS-1$ $NON-NLS-2$ 1295 if (!status.isOK()) { 1296 result = setStatus(String.format("Project package: %s", status.getMessage()), 1297 status.getSeverity() == IStatus.ERROR ? MSG_ERROR : MSG_WARNING); 1298 } 1299 1300 // The Android Activity Manager does not accept packages names with only one 1301 // identifier. Check the package name has at least one dot in them (the previous rule 1302 // validated that if such a dot exist, it's not the first nor last characters of the 1303 // string.) 1304 if (result != MSG_ERROR && packageName.indexOf('.') == -1) { 1305 return setStatus("Project package name must have at least two identifiers.", MSG_ERROR); 1306 } 1307 1308 // Check that the target package name is valid too 1309 packageName = mInfo.getTargetPackageName(); 1310 if (packageName.length() == 0) { 1311 return setStatus("Target package name must be specified.", MSG_ERROR); 1312 } 1313 1314 // Check it's a valid package string 1315 status = JavaConventions.validatePackageName(packageName, "1.5", "1.5"); //$NON-NLS-1$ $NON-NLS-2$ 1316 if (!status.isOK()) { 1317 result = setStatus(String.format("Target package: %s", status.getMessage()), 1318 status.getSeverity() == IStatus.ERROR ? MSG_ERROR : MSG_WARNING); 1319 } 1320 1321 if (result != MSG_ERROR && packageName.indexOf('.') == -1) { 1322 return setStatus("Target name must have at least two identifiers.", MSG_ERROR); 1323 } 1324 1325 return result; 1326 } 1327 1328 /** 1329 * Sets the error message for the wizard with the given message icon. 1330 * 1331 * @param message The wizard message type, one of MSG_ERROR or MSG_WARNING. 1332 * @return As a convenience, always returns messageType so that the caller can return 1333 * immediately. 1334 */ 1335 private int setStatus(String message, int messageType) { 1336 if (message == null) { 1337 setErrorMessage(null); 1338 setMessage(null); 1339 } else if (!message.equals(getMessage())) { 1340 setMessage(message, messageType == MSG_WARNING ? WizardPage.WARNING : WizardPage.ERROR); 1341 } 1342 return messageType; 1343 } 1344 1345 } 1346