/* * Copyright 2013 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.explorer.dynamodb; import java.util.Arrays; import java.util.LinkedList; import org.eclipse.core.databinding.AggregateValidationStatus; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.beans.PojoObservables; import org.eclipse.core.databinding.observable.ChangeEvent; import org.eclipse.core.databinding.observable.IChangeListener; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.databinding.swt.SWTObservables; import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.ListViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import com.amazonaws.eclipse.core.AwsToolkitCore; import com.amazonaws.eclipse.databinding.ChainValidator; import com.amazonaws.eclipse.databinding.NotEmptyValidator; import com.amazonaws.eclipse.dynamodb.AbstractAddNewAttributeDialog; import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; import com.amazonaws.services.dynamodbv2.model.KeyType; import com.amazonaws.services.dynamodbv2.model.LocalSecondaryIndex; import com.amazonaws.services.dynamodbv2.model.Projection; public class AddLSIDialog extends TitleAreaDialog { /** Widget used as data-binding targets **/ private Text attributeNameText; private Text indexNameText; private Combo attributeTypeCombo; private Combo projectionTypeCombo; private Button addAttributeButton; private Button okButton; /** The data objects that will be used to generate the service request **/ private final LocalSecondaryIndex localSecondaryIndex; private final AttributeDefinition indexRangeKeyAttributeDefinition; private final DataBindingContext bindingContext = new DataBindingContext(); /** The model value objects for data-binding **/ private final IObservableValue indexNameModel; private final IObservableValue indexRangeKeyNameInKeySchemaDefinitionModel; private final IObservableValue indexRangeKeyNameInAttributeDefinitionsModel; private final IObservableValue indexRangeKeyAttributeTypeModel; private final IObservableValue projectionTypeModel; private final String primaryRangeKeyName; private final int primaryRangeKeyTypeComboIndex; private static final String[] DATA_TYPE_STRINGS = new String[] { "String", "Number", "Binary" }; private static final String[] PROJECTED_ATTRIBUTES = new String[] { "All Attributes", "Table and Index Keys", "Specify Attributes" }; public AddLSIDialog(Shell parentShell, CreateTableDataModel dataModel) { super(parentShell); // Initialize the variable necessary for data-binding localSecondaryIndex = new LocalSecondaryIndex(); // The index range key to be defined by the user KeySchemaElement rangeKeySchemaDefinition = new KeySchemaElement() .withAttributeName(null) .withKeyType(KeyType.RANGE); localSecondaryIndex.withKeySchema( new KeySchemaElement() .withAttributeName(dataModel.getHashKeyName()) .withKeyType(KeyType.HASH), rangeKeySchemaDefinition); localSecondaryIndex.setProjection(new Projection()); // The attribute definition for the index range key indexRangeKeyAttributeDefinition = new AttributeDefinition(); // Initialize IObservableValue objects that keep track of data variables indexNameModel = PojoObservables.observeValue(localSecondaryIndex, "indexName"); indexRangeKeyNameInKeySchemaDefinitionModel = PojoObservables.observeValue(rangeKeySchemaDefinition, "attributeName"); indexRangeKeyAttributeTypeModel = PojoObservables.observeValue(indexRangeKeyAttributeDefinition, "attributeType"); indexRangeKeyNameInAttributeDefinitionsModel = PojoObservables.observeValue(indexRangeKeyAttributeDefinition, "attributeName"); projectionTypeModel = PojoObservables.observeValue(localSecondaryIndex.getProjection(), "projectionType"); // Get the information of the primary range key if (dataModel.getEnableRangeKey()) { primaryRangeKeyName = dataModel.getRangeKeyName(); primaryRangeKeyTypeComboIndex = Arrays.<String>asList(DATA_TYPE_STRINGS).indexOf(dataModel.getRangeKeyType()); } else { primaryRangeKeyName = null; primaryRangeKeyTypeComboIndex = -1; } setShellStyle(SWT.RESIZE); } @Override protected Control createContents(Composite parent) { Control contents = super.createContents(parent); setTitle("Add Local Secondary Index"); setTitleImage(AwsToolkitCore.getDefault().getImageRegistry().get(AwsToolkitCore.IMAGE_AWS_LOGO)); okButton = getButton(IDialogConstants.OK_ID); okButton.setEnabled(false); return contents; } @Override protected void configureShell(Shell shell) { super.configureShell(shell); shell.setText("Add Local Secondary Index"); shell.setMinimumSize(400, 500); } @Override protected Control createDialogArea(Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); GridDataFactory.fillDefaults().grab(true, true).applyTo(composite); composite.setLayout(new GridLayout()); composite = new Composite(composite, SWT.NULL); GridDataFactory.fillDefaults().grab(true, true).applyTo(composite); composite.setLayout(new GridLayout(2, false)); // Index range key attribute name new Label(composite, SWT.NONE | SWT.READ_ONLY).setText("Attribute to Index:"); attributeNameText = new Text(composite, SWT.BORDER); bindingContext.bindValue(SWTObservables.observeText(attributeNameText, SWT.Modify), indexRangeKeyNameInKeySchemaDefinitionModel); ChainValidator<String> attributeNameValidationStatusProvider = new ChainValidator<String>(indexRangeKeyNameInKeySchemaDefinitionModel, new NotEmptyValidator("Please provide an attribute name")); bindingContext.addValidationStatusProvider(attributeNameValidationStatusProvider); bindingContext.bindValue(SWTObservables.observeText(attributeNameText, SWT.Modify), indexRangeKeyNameInAttributeDefinitionsModel); attributeNameText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { if (attributeNameText.getText().equals(primaryRangeKeyName) && attributeTypeCombo != null && primaryRangeKeyTypeComboIndex > -1) { attributeTypeCombo.select(primaryRangeKeyTypeComboIndex); attributeTypeCombo.setEnabled(false); } else if (attributeTypeCombo != null) { attributeTypeCombo.setEnabled(true); } } }); GridDataFactory.fillDefaults().grab(true, false).applyTo(attributeNameText); // Index range key attribute type new Label(composite, SWT.NONE | SWT.READ_ONLY).setText("Attribute Type:"); attributeTypeCombo = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY); attributeTypeCombo.setItems(DATA_TYPE_STRINGS); attributeTypeCombo.select(0); bindingContext.bindValue(SWTObservables.observeSelection(attributeTypeCombo), indexRangeKeyAttributeTypeModel); // Index name new Label(composite, SWT.NONE | SWT.READ_ONLY).setText("Index Name:"); indexNameText = new Text(composite, SWT.BORDER); GridDataFactory.fillDefaults().grab(true, false).applyTo(indexNameText); bindingContext.bindValue(SWTObservables.observeText(indexNameText, SWT.Modify), indexNameModel); ChainValidator<String> indexNameValidationStatusProvider = new ChainValidator<String>(indexNameModel, new NotEmptyValidator("Please provide an index name")); bindingContext.addValidationStatusProvider(indexNameValidationStatusProvider); // Projection type new Label(composite, SWT.NONE | SWT.READ_ONLY).setText("Projected Attributes:"); projectionTypeCombo = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY); projectionTypeCombo.setItems(PROJECTED_ATTRIBUTES); projectionTypeCombo.select(0); bindingContext.bindValue(SWTObservables.observeSelection(projectionTypeCombo), projectionTypeModel); projectionTypeCombo.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { if (projectionTypeCombo.getSelectionIndex() == 2) { // Enable the list for adding non-key attributes to the projection addAttributeButton.setEnabled(true); } else { addAttributeButton.setEnabled(false); } } public void widgetDefaultSelected(SelectionEvent e) { } }); // Non-key attributes in the projection final AttributeList attributeList = new AttributeList(composite); GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, SWT.DEFAULT).applyTo(attributeList); addAttributeButton = new Button(composite, SWT.PUSH); addAttributeButton.setText("Add Attribute"); addAttributeButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); addAttributeButton.setImage(AwsToolkitCore.getDefault().getImageRegistry().get(AwsToolkitCore.IMAGE_ADD)); addAttributeButton.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { AddNewAttributeDialog newAttributeTable = new AddNewAttributeDialog(); if (newAttributeTable.open() == 0) { // lazy-initialize the list if (null == localSecondaryIndex.getProjection().getNonKeyAttributes()) { localSecondaryIndex.getProjection().setNonKeyAttributes(new LinkedList<String>()); } localSecondaryIndex.getProjection().getNonKeyAttributes().add(newAttributeTable.getNewAttributeName()); attributeList.refresh(); } } public void widgetDefaultSelected(SelectionEvent e) { } }); addAttributeButton.setEnabled(false); // Finally provide aggregate status reporting for the entire wizard page final AggregateValidationStatus aggregateValidationStatus = new AggregateValidationStatus(bindingContext, AggregateValidationStatus.MAX_SEVERITY); aggregateValidationStatus.addChangeListener(new IChangeListener() { public void handleChange(ChangeEvent event) { Object value = aggregateValidationStatus.getValue(); if (value instanceof IStatus == false) return; IStatus status = (IStatus) value; if (status.getSeverity() == Status.ERROR) { setErrorMessage(status.getMessage()); if (okButton != null) { okButton.setEnabled(false); } } else { setErrorMessage(null); if (okButton != null) { okButton.setEnabled(true); } } } }); bindingContext.updateModels(); return composite; } public LocalSecondaryIndex getLocalSecondaryIndex() { return localSecondaryIndex; } /** * Get the AttributeDefinition of the index range key as specified in this dialog. */ public AttributeDefinition getIndexRangeKeyAttributeDefinition() { return indexRangeKeyAttributeDefinition; } private class AddNewAttributeDialog extends AbstractAddNewAttributeDialog { @Override public void validate() { if (getButton(0) == null) return; if (getNewAttributeName().length() == 0) { getButton(0).setEnabled(false); return; } getButton(0).setEnabled(true); return; } } /** The list widget for adding projected non-key attributes. **/ private class AttributeList extends Composite { private ListViewer viewer; private AttributeListContentProvider attributeListContentProvider; public AttributeList(Composite parent) { super(parent, SWT.NONE); this.setLayout(new GridLayout()); viewer = new ListViewer(this, SWT.BORDER | SWT.V_SCROLL | SWT.READ_ONLY); attributeListContentProvider = new AttributeListContentProvider(); viewer.setContentProvider(attributeListContentProvider); viewer.setLabelProvider(new LabelProvider()); GridDataFactory.fillDefaults().grab(true, true).applyTo(viewer.getList()); viewer.getList().setVisible(true); MenuManager menuManager = new MenuManager("#PopupMenu"); menuManager.setRemoveAllWhenShown(true); menuManager.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { if (viewer.getList().getSelectionCount() > 0) { manager.add(new Action() { @Override public ImageDescriptor getImageDescriptor() { return AwsToolkitCore.getDefault().getImageRegistry().getDescriptor(AwsToolkitCore.IMAGE_REMOVE); } @Override public void run() { // In theory, this should never be null. if (null != localSecondaryIndex.getProjection().getNonKeyAttributes()) { localSecondaryIndex.getProjection().getNonKeyAttributes().remove(viewer.getList().getSelectionIndex()); } refresh(); } @Override public String getText() { return "Delete Attribute"; } }); } } }); viewer.getList().setMenu(menuManager.createContextMenu(viewer.getList())); } // Enforce to call getElements to update list public void refresh() { viewer.setInput(new Object()); } } private class AttributeListContentProvider extends ObservableListContentProvider { @Override public void dispose() { } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } @Override public Object[] getElements(Object inputElement) { return localSecondaryIndex.getProjection().getNonKeyAttributes() != null ? localSecondaryIndex.getProjection().getNonKeyAttributes().toArray() : new String[] {}; } } }