Added
Link Here
|
1 |
/* |
2 |
* Sun Public License Notice |
3 |
* |
4 |
* The contents of this file are subject to the Sun Public License |
5 |
* Version 1.0 (the "License"). You may not use this file except in |
6 |
* compliance with the License. A copy of the License is available at |
7 |
* http://www.sun.com/ |
8 |
* |
9 |
* The Original Code is NetBeans. The Initial Developer of the Original |
10 |
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2005 Sun |
11 |
* Microsystems, Inc. All Rights Reserved. |
12 |
*/ |
13 |
|
14 |
package org.netbeans.modules.project.uiapi; |
15 |
|
16 |
import java.awt.Component; |
17 |
import java.awt.Dialog; |
18 |
import java.awt.GridBagConstraints; |
19 |
import java.awt.Insets; |
20 |
import java.awt.Window; |
21 |
import java.awt.event.ActionEvent; |
22 |
import java.awt.event.ActionListener; |
23 |
import java.io.File; |
24 |
import java.io.FileOutputStream; |
25 |
import java.io.IOException; |
26 |
import java.io.InputStream; |
27 |
import java.io.OutputStream; |
28 |
import java.util.ArrayList; |
29 |
import java.util.Arrays; |
30 |
import java.util.Iterator; |
31 |
import java.util.List; |
32 |
import javax.swing.JButton; |
33 |
import javax.swing.JComponent; |
34 |
import javax.swing.JLabel; |
35 |
import javax.swing.JPanel; |
36 |
import javax.swing.SwingUtilities; |
37 |
import javax.swing.event.ChangeEvent; |
38 |
import javax.swing.event.ChangeListener; |
39 |
import org.netbeans.api.progress.ProgressHandle; |
40 |
import org.netbeans.api.progress.ProgressHandleFactory; |
41 |
import org.netbeans.api.project.FileOwnerQuery; |
42 |
import org.netbeans.api.project.Project; |
43 |
import org.netbeans.api.project.ProjectManager; |
44 |
import org.netbeans.api.project.ProjectUtils; |
45 |
import org.netbeans.api.project.ui.OpenProjects; |
46 |
import org.netbeans.spi.project.support.ProjectOperations; |
47 |
import org.netbeans.api.queries.VisibilityQuery; |
48 |
import org.openide.DialogDescriptor; |
49 |
import org.openide.DialogDisplayer; |
50 |
import org.openide.ErrorManager; |
51 |
import org.openide.NotifyDescriptor; |
52 |
import org.openide.filesystems.FileObject; |
53 |
import org.openide.filesystems.FileUtil; |
54 |
import org.openide.util.NbBundle; |
55 |
import org.openide.util.RequestProcessor; |
56 |
|
57 |
|
58 |
/** |
59 |
* |
60 |
* @author Jan Lahoda |
61 |
*/ |
62 |
public final class DefaultProjectOperationsImplementation { |
63 |
|
64 |
private static final ErrorManager ERR = ErrorManager.getDefault(); // NOI18N |
65 |
|
66 |
private DefaultProjectOperationsImplementation() { |
67 |
} |
68 |
|
69 |
private static String getDisplayName(Project project) { |
70 |
return ProjectUtils.getInformation(project).getDisplayName(); |
71 |
} |
72 |
|
73 |
//<editor-fold defaultstate="collapsed" desc="Delete Operation"> |
74 |
/** |
75 |
* @return true if success |
76 |
*/ |
77 |
private static boolean performDelete(Project project, List/*FileObject>*/ toDelete, ProgressHandle handle) { |
78 |
try { |
79 |
handle.start(toDelete.size() + 1 /*clean*/); |
80 |
|
81 |
int done = 0; |
82 |
|
83 |
handle.progress(NbBundle.getMessage(DefaultProjectOperationsImplementation.class, "LBL_Progress_Cleaning_Project")); |
84 |
|
85 |
ProjectOperations.notifyDeleting(project); |
86 |
|
87 |
handle.progress(++done); |
88 |
|
89 |
for (Iterator i = toDelete.iterator(); i.hasNext(); ) { |
90 |
FileObject f = (FileObject) i.next(); |
91 |
|
92 |
handle.progress(NbBundle.getMessage(DefaultProjectOperationsImplementation.class, "LBL_Progress_Deleting_File", new Object[] {FileUtil.getFileDisplayName(f)})); |
93 |
|
94 |
if (f != null) |
95 |
f.delete(); |
96 |
|
97 |
handle.progress(++done); |
98 |
} |
99 |
|
100 |
FileObject projectFolder = project.getProjectDirectory(); |
101 |
|
102 |
if (projectFolder.getChildren().length == 0) { |
103 |
//empty, delete: |
104 |
projectFolder.delete(); |
105 |
} |
106 |
|
107 |
handle.finish(); |
108 |
|
109 |
ProjectOperations.notifyDeleted(project); |
110 |
return true; |
111 |
} catch (IOException e) { |
112 |
String displayName = getDisplayName(project); |
113 |
String message = NbBundle.getMessage(DefaultProjectOperationsImplementation.class, "LBL_Project_cannot_be_deleted.", new Object[] {displayName}); |
114 |
|
115 |
ErrorManager.getDefault().annotate(e, message); |
116 |
ErrorManager.getDefault().notify(ErrorManager.USER, e); |
117 |
|
118 |
return false; |
119 |
} |
120 |
} |
121 |
|
122 |
public static void deleteProject(final Project project) { |
123 |
Runnable r = new Runnable() { |
124 |
public void run() { |
125 |
deleteProject(project, new GUIUserInputHandler()); |
126 |
} |
127 |
}; |
128 |
|
129 |
if (SwingUtilities.isEventDispatchThread()) { |
130 |
r.run(); |
131 |
} else { |
132 |
SwingUtilities.invokeLater(r); |
133 |
} |
134 |
} |
135 |
|
136 |
/*package private*/static void deleteProject(final Project project, final UserInputHandler handler) { |
137 |
String displayName = getDisplayName(project); |
138 |
FileObject projectFolder = project.getProjectDirectory(); |
139 |
|
140 |
if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { |
141 |
ERR.log(ErrorManager.INFORMATIONAL, "delete started: " + displayName); // NOI18N |
142 |
} |
143 |
|
144 |
List/*<FileObject>*/ metadataFiles = ProjectOperations.getMetadataFiles(project); |
145 |
List/*<FileObject>*/ dataFiles = ProjectOperations.getDataFiles(project); |
146 |
List/*<FileObject>*/ allFiles = new ArrayList/*<FileObject>*/(); |
147 |
|
148 |
allFiles.addAll(metadataFiles); |
149 |
allFiles.addAll(dataFiles); |
150 |
|
151 |
for (Iterator i = allFiles.iterator(); i.hasNext(); ) { |
152 |
FileObject f = (FileObject) i.next(); |
153 |
|
154 |
if (!FileUtil.isParentOf(projectFolder, f)) { |
155 |
i.remove(); |
156 |
} |
157 |
} |
158 |
|
159 |
int userAnswer = handler.userConfirmation(displayName, FileUtil.getFileDisplayName(projectFolder), !dataFiles.isEmpty()); |
160 |
List/*<FileObject>*/ toDeleteImpl = null; |
161 |
|
162 |
switch (userAnswer) { |
163 |
case UserInputHandler.USER_CANCEL: |
164 |
return ; |
165 |
case UserInputHandler.USER_OK_METADATA: |
166 |
toDeleteImpl = metadataFiles; |
167 |
break; |
168 |
case UserInputHandler.USER_OK_ALL: |
169 |
toDeleteImpl = allFiles; |
170 |
break; |
171 |
default: |
172 |
throw new IllegalStateException("Invalid user answer: " + userAnswer); |
173 |
} |
174 |
|
175 |
final ProgressHandle handle = handler.getProgressHandle(); |
176 |
final List/*<FileObject>*/ toDelete = toDeleteImpl; |
177 |
final boolean[] result = new boolean[1]; |
178 |
|
179 |
OpenProjects.getDefault().close(new Project[] {project}); |
180 |
|
181 |
handler.delete(new Runnable() { |
182 |
public void run() { |
183 |
result[0] = performDelete(project, toDelete, handle); |
184 |
} |
185 |
}); |
186 |
|
187 |
if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { |
188 |
ERR.log(ErrorManager.INFORMATIONAL, "delete done: " + displayName); // NOI18N |
189 |
} |
190 |
} |
191 |
|
192 |
/*package private*/interface UserInputHandler { |
193 |
|
194 |
public int USER_CANCEL = 1; |
195 |
public int USER_OK_METADATA = 2; |
196 |
public int USER_OK_ALL = 3; |
197 |
|
198 |
public abstract int userConfirmation(String displayName, String projectFolder, boolean enableData); |
199 |
|
200 |
public abstract ProgressHandle getProgressHandle(); |
201 |
|
202 |
public abstract void delete(Runnable r); |
203 |
|
204 |
} |
205 |
|
206 |
private static final class GUIUserInputHandler implements UserInputHandler { |
207 |
|
208 |
public int userConfirmation(String displayName, String projectFolder, boolean enableData) { |
209 |
DefaultProjectDeletePanel deletePanel = new DefaultProjectDeletePanel(displayName, projectFolder, enableData); |
210 |
|
211 |
String caption = NbBundle.getMessage(DefaultProjectOperationsImplementation.class, "LBL_Delete_Project_Caption"); |
212 |
|
213 |
NotifyDescriptor nd = new NotifyDescriptor.Confirmation(deletePanel, caption, NotifyDescriptor.YES_NO_OPTION); |
214 |
|
215 |
if (DialogDisplayer.getDefault().notify(nd) == NotifyDescriptor.YES_OPTION) { |
216 |
if (deletePanel.isDeleteSources()) { |
217 |
return USER_OK_ALL; |
218 |
} else { |
219 |
return USER_OK_METADATA; |
220 |
} |
221 |
} else { |
222 |
return USER_CANCEL; |
223 |
} |
224 |
} |
225 |
|
226 |
private ProgressHandle handle = null; |
227 |
|
228 |
public synchronized ProgressHandle getProgressHandle() { |
229 |
if (handle == null) { |
230 |
handle = ProgressHandleFactory.createHandle(NbBundle.getMessage(DefaultProjectOperationsImplementation.class, "LBL_Delete_Project_Caption")); |
231 |
} |
232 |
|
233 |
return handle; |
234 |
} |
235 |
|
236 |
public void delete(final Runnable r) { |
237 |
RequestProcessor.getDefault().post(new Runnable() { |
238 |
public void run() { |
239 |
r.run(); |
240 |
|
241 |
if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { |
242 |
ERR.log(ErrorManager.INFORMATIONAL, "delete finished"); // NOI18N |
243 |
} |
244 |
} |
245 |
}); |
246 |
} |
247 |
|
248 |
} |
249 |
|
250 |
private static JComponent createProgressDialog(ProgressHandle handle) { |
251 |
JPanel dialog = new JPanel(); |
252 |
|
253 |
GridBagConstraints gridBagConstraints; |
254 |
|
255 |
JLabel jLabel1 = new JLabel(); |
256 |
JComponent progress = ProgressHandleFactory.createProgressComponent(handle); |
257 |
JPanel padding = new JPanel(); |
258 |
|
259 |
dialog.setLayout(new java.awt.GridBagLayout()); |
260 |
|
261 |
jLabel1.setText(NbBundle.getMessage(DefaultProjectOperationsImplementation.class, "LBL_Deleting_Project")); |
262 |
gridBagConstraints = new GridBagConstraints(); |
263 |
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; |
264 |
gridBagConstraints.insets = new Insets(12, 12, 0, 12); |
265 |
dialog.add(jLabel1, gridBagConstraints); |
266 |
|
267 |
gridBagConstraints = new GridBagConstraints(); |
268 |
gridBagConstraints.gridx = 0; |
269 |
gridBagConstraints.gridy = 1; |
270 |
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; |
271 |
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; |
272 |
gridBagConstraints.weightx = 1.0; |
273 |
gridBagConstraints.insets = new Insets(12, 12, 0, 12); |
274 |
dialog.add(progress, gridBagConstraints); |
275 |
|
276 |
gridBagConstraints = new GridBagConstraints(); |
277 |
gridBagConstraints.gridx = 0; |
278 |
gridBagConstraints.gridy = 2; |
279 |
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL | GridBagConstraints.VERTICAL; |
280 |
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; |
281 |
gridBagConstraints.weightx = 1.0; |
282 |
gridBagConstraints.weighty = 1.0; |
283 |
gridBagConstraints.insets = new Insets(12, 12, 12, 12); |
284 |
dialog.add(padding, gridBagConstraints); |
285 |
|
286 |
return dialog; |
287 |
} |
288 |
//</editor-fold> |
289 |
|
290 |
//<editor-fold defaultstate="collapsed" desc="Copy Operation"> |
291 |
public static void copyProject(final Project project) { |
292 |
final ProgressHandle handle = ProgressHandleFactory.createHandle("Copy Project"); |
293 |
final ProjectCopyPanel panel = new ProjectCopyPanel(handle, project, false); |
294 |
|
295 |
showConfirmationDialog(panel, project, "Copy Project", "Copy", new Executor() { |
296 |
public void execute() { |
297 |
String nueName = panel.getNewName(); |
298 |
File newTarget = panel.getNewDirectory(); |
299 |
|
300 |
doCopyProject(handle, project, nueName, newTarget); |
301 |
} |
302 |
}); |
303 |
} |
304 |
|
305 |
/*package private for tests*/ static void doCopyProject(ProgressHandle handle, Project project, String nueName, File newTarget) { |
306 |
try { |
307 |
File target = new File(newTarget, nueName); |
308 |
|
309 |
assert !target.exists(); |
310 |
|
311 |
ProjectOperations.notifyCopying(project); |
312 |
|
313 |
target.mkdirs(); |
314 |
|
315 |
FileObject projectDirectory = project.getProjectDirectory(); |
316 |
List/*<FileObject>*/ toCopyList = Arrays.asList(projectDirectory.getChildren()); |
317 |
|
318 |
handle.start(toCopyList.size()); |
319 |
|
320 |
int workDone = 0; |
321 |
|
322 |
for (Iterator i = toCopyList.iterator(); i.hasNext(); ) { |
323 |
FileObject toCopy = (FileObject) i.next(); |
324 |
File toCopyFile = FileUtil.toFile(toCopy); |
325 |
|
326 |
doCopy(project, toCopy, new File(target, toCopy.getNameExt())); |
327 |
handle.progress(++workDone); |
328 |
} |
329 |
|
330 |
Project nue = ProjectManager.getDefault().findProject(FileUtil.toFileObject(target)); |
331 |
|
332 |
assert nue != null; |
333 |
|
334 |
ProjectOperations.notifyCopied(project, nue, FileUtil.toFile(project.getProjectDirectory()), nueName); |
335 |
|
336 |
ProjectManager.getDefault().saveProject(nue); |
337 |
|
338 |
OpenProjects.getDefault().open(new Project[] {nue}, false); |
339 |
|
340 |
handle.finish(); |
341 |
} catch (IOException e) { |
342 |
ErrorManager.getDefault().notify(e); |
343 |
e.printStackTrace(); |
344 |
} |
345 |
} |
346 |
//</editor-fold> |
347 |
|
348 |
//<editor-fold defaultstate="collapsed" desc="Move Operation"> |
349 |
public static void moveProject(final Project project) { |
350 |
final ProgressHandle handle = ProgressHandleFactory.createHandle("Move Project"); |
351 |
final ProjectCopyPanel panel = new ProjectCopyPanel(handle, project, true); |
352 |
|
353 |
showConfirmationDialog(panel, project, "Move Project", "Move", new Executor() { |
354 |
public void execute() { |
355 |
String nueName = panel.getNewName(); |
356 |
File newTarget = panel.getNewDirectory(); |
357 |
|
358 |
doMoveProject(handle, project, nueName, newTarget); |
359 |
} |
360 |
}); |
361 |
} |
362 |
|
363 |
public static void renameProject(Project project) { |
364 |
renameProject(project, null); |
365 |
} |
366 |
|
367 |
public static void renameProject(final Project project, final String nueName) { |
368 |
final ProgressHandle handle = ProgressHandleFactory.createHandle("Copy Project"); |
369 |
final DefaultProjectRenamePanel panel = new DefaultProjectRenamePanel(project, nueName); |
370 |
|
371 |
showConfirmationDialog(panel, project, "Rename Project", "Rename", new Executor() { |
372 |
|
373 |
public void execute() { |
374 |
String nueName = panel.getNewName(); |
375 |
|
376 |
doMoveProject(handle, project, nueName, FileUtil.toFile(project.getProjectDirectory().getParent())); |
377 |
} |
378 |
}); |
379 |
} |
380 |
|
381 |
/*package private for tests*/ static void doMoveProject(ProgressHandle handle, Project project, String nueName, File newTarget) { |
382 |
try { |
383 |
File target = new File(newTarget, nueName); |
384 |
|
385 |
assert !target.exists(); |
386 |
|
387 |
ProjectOperations.notifyMoving(project); |
388 |
|
389 |
OpenProjects.getDefault().close(new Project[] {project}); |
390 |
|
391 |
FileObject projectDirectory = project.getProjectDirectory(); |
392 |
List/*<FileObject>*/ toMoveList = Arrays.asList(projectDirectory.getChildren()); |
393 |
|
394 |
target.mkdirs(); |
395 |
|
396 |
for (Iterator i = toMoveList.iterator(); i.hasNext(); ) { |
397 |
FileObject toCopy = (FileObject) i.next(); |
398 |
File toCopyFile = FileUtil.toFile(toCopy); |
399 |
|
400 |
doCopy(project, toCopy, new File(target, toCopy.getNameExt())); |
401 |
delete(toCopyFile); |
402 |
} |
403 |
|
404 |
projectDirectory.refresh();//TODO: better way. |
405 |
projectDirectory.refresh(); |
406 |
|
407 |
if (projectDirectory.getChildren().length == 0) { |
408 |
projectDirectory.delete(); |
409 |
} |
410 |
|
411 |
ProjectOperations.notifyDeleted(project); |
412 |
|
413 |
Project nue = ProjectManager.getDefault().findProject(FileUtil.toFileObject(target)); |
414 |
|
415 |
assert nue != null; |
416 |
|
417 |
ProjectOperations.notifyMoved(project, nue, FileUtil.toFile(project.getProjectDirectory()), nueName); |
418 |
|
419 |
ProjectManager.getDefault().saveProject(nue); |
420 |
|
421 |
OpenProjects.getDefault().open(new Project[] {nue}, false); |
422 |
} catch (IOException e) { |
423 |
ErrorManager.getDefault().notify(e); |
424 |
} |
425 |
} |
426 |
//</editor-fold> |
427 |
|
428 |
//<editor-fold defaultstate="collapsed" desc="Copy Move Utilities"> |
429 |
private static void doCopy(Project original, FileObject from, File to) throws IOException { |
430 |
if (!VisibilityQuery.getDefault().isVisible(from)) { |
431 |
//Do not copy invisible files/folders. |
432 |
return ; |
433 |
} |
434 |
|
435 |
if (!original.getProjectDirectory().equals(FileOwnerQuery.getOwner(from).getProjectDirectory())) { |
436 |
return ; |
437 |
} |
438 |
|
439 |
if (from.isFolder()) { |
440 |
to.mkdir(); |
441 |
FileObject[] kids = from.getChildren(); |
442 |
for (int i = 0; i < kids.length; i++) { |
443 |
doCopy(original, kids[i], new File(to, kids[i].getNameExt())); |
444 |
} |
445 |
} else { |
446 |
assert from.isData(); |
447 |
InputStream is = from.getInputStream(); |
448 |
try { |
449 |
OutputStream os = new FileOutputStream(to); |
450 |
try { |
451 |
FileUtil.copy(is, os); |
452 |
} finally { |
453 |
os.close(); |
454 |
} |
455 |
} finally { |
456 |
is.close(); |
457 |
} |
458 |
} |
459 |
} |
460 |
|
461 |
private static void delete(File f) throws IOException { |
462 |
if (f.isDirectory()) { |
463 |
File[] children = f.listFiles(); |
464 |
|
465 |
for (int cntr = 0; cntr < children.length; cntr++) { |
466 |
delete(children[cntr]); |
467 |
} |
468 |
} |
469 |
|
470 |
f.delete(); |
471 |
} |
472 |
|
473 |
private static void showConfirmationDialog(final JComponent panel, Project project, String caption, String confirmButton, final Executor executor) { |
474 |
final JButton confirm = new JButton(confirmButton); |
475 |
final JButton cancel = new JButton("Cancel"); |
476 |
|
477 |
assert panel instanceof InvalidablePanel; |
478 |
|
479 |
((InvalidablePanel) panel).addChangeListener(new ChangeListener() { |
480 |
public void stateChanged(ChangeEvent e) { |
481 |
confirm.setEnabled(((InvalidablePanel) panel).isValid()); |
482 |
} |
483 |
}); |
484 |
|
485 |
confirm.setEnabled(panel.isValid()); |
486 |
|
487 |
final Dialog[] dialog = new Dialog[1]; |
488 |
|
489 |
DialogDescriptor dd = new DialogDescriptor(panel, caption, true, new Object[] {confirm, cancel}, confirm, DialogDescriptor.DEFAULT_ALIGN, null, new ActionListener() { |
490 |
public void actionPerformed(ActionEvent e) { |
491 |
if (e.getSource() == confirm) { |
492 |
confirm.setEnabled(false); |
493 |
cancel.setEnabled(false); |
494 |
((InvalidablePanel) panel).showProgress(); |
495 |
|
496 |
Component findParent = panel; |
497 |
|
498 |
while (findParent != null && !(findParent instanceof Window)) { |
499 |
findParent = findParent.getParent(); |
500 |
} |
501 |
|
502 |
if (findParent != null) { |
503 |
((Window) findParent).pack(); |
504 |
} |
505 |
|
506 |
RequestProcessor.getDefault().post(new Runnable() { |
507 |
public void run() { |
508 |
executor.execute(); |
509 |
|
510 |
SwingUtilities.invokeLater(new Runnable() { |
511 |
public void run() { |
512 |
dialog[0].setVisible(false); |
513 |
} |
514 |
}); |
515 |
} |
516 |
}); |
517 |
} |
518 |
} |
519 |
}); |
520 |
|
521 |
dialog[0] = DialogDisplayer.getDefault().createDialog(dd); |
522 |
|
523 |
dialog[0].setVisible(true); |
524 |
|
525 |
dialog[0] = null; |
526 |
} |
527 |
|
528 |
private static interface Executor { |
529 |
public void execute(); |
530 |
} |
531 |
|
532 |
public static interface InvalidablePanel { |
533 |
public void addChangeListener(ChangeListener l); |
534 |
public void removeChangeListener(ChangeListener l); |
535 |
public boolean isValid(); |
536 |
public void showProgress(); |
537 |
} |
538 |
//</editor-fold> |
539 |
|
540 |
} |