[#77] Fix deprecation warnings for org.openide.NotifyDescriptor.Exception
[nbgit.git] / src / org / nbgit / ui / commit / CommitAction.java
blob9d476c22cc4e9aad50eb938b080707e81ee5d561
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common
8 * Development and Distribution License("CDDL") (collectively, the
9 * "License"). You may not use this file except in compliance with the
10 * License. You can obtain a copy of the License at
11 * http://www.netbeans.org/cddl-gplv2.html
12 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13 * specific language governing permissions and limitations under the
14 * License. When distributing the software, include this License Header
15 * Notice in each file and include the License file at
16 * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17 * particular file as subject to the "Classpath" exception as provided
18 * by Sun in the GPL Version 2 section of the License file that
19 * accompanied this code. If applicable, add the following below the
20 * License Header, with the fields enclosed by brackets [] replaced by
21 * your own identifying information:
22 * "Portions Copyrighted [year] [name of copyright owner]"
24 * Contributor(s):
26 * The Original Software is NetBeans. The Initial Developer of the Original
27 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28 * Microsystems, Inc. All Rights Reserved.
29 * Portions Copyright 2008 Alexander Coles (Ikonoklastik Productions).
31 * If you wish your version of this file to be governed by only the CDDL
32 * or only the GPL Version 2, indicate your decision by adding
33 * "[Contributor] elects to include this software in this distribution
34 * under the [CDDL or GPL Version 2] license." If you do not indicate a
35 * single choice of license, a recipient has the option to distribute
36 * your version of this file under either the CDDL, the GPL Version 2 or
37 * to extend the choice of license to its licensees as provided above.
38 * However, if you add GPL Version 2 code and therefore, elected the GPL
39 * Version 2 license, then the option applies only if the new code is
40 * made subject to such option by the copyright holder.
42 package org.nbgit.ui.commit;
44 import java.awt.BorderLayout;
45 import java.awt.Dialog;
46 import java.awt.event.ActionEvent;
47 import java.io.File;
48 import java.text.MessageFormat;
49 import java.util.ArrayList;
50 import java.util.HashSet;
51 import java.util.List;
52 import java.util.Map;
53 import java.util.Properties;
54 import java.util.ResourceBundle;
55 import java.util.Set;
56 import javax.swing.JButton;
57 import javax.swing.JComponent;
58 import javax.swing.JOptionPane;
59 import javax.swing.event.TableModelEvent;
60 import javax.swing.event.TableModelListener;
61 import org.nbgit.StatusInfo;
62 import org.nbgit.StatusCache;
63 import org.nbgit.Git;
64 import org.nbgit.GitModuleConfig;
65 import org.nbgit.GitProgressSupport;
66 import org.nbgit.OutputLogger;
67 import org.nbgit.client.CommitBuilder;
68 import org.nbgit.ui.GitFileNode;
69 import org.nbgit.ui.ContextAction;
70 import org.nbgit.util.GitProjectUtils;
71 import org.nbgit.util.GitUtils;
72 import org.netbeans.modules.versioning.spi.VCSContext;
73 import org.netbeans.modules.versioning.util.DialogBoundsPreserver;
74 import org.netbeans.modules.versioning.util.Utils;
75 import org.netbeans.modules.versioning.util.VersioningEvent;
76 import org.netbeans.modules.versioning.util.VersioningListener;
77 import org.openide.DialogDescriptor;
78 import org.openide.DialogDisplayer;
79 import org.openide.util.HelpCtx;
80 import org.openide.util.NbBundle;
81 import org.openide.util.RequestProcessor;
83 /**
84 * Commit action for Git:
85 * git commit - commit the specified files or all outstanding changes
87 * @author John Rice
89 public class CommitAction extends ContextAction {
91 static final String RECENT_COMMIT_MESSAGES = "recentCommitMessage"; // NOI18N
92 static final String SIGN_OFF_MESSAGE = "signOffMessage"; // NOI18N
94 public CommitAction(String name, VCSContext context) {
95 super(name, context);
98 @Override
99 public boolean isEnabled() {
100 StatusCache cache = Git.getInstance().getStatusCache();
101 return cache.containsFileOfStatus(context, StatusInfo.STATUS_LOCAL_CHANGE);
104 public void performAction(ActionEvent e) {
105 final File root = GitUtils.getRootFile(context);
106 if (root == null) {
107 OutputLogger logger = OutputLogger.getLogger(Git.GIT_OUTPUT_TAB_TITLE);
108 logger.outputInRed(NbBundle.getMessage(CommitAction.class, "MSG_COMMIT_TITLE")); // NOI18N
109 logger.outputInRed(NbBundle.getMessage(CommitAction.class, "MSG_COMMIT_TITLE_SEP")); // NOI18N
110 logger.outputInRed(
111 NbBundle.getMessage(CommitAction.class, "MSG_COMMIT_NOT_SUPPORTED_INVIEW_INFO")); // NOI18N
112 logger.output(""); // NOI18N
113 logger.closeLog();
114 JOptionPane.showMessageDialog(null,
115 NbBundle.getMessage(CommitAction.class, "MSG_COMMIT_NOT_SUPPORTED_INVIEW"),// NOI18N
116 NbBundle.getMessage(CommitAction.class, "MSG_COMMIT_NOT_SUPPORTED_INVIEW_TITLE"),// NOI18N
117 JOptionPane.INFORMATION_MESSAGE);
118 return;
120 String contentTitle = Utils.getContextDisplayName(context);
122 commit(contentTitle, context);
125 public static void commit(String contentTitle, final VCSContext ctx) {
126 StatusCache cache = Git.getInstance().getStatusCache();
127 File[] roots = ctx.getRootFiles().toArray(new File[ctx.getRootFiles().size()]);
128 if (roots == null || roots.length == 0) {
129 return;
131 final File repository = GitUtils.getRootFile(ctx);
132 if (repository == null) {
133 return;
135 String projName = GitProjectUtils.getProjectName(repository);
136 if (projName == null) {
137 File projFile = GitUtils.getProjectFile(ctx);
138 projName = GitProjectUtils.getProjectName(projFile);
140 final String prjName = projName;
142 File[][] split = Utils.splitFlatOthers(roots);
143 List<File> fileList = new ArrayList<File>();
144 for (int c = 0; c < split.length; c++) {
145 roots = split[c];
146 boolean recursive = c == 1;
147 if (recursive) {
148 File[] files = cache.listFiles(ctx, StatusInfo.STATUS_LOCAL_CHANGE);
149 for (int i = 0; i < files.length; i++) {
150 for (int r = 0; r < roots.length; r++) {
151 if (GitUtils.isParentOrEqual(roots[r], files[i])) {
152 if (!fileList.contains(files[i])) {
153 fileList.add(files[i]);
158 } else {
159 File[] files = GitUtils.flatten(roots, StatusInfo.STATUS_LOCAL_CHANGE);
160 for (int i = 0; i < files.length; i++) {
161 if (!fileList.contains(files[i])) {
162 fileList.add(files[i]);
168 if (fileList.size() == 0) {
169 return; // show commit dialog
171 final CommitPanel panel = new CommitPanel();
172 final CommitTable data = new CommitTable(panel.filesLabel, CommitTable.COMMIT_COLUMNS, new String[]{CommitTableModel.COLUMN_NAME_PATH});
174 panel.setCommitTable(data);
176 Properties props = GitModuleConfig.getDefault().getProperties(repository);
177 String signOff = props.getProperty("nbgit.signoff");
178 if (signOff.equals("yes")) {
179 signOff = "Signed-off-by: " +props.getProperty("user.name")
180 + " <" + props.getProperty("user.email") + ">";
181 panel.putClientProperty(SIGN_OFF_MESSAGE, signOff);
184 GitFileNode[] nodes;
185 ArrayList<GitFileNode> nodesList = new ArrayList<GitFileNode>(fileList.size());
187 for (File file : fileList) {
188 GitFileNode node = new GitFileNode(file);
189 nodesList.add(node);
191 nodes = nodesList.toArray(new GitFileNode[fileList.size()]);
192 data.setNodes(nodes);
194 JComponent component = data.getComponent();
195 panel.filesPanel.setLayout(new BorderLayout());
196 panel.filesPanel.add(component, BorderLayout.CENTER);
198 DialogDescriptor dd = new DialogDescriptor(panel, org.openide.util.NbBundle.getMessage(CommitAction.class, "CTL_CommitDialog_Title", contentTitle)); // NOI18N
199 dd.setModal(true);
200 final JButton commitButton = new JButton();
201 org.openide.awt.Mnemonics.setLocalizedText(commitButton, org.openide.util.NbBundle.getMessage(CommitAction.class, "CTL_Commit_Action_Commit"));
202 commitButton.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CommitAction.class, "ACSN_Commit_Action_Commit"));
203 commitButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CommitAction.class, "ACSD_Commit_Action_Commit"));
204 final JButton cancelButton = new JButton(org.openide.util.NbBundle.getMessage(CommitAction.class, "CTL_Commit_Action_Cancel")); // NOI18N
205 org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(CommitAction.class, "CTL_Commit_Action_Cancel"));
206 cancelButton.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CommitAction.class, "ACSN_Commit_Action_Cancel"));
207 cancelButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(CommitAction.class, "ACSD_Commit_Action_Cancel"));
209 commitButton.setEnabled(false);
210 dd.setOptions(new Object[]{commitButton, cancelButton});
211 dd.setHelpCtx(new HelpCtx(CommitAction.class));
212 panel.addVersioningListener(new VersioningListener() {
214 public void versioningEvent(VersioningEvent event) {
215 refreshCommitDialog(panel, data, commitButton);
218 data.getTableModel().addTableModelListener(new TableModelListener() {
220 public void tableChanged(TableModelEvent e) {
221 refreshCommitDialog(panel, data, commitButton);
224 commitButton.setEnabled(containsCommitable(data));
226 panel.putClientProperty("contentTitle", contentTitle); // NOI18N
227 panel.putClientProperty("DialogDescriptor", dd); // NOI18N
228 final Dialog dialog = DialogDisplayer.getDefault().createDialog(dd);
230 dialog.addWindowListener(new DialogBoundsPreserver(GitModuleConfig.getDefault().getPreferences(), "git.commit.dialog")); // NOI18N
231 dialog.pack();
232 dialog.setVisible(true);
234 if (dd.getValue() == commitButton) {
236 final Map<GitFileNode, CommitOptions> commitFiles = data.getCommitFiles();
237 String origMessage = panel.messageTextArea.getText();
238 String stripSpace = props.getProperty("nbgit.stripspace");
239 if (stripSpace.equals("yes")) {
240 origMessage = stripSpace(origMessage);
242 final String message = origMessage;
243 org.netbeans.modules.versioning.util.Utils.insert(GitModuleConfig.getDefault().getPreferences(), RECENT_COMMIT_MESSAGES, origMessage, 20);
244 RequestProcessor rp = Git.getInstance().getRequestProcessor(repository.getAbsolutePath());
245 GitProgressSupport support = new GitProgressSupport() {
247 public void perform() {
248 OutputLogger logger = getLogger();
249 performCommit(message, commitFiles, ctx, this, prjName, logger);
252 support.start(rp, repository.getAbsolutePath(), org.openide.util.NbBundle.getMessage(CommitAction.class, "LBL_Commit_Progress")); // NOI18N
256 private static boolean containsCommitable(CommitTable data) {
257 Map<GitFileNode, CommitOptions> map = data.getCommitFiles();
258 for (CommitOptions co : map.values()) {
259 if (co != CommitOptions.EXCLUDE) {
260 return true;
263 return false;
267 * User changed a commit action.
269 * @param panel
270 * @param commit
272 private static void refreshCommitDialog(CommitPanel panel, CommitTable table, JButton commit) {
273 ResourceBundle loc = NbBundle.getBundle(CommitAction.class);
274 Map<GitFileNode, CommitOptions> files = table.getCommitFiles();
275 Set<String> stickyTags = new HashSet<String>();
276 boolean conflicts = false;
278 boolean enabled = commit.isEnabled();
280 for (GitFileNode fileNode : files.keySet()) {
282 CommitOptions options = files.get(fileNode);
283 if (options == CommitOptions.EXCLUDE) {
284 continue;
285 //stickyTags.add(GitUtils.getCopy(fileNode.getFile()));
287 int status = fileNode.getInformation().getStatus();
288 if ((status & StatusInfo.STATUS_REMOTE_CHANGE) != 0 || status == StatusInfo.STATUS_VERSIONED_CONFLICT) {
289 enabled = false;
290 String msg = (status == StatusInfo.STATUS_VERSIONED_CONFLICT) ? loc.getString("MSG_CommitForm_ErrorConflicts") : // NOI18N
291 loc.getString("MSG_CommitForm_ErrorRemoteChanges"); // NOI18N
292 panel.setErrorLabel("<html><font color=\"#002080\">" + msg + "</font></html>"); // NOI18N
293 conflicts = true;
295 //stickyTags.add(GitUtils.getCopy(fileNode.getFile()));
299 if (stickyTags.size() > 1) {
300 table.setColumns(new String[]{CommitTableModel.COLUMN_NAME_NAME, CommitTableModel.COLUMN_NAME_BRANCH, CommitTableModel.COLUMN_NAME_STATUS,
301 CommitTableModel.COLUMN_NAME_ACTION, CommitTableModel.COLUMN_NAME_PATH
303 } else {
304 table.setColumns(new String[]{CommitTableModel.COLUMN_NAME_NAME, CommitTableModel.COLUMN_NAME_STATUS,
305 CommitTableModel.COLUMN_NAME_ACTION, CommitTableModel.COLUMN_NAME_PATH
308 String contentTitle = (String) panel.getClientProperty("contentTitle"); // NOI18N
309 // NOI18N
310 DialogDescriptor dd = (DialogDescriptor) panel.getClientProperty("DialogDescriptor"); // NOI18N
311 String errorLabel;
312 if (stickyTags.size() <= 1) {
313 String stickyTag = stickyTags.size() == 0 ? null : stickyTags.iterator().next();
314 if (stickyTag == null) {
315 dd.setTitle(MessageFormat.format(loc.getString("CTL_CommitDialog_Title"), new Object[]{contentTitle})); // NOI18N
316 errorLabel = ""; // NOI18N
317 } else {
318 dd.setTitle(MessageFormat.format(loc.getString("CTL_CommitDialog_Title_Branch"), new Object[]{contentTitle, stickyTag})); // NOI18N
319 String msg = MessageFormat.format(loc.getString("MSG_CommitForm_InfoBranch"), new Object[]{stickyTag}); // NOI18N
320 errorLabel = "<html><font color=\"#002080\">" + msg + "</font></html>"; // NOI18N
322 } else {
323 dd.setTitle(MessageFormat.format(loc.getString("CTL_CommitDialog_Title_Branches"), new Object[]{contentTitle})); // NOI18N
324 String msg = loc.getString("MSG_CommitForm_ErrorMultipleBranches"); // NOI18N
325 errorLabel = "<html><font color=\"#CC0000\">" + msg + "</font></html>"; // NOI18N
327 if (!conflicts) {
328 panel.setErrorLabel(errorLabel);
329 enabled = true;
331 commit.setEnabled(enabled && containsCommitable(table));
334 private static void performCommit(String message, Map<GitFileNode, CommitOptions> commitFiles,
335 VCSContext ctx, GitProgressSupport support, String prjName, OutputLogger logger) {
336 StatusCache cache = Git.getInstance().getStatusCache();
337 final File repository = GitUtils.getRootFile(ctx);
338 List<File> addCandidates = new ArrayList<File>();
339 List<File> deleteCandidates = new ArrayList<File>();
340 int commitCandidates = 0;
342 List<String> excPaths = new ArrayList<String>();
343 List<String> incPaths = new ArrayList<String>();
344 for (GitFileNode node : commitFiles.keySet()) {
345 if (support.isCanceled()) {
346 return;
348 CommitOptions option = commitFiles.get(node);
349 if (option != CommitOptions.EXCLUDE) {
350 int status = cache.getStatus(node.getFile()).getStatus();
351 if ((status & StatusInfo.STATUS_NOTVERSIONED_NEWLOCALLY) != 0) {
352 addCandidates.add(node.getFile());
353 } else if ((status & StatusInfo.STATUS_VERSIONED_DELETEDLOCALLY) != 0) {
354 deleteCandidates.add(node.getFile());
355 } else {
356 addCandidates.add(node.getFile());
358 commitCandidates++;
359 incPaths.add(node.getFile().getAbsolutePath());
360 } else {
361 excPaths.add(node.getFile().getAbsolutePath());
364 if (support.isCanceled()) {
365 return;
367 if (!excPaths.isEmpty()) {
368 GitModuleConfig.getDefault().addExclusionPaths(excPaths);
370 if (!incPaths.isEmpty()) {
371 GitModuleConfig.getDefault().removeExclusionPaths(incPaths);
373 try {
374 logger.outputInRed(
375 NbBundle.getMessage(CommitAction.class,
376 "MSG_COMMIT_TITLE")); // NOI18N
377 logger.outputInRed(
378 NbBundle.getMessage(CommitAction.class,
379 "MSG_COMMIT_TITLE_SEP")); // NOI18N
380 logger.output(message); // NOI18N
382 CommitBuilder.create(repository).
383 log(logger).
384 addAll(addCandidates).
385 deleteAll(deleteCandidates).
386 message(message).
387 write();
389 if (commitCandidates == 1) {
390 logger.output(
391 NbBundle.getMessage(CommitAction.class,
392 "MSG_COMMIT_INIT_SEP_ONE", commitCandidates, prjName));
393 } else {
394 logger.output(
395 NbBundle.getMessage(CommitAction.class,
396 "MSG_COMMIT_INIT_SEP", commitCandidates, prjName));
398 } catch (Exception ex) {
399 notifyLater(ex);
400 } finally {
401 cache.refreshCached(ctx);
402 logger.outputInRed(NbBundle.getMessage(CommitAction.class, "MSG_COMMIT_DONE")); // NOI18N
403 logger.output(""); // NOI18N
407 static String stripSpace(String message) {
408 StringBuilder builder = new StringBuilder();
409 String[] lines = message.split("\n");
410 String sep = "\n";
411 int i = 0;
413 for (String line : lines) {
414 int j = line.length();
416 while (j > 0) {
417 if (!Character.isWhitespace(line.charAt(j - 1))) {
418 break;
420 j--;
423 if (j > 0) {
424 if (builder.length() > 0) {
425 builder.append(sep);
427 builder.append(line, 0, j);
428 builder.append("\n");
429 sep = "";
430 } else if (sep.length() == 0) {
431 sep = "\n";
432 } else {
433 continue;
437 return builder.toString();