/* * Copyright 2008-2014 Amazon Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://aws.amazon.com/apache2.0 * * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES * OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and * limitations under the License. */ package com.amazonaws.eclipse.core.ui.preferences; import java.io.File; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.UUID; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.forms.events.ExpansionEvent; import org.eclipse.ui.forms.events.IExpansionListener; import org.eclipse.ui.forms.widgets.ExpandableComposite; import org.eclipse.ui.forms.widgets.Section; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.auth.profile.internal.Profile; import com.amazonaws.eclipse.core.AccountInfo; import com.amazonaws.eclipse.core.AwsToolkitCore; import com.amazonaws.eclipse.core.AwsUrls; import com.amazonaws.eclipse.core.accounts.AccountInfoImpl; import com.amazonaws.eclipse.core.accounts.preferences.PluginPreferenceStoreAccountOptionalConfiguration; import com.amazonaws.eclipse.core.accounts.profiles.SdkProfilesCredentialsConfiguration; import com.amazonaws.eclipse.core.preferences.PreferenceConstants; import com.amazonaws.eclipse.core.regions.Region; import com.amazonaws.eclipse.core.ui.WebLinkListener; import com.amazonaws.eclipse.core.ui.preferences.accounts.AccountInfoPropertyEditor; import com.amazonaws.eclipse.core.ui.preferences.accounts.AccountInfoPropertyEditorFactory; import com.amazonaws.eclipse.core.ui.preferences.accounts.AccountInfoPropertyEditorFactory.AccountInfoFilePropertyEditor; import com.amazonaws.eclipse.core.ui.preferences.accounts.AccountInfoPropertyEditorFactory.AccountInfoStringPropertyEditor; import com.amazonaws.eclipse.core.ui.preferences.accounts.AccountInfoPropertyEditorFactory.PropertyType; /** * A tab item contained in the AWS account preference page. Each tab item * corresponds to a set of region-specific (or global) accounts. */ public class AwsAccountPreferencePageTab extends TabItem { /** The preference store persisted by this tab */ private final IPreferenceStore prefStore; /** * The preference page to which the validation error message should be * reported. */ private final AwsAccountPreferencePage parentPrefPage; /** * The region associated with this tab. Null if this is the tab for global * account */ private final Region region; /** * The identifier of the current default account for the region represented * by this tab */ private String currentRegionAccountId; /** * @see AwsAccountPreferencePage#getAccountInfoByIdentifier() */ private final LinkedHashMap<String, AccountInfo> accountInfoByIdentifier; /** * @see AwsAccountPreferencePage#getAccountInfoToBeDeleted() */ private final Set<AccountInfo> accountInfoToBeDeleted; /** * The DataBindingContext instance shared by all the account info property * editors */ private final DataBindingContext dataBindingContext = new DataBindingContext(); /** * Additional listeners to be notified when the account information is modified */ private final List<ModifyListener> accountInfoFieldEditorListeners = new LinkedList<ModifyListener>(); /** Page controls */ BooleanFieldEditor enableRegionDefaultAccount; private Composite accountInfoSection; private ComboViewer accountSelector; private Button deleteAccount; private Label defaultAccountExplanationLabel; private AccountInfoPropertyEditor accountNameFieldEditor; private AccountInfoPropertyEditor accessKeyFieldEditor; private AccountInfoPropertyEditor secretKeyFieldEditor; private AccountInfoPropertyEditor sessionTokenFieldEditor; private AccountInfoPropertyEditor userIdFieldEditor; private AccountInfoPropertyEditor certificateFieldEditor; private AccountInfoPropertyEditor certificatePrivateKeyFieldEditor; /** * The set of field editors that need to know when the account or its name * changes. */ private final Collection<AccountInfoPropertyEditor> accountFieldEditors = new LinkedList<AccountInfoPropertyEditor>(); /** The checkbox controlling how we display the secret key */ private Button hideSecretKeyCheckbox; /** The checkbox controlling whether the credential includes the session token */ private Button useSessionTokenCheckbox; /** The Text control in the secret key field editor */ private Text secretKeyText; public AwsAccountPreferencePageTab(TabFolder tabFolder, int tabIndex, AwsAccountPreferencePage parentPrefPage, Region region) { this(tabFolder, tabIndex, parentPrefPage, region, AwsToolkitCore .getDefault().getPreferenceStore()); } /** * Construct a new tab in the given TabFolder at the given index, relating * to the given region. * * @param tabFolder * TabFolder where this tab is inserted. * @param tabIndex * The index where this tab should be inserted. * @param parentPrefPage * The parent account preference page which contains this tab. * @param region * The region which this page tab manages * @param prefStore * The preference store that this tab persists with. */ public AwsAccountPreferencePageTab(TabFolder tabFolder, int tabIndex, AwsAccountPreferencePage parentPrefPage, Region region, IPreferenceStore prefStore) { super(tabFolder, SWT.NONE, tabIndex); this.parentPrefPage = parentPrefPage; this.accountInfoByIdentifier = parentPrefPage.getAccountInfoByIdentifier(); this.accountInfoToBeDeleted = parentPrefPage.getAccountInfoToBeDeleted(); this.region = region; this.prefStore = prefStore; if (region == null) { setText("Global Configuration"); } else { // Use the region name as the tab title setText(region.getName()); } loadCurrentDefaultAccountId(); writeDefaultPreferenceValues(); final Composite composite = setUpCompositeLayout(); // Not strictly necessary, since AwsAccountPreferencePage will never // construct this class with an empty map of accounts. if (!accountInfoByIdentifier.isEmpty()) { addControls(composite); } else { addCredentialsFileLoadingFailureLabel(composite); } setControl(composite); } /* * Public interfaces */ public void loadDefault() { if (enableRegionDefaultAccount != null) { enableRegionDefaultAccount.loadDefault(); } } public void doStore() { // Store the check box on whether region-default-account is enabled if (enableRegionDefaultAccount != null) { enableRegionDefaultAccount.store(); } // Save the id of the current default region account getPreferenceStore().setValue(getRegionCurrentAccountPrefKey(), currentRegionAccountId); // Refresh the UI of the account selector refreshAccountSelectorUI(); } public Region getRegion() { return region; } public boolean isGlobalAccoutTab() { return region == null; } /** * Returns names of all the accounts. */ public List<String> getAccountNames() { List<String> accountNames = new LinkedList<String>(); for (AccountInfo account : accountInfoByIdentifier.values()) { accountNames.add(account.getAccountName()); } return accountNames; } /** * Check for duplicate account names in this tab. * * @return True if duplicate account names are found in this tab. */ public boolean checkDuplicateAccountName() { Set<String> accountNames = new HashSet<String>(); for (String accountName : getAccountNames()) { if (!accountNames.add(accountName)) { return true; } } return false; } synchronized void addAccountInfoFieldEditorModifyListener(ModifyListener listener) { accountInfoFieldEditorListeners.add(listener); } /* * Private Interface */ private void loadCurrentDefaultAccountId() { currentRegionAccountId = getPreferenceStore().getString( getRegionCurrentAccountPrefKey()); } private void writeDefaultPreferenceValues() { if (!isGlobalAccoutTab()) { getPreferenceStore().setDefault(getRegionAccountEnabledPrefKey(), true); } } private Composite setUpCompositeLayout() { Composite composite = new Composite(getParent(), SWT.NONE); composite.setLayout(new GridLayout(3, false)); GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); composite.setLayoutData(gridData); return composite; } private void addCredentialsFileLoadingFailureLabel(Composite composite) { new Label(composite, SWT.READ_ONLY) .setText(String .format("Failed to load credential profiles from (%s).%n" + "Please check that your credentials file is in the correct format.", prefStore .getString(PreferenceConstants.P_CREDENTIAL_PROFILE_FILE_LOCATION))); } private void addControls(final Composite composite) { // If it's regional tab, add the enable-region-default-account check-box // and the remove-this-tab button if (!isGlobalAccoutTab()) { addEnableRegionDefaultControls(composite); } // The main account info section accountInfoSection = createAccountInfoSection(composite); // Set up the change listener on the enable-region-default-account // check-box, so that the account info section is grayed out whenever // it's unchecked. setUpEnableRegionDefaultChangeListener(); AwsToolkitPreferencePage .tweakLayout((GridLayout) composite.getLayout()); } /** * Add the enable-region-default-account check-box and the remove-this-tab * button */ private void addEnableRegionDefaultControls(Composite parent) { enableRegionDefaultAccount = new BooleanFieldEditor( getRegionAccountEnabledPrefKey(), "Enable region default account for " + region.getName(), parent); enableRegionDefaultAccount.setPreferenceStore(getPreferenceStore()); Button removeTabButton = new Button(parent, SWT.PUSH); removeTabButton.setText("Remove"); removeTabButton .setToolTipText("Remove default account configuration for this region."); removeTabButton.setImage(AwsToolkitCore.getDefault().getImageRegistry() .get(AwsToolkitCore.IMAGE_REMOVE)); removeTabButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); removeTabButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { MessageDialog confirmRemoveTabDialog = new MessageDialog( Display.getDefault().getActiveShell(), "Remove all accounts for " + region.getName(), AwsToolkitCore.getDefault().getImageRegistry() .get(AwsToolkitCore.IMAGE_AWS_ICON), "Are you sure you want to remove all the configured accounts for " + region.getName() + "?", MessageDialog.CONFIRM, new String[] { "Cancel", "OK" }, 1); if (confirmRemoveTabDialog.open() == 1) { AwsAccountPreferencePageTab.this.dispose(); } } }); } /** * Creates the whole section of account info */ private Composite createAccountInfoSection(Composite parent) { Composite accountInfoSection = new Composite(parent, SWT.NONE); accountInfoSection.setLayout(new GridLayout()); GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); gridData.horizontalSpan = 3; accountInfoSection.setLayoutData(gridData); createAccountSelector(accountInfoSection); WebLinkListener webLinkListener = new WebLinkListener(); Group awsGroup = createAccountDetailSectionGroup(accountInfoSection, webLinkListener); createOptionalSection(awsGroup, webLinkListener); return accountInfoSection; } /** * Creates the account selection section. */ private Composite createAccountSelector(final Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); composite.setLayout(new GridLayout(4, false)); new Label(composite, SWT.READ_ONLY).setText("Default Profile: "); accountSelector = new ComboViewer(composite, SWT.DROP_DOWN | SWT.READ_ONLY); accountSelector.getCombo().setLayoutData( new GridData(SWT.FILL, SWT.CENTER, true, false)); // Use a List of AccountInfo objects as the data input for the combo // viewer accountSelector.setContentProvider(ArrayContentProvider.getInstance()); accountSelector.setLabelProvider(new LabelProvider() { @Override public String getText(Object element) { if (element instanceof AccountInfo) { AccountInfo account = (AccountInfo) element; if (account.isDirty()) { return "*" + account.getAccountName(); } else { return account.getAccountName(); } } return super.getText(element); } }); AccountInfo currentRegionAccount = accountInfoByIdentifier .get(currentRegionAccountId); // In some of the edge-cases, currentRegionAccount could be null. // e.g. a specific credential profile account is removed externally, but // the data in the preference store is not yet updated. if (currentRegionAccount == null) { currentRegionAccount = accountInfoByIdentifier.values().iterator() .next(); currentRegionAccountId = currentRegionAccount .getInternalAccountId(); } final List<AccountInfo> allAccounts = new LinkedList<AccountInfo>( accountInfoByIdentifier.values()); setUpAccountSelectorItems(allAccounts, currentRegionAccount); // Add selection listener to the account selector, so that all the // account info editors are notified of the newly selected AccountInfo // object. accountSelector .addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection selection = (IStructuredSelection) event .getSelection(); Object selectedObject = selection.getFirstElement(); if (selectedObject instanceof AccountInfo) { AccountInfo accountInfo = (AccountInfo) selectedObject; accountChanged(accountInfo); } } }); final Button addNewAccount = new Button(composite, SWT.PUSH); addNewAccount.setText("Add profile"); deleteAccount = new Button(composite, SWT.PUSH); deleteAccount.setText("Remove profile"); deleteAccount.setEnabled(allAccounts.size() > 1); defaultAccountExplanationLabel = new Label(composite, SWT.WRAP); defaultAccountExplanationLabel .setText(getDefaultAccountExplanationText()); parentPrefPage.setItalicFont(defaultAccountExplanationLabel); GridData layoutData = new GridData(SWT.FILL, SWT.TOP, true, false); layoutData.horizontalSpan = 4; layoutData.widthHint = 200; defaultAccountExplanationLabel.setLayoutData(layoutData); addNewAccount.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { String newAccountId = UUID.randomUUID().toString(); AccountInfo newAccountInfo = createNewProfileAccountInfo(newAccountId); String newAccountName = region == null ? "New Profile" : "New " + region.getName() + " Profile"; newAccountInfo.setAccountName(newAccountName); // this will mark the AccountInfo object dirty accountInfoByIdentifier.put(newAccountId, newAccountInfo); setUpAccountSelectorItems(accountInfoByIdentifier.values(), newAccountInfo); for (AwsAccountPreferencePageTab tab : parentPrefPage.getAllAccountPreferencePageTabs()) { if (tab != AwsAccountPreferencePageTab.this) { tab.refreshAccountSelectorItems(); } } parentPrefPage.updatePageValidationOfAllTabs(); } }); deleteAccount.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { accountInfoToBeDeleted.add(accountInfoByIdentifier .get(currentRegionAccountId)); accountInfoByIdentifier.remove(currentRegionAccountId); // If all the accounts are deleted, create a temporary // AccountInfo object if (accountInfoByIdentifier.isEmpty()) { String newAccountId = UUID.randomUUID().toString(); AccountInfo newAccountInfo = createNewProfileAccountInfo(newAccountId); // Account name : default-region-id newAccountInfo .setAccountName(getRegionAccountDefaultName()); accountInfoByIdentifier.put(newAccountId, newAccountInfo); } // Use the first AccountInfo as the next selected account AccountInfo nextDefaultAccount = accountInfoByIdentifier .values().iterator().next(); setUpAccountSelectorItems(accountInfoByIdentifier.values(), nextDefaultAccount); for (AwsAccountPreferencePageTab tab : parentPrefPage.getAllAccountPreferencePageTabs()) { if (tab != AwsAccountPreferencePageTab.this) { tab.refreshAccountSelectorItems(); } } parentPrefPage.updatePageValidationOfAllTabs(); } }); accountSelector.getCombo().addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent arg0) { if (accountSelector.getCombo().getItemCount() > 1) { deleteAccount.setEnabled(true); } else { deleteAccount.setEnabled(false); } } }); return composite; } /** * Refreshes the UI of the account selector. */ private void refreshAccountSelectorUI() { accountSelector.refresh(); } /** * Refreshes the input data for the account selector combo and keep the current * selection. */ private void refreshAccountSelectorItems() { setUpAccountSelectorItems(accountInfoByIdentifier.values(), accountInfoByIdentifier.get(currentRegionAccountId)); } /** * Set the input data for the account selector combo and also set the * selection to the specified AccountInfo object. */ private void setUpAccountSelectorItems(Collection<AccountInfo> allAccounts, AccountInfo selectedAccount) { accountSelector.setInput(allAccounts); // If the given account is not found, then select the first element if ( !allAccounts.contains(selectedAccount) ) { selectedAccount = allAccounts.iterator().next(); } accountSelector.setSelection(new StructuredSelection(selectedAccount), true); // visible=true // TODO: copied from the existing code, not sure why it's necessary accountSelector.getCombo().getParent().getParent().layout(); } /** * Creates the widgets for the AWS account information section on this * preference page. * * @param parent * The parent preference page composite. * @param webLinkListener * The listener to attach to links. */ private Group createAccountDetailSectionGroup(final Composite parent, WebLinkListener webLinkListener) { final Group awsAccountGroup = new Group(parent, SWT.NONE); GridData gridData1 = new GridData(SWT.FILL, SWT.TOP, true, false); gridData1.horizontalSpan = 4; gridData1.verticalIndent = 10; awsAccountGroup.setLayoutData(gridData1); awsAccountGroup.setText("Profile Details:"); if (isGlobalAccoutTab()) { String linkText = "<a href=\"" + AwsUrls.SIGN_UP_URL + "\">Sign up for a new AWS account</a> or " + "<a href=\"" + AwsUrls.SECURITY_CREDENTIALS_URL + "\">manage your existing AWS security credentials</a>."; AwsToolkitPreferencePage.newLink(webLinkListener, linkText, awsAccountGroup); AwsToolkitPreferencePage.createSpacer(awsAccountGroup); } accountNameFieldEditor = newStringFieldEditor( accountInfoByIdentifier.get(currentRegionAccountId), "accountName", "&Profile Name:", awsAccountGroup); accessKeyFieldEditor = newStringFieldEditor( accountInfoByIdentifier.get(currentRegionAccountId), "accessKey", "&Access Key ID:", awsAccountGroup); /* * Secret key editor and hide check-box */ secretKeyFieldEditor = newStringFieldEditor( accountInfoByIdentifier.get(currentRegionAccountId), "secretKey", "&Secret Access Key:", awsAccountGroup); // create an empty label in the first column so that the hide secret key // checkbox lines up with the other text controls new Label(awsAccountGroup, SWT.NONE); hideSecretKeyCheckbox = createCheckbox(awsAccountGroup, "Show secret access key", false); hideSecretKeyCheckbox.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { updateSecretKeyText(); } }); secretKeyText = secretKeyFieldEditor.getTextControl(); updateSecretKeyText(); /* * Session token input controls */ sessionTokenFieldEditor = newStringFieldEditor( accountInfoByIdentifier.get(currentRegionAccountId), "sessionToken", "Session &Token:", awsAccountGroup); new Label(awsAccountGroup, SWT.NONE); useSessionTokenCheckbox = createCheckbox(awsAccountGroup, "Use session token", false); useSessionTokenCheckbox.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { boolean useSessionToken = useSessionTokenCheckbox.getSelection(); AccountInfo currentAccount = accountInfoByIdentifier.get(currentRegionAccountId); currentAccount.setUseSessionToken(useSessionToken); updateSessionTokenControls(); parentPrefPage.updatePageValidationOfAllTabs(); } }); // Update session token controls according to the current account updateSessionTokenControls(); AwsToolkitPreferencePage.tweakLayout((GridLayout) awsAccountGroup .getLayout()); accountFieldEditors.add(accountNameFieldEditor); accountFieldEditors.add(accessKeyFieldEditor); accountFieldEditors.add(secretKeyFieldEditor); accountFieldEditors.add(sessionTokenFieldEditor); return awsAccountGroup; } private Button createCheckbox(Composite parent, String text, boolean defaultSelection) { Button checkbox = new Button(parent, SWT.CHECK); checkbox.setText(text); checkbox.setSelection(defaultSelection); GridData gridData = new GridData(GridData.FILL_HORIZONTAL); gridData.horizontalSpan = 2; gridData.verticalIndent = -6; gridData.horizontalIndent = 3; checkbox.setLayoutData(gridData); return checkbox; } /** * Creates the widgets for the optional configuration section on this * preference page. * * @param parent * The parent preference page composite. * @param webLinkListener * The listener to attach to links. */ private void createOptionalSection(final Composite parent, WebLinkListener webLinkListener) { Section expandableComposite = new Section(parent, ExpandableComposite.TWISTIE); GridData gd = new GridData(SWT.FILL, SWT.TOP, true, false); gd.horizontalSpan = AwsToolkitPreferencePage.LAYOUT_COLUMN_WIDTH; expandableComposite.setLayoutData(gd); Composite optionalConfigGroup = new Composite(expandableComposite, SWT.NONE); optionalConfigGroup.setLayout(new GridLayout()); optionalConfigGroup.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); expandableComposite.setClient(optionalConfigGroup); expandableComposite.setText("Optional configuration:"); expandableComposite.setExpanded(false); expandableComposite.addExpansionListener(new IExpansionListener() { public void expansionStateChanging(ExpansionEvent e) { } public void expansionStateChanged(ExpansionEvent e) { parent.getParent().layout(); } }); String linkText = "Your AWS account number and X.509 certificate are only needed if you want to bundle EC2 instances from Eclipse. " + "<a href=\"" + AwsUrls.SECURITY_CREDENTIALS_URL + "\">Manage your AWS X.509 certificate</a>."; AwsToolkitPreferencePage.newLink(webLinkListener, linkText, optionalConfigGroup); AwsToolkitPreferencePage.createSpacer(optionalConfigGroup); userIdFieldEditor = newStringFieldEditor( accountInfoByIdentifier.get(currentRegionAccountId), "userId", "AWS Account &Number:", optionalConfigGroup); createFieldExampleLabel(optionalConfigGroup, "ex: 1111-2222-3333"); certificateFieldEditor = newFileFieldEditor( accountInfoByIdentifier.get(currentRegionAccountId), "ec2CertificateFile", "&Certificate File:", optionalConfigGroup); certificatePrivateKeyFieldEditor = newFileFieldEditor( accountInfoByIdentifier.get(currentRegionAccountId), "ec2PrivateKeyFile", "&Private Key File:", optionalConfigGroup); AwsToolkitPreferencePage.tweakLayout((GridLayout) optionalConfigGroup .getLayout()); accountFieldEditors.add(userIdFieldEditor); accountFieldEditors.add(certificateFieldEditor); accountFieldEditors.add(certificatePrivateKeyFieldEditor); } /** * Set up the change listener on the enable-region-default-account * check-box, so that the account info section is grayed out whenever it's * unchecked. */ private void setUpEnableRegionDefaultChangeListener() { if (enableRegionDefaultAccount != null) { enableRegionDefaultAccount .setPropertyChangeListener(new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { boolean enabled = (Boolean) event.getNewValue(); AwsAccountPreferencePageTab.this .toggleAccountInfoSectionEnabled(enabled); } }); enableRegionDefaultAccount.load(); toggleAccountInfoSectionEnabled(enableRegionDefaultAccount .getBooleanValue()); } } /** * Creates a new label to serve as an example for a field, using the * specified text. The label will be displayed with a subtle font. This * method assumes that the grid layout for the specified composite contains * three columns. * * @param composite * The parent component for this new widget. * @param text * The example text to display in the new label. */ private void createFieldExampleLabel(Composite composite, String text) { Label label = new Label(composite, SWT.NONE); Font font = label.getFont(); label = new Label(composite, SWT.NONE); label.setText(text); FontData[] fontData = font.getFontData(); if (fontData.length > 0) { FontData fd = fontData[0]; fd.setHeight(10); fd.setStyle(SWT.ITALIC); label.setFont(new Font(Display.getCurrent(), fd)); } GridData gridData = new GridData(GridData.FILL_HORIZONTAL); gridData.horizontalSpan = 2; gridData.verticalAlignment = SWT.TOP; gridData.horizontalIndent = 3; gridData.verticalIndent = -4; label.setLayoutData(gridData); } /** * Invoked whenever the selected account information changes. */ private void accountChanged(AccountInfo accountInfo) { currentRegionAccountId = accountInfo.getInternalAccountId(); for (AccountInfoPropertyEditor editor : accountFieldEditors) { editor.accountChanged(accountInfo); } updateSessionTokenControls(); } private void updateSessionTokenControls() { AccountInfo accountInfo = accountInfoByIdentifier.get(currentRegionAccountId); useSessionTokenCheckbox.setSelection(accountInfo.isUseSessionToken()); sessionTokenFieldEditor.getTextControl().setEnabled(accountInfo.isUseSessionToken()); } /** * Update or clear the error message and validity of the page. * * @return True if the page is invalid. */ boolean updatePageValidation() { String errorString = validateFieldValues(); if (errorString != null) { parentPrefPage.setValid(false); parentPrefPage.setErrorMessage(errorString); return true; } if (checkDuplicateAccountName()) { parentPrefPage.setValid(false); parentPrefPage.setErrorMessage("Duplicate account name defined"); return true; } return false; } /** * Returns an error message if there's a problem with the page's fields, or * null if there are no errors. */ private String validateFieldValues() { for (AccountInfo accountInfo : accountInfoByIdentifier.values()) { if (accountInfo.getAccountName().trim().isEmpty()) { return "Account name must not be blank"; } if (accountInfo.isUseSessionToken() && (accountInfo.getSessionToken() == null || accountInfo.getSessionToken().trim().isEmpty())) { return "Session token must not be blank"; } if (invalidFile(accountInfo.getEc2CertificateFile())) { return "Certificate file does not exist"; } if (invalidFile(accountInfo.getEc2PrivateKeyFile())) { return "Private key file does not exist"; } } return null; } private boolean invalidFile(String certFile) { return certFile.trim().length() > 0 && !new File(certFile).exists(); } /** * Updates the secret key text according to whether or not the * "display secret key in plain text" checkbox is selected or not. */ private void updateSecretKeyText() { if (hideSecretKeyCheckbox == null) return; if (secretKeyText == null) return; if (hideSecretKeyCheckbox.getSelection()) { secretKeyText.setEchoChar('\0'); } else { secretKeyText.setEchoChar('*'); } } private AccountInfoPropertyEditor newStringFieldEditor( AccountInfo currentAccount, String propertyName, String label, Composite parent) { AccountInfoPropertyEditor fieldEditor = AccountInfoPropertyEditorFactory .getAccountInfoPropertyEditor(currentAccount, propertyName, PropertyType.STRING_PROPERTY, label, parent, dataBindingContext); setUpFieldEditor(fieldEditor, parent); return fieldEditor; } private AccountInfoPropertyEditor newFileFieldEditor( AccountInfo currentAccount, String propertyName, String label, Composite parent) { AccountInfoPropertyEditor fieldEditor = AccountInfoPropertyEditorFactory .getAccountInfoPropertyEditor(currentAccount, propertyName, PropertyType.FILE_PROPERTY, label, parent, dataBindingContext); setUpFieldEditor(fieldEditor, parent); return fieldEditor; } protected void setUpFieldEditor( final AccountInfoPropertyEditor fieldEditor, Composite parent) { if (fieldEditor instanceof AccountInfoStringPropertyEditor) { ((AccountInfoStringPropertyEditor) fieldEditor) .getStringFieldEditor().fillIntoGrid(parent, AwsToolkitPreferencePage.LAYOUT_COLUMN_WIDTH); } else if (fieldEditor instanceof AccountInfoFilePropertyEditor) { ((AccountInfoFilePropertyEditor) fieldEditor).getFileFieldEditor() .fillIntoGrid(parent, AwsToolkitPreferencePage.LAYOUT_COLUMN_WIDTH); } // Validate the page whenever the editors are touched fieldEditor.getTextControl().addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { /* * Since the data-binding also observes the Modify event on the * text control, there is an edge case where the AccountInfo * model might not have been updated when the modifyText * callbacked is triggered. To make sure the updated data model * is visible to the subsequent operations, we forcefully push * the data from the editor to the data model. */ fieldEditor.forceUpdateEditorValueToAccountInfoModel(); /* * Now that the property value change has been applied to the * AccountInfo object. Since the accountSelector is backed by * the collection of the same AccountInfo objects, we can simply * refresh the UI of accountSelector; all the account name * changes will be reflected. */ for (AwsAccountPreferencePageTab tab : parentPrefPage.getAllAccountPreferencePageTabs()) { tab.refreshAccountSelectorUI(); } parentPrefPage.updatePageValidationOfAllTabs(); for (ModifyListener listener : accountInfoFieldEditorListeners) { listener.modifyText(null); } } }); } private String getRegionCurrentAccountPrefKey() { return PreferenceConstants.P_REGION_CURRENT_DEFAULT_ACCOUNT(region); } private String getRegionAccountDefaultName() { return PreferenceConstants.DEFAULT_ACCOUNT_NAME(region); } private String getRegionAccountEnabledPrefKey() { return PreferenceConstants.P_REGION_DEFAULT_ACCOUNT_ENABLED(region); } private IPreferenceStore getPreferenceStore() { return prefStore; } /** * Recursively enable/disable all the children controls of account-info * section. */ private void toggleAccountInfoSectionEnabled(boolean enabled) { AwsAccountPreferencePage.setEnabledOnAllChildern(accountInfoSection, enabled); } private AccountInfo createNewProfileAccountInfo(String newAccountId) { Profile emptyProfile = new Profile("", new BasicAWSCredentials("", "")); return new AccountInfoImpl(newAccountId, new SdkProfilesCredentialsConfiguration(prefStore, newAccountId, emptyProfile), new PluginPreferenceStoreAccountOptionalConfiguration( prefStore, newAccountId)); } private String getDefaultAccountExplanationText() { if (isGlobalAccoutTab()) { return "This credential profile will be used by default to access all AWS regions " + "that are not configured with a region-specific account."; } else { return "This credential profile will be used by default to access AWS resources in " + region.getName() + "(" + region.getId() + ") region."; } } /** * For debug purpose only. */ @SuppressWarnings("unused") private void printAccounts() { System.out.println("*** Default accounts(" + accountInfoByIdentifier.size() + ") tab for " + this.getText() + " ***"); for (String id : accountInfoByIdentifier.keySet()) { System.out.println(" " + accountInfoByIdentifier.get(id) + "(" + id + ")"); } System.out.println("Current default: " + accountInfoByIdentifier.get(currentRegionAccountId)); } /** * Override this method in order to let SWT allow sub-classing TabItem */ @Override public void checkSubclass() { // no-op } }