summaryrefslogtreecommitdiffstats
path: root/plugins/org.yocto.bc.ui/src/org/yocto/bc/ui/wizards/NewBitBakeFileRecipeWizardPage.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/org.yocto.bc.ui/src/org/yocto/bc/ui/wizards/NewBitBakeFileRecipeWizardPage.java')
-rw-r--r--plugins/org.yocto.bc.ui/src/org/yocto/bc/ui/wizards/NewBitBakeFileRecipeWizardPage.java592
1 files changed, 592 insertions, 0 deletions
diff --git a/plugins/org.yocto.bc.ui/src/org/yocto/bc/ui/wizards/NewBitBakeFileRecipeWizardPage.java b/plugins/org.yocto.bc.ui/src/org/yocto/bc/ui/wizards/NewBitBakeFileRecipeWizardPage.java
new file mode 100644
index 0000000..3bdcffd
--- /dev/null
+++ b/plugins/org.yocto.bc.ui/src/org/yocto/bc/ui/wizards/NewBitBakeFileRecipeWizardPage.java
@@ -0,0 +1,592 @@
1/*****************************************************************************
2 * Copyright (c) 2013 Ken Gilmer, Intel Corporation
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Ken Gilmer - initial API and implementation
10 * Jessica Zhang (Intel) - Extend to support auto-fill base on src_uri value
11 * Ioana Grigoropol (Intel) - adapt class for remote support
12 *******************************************************************************/
13package org.yocto.bc.ui.wizards;
14
15import java.util.List;
16import org.eclipse.core.resources.IContainer;
17import org.eclipse.core.resources.IProject;
18import org.eclipse.core.resources.IResource;
19import org.eclipse.core.resources.ResourcesPlugin;
20import org.eclipse.core.runtime.CoreException;
21import org.eclipse.core.runtime.IProgressMonitor;
22import org.eclipse.core.runtime.NullProgressMonitor;
23import org.eclipse.core.runtime.Path;
24import org.eclipse.core.runtime.IPath;
25import org.eclipse.core.runtime.SubProgressMonitor;
26import org.eclipse.jface.operation.IRunnableWithProgress;
27import org.eclipse.jface.viewers.ISelection;
28import org.eclipse.jface.viewers.IStructuredSelection;
29import org.eclipse.jface.window.Window;
30import org.eclipse.jface.wizard.WizardPage;
31import org.eclipse.rse.core.model.IHost;
32import org.eclipse.rse.services.files.IFileService;
33import org.eclipse.rse.services.files.IHostFile;
34import org.eclipse.swt.SWT;
35import org.eclipse.swt.events.ModifyEvent;
36import org.eclipse.swt.events.ModifyListener;
37import org.eclipse.swt.events.SelectionAdapter;
38import org.eclipse.swt.events.SelectionEvent;
39import org.eclipse.swt.layout.GridData;
40import org.eclipse.swt.layout.GridLayout;
41import org.eclipse.swt.widgets.Button;
42import org.eclipse.swt.widgets.Composite;
43import org.eclipse.swt.widgets.DirectoryDialog;
44import org.eclipse.swt.widgets.FileDialog;
45import org.eclipse.swt.widgets.Label;
46import org.eclipse.swt.widgets.Text;
47import org.eclipse.ui.dialogs.ContainerSelectionDialog;
48import org.yocto.bc.ui.Activator;
49import org.yocto.bc.ui.model.ProjectInfo;
50import org.yocto.remote.utils.ProcessStreamBuffer;
51import org.yocto.remote.utils.RemoteHelper;
52import org.yocto.remote.utils.YoctoCommand;
53
54import java.util.HashMap;
55import java.util.Hashtable;
56import java.util.Set;
57import java.util.ArrayList;
58import java.util.Enumeration;
59import java.util.Iterator;
60import java.util.regex.Pattern;
61
62import java.io.BufferedReader;
63import java.io.InputStreamReader;
64import java.io.File;
65import java.io.FileReader;
66import java.io.IOException;
67import java.io.FileInputStream;
68import java.io.InputStream;
69import java.io.FilenameFilter;
70import java.security.MessageDigest;
71import java.lang.reflect.InvocationTargetException;
72import java.math.BigInteger;
73import java.net.URI;
74import java.net.URISyntaxException;
75
76public class NewBitBakeFileRecipeWizardPage extends WizardPage {
77 private Text containerText;
78 private Text fileText;
79
80 private Text descriptionText;
81 private Text licenseText;
82 private Text checksumText;
83 private Text homepageText;
84 private Text authorText;
85 private Text sectionText;
86 private Text txtSrcURI;
87 private Text md5sumText;
88 private Text sha256sumText;
89 private Button btnPopulate;
90
91 private BitbakeRecipeUIElement element;
92
93 private ISelection selection;
94 private URI metaDirLoc;
95 private ArrayList inheritance;
96 private final IHost connection;
97
98 private String tempFolderPath;
99 private String srcFileNameExt;
100 private String srcFileName;
101
102 public static final String TEMP_FOLDER_NAME = "temp";
103 public static final String TAR_BZ2_EXT = ".tar.bz2";
104 public static final String TAR_GZ_EXT = ".tar.gz";
105 public static final String HTTP = "http";
106 public static final String FTP ="ftp";
107 public static final String BB_RECIPE_EXT =".bb";
108 private static final String COPYING_FILE = "COPYING";
109
110 private static final String CMAKE_LIST = "cmakelists.txt";
111 private static final String CMAKE = "cmake";
112 private static final String SETUP_SCRIPT = "setup.py";
113 private static final String DISUTILS = "disutils";
114 private static final String CONFIGURE_IN = "configure.in";
115 private static final String CONFIGURE_AC = "configure.ac";
116 private static final String AUTOTOOLS = "autotools";
117
118 private static final String md5Pattern = "^[0-9a-f]{32}$";
119 protected static final String sha256Pattern = "^[0-9a-f]{64}$";
120
121 private static final String MIRRORS_FILE = "mirrors.bbclass";
122 private static final String CLASSES_FOLDER = "classes";
123
124 private HashMap<String, String> mirrorTable;
125 private URI extractDir;
126
127 protected ProcessStreamBuffer md5Buffer;
128 protected ProcessStreamBuffer sha256Buffer;
129 protected ProcessStreamBuffer md5CopyingBuffer;
130
131 public NewBitBakeFileRecipeWizardPage(ISelection selection, IHost connection) {
132 super("wizardPage");
133 setTitle("BitBake Recipe");
134 setDescription("Create a new BitBake recipe.");
135 this.selection = selection;
136 this.connection = connection;
137 element = new BitbakeRecipeUIElement();
138 inheritance = new ArrayList();
139 }
140
141 public void createControl(Composite parent) {
142 final Composite container = new Composite(parent, SWT.NULL);
143 GridLayout layout = new GridLayout();
144 container.setLayout(layout);
145 layout.numColumns = 3;
146 layout.verticalSpacing = 9;
147
148 Label label = new Label(container, SWT.NULL);
149 GridData gd = new GridData();
150 gd.horizontalSpan = 3;
151 label.setLayoutData(gd);
152
153 label = new Label(container, SWT.NULL);
154 label.setText("Recipe &Directory:");
155
156 containerText = new Text(container, SWT.BORDER | SWT.SINGLE);
157 gd = new GridData(GridData.FILL_HORIZONTAL);
158 containerText.setLayoutData(gd);
159 containerText.addModifyListener(new ModifyListener() {
160 public void modifyText(ModifyEvent e) {
161 dialogChanged();
162 }
163 });
164
165 Button buttonBrowse = new Button(container, SWT.PUSH);
166 buttonBrowse.setText("Browse...");
167 buttonBrowse.addSelectionListener(new SelectionAdapter() {
168 @Override
169 public void widgetSelected(SelectionEvent e) {
170 handleBrowse(container, containerText);
171 }
172 });
173
174 label = new Label(container, SWT.NULL);
175 gd = new GridData();
176 gd.horizontalSpan = 3;
177 label.setLayoutData(gd);
178
179 label = new Label(container, SWT.NULL);
180 label.setText("SRC_&URI:");
181
182 txtSrcURI = new Text(container, SWT.BORDER | SWT.SINGLE);
183 gd = new GridData(GridData.FILL_HORIZONTAL);
184 txtSrcURI.setLayoutData(gd);
185 txtSrcURI.addModifyListener(new ModifyListener() {
186 public void modifyText(ModifyEvent e) {
187 if (txtSrcURI.getText().trim().isEmpty()) {
188 if (btnPopulate != null)
189 btnPopulate.setEnabled(false);
190 } else if (btnPopulate != null){
191 btnPopulate.setEnabled(true);
192 }
193 dialogChanged();
194 }
195 });
196
197 btnPopulate = new Button(container, SWT.PUSH);
198 btnPopulate.setText("Populate...");
199 btnPopulate.setEnabled(false);
200 btnPopulate.addSelectionListener(new SelectionAdapter() {
201 @Override
202 public void widgetSelected(SelectionEvent e) {
203 handlePopulate();
204 }
205 });
206
207 createField(container, "&Recipe Name:", (fileText = new Text(container, SWT.BORDER | SWT.SINGLE)));
208 createField(container, "SRC_URI[&md5sum]:", (md5sumText = new Text(container, SWT.BORDER | SWT.SINGLE)));
209 createField(container, "SRC_URI[&sha256sum]:", (sha256sumText = new Text(container, SWT.BORDER | SWT.SINGLE)));
210 createField(container, "License File &Checksum:", (checksumText = new Text(container, SWT.BORDER | SWT.SINGLE)));
211 createField(container, "&Package Description:", (descriptionText = new Text(container, SWT.BORDER | SWT.SINGLE)));
212 createField(container, "&License:", (licenseText = new Text(container, SWT.BORDER | SWT.SINGLE)));
213
214 createField(container, "&Homepage:", (homepageText = new Text(container, SWT.BORDER | SWT.SINGLE)));
215 createField(container, "Package &Author:", (authorText = new Text(container, SWT.BORDER | SWT.SINGLE)));
216 createField(container, "&Section:", (sectionText = new Text(container, SWT.BORDER | SWT.SINGLE)));
217
218 initialize();
219 dialogChanged();
220 setControl(container);
221 }
222
223 private void createField(Composite container, String title, Text control) {
224 Label label = new Label(container, SWT.NONE);
225 label.setText(title);
226 label.moveAbove(control);
227
228 GridData gd = new GridData(GridData.FILL_HORIZONTAL);
229 gd.horizontalSpan = 2;
230 control.setLayoutData(gd);
231 control.addModifyListener(new ModifyListener() {
232
233 public void modifyText(ModifyEvent e) {
234 dialogChanged();
235 }
236
237 });
238 }
239
240 private void dialogChanged() {
241 String containerName = containerText.getText();
242 IResource container = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(containerName));
243 String fileName = fileText.getText();
244
245 if (containerName.length() == 0) {
246 updateStatus("Directory must be specified");
247 return;
248 }
249
250 if (container == null || (container.getType() & (IResource.PROJECT | IResource.FOLDER)) == 0) {
251 updateStatus("File container must exist");
252 return;
253 }
254 if (!container.isAccessible()) {
255 updateStatus("Project must be writable");
256 return;
257 }
258
259 IProject project = container.getProject();
260 ProjectInfo projInfo = null;
261 try {
262 projInfo = Activator.getProjInfo(project.getLocationURI());
263 } catch (InvocationTargetException e) {
264 e.printStackTrace();
265 } catch (CoreException e) {
266 e.printStackTrace();
267 } catch (InterruptedException e) {
268 e.printStackTrace();
269 }
270 metaDirLoc = RemoteHelper.createNewURI(projInfo.getOriginalURI(), "meta");
271
272 if (fileName.length() == 0) {
273 updateStatus("File name must be specified");
274 return;
275 }
276 if (fileName.contains(" ")) {
277 updateStatus("File name must be valid with no space in it");
278 return;
279 }
280 if (fileName.replace('\\', '/').indexOf('/', 1) > 0) {
281 updateStatus("File name must be valid");
282 return;
283 }
284
285 if (descriptionText.getText().length() == 0) {
286 updateStatus("Recipe must have a description");
287 return;
288 }
289
290 if (licenseText.getText().length() == 0) {
291 updateStatus("Recipe must have a license");
292 return;
293 }
294
295 if (txtSrcURI.getText().length() == 0) {
296 updateStatus("SRC_URI can't be empty");
297 }
298
299 updateStatus(null);
300 }
301
302 public BitbakeRecipeUIElement populateUIElement() {
303 element.setAuthor(authorText.getText());
304 element.setChecksum(checksumText.getText());
305 element.setContainer(containerText.getText());
306 element.setDescription(descriptionText.getText());
307 element.setFile(fileText.getText());
308 element.setHomePage(homepageText.getText());
309 element.setLicense(licenseText.getText());
310 element.setMd5sum(md5sumText.getText());
311 element.setSection(sectionText.getText());
312 element.setSha256sum(sha256sumText.getText());
313 element.setSrcuri(txtSrcURI.getText());
314 element.setInheritance(inheritance);
315 element.setMetaDir(metaDirLoc);
316
317 return element;
318 }
319
320 private void handleBrowse(final Composite parent, final Text text) {
321 ContainerSelectionDialog dialog = new ContainerSelectionDialog(getShell(), ResourcesPlugin.getWorkspace().getRoot(), false, "Select project directory");
322 if (dialog.open() == Window.OK) {
323 Object[] result = dialog.getResult();
324 if (result.length == 1) {
325 text.setText(((Path) result[0]).toString());
326 }
327 }
328 }
329
330 private void handlePopulate() {
331 try {
332 IProgressMonitor monitor = new NullProgressMonitor();
333 URI srcURI = new URI(txtSrcURI.getText().trim());
334 String scheme = srcURI.getScheme();
335 this.srcFileNameExt = getSrcFileName(true);
336 this.srcFileName = getSrcFileName(false);
337 if ((scheme.equals(HTTP) || scheme.equals(FTP))
338 && (srcFileNameExt.endsWith(TAR_GZ_EXT) || srcFileNameExt.endsWith(TAR_BZ2_EXT))) {
339 handleRemotePopulate(srcURI, monitor);
340 } else {
341 String packageName = srcFileName.replace("-", "_");
342 fileText.setText(packageName + BB_RECIPE_EXT);
343
344 handleLocalPopulate(srcURI, monitor);
345 }
346 } catch (URISyntaxException e) {
347 e.printStackTrace();
348 } catch (InvocationTargetException e) {
349 e.printStackTrace();
350 } catch (InterruptedException e) {
351 e.printStackTrace();
352 }
353 }
354
355 private String getSrcFileName(boolean withExt){
356 URI srcURI;
357 try {
358 srcURI = new URI(txtSrcURI.getText().trim());
359 String path = srcURI.getPath();
360 String fileName = path.substring(path.lastIndexOf("/") + 1);
361 if (withExt)
362 return fileName;
363 else {
364 if (fileName.endsWith(TAR_BZ2_EXT)) {
365 return fileName.substring(0, fileName.indexOf(TAR_BZ2_EXT));
366 } else if(fileName.endsWith(TAR_GZ_EXT)){
367 return fileName.substring(0, fileName.indexOf(TAR_GZ_EXT));
368 }
369 }
370 } catch (URISyntaxException e) {
371 e.printStackTrace();
372 }
373 return "";
374 }
375 private void handleRemotePopulate(final URI srcURI, IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
376 populateRecipeName();
377
378 this.getContainer().run(true, true, new IRunnableWithProgress() {
379
380 @Override
381 public void run(IProgressMonitor monitor) throws InvocationTargetException,
382 InterruptedException {
383 monitor.beginTask("Populating recipe fields ... ", 100);
384 try {
385 String metaDirLocPath = metaDirLoc.getPath();
386 monitor.subTask("Cleaning environment");
387 YoctoCommand rmYCmd = new YoctoCommand("rm -rf " + TEMP_FOLDER_NAME, metaDirLocPath, "");
388 RemoteHelper.handleRunCommandRemote(connection, rmYCmd, new SubProgressMonitor(monitor, 5));
389
390 YoctoCommand mkdirYCmd = new YoctoCommand( "mkdir " + TEMP_FOLDER_NAME, metaDirLocPath, "");
391 RemoteHelper.handleRunCommandRemote(connection, mkdirYCmd, new SubProgressMonitor(monitor, 5));
392
393 updateTempFolderPath();
394 monitor.worked(10);
395
396 monitor.subTask("Downloading package sources");
397
398 updateTempFolderPath();
399
400 YoctoCommand wgetYCmd = new YoctoCommand("wget " + srcURI.toURL(), tempFolderPath, "");
401 RemoteHelper.handleRunCommandRemote(connection, wgetYCmd, new SubProgressMonitor(monitor, 40));
402
403 monitor.worked(50);
404
405 monitor.subTask("Compute package checksums");
406 String md5Cmd = "md5sum " + srcFileNameExt;
407 YoctoCommand md5YCmd = new YoctoCommand(md5Cmd, tempFolderPath, "");
408
409 RemoteHelper.handleRunCommandRemote(connection, md5YCmd, new SubProgressMonitor(monitor, 10));
410 md5Buffer = md5YCmd.getProcessBuffer();
411
412 monitor.worked(60);
413
414 String sha256Cmd = "sha256sum " + srcFileNameExt;
415 YoctoCommand sha256YCmd = new YoctoCommand(sha256Cmd, tempFolderPath, "");
416 RemoteHelper.handleRunCommandRemote(connection, sha256YCmd, new SubProgressMonitor(monitor, 10));
417 sha256Buffer = sha256YCmd.getProcessBuffer();
418
419 monitor.worked(70);
420
421 monitor.subTask("Extracting package");
422 extractDir = extractPackage(srcURI, new SubProgressMonitor(monitor, 0));
423 monitor.worked(80);
424
425 YoctoCommand licenseChecksumCmd = populateLicenseFileChecksum(extractDir, new SubProgressMonitor(monitor, 10));
426 md5CopyingBuffer = licenseChecksumCmd.getProcessBuffer();
427
428 monitor.subTask("Creating mirror lookup table");
429 mirrorTable = createMirrorLookupTable(new SubProgressMonitor(monitor, 10));
430
431 monitor.worked(90);
432 monitor.done();
433 } catch (Exception e) {
434 e.printStackTrace();
435 }
436 }
437 });
438 updateSrcUri(mirrorTable, srcURI);
439 populateInheritance(extractDir, monitor);
440
441 String md5Val = md5Buffer.getOutputLineContaining(srcFileNameExt, md5Pattern);
442 md5sumText.setText(Pattern.matches(md5Pattern, md5Val) ? md5Val : "");
443 String sha256Val = sha256Buffer.getOutputLineContaining(srcFileNameExt, sha256Pattern);
444 sha256sumText.setText(Pattern.matches(sha256Pattern, sha256Val) ? sha256Val : "");
445 String checkSumVal = md5CopyingBuffer.getOutputLineContaining(COPYING_FILE, md5Pattern);
446 checksumText.setText(RemoteHelper.createNewURI(extractDir, COPYING_FILE).toString() + ";md5=" + (Pattern.matches(md5Pattern, checkSumVal) ? checkSumVal : ""));
447 }
448
449 private void updateTempFolderPath(){
450 this.tempFolderPath = getMetaFolderPath() + TEMP_FOLDER_NAME + "/";
451 }
452
453 private String getMetaFolderPath(){
454 String sep = metaDirLoc.getPath().endsWith("/")? "" : "/";
455 return metaDirLoc.getPath() + sep;
456 }
457
458 private void handleLocalPopulate(URI srcURI, IProgressMonitor monitor) {
459 populateLicenseFileChecksum(srcURI, monitor);
460 populateInheritance(srcURI, monitor);
461 }
462
463 private URI extractPackage(URI srcURI, IProgressMonitor monitor) {
464 try {
465 String path = srcFileNameExt;
466 String tarCmd = "tar ";
467 if (path.endsWith(TAR_BZ2_EXT)) {
468 tarCmd += "-zxvf ";
469 } else if(path.endsWith(TAR_GZ_EXT)){
470 tarCmd += "-xvf ";
471 }
472
473 RemoteHelper.handleRunCommandRemote(connection, new YoctoCommand(tarCmd + path, tempFolderPath, ""), monitor);
474
475 return RemoteHelper.createNewURI(metaDirLoc, TEMP_FOLDER_NAME + "/" + srcFileName);
476
477 } catch (Exception e) {
478 e.printStackTrace();
479 }
480 return null;
481 }
482
483 private void populateInheritance(URI extractDir, IProgressMonitor monitor) {
484 IHostFile[] hostFiles = RemoteHelper.getRemoteDirContent(connection, metaDirLoc.getPath(), "", IFileService.FILE_TYPE_FILES, monitor);
485 if (hostFiles == null)
486 return;
487
488 for (IHostFile file: hostFiles) {
489 String fileName = file.getName();
490 if (fileName.equalsIgnoreCase(CMAKE_LIST)){
491 inheritance.add(CMAKE);
492 } else if (fileName.equalsIgnoreCase(SETUP_SCRIPT)) {
493 inheritance.add(DISUTILS);
494 } else if (fileName.equalsIgnoreCase(CONFIGURE_AC) || file.getName().equalsIgnoreCase(CONFIGURE_IN)) {
495 inheritance.add(AUTOTOOLS);
496 }
497 }
498 }
499
500 private YoctoCommand populateLicenseFileChecksum(URI extractDir, IProgressMonitor monitor) {
501 if (extractDir == null)
502 throw new RuntimeException("Something went wrong during source extraction!");
503
504 try {
505 YoctoCommand catCmd = new YoctoCommand("md5sum " + COPYING_FILE, extractDir.getPath(), "");
506 RemoteHelper.handleRunCommandRemote(connection, catCmd, monitor);
507 return catCmd;
508 } catch (Exception e) {
509 throw new RuntimeException("Unable to process file for MD5 calculation", e);
510 }
511
512 }
513
514 private HashMap<String, String> createMirrorLookupTable(IProgressMonitor monitor) throws Exception {
515 HashMap<String, String> mirrorMap = new HashMap<String, String>();
516
517 YoctoCommand cmd = new YoctoCommand("cat " + MIRRORS_FILE, getMetaFolderPath() + CLASSES_FOLDER, "");
518 RemoteHelper.handleRunCommandRemote(connection, cmd, monitor);
519
520 if (!cmd.getProcessBuffer().hasErrors()){
521 String delims = "[\\t]+";
522 List<String> outputLines = cmd.getProcessBuffer().getOutputLines();
523 for (String outLine : outputLines) {
524 String[] tokens = outLine.split(delims);
525 if (tokens.length < 2)
526 continue;
527 String endingStr = " \\n \\";
528 int idx = tokens[1].lastIndexOf(endingStr);
529 String key = tokens[1].substring(0, idx);
530 mirrorMap.put(key, tokens[0]);
531 }
532 }
533 return mirrorMap;
534 }
535
536 private void populateRecipeName() {
537 String fileName = fileText.getText();
538 if (!fileName.isEmpty())
539 return;
540
541 String recipeFile = srcFileName.replace("-", "_");
542 recipeFile += BB_RECIPE_EXT;
543 if (recipeFile != null)
544 fileText.setText(recipeFile);
545 }
546
547 private void updateSrcUri(HashMap<String, String> mirrorsMap, URI srcUri) {
548 Set<String> mirrors = mirrorsMap.keySet();
549 Iterator<String> iter = mirrors.iterator();
550 String mirror_key = null;
551 String srcURL = srcUri.toString();
552
553 while (iter.hasNext()) {
554 String value = iter.next();
555 if (srcURL.startsWith(value)) {
556 mirror_key = value;
557 break;
558 }
559 }
560
561 if (mirror_key != null) {
562 String replace_string = mirrorsMap.get(mirror_key);
563 if (replace_string != null)
564 srcURL = replace_string + srcURL.substring(mirror_key.length());
565 }
566 int idx = srcURL.lastIndexOf("-");
567 String new_src_uri = srcURL.substring(0, idx)+"-${PV}" + TAR_GZ_EXT;
568 txtSrcURI.setText(new_src_uri);
569 }
570
571 private void initialize() {
572 if (selection != null && selection.isEmpty() == false && selection instanceof IStructuredSelection) {
573 IStructuredSelection ssel = (IStructuredSelection) selection;
574 if (ssel.size() > 1)
575 return;
576 Object obj = ssel.getFirstElement();
577 if (obj instanceof IResource) {
578 IContainer container;
579 if (obj instanceof IContainer)
580 container = (IContainer) obj;
581 else
582 container = ((IResource) obj).getParent();
583 containerText.setText(container.getFullPath().toString());
584 }
585 }
586 }
587
588 private void updateStatus(String message) {
589 setErrorMessage(message);
590 setPageComplete(message == null);
591 }
592}