/******************************************************************************* * Copyright (c) 2000, 2007 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation * Alexander Fedorov <Alexander.Fedorov@borland.com> * - Bug 172000 [Wizards] WizardNewFileCreationPage should support overwriting existing resources *******************************************************************************/ package org.eclipse.ui.internal.ide.misc; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; /** * Workbench-level composite for resource and container specification by the * user. Services such as field validation are performed by the group. The group * can be configured to accept existing resources, or only new resources. */ public class ResourceAndContainerGroup implements Listener { // problem identifiers /** * Constant for no problem. */ public static final int PROBLEM_NONE = 0; /** * Constant for empty resource. */ public static final int PROBLEM_RESOURCE_EMPTY = 1; /** * Constant for resource already exists. */ public static final int PROBLEM_RESOURCE_EXIST = 2; /** * Constant for invalid path. */ public static final int PROBLEM_PATH_INVALID = 4; /** * Constant for empty container. */ public static final int PROBLEM_CONTAINER_EMPTY = 5; /** * Constant for project does not exist. */ public static final int PROBLEM_PROJECT_DOES_NOT_EXIST = 6; /** * Constant for invalid name. */ public static final int PROBLEM_NAME_INVALID = 7; /** * Constant for path already occupied. */ public static final int PROBLEM_PATH_OCCUPIED = 8; // the client to notify of changes private Listener client; // whether to allow existing resources private boolean allowExistingResources = false; // resource type (file, folder, project) private String resourceType = IDEWorkbenchMessages.ResourceGroup_resource; // show closed projects in the tree, by default private boolean showClosedProjects = true; // problem indicator private String problemMessage = "";//$NON-NLS-1$ private int problemType = PROBLEM_NONE; // widgets private ContainerSelectionGroup containerGroup; private Text resourceNameField; /** * The resource extension for the resource name field. * * @see ResourceAndContainerGroup#setResourceExtension(String) * @since 3.3 */ private String resourceExtension; // constants private static final int SIZING_TEXT_FIELD_WIDTH = 250; /** * Create an instance of the group to allow the user to enter/select a * container and specify a resource name. * * @param parent * composite widget to parent the group * @param client * object interested in changes to the group's fields value * @param resourceFieldLabel * label to use in front of the resource name field * @param resourceType * one word, in lowercase, to describe the resource to the user * (file, folder, project) */ public ResourceAndContainerGroup(Composite parent, Listener client, String resourceFieldLabel, String resourceType) { this(parent, client, resourceFieldLabel, resourceType, true); } /** * Create an instance of the group to allow the user to enter/select a * container and specify a resource name. * * @param parent * composite widget to parent the group * @param client * object interested in changes to the group's fields value * @param resourceFieldLabel * label to use in front of the resource name field * @param resourceType * one word, in lowercase, to describe the resource to the user * (file, folder, project) * @param showClosedProjects * whether or not to show closed projects */ public ResourceAndContainerGroup(Composite parent, Listener client, String resourceFieldLabel, String resourceType, boolean showClosedProjects) { this(parent, client, resourceFieldLabel, resourceType, showClosedProjects, SWT.DEFAULT); } /** * Create an instance of the group to allow the user to enter/select a * container and specify a resource name. * * @param parent * composite widget to parent the group * @param client * object interested in changes to the group's fields value * @param resourceFieldLabel * label to use in front of the resource name field * @param resourceType * one word, in lowercase, to describe the resource to the user * (file, folder, project) * @param showClosedProjects * whether or not to show closed projects * @param heightHint * height hint for the container selection widget group */ public ResourceAndContainerGroup(Composite parent, Listener client, String resourceFieldLabel, String resourceType, boolean showClosedProjects, int heightHint) { super(); this.resourceType = resourceType; this.showClosedProjects = showClosedProjects; createContents(parent, resourceFieldLabel, heightHint); this.client = client; } /** * Returns a boolean indicating whether all controls in this group contain * valid values. * * @return boolean */ public boolean areAllValuesValid() { return problemType == PROBLEM_NONE; } /** * Creates this object's visual components. * * @param parent * org.eclipse.swt.widgets.Composite * @param heightHint * height hint for the container selection widget group */ protected void createContents(Composite parent, String resourceLabelString, int heightHint) { Font font = parent.getFont(); // server name group Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.marginHeight = 0; composite.setLayout(layout); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); composite.setFont(font); // container group if (heightHint == SWT.DEFAULT) { containerGroup = new ContainerSelectionGroup(composite, this, true, null, showClosedProjects); } else { containerGroup = new ContainerSelectionGroup(composite, this, true, null, showClosedProjects, heightHint, SIZING_TEXT_FIELD_WIDTH); } // resource name group Composite nameGroup = new Composite(composite, SWT.NONE); layout = new GridLayout(); layout.numColumns = 2; layout.marginWidth = 0; nameGroup.setLayout(layout); nameGroup.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL)); nameGroup.setFont(font); Label label = new Label(nameGroup, SWT.NONE); label.setText(resourceLabelString); label.setFont(font); // resource name entry field resourceNameField = new Text(nameGroup, SWT.BORDER); resourceNameField.addListener(SWT.Modify, this); resourceNameField.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent e) { handleResourceNameFocusLostEvent(); } }); GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL); data.widthHint = SIZING_TEXT_FIELD_WIDTH; resourceNameField.setLayoutData(data); resourceNameField.setFont(font); validateControls(); } /** * Returns the path of the currently selected container or null if no * container has been selected. Note that the container may not exist yet if * the user entered a new container name in the field. * * @return The path of the container, or <code>null</code> */ public IPath getContainerFullPath() { return containerGroup.getContainerFullPath(); } /** * Returns an error message indicating the current problem with the value of * a control in the group, or an empty message if all controls in the group * contain valid values. * * @return java.lang.String */ public String getProblemMessage() { return problemMessage; } /** * Returns the type of problem with the value of a control in the group. * * @return one of the PROBLEM_* constants */ public int getProblemType() { return problemType; } /** * Returns a string that is the name of the chosen resource, or an empty * string if no resource has been entered. <br> * <br> * The name will include the resource extension if the preconditions are * met. * * @see ResourceAndContainerGroup#setResourceExtension(String) * * @return The resource name * @since 3.3 */ public String getResource() { String resource = resourceNameField.getText(); if (useResourceExtension()) { return resource + '.' + resourceExtension; } return resource; } /** * Returns the resource extension. * * @return The resource extension or <code>null</code>. * @see ResourceAndContainerGroup#setResourceExtension(String) * @since 3.3 */ public String getResourceExtension() { return resourceExtension; } /** * Determines whether the resource extension should be added to the resource * name field. <br> * <br> * * @see ResourceAndContainerGroup#setResourceExtension(String) * @return <code>true</code> if the preconditions are met; otherwise, * <code>false</code>. * @since 3.3 */ private boolean useResourceExtension() { String resource = resourceNameField.getText(); if ((resourceExtension != null) && (resourceExtension.length() > 0) && (resource.length() > 0) && (resource.endsWith('.' + resourceExtension) == false)) { return true; } return false; } /** * Handle the focus lost event from the resource name field. <br> * Adds the resource extension to the resource name field when it loses * focus (if the preconditions are met). * * @see ResourceAndContainerGroup#setResourceExtension(String) * @since 3.3 */ private void handleResourceNameFocusLostEvent() { if (useResourceExtension()) { setResource(resourceNameField.getText() + '.' + resourceExtension); } } /** * Handles events for all controls in the group. * * @param e * org.eclipse.swt.widgets.Event */ public void handleEvent(Event e) { validateControls(); if (client != null) { client.handleEvent(e); } } /** * Sets the flag indicating whether existing resources are permitted. * @param value */ public void setAllowExistingResources(boolean value) { allowExistingResources = value; } /** * Sets the value of this page's container. * * @param path * Full path to the container. */ public void setContainerFullPath(IPath path) { IResource initial = ResourcesPlugin.getWorkspace().getRoot() .findMember(path); if (initial != null) { if (!(initial instanceof IContainer)) { initial = initial.getParent(); } containerGroup.setSelectedContainer((IContainer) initial); } validateControls(); } /** * Gives focus to the resource name field and selects its contents */ public void setFocus() { // select the whole resource name. resourceNameField.setSelection(0, resourceNameField.getText().length()); resourceNameField.setFocus(); } /** * Sets the value of this page's resource name. * * @param value * new value */ public void setResource(String value) { resourceNameField.setText(value); validateControls(); } /** * Set the only file extension allowed for the resource name field. <br> * <br> * If a resource extension is specified, then it will always be appended * with a '.' to the text from the resource name field for validation when * the following conditions are met: <br> * <br> * (1) Resource extension length is greater than 0 <br> * (2) Resource name field text length is greater than 0 <br> * (3) Resource name field text does not already end with a '.' and the * resource extension specified (case sensitive) <br> * <br> * The resource extension will not be reflected in the actual resource name * field until the resource name field loses focus. * * @param value * The resource extension without the '.' prefix (e.g. 'java', * 'xml') * @since 3.3 */ public void setResourceExtension(String value) { resourceExtension = value; validateControls(); } /** * Returns a <code>boolean</code> indicating whether a container name * represents a valid container resource in the workbench. An error message * is stored for future reference if the name does not represent a valid * container. * * @return <code>boolean</code> indicating validity of the container name */ protected boolean validateContainer() { IPath path = containerGroup.getContainerFullPath(); if (path == null) { problemType = PROBLEM_CONTAINER_EMPTY; problemMessage = IDEWorkbenchMessages.ResourceGroup_folderEmpty; return false; } IWorkspace workspace = ResourcesPlugin.getWorkspace(); String projectName = path.segment(0); if (projectName == null || !workspace.getRoot().getProject(projectName).exists()) { problemType = PROBLEM_PROJECT_DOES_NOT_EXIST; problemMessage = IDEWorkbenchMessages.ResourceGroup_noProject; return false; } // path is invalid if any prefix is occupied by a file IWorkspaceRoot root = workspace.getRoot(); while (path.segmentCount() > 1) { if (root.getFile(path).exists()) { problemType = PROBLEM_PATH_OCCUPIED; problemMessage = NLS.bind( IDEWorkbenchMessages.ResourceGroup_pathOccupied, path .makeRelative()); return false; } path = path.removeLastSegments(1); } return true; } /** * Validates the values for each of the group's controls. If an invalid * value is found then a descriptive error message is stored for later * reference. Returns a boolean indicating the validity of all of the * controls in the group. */ protected boolean validateControls() { // don't attempt to validate controls until they have been created if (containerGroup == null) { return false; } problemType = PROBLEM_NONE; problemMessage = "";//$NON-NLS-1$ if (!validateContainer() || !validateResourceName()) { return false; } IPath path = containerGroup.getContainerFullPath() .append(getResource()); return validateFullResourcePath(path); } /** * Returns a <code>boolean</code> indicating whether the specified * resource path represents a valid new resource in the workbench. An error * message is stored for future reference if the path does not represent a * valid new resource path. * * @param resourcePath * the path to validate * @return <code>boolean</code> indicating validity of the resource path */ protected boolean validateFullResourcePath(IPath resourcePath) { IWorkspace workspace = ResourcesPlugin.getWorkspace(); IStatus result = workspace.validatePath(resourcePath.toString(), IResource.FOLDER); if (!result.isOK()) { problemType = PROBLEM_PATH_INVALID; problemMessage = result.getMessage(); return false; } if (!allowExistingResources && (workspace.getRoot().getFolder(resourcePath).exists() || workspace .getRoot().getFile(resourcePath).exists())) { problemType = PROBLEM_RESOURCE_EXIST; problemMessage = NLS.bind( IDEWorkbenchMessages.ResourceGroup_nameExists, getResource()); return false; } return true; } /** * Returns a <code>boolean</code> indicating whether the resource name * rep- resents a valid resource name in the workbench. An error message is * stored for future reference if the name does not represent a valid * resource name. * * @return <code>boolean</code> indicating validity of the resource name */ protected boolean validateResourceName() { String resourceName = getResource(); if (resourceName.length() == 0) { problemType = PROBLEM_RESOURCE_EMPTY; problemMessage = NLS.bind( IDEWorkbenchMessages.ResourceGroup_emptyName, resourceType); return false; } if (!Path.ROOT.isValidPath(resourceName)) { problemType = PROBLEM_NAME_INVALID; problemMessage = NLS.bind( IDEWorkbenchMessages.ResourceGroup_invalidFilename, resourceName); return false; } return true; } /** * Returns the flag indicating whether existing resources are permitted. * * @return The allow existing resources flag. * @see ResourceAndContainerGroup#setAllowExistingResources(boolean) * @since 3.4 */ public boolean getAllowExistingResources() { return allowExistingResources; } }