001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.ide.misc;
011:
012: import org.eclipse.core.resources.IContainer;
013: import org.eclipse.core.resources.IResource;
014: import org.eclipse.core.resources.IWorkspace;
015: import org.eclipse.core.resources.IWorkspaceRoot;
016: import org.eclipse.core.resources.ResourcesPlugin;
017: import org.eclipse.core.runtime.IPath;
018: import org.eclipse.core.runtime.IStatus;
019: import org.eclipse.core.runtime.Path;
020: import org.eclipse.osgi.util.NLS;
021: import org.eclipse.swt.SWT;
022: import org.eclipse.swt.events.FocusAdapter;
023: import org.eclipse.swt.events.FocusEvent;
024: import org.eclipse.swt.graphics.Font;
025: import org.eclipse.swt.layout.GridData;
026: import org.eclipse.swt.layout.GridLayout;
027: import org.eclipse.swt.widgets.Composite;
028: import org.eclipse.swt.widgets.Event;
029: import org.eclipse.swt.widgets.Label;
030: import org.eclipse.swt.widgets.Listener;
031: import org.eclipse.swt.widgets.Text;
032: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
033:
034: /**
035: * Workbench-level composite for resource and container specification by the user.
036: * Services such as field validation are performed by the group.
037: * The group can be configured to accept existing resources, or only
038: * new resources.
039: */
040: public class ResourceAndContainerGroup implements Listener {
041: // problem identifiers
042: public static final int PROBLEM_NONE = 0;
043:
044: public static final int PROBLEM_RESOURCE_EMPTY = 1;
045:
046: public static final int PROBLEM_RESOURCE_EXIST = 2;
047:
048: public static final int PROBLEM_PATH_INVALID = 4;
049:
050: public static final int PROBLEM_CONTAINER_EMPTY = 5;
051:
052: public static final int PROBLEM_PROJECT_DOES_NOT_EXIST = 6;
053:
054: public static final int PROBLEM_NAME_INVALID = 7;
055:
056: public static final int PROBLEM_PATH_OCCUPIED = 8;
057:
058: // the client to notify of changes
059: private Listener client;
060:
061: // whether to allow existing resources
062: private boolean allowExistingResources = false;
063:
064: // resource type (file, folder, project)
065: private String resourceType = IDEWorkbenchMessages.ResourceGroup_resource;
066:
067: // show closed projects in the tree, by default
068: private boolean showClosedProjects = true;
069:
070: // problem indicator
071: private String problemMessage = "";//$NON-NLS-1$
072:
073: private int problemType = PROBLEM_NONE;
074:
075: // widgets
076: private ContainerSelectionGroup containerGroup;
077:
078: private Text resourceNameField;
079:
080: /**
081: * The resource extension for the resource name field.
082: * @see ResourceAndContainerGroup#setResourceExtension(String)
083: * @since 3.3
084: */
085: private String resourceExtension;
086:
087: // constants
088: private static final int SIZING_TEXT_FIELD_WIDTH = 250;
089:
090: /**
091: * Create an instance of the group to allow the user
092: * to enter/select a container and specify a resource
093: * name.
094: *
095: * @param parent composite widget to parent the group
096: * @param client object interested in changes to the group's fields value
097: * @param resourceFieldLabel label to use in front of the resource name field
098: * @param resourceType one word, in lowercase, to describe the resource to the user (file, folder, project)
099: */
100: public ResourceAndContainerGroup(Composite parent, Listener client,
101: String resourceFieldLabel, String resourceType) {
102: this (parent, client, resourceFieldLabel, resourceType, true);
103: }
104:
105: /**
106: * Create an instance of the group to allow the user
107: * to enter/select a container and specify a resource
108: * name.
109: *
110: * @param parent composite widget to parent the group
111: * @param client object interested in changes to the group's fields value
112: * @param resourceFieldLabel label to use in front of the resource name field
113: * @param resourceType one word, in lowercase, to describe the resource to the user (file, folder, project)
114: * @param showClosedProjects whether or not to show closed projects
115: */
116: public ResourceAndContainerGroup(Composite parent, Listener client,
117: String resourceFieldLabel, String resourceType,
118: boolean showClosedProjects) {
119: this (parent, client, resourceFieldLabel, resourceType,
120: showClosedProjects, SWT.DEFAULT);
121: }
122:
123: /**
124: * Create an instance of the group to allow the user
125: * to enter/select a container and specify a resource
126: * name.
127: *
128: * @param parent composite widget to parent the group
129: * @param client object interested in changes to the group's fields value
130: * @param resourceFieldLabel label to use in front of the resource name field
131: * @param resourceType one word, in lowercase, to describe the resource to the user (file, folder, project)
132: * @param showClosedProjects whether or not to show closed projects
133: * @param heightHint height hint for the container selection widget group
134: */
135: public ResourceAndContainerGroup(Composite parent, Listener client,
136: String resourceFieldLabel, String resourceType,
137: boolean showClosedProjects, int heightHint) {
138: super ();
139: this .resourceType = resourceType;
140: this .showClosedProjects = showClosedProjects;
141: createContents(parent, resourceFieldLabel, heightHint);
142: this .client = client;
143: }
144:
145: /**
146: * Returns a boolean indicating whether all controls in this group
147: * contain valid values.
148: *
149: * @return boolean
150: */
151: public boolean areAllValuesValid() {
152: return problemType == PROBLEM_NONE;
153: }
154:
155: /**
156: * Creates this object's visual components.
157: *
158: * @param parent org.eclipse.swt.widgets.Composite
159: * @param heightHint height hint for the container selection widget group
160: */
161: protected void createContents(Composite parent,
162: String resourceLabelString, int heightHint) {
163:
164: Font font = parent.getFont();
165: // server name group
166: Composite composite = new Composite(parent, SWT.NONE);
167: GridLayout layout = new GridLayout();
168: layout.marginWidth = 0;
169: layout.marginHeight = 0;
170: composite.setLayout(layout);
171: composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
172: true));
173: composite.setFont(font);
174:
175: // container group
176: if (heightHint == SWT.DEFAULT) {
177: containerGroup = new ContainerSelectionGroup(composite,
178: this , true, null, showClosedProjects);
179: } else {
180: containerGroup = new ContainerSelectionGroup(composite,
181: this , true, null, showClosedProjects, heightHint,
182: SIZING_TEXT_FIELD_WIDTH);
183: }
184:
185: // resource name group
186: Composite nameGroup = new Composite(composite, SWT.NONE);
187: layout = new GridLayout();
188: layout.numColumns = 2;
189: layout.marginWidth = 0;
190: nameGroup.setLayout(layout);
191: nameGroup.setLayoutData(new GridData(
192: GridData.HORIZONTAL_ALIGN_FILL
193: | GridData.GRAB_HORIZONTAL));
194: nameGroup.setFont(font);
195:
196: Label label = new Label(nameGroup, SWT.NONE);
197: label.setText(resourceLabelString);
198: label.setFont(font);
199:
200: // resource name entry field
201: resourceNameField = new Text(nameGroup, SWT.BORDER);
202: resourceNameField.addListener(SWT.Modify, this );
203: resourceNameField.addFocusListener(new FocusAdapter() {
204: public void focusLost(FocusEvent e) {
205: handleResourceNameFocusLostEvent();
206: }
207: });
208: GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL
209: | GridData.GRAB_HORIZONTAL);
210: data.widthHint = SIZING_TEXT_FIELD_WIDTH;
211: resourceNameField.setLayoutData(data);
212: resourceNameField.setFont(font);
213: validateControls();
214: }
215:
216: /**
217: * Returns the path of the currently selected container
218: * or null if no container has been selected. Note that
219: * the container may not exist yet if the user entered
220: * a new container name in the field.
221: * @return The path of the container, or <code>null</code>
222: */
223: public IPath getContainerFullPath() {
224: return containerGroup.getContainerFullPath();
225: }
226:
227: /**
228: * Returns an error message indicating the current problem with the value
229: * of a control in the group, or an empty message if all controls in the
230: * group contain valid values.
231: *
232: * @return java.lang.String
233: */
234: public String getProblemMessage() {
235: return problemMessage;
236: }
237:
238: /**
239: * Returns the type of problem with the value of a control
240: * in the group.
241: *
242: * @return one of the PROBLEM_* constants
243: */
244: public int getProblemType() {
245: return problemType;
246: }
247:
248: /**
249: * Returns a string that is the name of the chosen resource, or an
250: * empty string if no resource has been entered.
251: * <br><br>
252: * The name will include the resource extension if the
253: * preconditions are met.
254: * @see ResourceAndContainerGroup#setResourceExtension(String)
255: *
256: * @return The resource name
257: * @since 3.3
258: */
259: public String getResource() {
260: String resource = resourceNameField.getText();
261: if (useResourceExtension()) {
262: return resource + '.' + resourceExtension;
263: }
264: return resource;
265: }
266:
267: /**
268: * Returns the resource extension.
269: *
270: * @return The resource extension or <code>null</code>.
271: * @see ResourceAndContainerGroup#setResourceExtension(String)
272: * @since 3.3
273: */
274: public String getResourceExtension() {
275: return resourceExtension;
276: }
277:
278: /**
279: * Determines whether the resource extension should be added to the
280: * resource name field.
281: * <br><br>
282: * @see ResourceAndContainerGroup#setResourceExtension(String)
283: * @return <code>true</code> if the preconditions are met; otherwise,
284: * <code>false</code>.
285: * @since 3.3
286: */
287: private boolean useResourceExtension() {
288: String resource = resourceNameField.getText();
289: if ((resourceExtension != null)
290: && (resourceExtension.length() > 0)
291: && (resource.length() > 0)
292: && (resource.endsWith('.' + resourceExtension) == false)) {
293: return true;
294: }
295: return false;
296: }
297:
298: /**
299: * Handle the focus lost event from the resource name field.
300: * <br>
301: * Adds the resource extension to the resource name field when it
302: * loses focus (if the preconditions are met).
303: * @see ResourceNameFocusListener
304: * @see ResourceAndContainerGroup#setResourceExtension(String)
305: * @since 3.3
306: */
307: private void handleResourceNameFocusLostEvent() {
308: if (useResourceExtension()) {
309: setResource(resourceNameField.getText() + '.'
310: + resourceExtension);
311: }
312: }
313:
314: /**
315: * Handles events for all controls in the group.
316: *
317: * @param e org.eclipse.swt.widgets.Event
318: */
319: public void handleEvent(Event e) {
320: validateControls();
321: if (client != null) {
322: client.handleEvent(e);
323: }
324: }
325:
326: /**
327: * Sets the flag indicating whether existing resources are permitted.
328: */
329: public void setAllowExistingResources(boolean value) {
330: allowExistingResources = value;
331: }
332:
333: /**
334: * Sets the value of this page's container.
335: *
336: * @param path Full path to the container.
337: */
338: public void setContainerFullPath(IPath path) {
339: IResource initial = ResourcesPlugin.getWorkspace().getRoot()
340: .findMember(path);
341: if (initial != null) {
342: if (!(initial instanceof IContainer)) {
343: initial = initial.getParent();
344: }
345: containerGroup.setSelectedContainer((IContainer) initial);
346: }
347: validateControls();
348: }
349:
350: /**
351: * Gives focus to the resource name field and selects its contents
352: */
353: public void setFocus() {
354: //select the whole resource name.
355: resourceNameField.setSelection(0, resourceNameField.getText()
356: .length());
357: resourceNameField.setFocus();
358: }
359:
360: /**
361: * Sets the value of this page's resource name.
362: *
363: * @param value new value
364: */
365: public void setResource(String value) {
366: resourceNameField.setText(value);
367: validateControls();
368: }
369:
370: /**
371: * Set the only file extension allowed for the resource name field.
372: * <br><br>
373: * If a resource extension is specified, then it will always be
374: * appended with a '.' to the text from the resource name field for
375: * validation when the following conditions are met:
376: * <br><br>
377: * (1) Resource extension length is greater than 0
378: * <br>
379: * (2) Resource name field text length is greater than 0
380: * <br>
381: * (3) Resource name field text does not already end with a '.' and the
382: * resource extension specified (case sensitive)
383: * <br><br>
384: * The resource extension will not be reflected in the actual
385: * resource name field until the resource name field loses focus.
386: *
387: * @param value
388: * The resource extension without the '.' prefix
389: * (e.g. 'java', 'xml')
390: * @since 3.3
391: */
392: public void setResourceExtension(String value) {
393: resourceExtension = value;
394: validateControls();
395: }
396:
397: /**
398: * Returns a <code>boolean</code> indicating whether a container name represents
399: * a valid container resource in the workbench. An error message is stored for
400: * future reference if the name does not represent a valid container.
401: *
402: * @return <code>boolean</code> indicating validity of the container name
403: */
404: protected boolean validateContainer() {
405: IPath path = containerGroup.getContainerFullPath();
406: if (path == null) {
407: problemType = PROBLEM_CONTAINER_EMPTY;
408: problemMessage = IDEWorkbenchMessages.ResourceGroup_folderEmpty;
409: return false;
410: }
411: IWorkspace workspace = ResourcesPlugin.getWorkspace();
412: String projectName = path.segment(0);
413: if (projectName == null
414: || !workspace.getRoot().getProject(projectName)
415: .exists()) {
416: problemType = PROBLEM_PROJECT_DOES_NOT_EXIST;
417: problemMessage = IDEWorkbenchMessages.ResourceGroup_noProject;
418: return false;
419: }
420: //path is invalid if any prefix is occupied by a file
421: IWorkspaceRoot root = workspace.getRoot();
422: while (path.segmentCount() > 1) {
423: if (root.getFile(path).exists()) {
424: problemType = PROBLEM_PATH_OCCUPIED;
425: problemMessage = NLS
426: .bind(
427: IDEWorkbenchMessages.ResourceGroup_pathOccupied,
428: path.makeRelative());
429: return false;
430: }
431: path = path.removeLastSegments(1);
432: }
433: return true;
434: }
435:
436: /**
437: * Validates the values for each of the group's controls. If an invalid
438: * value is found then a descriptive error message is stored for later
439: * reference. Returns a boolean indicating the validity of all of the
440: * controls in the group.
441: */
442: protected boolean validateControls() {
443: // don't attempt to validate controls until they have been created
444: if (containerGroup == null) {
445: return false;
446: }
447: problemType = PROBLEM_NONE;
448: problemMessage = "";//$NON-NLS-1$
449:
450: if (!validateContainer() || !validateResourceName()) {
451: return false;
452: }
453:
454: IPath path = containerGroup.getContainerFullPath().append(
455: getResource());
456: return validateFullResourcePath(path);
457: }
458:
459: /**
460: * Returns a <code>boolean</code> indicating whether the specified resource
461: * path represents a valid new resource in the workbench. An error message
462: * is stored for future reference if the path does not represent a valid
463: * new resource path.
464: *
465: * @param resourcePath the path to validate
466: * @return <code>boolean</code> indicating validity of the resource path
467: */
468: protected boolean validateFullResourcePath(IPath resourcePath) {
469: IWorkspace workspace = ResourcesPlugin.getWorkspace();
470:
471: IStatus result = workspace.validatePath(
472: resourcePath.toString(), IResource.FOLDER);
473: if (!result.isOK()) {
474: problemType = PROBLEM_PATH_INVALID;
475: problemMessage = result.getMessage();
476: return false;
477: }
478:
479: if (!allowExistingResources
480: && (workspace.getRoot().getFolder(resourcePath)
481: .exists() || workspace.getRoot().getFile(
482: resourcePath).exists())) {
483: problemType = PROBLEM_RESOURCE_EXIST;
484: problemMessage = NLS.bind(
485: IDEWorkbenchMessages.ResourceGroup_nameExists,
486: getResource());
487: return false;
488: }
489: return true;
490: }
491:
492: /**
493: * Returns a <code>boolean</code> indicating whether the resource name rep-
494: * resents a valid resource name in the workbench. An error message is stored
495: * for future reference if the name does not represent a valid resource name.
496: *
497: * @return <code>boolean</code> indicating validity of the resource name
498: */
499: protected boolean validateResourceName() {
500: String resourceName = getResource();
501:
502: if (resourceName.length() == 0) {
503: problemType = PROBLEM_RESOURCE_EMPTY;
504: problemMessage = NLS.bind(
505: IDEWorkbenchMessages.ResourceGroup_emptyName,
506: resourceType);
507: return false;
508: }
509:
510: if (!Path.ROOT.isValidPath(resourceName)) {
511: problemType = PROBLEM_NAME_INVALID;
512: problemMessage = NLS.bind(
513: IDEWorkbenchMessages.ResourceGroup_invalidFilename,
514: resourceName);
515: return false;
516: }
517: return true;
518: }
519:
520: }
|