Java tutorial
/** * Copyright (c) 2016 NumberFour AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * NumberFour AG - Initial API and implementation */ package eu.numberfour.n4js.ui.wizard.classifiers; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.UpdateValueStrategy; import org.eclipse.core.databinding.beans.BeanProperties; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.IValueChangeListener; import org.eclipse.core.databinding.observable.value.ValueChangeEvent; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import eu.numberfour.n4js.N4JSGlobals; import eu.numberfour.n4js.ui.dialog.ModuleSpecifierSelectionDialog; import eu.numberfour.n4js.ui.wizard.components.AccessModifierComponent; import eu.numberfour.n4js.ui.wizard.components.NameComponent; import eu.numberfour.n4js.ui.wizard.components.OtherClassifierModifiersComponent; import eu.numberfour.n4js.ui.wizard.components.WizardComponentDataConverters; import eu.numberfour.n4js.ui.wizard.generator.ContentBlock; import eu.numberfour.n4js.ui.wizard.generator.WorkspaceWizardGenerator; import eu.numberfour.n4js.ui.wizard.interfaces.N4JSInterfaceWizardModel; import eu.numberfour.n4js.ui.wizard.model.AccessModifier; import eu.numberfour.n4js.ui.wizard.model.DefinitionFileModel; import eu.numberfour.n4js.ui.wizard.workspace.PreviewableWizardPage; import eu.numberfour.n4js.ui.wizard.workspace.SuffixText; import eu.numberfour.n4js.ui.wizard.workspace.WizardPreviewProvider.WizardPreview; import eu.numberfour.n4js.ui.wizard.workspace.WorkspaceWizardModel; import eu.numberfour.n4js.ui.wizard.workspace.WorkspaceWizardModelValidator; import eu.numberfour.n4js.ui.wizard.workspace.WorkspaceWizardModelValidator.ValidationResult; /** * Generic wizard page for all N4JS classifiers. */ public abstract class N4JSNewClassifierWizardPage<M extends N4JSClassifierWizardModel> extends PreviewableWizardPage<M> { /** Component for the classifier name. */ protected NameComponent nameComponent; /** Component for the access modifier buttons with the bindings. */ protected AccessModifierComponent accessModifierComponent; /** Component for the other modifier, such as @N4JS or @Internal. */ protected OtherClassifierModifiersComponent otherClassifierModifiersComponent; @Override public void openModuleSpecifierDialog(Shell shell) { ModuleSpecifierSelectionDialog dialog = new ModuleSpecifierSelectionDialog(shell, getModel().getProject().append(getModel().getSourceFolder())); if (!getModel().getEffectiveModuleSpecifier().isEmpty()) { String initialSelectionSpecifier = getModel().getEffectiveModuleSpecifier(); String fileExtension = getModel().computeFileLocation().getFileExtension(); if (fileExtension != null) { dialog.setDefaultFileExtension(fileExtension); } dialog.setInitialSelection(initialSelectionSpecifier); } dialog.open(); Object result = dialog.getFirstResult(); if (result instanceof String) { IPath specifierPath = new Path((String) result); String fileExtension = specifierPath.getFileExtension(); // If the selected module specifier is a file if (fileExtension != null && !fileExtension.isEmpty()) { // and its file extension suggests a different external value than the model if (fileExtension.equals(N4JSGlobals.N4JSD_FILE_EXTENSION) != getModel().isDefinitionFile()) { // toggle the external value of the model getModel().setDefinitionFile(!getModel().isDefinitionFile()); } specifierPath = specifierPath.removeFileExtension(); } // If the last segment corresponds with the non-empty interface name remove it if (specifierPath.segmentCount() > 0 && specifierPath.removeFileExtension().lastSegment().equals(getModel().getName()) && !getModel().getName().isEmpty()) { if (specifierPath.segmentCount() > 1) { specifierPath = specifierPath.removeLastSegments(1).addTrailingSeparator(); } else { specifierPath = specifierPath.removeLastSegments(1); } } getModel().setModuleSpecifier(specifierPath.toString()); } } /** * Setup additional non-component contained bindings */ protected void setupBindings() { DataBindingContext dataBindingContext = this.getDataBindingContext(); IObservableValue moduleSpecifierValue = BeanProperties .value(WorkspaceWizardModel.class, WorkspaceWizardModel.MODULE_SPECIFIER_PROPERTY) .observe(getModel()); IObservableValue suffixVisibilityValue = BeanProperties .value(SuffixText.class, SuffixText.SUFFIX_VISIBILITY_PROPERTY) .observe(workspaceWizardControl.getModuleSpecifierText()); //// Only show the suffix on input values ending with a '/' character or empty module specifiers. dataBindingContext.bindValue(suffixVisibilityValue, moduleSpecifierValue, noUpdateValueStrategy(), WizardComponentDataConverters.strategyForPredicate(m -> { String moduleSpecifier = (String) m; return (moduleSpecifier.isEmpty() || moduleSpecifier.charAt(moduleSpecifier.length() - 1) == IPath.SEPARATOR); })); //// interface name to module specifier suffix binding IObservableValue interfaceNameModelValue = BeanProperties .value(N4JSInterfaceWizardModel.class, N4JSClassifierWizardModel.NAME_PROPERTY).observe(getModel()); IObservableValue greySuffixValue = BeanProperties.value(SuffixText.class, SuffixText.SUFFIX_PROPERTY) .observe(workspaceWizardControl.getModuleSpecifierText()); dataBindingContext.bindValue(greySuffixValue, interfaceNameModelValue, noUpdateValueStrategy(), new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE)); //// Enable n4js <-> Definition value(external) is selected IObservableValue externalValue = BeanProperties .value(DefinitionFileModel.class, N4JSClassifierWizardModel.DEFINITION_FILE_PROPERTY) .observe(getModel()); IObservableValue n4jsEnabled = WidgetProperties.enabled() .observe(otherClassifierModifiersComponent.getN4jsAnnotationBox()); dataBindingContext.bindValue(n4jsEnabled, externalValue, noUpdateValueStrategy(), WizardComponentDataConverters.strategyForPredicate(input -> getModel().isDefinitionFile() && AccessModifier.PRIVATE != getModel().getAccessModifier())); // One way binding of the access modifiers to the enabled state of internal checkbox IObservableValue internalEnabledValue = WidgetProperties.enabled() .observe(accessModifierComponent.getInternalAnnotationBox()); IObservableValue accessModifierSelectObservable = BeanProperties .value(N4JSInterfaceWizardModel.class, N4JSClassifierWizardModel.ACCESS_MODIFIER_PROPERTY) .observe(getModel()); dataBindingContext.bindValue(internalEnabledValue, accessModifierSelectObservable, noUpdateValueStrategy(), WizardComponentDataConverters.strategyForPredicate(object -> { if (object instanceof AccessModifier) { return isInternalAccessModifierEnabled((AccessModifier) object); } return false; })); // N4JS annotation checkbox disabled when access modifier is private IObservableValue n4jsEnabledValue = WidgetProperties.enabled() .observe(otherClassifierModifiersComponent.getN4jsAnnotationBox()); dataBindingContext.bindValue(n4jsEnabledValue, accessModifierSelectObservable, noUpdateValueStrategy(), WizardComponentDataConverters.strategyForPredicate(object -> { if (object instanceof AccessModifier) { return ((AccessModifier) object != AccessModifier.PRIVATE) && getModel().isDefinitionFile(); } return false; })); // Refresh wizard state on validation change IObservableValue observableValidationValue = BeanProperties .value(WorkspaceWizardModelValidator.VALIDATION_RESULT).observe(getValidator()); observableValidationValue.addValueChangeListener(new IValueChangeListener() { @Override public void handleValueChange(ValueChangeEvent event) { onValidationChange((ValidationResult) event.diff.getNewValue()); } }); } /** * Returns {@code true} if the <code>@Internal</code> access modifier should be enabled for selection in the UI. * * @param modifier * the currently selected modifier on the UI. * @return {@code true} if the internal visibility should be enabled on the UI, otherwise {@code false}. */ private boolean isInternalAccessModifierEnabled(AccessModifier modifier) { return modifier == AccessModifier.PUBLIC; } @Override public void createControl(Composite parent) { super.createControl(parent); // Set initial UI state. getDataBindingContext().updateTargets(); } @Override protected boolean setInitialFocus() { if (!super.setInitialFocus()) { this.nameComponent.setFocus(); } return false; } @Override protected void updateContentPreview(WizardPreview contentPreview) { Display.getCurrent().asyncExec(() -> { ContentBlock[] codeBlocks = getGenerator().generateContentPreview(getModel()); contentPreview.setContent(codeBlocks); // Reveal last content block (class code) contentPreview.revealContentBlock(codeBlocks[codeBlocks.length - 1]); // Show file location in the info bar contentPreview.setInfo(getModel().computeFileLocation().toString()); }); } /** * Returns the {@link WorkspaceWizardGenerator} of this page. */ public abstract WorkspaceWizardGenerator<M> getGenerator(); /** * Invoked when the validation result of the model has changed. * * @param result * The most recent validation result */ private void onValidationChange(ValidationResult result) { if (result.valid) { this.setPageComplete(true); this.setMessage("Press finish to create the new " + getModel().getClassifierName()); this.setErrorMessage(null); } else { this.setPageComplete(false); this.setErrorMessage(result.errorMessage); } } private static UpdateValueStrategy noUpdateValueStrategy() { return new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER); } }