Java tutorial
/******************************************************************************* * Copyright (c) 2014 Pivotal Software Inc and IBM Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of 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://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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. * * Contributors: * Pivotal Software, Inc. - initial API and implementation * IBM Corporation - Additions to services wizard *******************************************************************************/ package org.cloudfoundry.ide.eclipse.server.ui.internal.wizards; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.cloudfoundry.client.lib.domain.CloudServiceOffering; import org.cloudfoundry.client.lib.domain.CloudServicePlan; import org.cloudfoundry.ide.eclipse.server.core.internal.CloudFoundryServer; import org.cloudfoundry.ide.eclipse.server.core.internal.client.LocalCloudService; import org.cloudfoundry.ide.eclipse.server.ui.ICloudFoundryServiceWizardIconProvider; import org.cloudfoundry.ide.eclipse.server.ui.internal.CloudFoundryImages; import org.cloudfoundry.ide.eclipse.server.ui.internal.Messages; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseWheelListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; 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.Display; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.forms.widgets.Hyperlink; import org.eclipse.ui.statushandlers.StatusManager; import org.eclipse.wst.server.core.IRuntime; public class CloudFoundryServiceWizardPage1 extends WizardPage { protected static final String PAGE_NAME = CloudFoundryServiceWizardPage1.class.getName(); protected ScrolledComposite scrollComp; protected String filter; protected Font boldFont; protected Color selEdgeColor; protected Color selFillColor; protected Text nameText; protected boolean updating = false; private List<CloudServiceOffering> serviceOfferings; private List<CFServiceWizUI> allServicesList = new ArrayList<CFServiceWizUI>(); private List<CFServiceWizUI> selectedServicesList = new ArrayList<CFServiceWizUI>(); CFWizServicePage1Validation servicePageValidation; CloudFoundryServer cloudServer; protected Group serviceInfoComposite; public LocalCloudService getService() { List<CFServiceWizUI> selectedServices = getSelectedList(); if (selectedServices != null && selectedServices.size() == 1) { return selectedServices.get(0).convertToLocalCloudService(); } return null; } public static final String INSTALL_SERVICE_TEXT = Messages.CloudFoundryServiceWizardPage1_TEXT_SELECT_SERVICE; public static final String DEFAULT_FILTER_TEXT = Messages.CloudFoundryServiceWizardPage1_TEXT_TYPE_FILTER; public static final String REMOVE_SERVICE_TEXT = Messages.COMMONTXT_REMOVE; public static final String ADD_SERVICE_TEXT = Messages.CloudFoundryServiceWizardPage1_TEXT_SELECT; public static final String DESCRIPTION_FINAL = Messages.CloudFoundryServiceWizardPage1_TEXT_FINISH; public static final String FILTER_TEXT = Messages.CloudFoundryServiceWizardPage1_TEXT_FILTER; private Composite layoutList; private ServiceListTraverseListener traverseListener; private Text filterText; private Text serviceNameText; /** Optional field -- used to provide icons in service wizard if available */ CFServiceWizardDynamicIconLoader loader; private FontMetrics fontMetrics; public CloudFoundryServiceWizardPage1(CloudFoundryServer cloudServer) { super(PAGE_NAME); this.cloudServer = cloudServer; setTitle(Messages.CloudFoundryServiceWizardPage1_TTILE_SERVICE_CONFIG); setDescription(DESCRIPTION_FINAL); ImageDescriptor banner = CloudFoundryImages .getWizardBanner(cloudServer.getServer().getServerType().getId()); if (banner != null) { setImageDescriptor(banner); } this.cloudServer = cloudServer; // Is there an icon provide for the service, if so, use it IRuntime runtime = cloudServer.getServer().getRuntime(); String runtimeTypeId = runtime.getRuntimeType().getId(); ICloudFoundryServiceWizardIconProvider provider = CloudFoundryServiceWizardIconProviderRegistry .getInstance().getIconProvider(runtimeTypeId); if (provider != null) { loader = new CFServiceWizardDynamicIconLoader(provider, this.cloudServer); loader.start(); } servicePageValidation = new CFWizServicePage1Validation(this); setPageComplete(false); traverseListener = new ServiceListTraverseListener(); } private static void sortServicePlans(List<CloudServiceOffering> configurations) { for (CloudServiceOffering offering : configurations) { Collections.sort(offering.getCloudServicePlans(), new Comparator<CloudServicePlan>() { public int compare(CloudServicePlan o1, CloudServicePlan o2) { return o1.getName().compareTo(o2.getName()); } }); } } private boolean updateConfiguration() { try { getContainer().run(true, false, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { serviceOfferings = cloudServer.getBehaviour().getServiceOfferings(monitor); Collections.sort(serviceOfferings, new Comparator<CloudServiceOffering>() { public int compare(CloudServiceOffering o1, CloudServiceOffering o2) { return o1.getDescription().compareTo(o2.getDescription()); } }); // Pivotal Tracker [77602464] - Leaving plans in order // received by server keeps them in pricing order from // low to high // sortServicePlans(serviceOfferings); } catch (CoreException e) { throw new InvocationTargetException(e); } catch (OperationCanceledException e) { throw new InterruptedException(); } finally { monitor.done(); } } }); if (serviceOfferings != null) { for (CloudServiceOffering o : serviceOfferings) { if (!allServicesList.contains(new CFServiceWizUI(o))) { allServicesList.add(new CFServiceWizUI(o)); } } } return true; } catch (InvocationTargetException e) { IStatus status = cloudServer.error(NLS.bind( Messages.CloudFoundryServiceWizardPage1_ERROR_CONFIG_RETRIVE, e.getCause().getMessage()), e); StatusManager.getManager().handle(status, StatusManager.LOG); setMessage(status.getMessage(), IMessageProvider.ERROR); } catch (InterruptedException e) { // ignore } return false; } Composite topComp; public void createControl(Composite parent) { Composite comp = new Composite(parent, SWT.NONE); topComp = comp; GridLayout layout = new GridLayout(); layout.horizontalSpacing = convertHorizontalDLUsToPixels(comp, 4); layout.verticalSpacing = convertVerticalDLUsToPixels(comp, 4); layout.numColumns = 3; comp.setLayout(layout); GridData data; Composite filterComp = new Composite(comp, SWT.NONE); layout = new GridLayout(); layout.numColumns = 3; layout.marginHeight = 0; layout.marginWidth = 0; layout.horizontalSpacing = convertHorizontalDLUsToPixels(comp, 4); layout.verticalSpacing = convertHorizontalDLUsToPixels(comp, 4); filterComp.setLayout(layout); data = new GridData(SWT.FILL, SWT.BEGINNING, true, false); data.horizontalSpan = 3; data.horizontalIndent = 0; data.verticalIndent = 0; filterComp.setLayoutData(data); final Label filterLabel = new Label(filterComp, SWT.NONE); filterLabel.setText(FILTER_TEXT); data = new GridData(GridData.BEGINNING, GridData.CENTER, false, false); filterLabel.setLayoutData(data); filterText = new Text(filterComp, SWT.SEARCH); filterText.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 2, 1)); filterText.setText(DEFAULT_FILTER_TEXT); scrollComp = new ScrolledComposite(comp, SWT.BORDER | SWT.V_SCROLL); data = new GridData(GridData.FILL, GridData.FILL, true, true, 3, 1); data.heightHint = 300; scrollComp.setLayoutData(data); scrollComp.setAlwaysShowScrollBars(true); scrollComp.setBackground(scrollComp.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); int lineHeight = filterText.computeSize(SWT.DEFAULT, SWT.DEFAULT).y; scrollComp.getVerticalBar().setPageIncrement(lineHeight * 3); scrollComp.getVerticalBar().setIncrement(lineHeight); scrollComp.addControlListener(new ControlAdapter() { public void controlResized(ControlEvent event) { Control c = scrollComp.getContent(); if (c == null) { return; } Rectangle r = scrollComp.getClientArea(); r.height = c.computeSize(r.width, SWT.DEFAULT).y; c.setBounds(r); } }); layoutList = createLayoutList(scrollComp); filterText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent event) { if (updating) { return; } String text = filterText.getText(); if (DEFAULT_FILTER_TEXT.equals(text)) { filter = null; } else { filter = filterText.getText(); } updateServicesTable(); } }); filterText.addFocusListener(new FocusListener() { public void focusLost(FocusEvent e) { if (filterText.getText().isEmpty()) { updating = true; filterText.setText(DEFAULT_FILTER_TEXT); updating = false; } } public void focusGained(FocusEvent e) { if (DEFAULT_FILTER_TEXT.equals(filterText.getText())) { updating = true; filterText.setText(""); //$NON-NLS-1$ updating = false; } } }); filterText.addTraverseListener(traverseListener); serviceInfoComposite = new Group(comp, SWT.NONE); serviceInfoComposite.setText(Messages.CloudFoundryServiceWizardPage1_TEXT_SET_NAME_PLAN); data = new GridData(GridData.FILL, GridData.FILL, true, false, 3, 1); data.verticalIndent = 5; serviceInfoComposite.setLayoutData(data); layout = new GridLayout(); layout.numColumns = 2; layout.marginHeight = 5; layout.marginWidth = 5; serviceInfoComposite.setLayout(layout); serviceInfoComposite.setVisible(false); servicePageValidation.updatePageState(); Dialog.applyDialogFont(comp); setControl(comp); MouseWheelListener mwl = new MouseWheelListener() { public void mouseScrolled(MouseEvent event) { scrollComp.setFocus(); } }; filterText.addMouseWheelListener(mwl); // nameText.addMouseWheelListener(mwl); } private Composite createLayoutList(Composite parent) { Composite result = new Composite(parent, SWT.NONE); result.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); Display display = result.getDisplay(); Font font = result.getFont(); if (boldFont == null) { FontData[] fontData = font.getFontData(); fontData[0].setStyle(SWT.BOLD); fontData[0].setHeight(fontData[0].getHeight()); boldFont = new Font(display, fontData); Color c1 = display.getSystemColor(SWT.COLOR_LIST_SELECTION); Color c2 = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); selEdgeColor = new Color(display, (c1.getRed() + c2.getRed() * 3) / 4, (c1.getGreen() + c2.getGreen() * 3) / 4, (c1.getBlue() + c2.getBlue() * 3) / 4); selFillColor = new Color(display, (c1.getRed() + c2.getRed() * 8) / 9, (c1.getGreen() + c2.getGreen() * 8) / 9, (c1.getBlue() + c2.getBlue() * 8) / 9); result.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent event) { if (boldFont != null && !boldFont.isDisposed()) { boldFont.dispose(); } if (selEdgeColor != null && !selEdgeColor.isDisposed()) { selEdgeColor.dispose(); } if (selFillColor != null && !selFillColor.isDisposed()) { selFillColor.dispose(); } } }); } result.addPaintListener(new PaintListener() { public void paintControl(PaintEvent event) { if (selectedServicesList != null && selectedServicesList.size() > 0 && selectedServicesList.get(0).getNameLabel().isVisible()) { Rectangle r = selectedServicesList.get(0).getAppxLocation(); if (r != null) { Rectangle r2 = new Rectangle(r.x, r.y, r.width, r.height); r2.x -= 3; r2.width += 6; r2.y -= 3; r2.height += 6; event.gc.setBackground(selFillColor); event.gc.fillRoundRectangle(r2.x, r2.y, r2.width, r2.height, 7, 7); event.gc.setForeground(selEdgeColor); event.gc.drawRoundRectangle(r2.x, r2.y, r2.width, r2.height, 7, 7); } } } }); Color color = result.getBackground(); updateConfiguration(); Control[] tabList = new Control[allServicesList.size()]; for (int x = 0; x < allServicesList.size(); x++) { CFServiceWizUI service = allServicesList.get(x); final Label imgLabel = new Label(result, SWT.NONE); imgLabel.setBackground(color); imgLabel.setData(service); final Label nameLabel = new Label(result, SWT.WRAP); nameLabel.setData(service); nameLabel.setFont(boldFont); nameLabel.setBackground(color); nameLabel.setText(service.getName()); tabList[x] = nameLabel; final Label descLabel = new Label(result, SWT.WRAP); descLabel.setData(service); descLabel.setBackground(color); String desc = service.getDescription(); descLabel.setText(trimTextAtBound(desc, 200)); service.setDescriptionLabel(descLabel); service.setImageLabel(imgLabel); service.setNameLabel(nameLabel); imgLabel.addMouseListener(new CFWizSelectItemListener(service)); nameLabel.addMouseListener(new CFWizSelectItemListener(service)); descLabel.addMouseListener(new CFWizSelectItemListener(service)); descLabel.setToolTipText(wrapAndTrimTextAtBound(desc, 100)); } result.addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { for (final CFServiceWizUI service : allServicesList) { if (service.getAppxLocation().contains(e.x, e.y)) { selectItem(service); break; } } } }); result.setTabList(tabList); for (Control c : tabList) { c.addTraverseListener(traverseListener); } return result; } @Override public void setVisible(boolean isVis) { super.setVisible(isVis); if (isVis) { // Set focus on scrolled composite whenever we become visible to // prevent missing mouse wheel events scrollComp.setFocus(); updateConfiguration(); updateServicesTable(); } } protected void updateServicesTable() { Control content = scrollComp.getContent(); if (content != null) { content.dispose(); } if (allServicesList.isEmpty()) { Composite labelComp = new Composite(scrollComp, SWT.NONE); GridLayout layout = new GridLayout(); layout.horizontalSpacing = 8; layout.verticalSpacing = 12; labelComp.setLayout(layout); labelComp.setBackground(scrollComp.getBackground()); Label descriptionLabel = new Label(labelComp, SWT.NONE); descriptionLabel.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, false, false)); descriptionLabel.setBackground(scrollComp.getBackground()); descriptionLabel.setText(Messages.CloudFoundryServiceWizardPage1_LABEL_NO_AVAIL_SERVICE); scrollComp.setContent(labelComp); labelComp.setSize(labelComp.computeSize(SWT.DEFAULT, SWT.DEFAULT)); return; } final Composite comp = new Composite(scrollComp, SWT.NONE); comp.setBackground(scrollComp.getBackground()); comp.setLayout(new CFServiceWizardLayout(this)); comp.addPaintListener(new PaintListener() { public void paintControl(PaintEvent event) { List<CFServiceWizUI> selected = selectedServicesList; if (selected != null && selected.size() == 1) { Rectangle r = selected.get(0).getAppxLocation(); if (r != null) { Rectangle r2 = new Rectangle(r.x, r.y, r.width, r.height); r2.x -= 3; r2.width += 6; r2.y -= 3; r2.height += 6; event.gc.setBackground(selFillColor); event.gc.fillRoundRectangle(r2.x, r2.y, r2.width, r2.height, 7, 7); event.gc.setForeground(selEdgeColor); event.gc.drawRoundRectangle(r2.x, r2.y, r2.width, r2.height, 7, 7); } } } }); for (final CFServiceWizUI item : allServicesList) { Color c = null; boolean areWeInSelectedList = selectedServicesList.contains(item); if (areWeInSelectedList) { c = selFillColor; } else { c = comp.getBackground(); } if (filter == null || filter.isEmpty() || matches(item, filter)) { final Label imgLabel = new Label(comp, SWT.NONE); imgLabel.setBackground(c); imgLabel.setData(item); if (loader != null) { // // Uncomment out these lines for dynamic icon acquisition // imgLabel.addPaintListener(new PaintListener() { // // public void paintControl(PaintEvent e) { // // if(imgLabel.getImage() == null) { // loader.addIconToRetrieveList(item.getOffering(), // imgLabel); // } // } // }); loader.addIconToRetrieveList(item.getOffering(), imgLabel); } final Label nameLabel = new Label(comp, SWT.WRAP); nameLabel.setFont(boldFont); nameLabel.setBackground(c); nameLabel.setText(item.getName()); nameLabel.setData(item); final Label descLabel = new Label(comp, SWT.WRAP); descLabel.setBackground(c); String desc = item.getDescription(); if (desc == null) { desc = ""; //$NON-NLS-1$ } descLabel.setText(trimTextAtBound(desc, 200)); descLabel.setToolTipText(wrapAndTrimTextAtBound(desc, 100)); descLabel.setData(item); String hyperLink = null; if (hyperLink == null) { hyperLink = item.getOffering().getUrl(); } if (hyperLink == null) { hyperLink = item.getOffering().getInfoUrl(); } if (hyperLink == null) { hyperLink = item.getOffering().getDocumentationUrl(); } // final String hyperLinkFinal = hyperLink; final Hyperlink hyperLinkLabel = new Hyperlink(comp, SWT.WRAP); hyperLinkLabel.setBackground(c); hyperLinkLabel.setText(""); //$NON-NLS-1$ hyperLinkLabel.setVisible(false); // if(hyperLink != null) { // hyperLinkLabel.setText("(link)"); // hyperLinkLabel.setForeground(comp.getDisplay().getSystemColor(SWT.COLOR_DARK_BLUE)); // hyperLinkLabel.setUnderlined(true); // hyperLinkLabel.addHyperlinkListener(new HyperlinkAdapter() { // public void linkActivated(HyperlinkEvent e) { // if(hyperLinkFinal != null) { // org.eclipse.swt.program.Program.launch(hyperLinkFinal); // } // } // }); // } CFWizSelectItemListener itemListener = new CFWizSelectItemListener(item); imgLabel.addMouseListener(itemListener); nameLabel.addMouseListener(itemListener); descLabel.addMouseListener(itemListener); if (item != null) { if (item.getImageLabel() != null) item.getImageLabel().setVisible(true); if (item.getNameLabel() != null) item.getNameLabel().setVisible(true); if (item.getDescriptionLabel() != null) item.getDescriptionLabel().setVisible(true); } } else { // Filtered out items have no location item.setAppxLocation(null); if (item != null) { if (item.getImageLabel() != null) item.getImageLabel().setVisible(false); if (item.getNameLabel() != null) item.getNameLabel().setVisible(false); if (item.getDescriptionLabel() != null) item.getDescriptionLabel().setVisible(false); } } } if (layoutList != null) layoutList.layout(); comp.addMouseListener(new MouseListener() { public void mouseDown(MouseEvent e) { for (final CFServiceWizUI item : allServicesList) { // Skip filtered items that has null appxLocation. if (item.getAppxLocation() != null && item.getAppxLocation().contains(e.x, e.y)) { selectItem(item); break; } } } public void mouseDoubleClick(MouseEvent arg0) { // Ignore } public void mouseUp(MouseEvent arg0) { // Ignore } }); scrollComp.setContent(comp); Rectangle r = scrollComp.getClientArea(); r.height = comp.computeSize(r.width, SWT.DEFAULT).y; comp.setBounds(r); for (Control c : serviceInfoComposite.getChildren()) { c.dispose(); } createServiceInfoComposite(); serviceInfoComposite.layout(); topComp.layout(); servicePageValidation.updatePageState(); } private void createServiceInfoComposite() { String name = ""; //$NON-NLS-1$ final CFServiceWizUI selectedItemFinal; if (selectedServicesList.size() == 1) { selectedItemFinal = selectedServicesList.get(0); name = selectedItemFinal.getName(); } else { selectedItemFinal = null; } serviceInfoComposite.setEnabled(selectedItemFinal != null); final Label serviceNameLabel = new Label(serviceInfoComposite, SWT.NONE); serviceNameLabel.setText(Messages.COMMONTXT_NAME_WITH_COLON); GridData data = new GridData(GridData.BEGINNING, GridData.CENTER, false, false); serviceNameLabel.setLayoutData(data); serviceNameLabel.setEnabled(selectedItemFinal != null); serviceNameText = new Text(serviceInfoComposite, SWT.SEARCH); serviceNameText.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false, 1, 1)); serviceNameText.setText(utilRemoveSpaces(name)); serviceNameText.setEnabled(selectedItemFinal != null); serviceNameText.addTraverseListener(traverseListener); if (selectedItemFinal != null) { selectedItemFinal.setUserDefinedName(utilRemoveSpaces(name)); } serviceNameText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { String text = serviceNameText.getText(); if (selectedItemFinal != null) { selectedItemFinal.setUserDefinedName(text); } servicePageValidation.updatePageState(); } }); final Label servicePlanLabel = new Label(serviceInfoComposite, SWT.NONE); servicePlanLabel.setText(Messages.CloudFoundryServiceWizardPage1_LABEL_PLAN); servicePlanLabel.setEnabled(selectedItemFinal != null); data = new GridData(GridData.BEGINNING, GridData.CENTER, false, false); servicePlanLabel.setLayoutData(data); @SuppressWarnings("unchecked") final List<CloudServicePlan> plans = selectedItemFinal != null ? selectedItemFinal.getOffering().getCloudServicePlans() : Collections.EMPTY_LIST; String[] items = new String[plans.size()]; for (int x = 0; x < items.length; x++) { items[x] = plans.get(x).getName(); } final Combo typeCombo = new Combo(serviceInfoComposite, SWT.READ_ONLY | SWT.BORDER); typeCombo.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false, 1, 1)); typeCombo.setItems(items); typeCombo.select(0); typeCombo.setEnabled(selectedItemFinal != null); typeCombo.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { if (selectedItemFinal != null) { selectedItemFinal.setPlan(plans.get(typeCombo.getSelectionIndex())); } } public void widgetDefaultSelected(SelectionEvent e) { // Ignore. } }); serviceInfoComposite.setVisible(true); } private static String utilRemoveSpaces(String str) { if (str == null) { return null; } else { return str.replace(" ", ""); //$NON-NLS-1$ //$NON-NLS-2$ } } private void selectItem(CFServiceWizUI item) { if (selectedServicesList == null) return; if (selectedServicesList.contains(item)) { // Do nothing to disable deselect. return; } else { selectedServicesList.clear(); selectedServicesList.add(item); // If the plan has not been set, use the first if (item != null && item.getPlan() == null) { List<CloudServicePlan> plans = item.getOffering().getCloudServicePlans(); if (plans != null && plans.size() > 0) { item.setPlan(plans.get(0)); } } } servicePageValidation.updatePageState(); Point p = scrollComp.getOrigin(); updateServicesTable(); scrollComp.setFocus(); scrollComp.setOrigin(p); if (selectedServicesList.size() > 0) { selectServiceListItem(selectedServicesList.get(0)); } } /** Update the selection rectangle in the composite for the service*/ private void selectServiceListItem(CFServiceWizUI service) { if (service == null) return; if (selectedServicesList != null && selectedServicesList.size() > 0) { // Set the previously selected item back to normal selectedServicesList.get(0).getDescriptionLabel().setBackground(layoutList.getBackground()); selectedServicesList.get(0).getImageLabel().setBackground(layoutList.getBackground()); selectedServicesList.get(0).getNameLabel().setBackground(layoutList.getBackground()); } service.getDescriptionLabel().setBackground(selFillColor); service.getImageLabel().setBackground(selFillColor); service.getNameLabel().setBackground(selFillColor); service.getNameLabel().forceFocus(); layoutList.redraw(); if (service.getAppxLocation() != null) { Rectangle bounds = service.getAppxLocation(); //child.getBounds(); Rectangle area = scrollComp.getClientArea(); Point origin = scrollComp.getOrigin(); // Our view is lower than the item if (origin.y > bounds.y) { origin.y = Math.max(0, bounds.y); } // Our view is above the item if (origin.y + area.height < bounds.y + bounds.height) { origin.y = Math.max(0, bounds.y + bounds.height - area.height); } scrollComp.setOrigin(origin); } } class CFWizSelectItemListener implements SelectionListener, MouseListener { CFServiceWizUI service; public CFWizSelectItemListener(CFServiceWizUI item2) { this.service = item2; } public void widgetSelected(SelectionEvent event2) { selectItem(service); } public void mouseDoubleClick(MouseEvent arg0) { // Ignore. } public void mouseDown(MouseEvent arg0) { selectItem(service); } public void mouseUp(MouseEvent arg0) { // Ignore. } public void widgetDefaultSelected(SelectionEvent arg0) { // Ignore. } } protected boolean matches(CFServiceWizUI item, String filter) { return item.getName().toLowerCase().contains(filter.toLowerCase()) || item.getDescription().toLowerCase().contains(filter.toLowerCase()); } public String getServiceName() { return nameText.getText(); } public List<CFServiceWizUI> getSelectedList() { return selectedServicesList; } protected void initializeDialogUnits(Control testControl) { // Compute and store a font metric GC gc = new GC(testControl); gc.setFont(JFaceResources.getDialogFont()); fontMetrics = gc.getFontMetrics(); gc.dispose(); } public int convertHorizontalDLUsToPixels(Composite comp, int x) { if (fontMetrics == null) { initializeDialogUnits(comp); } return Dialog.convertHorizontalDLUsToPixels(fontMetrics, x); } public int convertVerticalDLUsToPixels(Composite comp, int y) { if (fontMetrics == null) { initializeDialogUnits(comp); } return Dialog.convertVerticalDLUsToPixels(fontMetrics, y); } private static String trimTextAtBound(String str, int bound) { if (str.length() > bound) { /** Use the existing word wrap function to wrap at the spot we want. */ String result = wrapAndTrimTextAtBound(str, bound); int index = result.indexOf("\n"); //$NON-NLS-1$ if (index == -1) { return str; } else { return str.substring(0, index) + "..."; //$NON-NLS-1$ } } else { return str; } } /** Simple text wrapper */ private static String wrapAndTrimTextAtBound(String text, int bound) { StringBuilder result = new StringBuilder(); String remainingString = text; // Last seen in the text int lastSpace = 0; for (int x = 0; x < remainingString.length(); x++) { if (remainingString.charAt(x) == ' ') { lastSpace = x; } if (x == bound) { // If there is no way to possible wrap it (e.g no spaces, or the // bound is too small) then don't bother if (lastSpace == 0) { return text; } String nextLine = remainingString.substring(0, lastSpace); remainingString = remainingString.substring(lastSpace).trim(); result.append(nextLine); result.append("\n"); //$NON-NLS-1$ x = 0; lastSpace = 0; } } result.append(remainingString.trim()); return result.toString(); } protected boolean isIconProviderAvailable() { return loader != null; } @Override public void dispose() { super.dispose(); if (loader != null) { this.loader.dispose(); } } class ServiceListTraverseListener implements TraverseListener { @Override public void keyTraversed(TraverseEvent e) { if (((e.getSource() == filterText) && (e.detail == SWT.TRAVERSE_TAB_NEXT)) || ((e.getSource() == serviceNameText) && (e.detail == SWT.TRAVERSE_TAB_PREVIOUS))) { // Only one service can currently be selected right now int listSize = selectedServicesList.size(); if (selectedServicesList != null && allServicesList != null) { // An item was previously selected. Select that item again if (listSize > 0) { CFServiceWizUI item = selectedServicesList.get(0); if (item != null && item.getNameLabel() != null && item.getNameLabel().isVisible()) { selectServiceListItem(item); // If doit is set to true, then the cursor will change focus to // the next text box. That behaviour is not good as it is difficult // for accessibility to lose focus of the items in the list e.doit = false; return; } } // Item is no longer selected (e.g. filter is used), so choose another item CFServiceWizUI service = null; int len = allServicesList.size(); for (int i = 0; i < len; i++) { CFServiceWizUI currItem = allServicesList.get(i); if (currItem != null && currItem.getNameLabel() != null) { if (allServicesList.get(i).getNameLabel().isVisible()) { service = allServicesList.get(i); break; } } } // Service can be null if filter leads to 0 results if (service != null) { selectItem(service); } else { // If there are no items, the user is stuck in the keyboard only case if // they tab next. Focus on the next text box instead if (((e.getSource() == filterText) && (e.detail == SWT.TRAVERSE_TAB_NEXT)) && serviceNameText != null) { serviceNameText.forceFocus(); } else if (((e.getSource() == serviceNameText) && (e.detail == SWT.TRAVERSE_TAB_PREVIOUS)) && filterText != null) { filterText.forceFocus(); } } } // If doit is set to true, then the cursor will change focus to // the next text box. That behaviour is not good as it is difficult // for accessibility to lose focus of the items in the list e.doit = false; } else { if (e.getSource() instanceof Label && e.getSource() != null && ((Label) e.getSource()).getData() instanceof CFServiceWizUI && allServicesList != null) { // If you press tab while in the list, go outside of the list if (e.detail == SWT.TRAVERSE_TAB_NEXT) { if (serviceNameText != null) serviceNameText.forceFocus(); } if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS) { if (filterText != null) filterText.forceFocus(); } CFServiceWizUI selectedService = (CFServiceWizUI) ((Label) e.getSource()).getData(); CFServiceWizUI service = null; int servicePos = -1; for (int x = 0; x < allServicesList.size(); x++) { if (selectedService == allServicesList.get(x)) { servicePos = x; break; } } // If you press the arrow keys inside the list, go to the next visible item if (servicePos != -1) { if (e.detail == SWT.TRAVERSE_ARROW_NEXT) { int len = allServicesList.size(); while (servicePos + 1 < len) { servicePos++; CFServiceWizUI currItem = allServicesList.get(servicePos); if (currItem != null && currItem.getNameLabel() != null && currItem.getNameLabel().isVisible()) { service = allServicesList.get(servicePos); break; } } } else if (e.detail == SWT.TRAVERSE_ARROW_PREVIOUS) { while (servicePos > 0) { servicePos--; CFServiceWizUI currItem = allServicesList.get(servicePos); if (currItem != null && currItem.getNameLabel() != null && currItem.getNameLabel().isVisible()) { service = allServicesList.get(servicePos); break; } } } } if (service != null) { selectItem(service); } } } } } } class CFServiceWizUI { private CloudServiceOffering offering; private Button button; private Rectangle appxLocation; private String userDefinedName = ""; //$NON-NLS-1$ private CloudServicePlan plan = null; Label nameLabel; Label descLabel; Label imageLabel; public CFServiceWizUI(CloudServiceOffering offering) { this.offering = offering; } public String getName() { return offering.getName(); } public String getDescription() { return offering.getDescription(); } public Button getButton() { return button; } public void setButton(Button button) { this.button = button; } public Rectangle getAppxLocation() { return appxLocation; } public void setAppxLocation(Rectangle appxLocation) { this.appxLocation = appxLocation; } public String getUserDefinedName() { return userDefinedName; } public void setUserDefinedName(String userDefinedName) { this.userDefinedName = userDefinedName; } public CloudServiceOffering getOffering() { return offering; } public CloudServicePlan getPlan() { return plan; } public void setPlan(CloudServicePlan plan) { this.plan = plan; } @Override public boolean equals(Object o) { if (!(o instanceof CFServiceWizUI)) { return false; } CloudServiceOffering offering = ((CFServiceWizUI) o).getOffering(); String name1 = "" + getOffering().getName(); //$NON-NLS-1$ String name2 = "" + offering.getName(); //$NON-NLS-1$ return name1.equals(name2); } public LocalCloudService convertToLocalCloudService() { final LocalCloudService localService = new LocalCloudService(""); //$NON-NLS-1$ localService.setName(getUserDefinedName()); localService.setVersion(getOffering().getVersion()); localService.setLabel(getOffering().getLabel()); localService.setProvider(getOffering().getProvider()); localService.setPlan(getPlan().getName()); return localService; } public Label getNameLabel() { return nameLabel; } public void setNameLabel(Label nameLabel) { this.nameLabel = nameLabel; } public Label getDescriptionLabel() { return descLabel; } public void setDescriptionLabel(Label descLabel) { this.descLabel = descLabel; } public Label getImageLabel() { return imageLabel; } public void setImageLabel(Label imageLabel) { this.imageLabel = imageLabel; } } class CFServiceWizardLayout extends Layout { private static final int BORDER = 5; private static int IMG_DEFAULT = 32; private static final int GAP = 3; private static final int V_SPACE = 3; protected int colWidth = -1; CloudFoundryServiceWizardPage1 parent; public CFServiceWizardLayout(CloudFoundryServiceWizardPage1 parent) { this.parent = parent; } @Override protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { int[] result = layoutImpl(composite, 0, flushCache, false); return new Point(result[0], result[1]); } @Override protected void layout(Composite composite, boolean flushCache) { int[] result = layoutImpl(composite, 0, flushCache, true); // Adjust the height to match the elments inside Rectangle r = composite.getBounds(); r.height = result[1]; composite.setBounds(r); } protected int[] layoutImpl(Composite composite, int wHint, boolean flushCache, boolean apply) { Rectangle r = composite.getClientArea(); int areaW = r.width > 0 ? r.width : wHint; int i = 0; int x = r.x + BORDER; int y = r.y + BORDER; int w = 0; if (areaW == 0) { w = composite.getParent().getBounds().width - composite.getParent().getBorderWidth() * 2; } else { w = areaW - BORDER * 2; } w = Math.max(400, w); int bottomMostY = -1; int rightMostX = -1; boolean iconsAvailable = parent.isIconProviderAvailable(); int IMG; if (iconsAvailable) { IMG = IMG_DEFAULT; } else { IMG = 0; } // children are: image, name, description, size Control[] children = composite.getChildren(); final int initialX = x; while (i < children.length) { x = initialX; if (i > 0) { y += V_SPACE; } Label localIconLabel = (Label) children[i]; Label localNameLabel = (Label) children[i + 1]; CFServiceWizUI product = (CFServiceWizUI) localNameLabel.getData(); // image if (apply) { localIconLabel.setBounds(x, y, IMG, IMG); } if (iconsAvailable) { x += 3; } // label Point p = localNameLabel.computeSize(w - IMG - GAP, SWT.DEFAULT, flushCache); if (apply) { localNameLabel.setBounds(x + IMG + GAP, y, p.x, p.y); } y += p.y + GAP; // hyperlink Hyperlink localLink = (Hyperlink) children[i + 3]; p = localLink.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); if (apply) { localLink.setBounds(x + w - p.x - 2, y, p.x, p.y); } // description Label localDescr = (Label) children[i + 2]; p = localDescr.computeSize(w - IMG - GAP * 2, SWT.DEFAULT, flushCache); if (apply) { localDescr.setBounds(x + IMG + GAP, y, p.x, p.y); } y = y + p.y; Rectangle imgLabelRect = localIconLabel.getBounds(); int topLeftX = imgLabelRect.x; int topLeftY = imgLabelRect.y; Rectangle descLabelRect = localDescr.getBounds(); int botRightX = descLabelRect.x + descLabelRect.width; int botRightY = descLabelRect.y + descLabelRect.height; bottomMostY = botRightY; rightMostX = botRightX; Rectangle rowPos = new Rectangle(topLeftX, topLeftY, botRightX - topLeftX, botRightY - topLeftY); if (apply) { product.setAppxLocation(rowPos); } y += BORDER; i += 4; } bottomMostY += BORDER; return new int[] { rightMostX, bottomMostY }; } } class CFWizServicePage1Validation { private static Pattern VALID_CHARS = Pattern.compile("[A-Za-z\\$_0-9\\-]+"); //$NON-NLS-1$ CloudFoundryServiceWizardPage1 wizardPage; public CFWizServicePage1Validation(CloudFoundryServiceWizardPage1 wizardPage) { this.wizardPage = wizardPage; } public void updatePageState() { boolean descriptionUpdated = false; if (!descriptionUpdated) { List<CFServiceWizUI> list = wizardPage.getSelectedList(); if (list == null || list.size() == 0) { wizardPage.setDescription(Messages.CloudFoundryServiceWizardPage1_TEXT_SELECT_SERVICE_LIST); wizardPage.setErrorMessage(null); descriptionUpdated = true; } } if (!descriptionUpdated) { List<CFServiceWizUI> list = wizardPage.getSelectedList(); if (list != null && list.size() == 1) { CFServiceWizUI service = list.get(0); String userDefinedName = service.getUserDefinedName(); if (service != null && userDefinedName != null) { if (userDefinedName.trim().length() == 0) { wizardPage.setDescription(Messages.CloudFoundryServiceWizardPage1_TEXT_SET_SERVICE_NAME); wizardPage.setErrorMessage(null); descriptionUpdated = true; } Matcher matcher = VALID_CHARS.matcher(userDefinedName); if (!descriptionUpdated && !matcher.matches()) { wizardPage.setErrorMessage(Messages.CloudFoundryServiceWizardPage1_ERROR_INVALID_CHAR); wizardPage.setDescription(null); descriptionUpdated = true; } } } } if (!descriptionUpdated) { wizardPage.setErrorMessage(null); wizardPage.setDescription(CloudFoundryServiceWizardPage1.DESCRIPTION_FINAL); wizardPage.setPageComplete(true); } else { wizardPage.setPageComplete(false); } } } class CFServiceWizardDynamicIconLoader extends Thread { private final Object lock = new Object(); // List synchronized on lock private List<ServiceWizardMapEntry> iconsToRetrieve = new ArrayList<ServiceWizardMapEntry>(); private CloudFoundryServer server; private ICloudFoundryServiceWizardIconProvider iconProvider; private boolean isRunning = true; // Synchronize on imageMap when accessing it private Map<String /* service id + provider */, Image> imageMap = new HashMap<String /* * offering * id */, Image>(); public CFServiceWizardDynamicIconLoader(ICloudFoundryServiceWizardIconProvider iconProvider, CloudFoundryServer server) { super(CFServiceWizardDynamicIconLoader.class.getName()); setDaemon(true); this.iconProvider = iconProvider; this.server = server; } /** * Add icon to the front of the list, for icons the user is currently * viewing */ public void addIconToFrontOfRetrieveList(CloudServiceOffering offering, Label imageLabel) { synchronized (lock) { // Add to front (this may create a duplicate; dupes are checked in // IconRetrieveRunnable) ServiceWizardMapEntry me = new ServiceWizardMapEntry(offering, imageLabel); iconsToRetrieve.add(0, me); lock.notifyAll(); } } /** Add icon to end of list */ public void addIconToRetrieveList(CloudServiceOffering offering, Label imageLabel) { ServiceWizardMapEntry me = new ServiceWizardMapEntry(offering, imageLabel); synchronized (lock) { iconsToRetrieve.add(me); lock.notifyAll(); } } public void run() { // Have up to 5 URL requests running at a time ExecutorService es = Executors.newFixedThreadPool(5); while (isRunning) { try { List<ServiceWizardMapEntry> localIconsToRetrieve = null; synchronized (lock) { lock.wait(1000); if (isRunning && iconsToRetrieve.size() > 0) { localIconsToRetrieve = new ArrayList<ServiceWizardMapEntry>(); localIconsToRetrieve.addAll(iconsToRetrieve); iconsToRetrieve.clear(); } } if (isRunning && localIconsToRetrieve != null) { // Process icon requests outside the lock for (ServiceWizardMapEntry e : localIconsToRetrieve) { IconRetrieveRunnable r = new IconRetrieveRunnable(e); es.execute(r); } localIconsToRetrieve.clear(); } } catch (InterruptedException e) { isRunning = false; } } es.shutdownNow(); try { // Wait at most 10 seconds for the remaining tasks to finish es.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e1) { // ignore. } synchronized (imageMap) { // Dispose of old images Set<Entry<String, Image>> s = imageMap.entrySet(); for (Entry<String, Image> e : s) { if (e.getValue().isDisposed()) { e.getValue().dispose(); } } } } public void dispose() { synchronized (lock) { isRunning = false; } } private class IconRetrieveRunnable implements Runnable { ServiceWizardMapEntry entry; public IconRetrieveRunnable(ServiceWizardMapEntry entry) { this.entry = entry; } public void run() { CloudServiceOffering cso = entry.getOffering(); final String mapId = "" + cso.getName() + "-" + cso.getProvider(); //$NON-NLS-1$ //$NON-NLS-2$ Image result = null; // Check the cache for the image synchronized (imageMap) { Image i = imageMap.get(mapId); if (i != null) { result = i; } } // Grab the image from the provider, if needed if (result == null) { ImageDescriptor imageDesc = iconProvider.getServiceIcon(entry.getOffering(), server); if (imageDesc != null) { Image img = imageDesc.createImage(); final Image resizeImg = resizeImage(img, 32, 32); result = resizeImg; synchronized (imageMap) { imageMap.put(mapId, resizeImg); } } } final Image labelImage = result; if (labelImage == null) return; // Replace the image label Display.getDefault().asyncExec(new Runnable() { public void run() { Label l = entry.getImageLabel(); if (!l.isDisposed()) { l.setImage(labelImage); } } }); } private Image resizeImage(Image oldImage, int newWidth, int newHeight) { Image newImage = new Image(Display.getDefault(), newWidth, newHeight); GC gc = new GC(newImage); gc.setAntialias(SWT.ON); gc.setInterpolation(SWT.HIGH); gc.drawImage(oldImage, 0, 0, oldImage.getBounds().width, oldImage.getBounds().height, 0, 0, newWidth, newHeight); gc.dispose(); oldImage.dispose(); return newImage; } } private static class ServiceWizardMapEntry { private CloudServiceOffering offering; private Label imageLabel; public ServiceWizardMapEntry(CloudServiceOffering offering, Label imageLabel) { super(); this.offering = offering; this.imageLabel = imageLabel; } public CloudServiceOffering getOffering() { return offering; } public Label getImageLabel() { return imageLabel; } } }