// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.debug.ui.source; import static org.chromium.debug.ui.DialogUtils.createErrorOptional; import static org.chromium.debug.ui.DialogUtils.createOptional; import static org.chromium.debug.ui.DialogUtils.createProcessor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.chromium.debug.core.ReverseSourceLookup; import org.chromium.debug.ui.DialogUtils.ExpressionProcessor; import org.chromium.debug.ui.DialogUtils.Gettable; import org.chromium.debug.ui.DialogUtils.Message; import org.chromium.debug.ui.DialogUtils.MessagePriority; import org.chromium.debug.ui.DialogUtils.OkButtonControl; import org.chromium.debug.ui.DialogUtils.Optional; import org.chromium.debug.ui.DialogUtils.Scope; import org.chromium.debug.ui.DialogUtils.Updater; import org.chromium.debug.ui.DialogUtils.ValueConsumer; import org.chromium.debug.ui.DialogUtils.ValueProcessor; import org.chromium.debug.ui.DialogUtils.ValueSource; import org.chromium.debug.ui.source.SourceNameMapperContainerDialog.ConfigureButtonAction; import org.chromium.debug.ui.source.SourceNameMapperContainerDialog.ContainerStatusGroup; import org.chromium.debug.ui.source.SourceNameMapperContainerDialog.Elements; import org.chromium.debug.ui.source.SourceNameMapperContainerDialog.PresetFieldValues; import org.chromium.debug.ui.source.SourceNameMapperContainerDialog.Result; import org.eclipse.debug.core.sourcelookup.ISourceContainer; import org.eclipse.debug.core.sourcelookup.ISourceContainerType; import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.osgi.util.NLS; 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.events.SelectionListener; import org.eclipse.swt.graphics.Image; /** * A separated logic of {@link SourceNameMapperContainerDialog}. It describes how data flows * from input elements to the OK button with all necessary asserts. Basically it only uses * {@link SourceNameMapperContainerDialog.Elements} interface from the dialog. */ abstract class SourceNameMapperContainerDialogLogic { abstract Result getResult(); abstract void updateAll(); static SourceNameMapperContainerDialogLogic create( final Elements elements, final ISourceLookupDirector director, final PresetFieldValues initialParams) { final Updater updater = new Updater(); Scope rootScope = updater.rootScope(); final List<ValueSource<String>> warningSources = new ArrayList<ValueSource<String>>(2); // Represents value entered as prefix. final ValueSource<String> prefixEditor = new ValueSource<String>() { public String getValue() { return elements.getPrefixField().getText(); } { if (initialParams != null) { elements.getPrefixField().setText(initialParams.getPrefix()); } final ValueSource<String> updatableThis = this; ModifyListener listener = new ModifyListener() { public void modifyText(ModifyEvent e) { updater.reportChanged(updatableThis); updater.update(); } }; elements.getPrefixField().addModifyListener(listener); } }; updater.addSource(rootScope, prefixEditor); // Represents prefix value after it has been validated. final ValueProcessor<Optional<String>> prefixValue = new ExpressionProcessor<String>( Collections.<ValueSource<? extends Optional<?>>>emptyList()) { @Override protected Optional<String> calculateNormal() { String prefix = prefixEditor.getValue(); Optional<String> result; if (prefix == null || prefix.length() == 0) { return createErrorOptional(new Message( Messages.SourceNameMapperContainerDialog_ENTER_PREFIX, MessagePriority.BLOCKING_INFO)); } else { return createOptional(prefix); } } }; updater.addSource(rootScope, prefixValue); updater.addConsumer(rootScope, prefixValue); updater.addDependency(prefixValue, prefixEditor); // Represents possible warning about prefix value having no trailing slash. ValueProcessor<String> noSlashWarning = new ValueProcessor<String>() { public void update(Updater updater) { Optional<String> prefix = prefixValue.getValue(); String result; if (prefix.isNormal() && !prefix.getNormal().endsWith("/")) { //$NON-NLS-1$ result = Messages.SourceNameMapperContainerDialog_PREFIX_NORMALLY_ENDS; } else { result = null; } setCurrentValue(result); updater.reportChanged(this); } }; updater.addSource(rootScope, noSlashWarning); updater.addConsumer(rootScope, noSlashWarning); updater.addDependency(noSlashWarning, prefixValue); warningSources.add(noSlashWarning); // Represents prefix rule example printer. ValueConsumer prefixExample = new ValueConsumer() { public void update(Updater updater) { Optional<String> prefix = prefixValue.getValue(); String line1; String line2; if (prefix.isNormal()) { String sampleFileName = Messages.SourceNameMapperContainerDialog_SAMPLE_FILE_NAME; line1 = NLS.bind(Messages.SourceNameMapperContainerDialog_EXAMPLE_1, prefix.getNormal() + sampleFileName); line2 = NLS.bind(Messages.SourceNameMapperContainerDialog_EXAMPLE_2, sampleFileName); } else { line1 = ""; //$NON-NLS-1$ line2 = ""; //$NON-NLS-1$ } elements.getPrefixExampleLine1Label().setText(line1); elements.getPrefixExampleLine2Label().setText(line2); } }; updater.addConsumer(rootScope, prefixExample); updater.addDependency(prefixExample, prefixValue); // Represents container type combo box. final ValueSource<ISourceContainerType> selectedTypeValue = new ValueSource<ISourceContainerType>() { public ISourceContainerType getValue() { return elements.getContainerTypeCombo().getSelected(); } { if (initialParams != null) { ISourceContainerType type = initialParams.getContainer().getType(); elements.getContainerTypeCombo().setSelected(type); } final ValueSource<ISourceContainerType> updatableThis = this; SelectionListener listener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { updater.reportChanged(updatableThis); updater.update(); } }; elements.getContainerTypeCombo().addSelectionListener(listener); } }; updater.addSource(rootScope, selectedTypeValue); // Represents "Configure" button that acts like a container factory. final ValueProcessor<ISourceContainer> containerFactoryButtonValue = new ValueProcessor<ISourceContainer>() { private ConfigureButtonAction preparedAction = null; { if (initialParams != null) { setCurrentValue(initialParams.getContainer()); } final ValueSource<ISourceContainer> valueSourceThis = this; elements.getConfigureButton().addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (preparedAction != null) { ISourceContainer value = preparedAction.run(elements.getShell()); if (value != null) { setCurrentValue(value); } updater.reportChanged(valueSourceThis); updater.update(); updateAction(); } } }); } public void update(Updater updater) { if (getValue() != null && !getValue().getType().equals(selectedTypeValue.getValue())) { setCurrentValue(null); updater.reportChanged(this); } updateAction(); } private void updateAction() { preparedAction = SourceNameMapperContainerDialog.prepareConfigureAction( selectedTypeValue.getValue(), getValue(), director); elements.getConfigureButton().setEnabled(preparedAction != null); } }; updater.addSource(rootScope, containerFactoryButtonValue); updater.addConsumer(rootScope, containerFactoryButtonValue); updater.addDependency(containerFactoryButtonValue, selectedTypeValue); // Represents printer that shows type and name of the created container. ValueConsumer showContainerTypeValue = new ValueConsumer() { public void update(Updater updater) { ISourceContainer container = containerFactoryButtonValue.getValue(); String status; Image image; String name; boolean enabled; if (container == null) { status = Messages.SourceNameMapperContainerDialog_NOTHING_CONFIGURED; name = ""; //$NON-NLS-1$ image = null; enabled = false; } else { status = Messages.SourceNameMapperContainerDialog_CONFIGURED_CONTAINER; ISourceContainerType type = container.getType(); name = container.getName(); image = DebugUITools.getSourceContainerImage(type.getId()); enabled = true; } ContainerStatusGroup group = elements.getContainerStatusGroup(); group.getStatusLabel().setText(status); group.getTypeImageLabel().setImage(image); group.getContainerNameLabel().setText(name); group.setEnabled(enabled); group.layout(); } }; updater.addConsumer(rootScope, showContainerTypeValue); updater.addDependency(showContainerTypeValue, containerFactoryButtonValue); // Represents possible warning about unsupported container. ValueProcessor<String> unsupportedContainerWarning = createProcessor(new Gettable<String>() { @Override public String getValue() { ISourceContainer container = containerFactoryButtonValue.getValue(); if (container == null) { return null; } if (ReverseSourceLookup.isGoodTargetContainer(container)) { return null; } return Messages.SourceNameMapperContainerDialogLogic_TARGET_CONTAINER_NOT_SUPPORTED0; } }); updater.addSource(rootScope, unsupportedContainerWarning); updater.addConsumer(rootScope, unsupportedContainerWarning); updater.addDependency(unsupportedContainerWarning, containerFactoryButtonValue); warningSources.add(unsupportedContainerWarning); // Represents expression that constructs dialog window result. final ValueProcessor<? extends Optional<Result>> resultValue = new ExpressionProcessor<Result>( Arrays.<ValueSource<? extends Optional<?>>>asList(prefixValue) ) { @Override protected Optional<Result> calculateNormal() { final String prefix = prefixValue.getValue().getNormal(); final ISourceContainer container = containerFactoryButtonValue.getValue(); if (container == null) { return createErrorOptional( new Message(Messages.SourceNameMapperContainerDialog_CONFIGURE_TARGET_CONTAINER, MessagePriority.BLOCKING_INFO)); } Result result = new Result() { public ISourceContainer getResultContainer() { return container; } public String getResultPrefix() { return prefix; } }; return createOptional(result); } }; updater.addSource(rootScope, resultValue); updater.addConsumer(rootScope, resultValue); updater.addDependency(resultValue, prefixValue); updater.addDependency(resultValue, containerFactoryButtonValue); // Represents controller that updates state of OK button and error messages. final OkButtonControl<Result> okButtonControl = new OkButtonControl<Result>(resultValue, warningSources, elements); updater.addConsumer(rootScope, okButtonControl); updater.addDependency(okButtonControl, okButtonControl.getDependencies()); return new SourceNameMapperContainerDialogLogic() { @Override Result getResult() { return okButtonControl.getNormalValue(); } @Override void updateAll() { updater.updateAll(); } }; } }