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]"
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.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.versioning.system.cvss.ui.actions.diff;
44
import org.netbeans.modules.versioning.system.cvss.FileInformation;
45
import org.netbeans.modules.versioning.system.cvss.ExecutorGroup;
46
import org.netbeans.modules.versioning.system.cvss.CvsModuleConfig;
47
import org.netbeans.modules.versioning.system.cvss.CvsVersioningSystem;
48
import org.netbeans.modules.versioning.system.cvss.util.Context;
49
import org.netbeans.modules.versioning.util.AccessibleJFileChooser;
50
import org.netbeans.modules.versioning.util.Utils;
51
import org.netbeans.modules.versioning.system.cvss.ui.actions.AbstractSystemAction;
52
import org.netbeans.modules.diff.builtin.visualizer.TextDiffVisualizer;
53
import org.netbeans.modules.proxy.Base64Encoder;
54
import org.netbeans.api.diff.Difference;
55
import org.netbeans.spi.diff.DiffProvider;
56
import org.netbeans.lib.cvsclient.admin.AdminHandler;
57
import org.openide.windows.TopComponent;
58
import org.openide.util.Lookup;
59
import org.openide.util.RequestProcessor;
60
import org.openide.util.NbBundle;
61
import org.openide.ErrorManager;
62
import org.openide.NotifyDescriptor;
63
import org.openide.DialogDisplayer;
64
import org.openide.DialogDescriptor;
65
import org.openide.nodes.Node;
66
import org.openide.awt.StatusDisplayer;
71
import java.util.List;
72
import java.awt.event.ActionListener;
73
import java.awt.event.ActionEvent;
77
* Exports diff to file:
80
* <li>for components that implements {@link DiffSetupSource} interface
81
* exports actually displayed diff.
83
* <li>for DataNodes <b>local</b> differencies between the current
84
* working copy and BASE repository version.
89
public class ExportDiffAction extends AbstractSystemAction {
91
private static final int enabledForStatus =
92
FileInformation.STATUS_VERSIONED_MERGE |
93
FileInformation.STATUS_VERSIONED_MODIFIEDLOCALLY |
94
FileInformation.STATUS_VERSIONED_DELETEDLOCALLY |
95
FileInformation.STATUS_VERSIONED_REMOVEDLOCALLY |
96
FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY |
97
FileInformation.STATUS_VERSIONED_ADDEDLOCALLY;
99
public ExportDiffAction() {
101
putValue("noIconInMenu", Boolean.TRUE); // NOI18N
104
protected String getBaseName(Node [] activatedNodes) {
105
return "CTL_MenuItem_ExportDiff"; // NOI18N
109
* First look for DiffSetupSource name then for super (context name).
111
public String getName() {
112
TopComponent activated = TopComponent.getRegistry().getActivated();
113
if (activated instanceof DiffSetupSource) {
114
String setupName = ((DiffSetupSource)activated).getSetupDisplayName();
115
if (setupName != null) {
116
return NbBundle.getMessage(this.getClass(), getBaseName(getActivatedNodes()) + "_Context", // NOI18N
120
return super.getName();
123
protected int getFileEnabledStatus() {
124
return enabledForStatus;
127
public boolean enable(Node[] nodes) {
128
TopComponent activated = TopComponent.getRegistry().getActivated();
129
if (activated instanceof DiffSetupSource) {
132
return super.enable(nodes) &&
133
Lookup.getDefault().lookup(DiffProvider.class) != null;
136
public void performCvsAction(final Node[] nodes) {
138
// reevaluate fast enablement logic guess
141
TopComponent activated = TopComponent.getRegistry().getActivated();
142
if (activated instanceof DiffSetupSource) {
143
noop = ((DiffSetupSource) activated).getSetups().isEmpty();
145
Context context = getContext(nodes);
146
File [] files = DiffExecutor.getModifiedFiles(context, FileInformation.STATUS_LOCAL_CHANGE);
147
noop = files.length == 0;
150
NotifyDescriptor msg = new NotifyDescriptor.Message(NbBundle.getMessage(ExportDiffAction.class, "BK3001"), NotifyDescriptor.INFORMATION_MESSAGE);
151
DialogDisplayer.getDefault().notify(msg);
155
final JFileChooser chooser = new AccessibleJFileChooser(NbBundle.getMessage(ExportDiffAction.class, "ACSD_Export"));
156
chooser.setDialogTitle(NbBundle.getMessage(ExportDiffAction.class, "CTL_Export_Title"));
157
chooser.setMultiSelectionEnabled(false);
158
javax.swing.filechooser.FileFilter[] old = chooser.getChoosableFileFilters();
159
for (int i = 0; i < old.length; i++) {
160
javax.swing.filechooser.FileFilter fileFilter = old[i];
161
chooser.removeChoosableFileFilter(fileFilter);
164
chooser.setCurrentDirectory(new File(CvsModuleConfig.getDefault().getPreferences().get("ExportDiff.saveFolder", System.getProperty("user.home")))); // NOI18N
165
chooser.addChoosableFileFilter(new javax.swing.filechooser.FileFilter() {
166
public boolean accept(File f) {
167
return f.getName().endsWith("diff") || f.getName().endsWith("patch") || f.isDirectory(); // NOI18N
169
public String getDescription() {
170
return NbBundle.getMessage(ExportDiffAction.class, "BK3002");
174
chooser.setDialogType( JFileChooser.SAVE_DIALOG ); // #71861
175
chooser.setApproveButtonMnemonic(NbBundle.getMessage(ExportDiffAction.class, "MNE_Export_ExportAction").charAt(0));
176
chooser.setApproveButtonText(NbBundle.getMessage(ExportDiffAction.class, "CTL_Export_ExportAction"));
177
DialogDescriptor dd = new DialogDescriptor(chooser, NbBundle.getMessage(ExportDiffAction.class, "CTL_Export_Title"));
178
dd.setOptions(new Object[0]);
179
final Dialog dialog = DialogDisplayer.getDefault().createDialog(dd);
181
chooser.addActionListener(new ActionListener() {
182
public void actionPerformed(ActionEvent e) {
183
String state = (String)e.getActionCommand();
184
if (state.equals(JFileChooser.APPROVE_SELECTION)) {
185
File destination = chooser.getSelectedFile();
186
String name = destination.getName();
187
boolean requiredExt = false;
188
requiredExt |= name.endsWith(".diff"); // NOI18N
189
requiredExt |= name.endsWith(".dif"); // NOI18N
190
requiredExt |= name.endsWith(".patch"); // NOI18N
191
if (requiredExt == false) {
192
File parent = destination.getParentFile();
193
destination = new File(parent, name + ".patch"); // NOI18N
196
if (destination.exists()) {
197
NotifyDescriptor nd = new NotifyDescriptor.Confirmation(NbBundle.getMessage(ExportDiffAction.class, "BK3005", destination.getAbsolutePath()));
198
nd.setOptionType(NotifyDescriptor.YES_NO_OPTION);
199
DialogDisplayer.getDefault().notify(nd);
200
if (nd.getValue().equals(NotifyDescriptor.OK_OPTION) == false) {
205
CvsModuleConfig.getDefault().getPreferences().put("ExportDiff.saveFolder", destination.getParent());
207
final File out = destination;
208
RequestProcessor.getDefault().post(new Runnable() {
217
dialog.setVisible(true);
221
protected boolean asynchronous() {
225
private void async(Node[] nodes, File destination) {
226
boolean success = false;
227
OutputStream out = null;
228
int exportedFiles = 0;
229
ExecutorGroup group = new ExecutorGroup(getRunningName(nodes));
232
// prepare setups and common parent - root
236
TopComponent activated = TopComponent.getRegistry().getActivated();
237
if (activated instanceof DiffSetupSource) {
238
setups = new ArrayList<Setup>(((DiffSetupSource) activated).getSetups());
239
List<File> setupFiles = new ArrayList<File>(setups.size());
240
for (Iterator i = setups.iterator(); i.hasNext();) {
241
Setup setup = (Setup) i.next();
242
setupFiles.add(setup.getBaseFile());
245
Context context = getContext(nodes);
246
File [] files = DiffExecutor.getModifiedFiles(context, FileInformation.STATUS_LOCAL_CHANGE);
247
setups = new ArrayList<Setup>(files.length);
248
for (int i = 0; i < files.length; i++) {
249
File file = files[i];
250
Setup setup = new Setup(file, Setup.DIFFTYPE_LOCAL);
255
String sep = System.getProperty("line.separator"); // NOI18N
256
out = new BufferedOutputStream(new FileOutputStream(destination));
257
// Used by PatchAction as MAGIC to detect right encoding
258
out.write(("# This patch file was generated by NetBeans IDE" + sep).getBytes("utf8")); // NOI18N
259
out.write(("# This patch can be applied using context Tools: Apply Diff Patch action on respective folder." + sep).getBytes("utf8")); // NOI18N
260
out.write(("# It uses platform neutral UTF-8 encoding." + sep).getBytes("utf8")); // NOI18N
261
out.write(("# Above lines and this line are ignored by the patching process." + sep).getBytes("utf8")); // NOI18N
264
Collections.sort(setups, new Comparator<Setup>() {
265
public int compare(Setup o1, Setup o2) {
266
return o1.getBaseFile().compareTo(o2.getBaseFile());
269
Iterator it = setups.iterator();
271
while (it.hasNext()) {
272
Setup setup = (Setup) it.next();
273
File file = setup.getBaseFile();
274
group.progress(file.getName());
276
String relativePath = getIndexPath(file);
277
String index = "Index: " + relativePath + sep; // NOI18N
278
out.write(index.getBytes("utf8")); // NOI18N
279
exportDiff(group, setup, relativePath, out);
285
} catch (IOException ex) {
286
ErrorManager.getDefault().annotate(ex, NbBundle.getMessage(ExportDiffAction.class, "BK3003"));
287
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); // stack trace to log
288
ErrorManager.getDefault().notify(ErrorManager.USER, ex); // message to user
295
} catch (IOException alreadyClsoed) {
299
StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(ExportDiffAction.class, "BK3004", new Integer(exportedFiles)));
300
if (exportedFiles == 0) {
301
destination.delete();
303
Utils.openFile(destination);
306
destination.delete();
312
private static String getIndexPath(File file) {
313
AdminHandler ah = CvsVersioningSystem.getInstance().getAdminHandler();
315
return ah.getRepositoryForDirectory(file.getParent(), "").substring(1) + "/" + file.getName();
316
} catch (IOException e) {
321
/** Writes contextual diff into given stream.*/
322
private void exportDiff(ExecutorGroup group, Setup setup, String relativePath, OutputStream out) throws IOException {
323
setup.initSources(group);
324
DiffProvider diff = (DiffProvider) Lookup.getDefault().lookup(DiffProvider.class);
328
Difference[] differences;
331
r1 = setup.getFirstSource().createReader();
332
if (r1 == null) r1 = new StringReader(""); // NOI18N
333
r2 = setup.getSecondSource().createReader();
334
if (r2 == null) r2 = new StringReader(""); // NOI18N
335
differences = diff.computeDiff(r1, r2);
337
if (r1 != null) try { r1.close(); } catch (Exception e) {}
338
if (r2 != null) try { r2.close(); } catch (Exception e) {}
341
File file = setup.getBaseFile();
344
if (!CvsVersioningSystem.getInstance().isText(file) && differences.length == 0) {
345
// assume the file is binary
346
is = new ByteArrayInputStream(exportBinaryFile(file).getBytes("utf8")); // NOI18N
348
r1 = setup.getFirstSource().createReader();
349
if (r1 == null) r1 = new StringReader(""); // NOI18N
350
r2 = setup.getSecondSource().createReader();
351
if (r2 == null) r2 = new StringReader(""); // NOI18N
352
TextDiffVisualizer.TextDiffInfo info = new TextDiffVisualizer.TextDiffInfo(
353
relativePath + " " + setup.getFirstSource().getTitle(), // NOI18N
354
relativePath + " " + setup.getSecondSource().getTitle(), // NOI18N
361
info.setContextMode(true, 3);
362
String diffText = TextDiffVisualizer.differenceToUnifiedDiffText(info);
363
is = new ByteArrayInputStream(diffText.getBytes("utf8")); // NOI18N
371
if (r1 != null) try { r1.close(); } catch (Exception e) {}
372
if (r2 != null) try { r2.close(); } catch (Exception e) {}
376
private String exportBinaryFile(File file) throws IOException {
377
ByteArrayOutputStream baos = new ByteArrayOutputStream();
378
StringBuilder sb = new StringBuilder((int) file.length());
379
if (file.canRead()) {
380
Utils.copyStreamsCloseAll(baos, new FileInputStream(file));
382
sb.append("MIME: application/octet-stream; encoding: Base64; length: " + (file.canRead() ? file.length() : -1)); // NOI18N
383
sb.append(System.getProperty("line.separator")); // NOI18N
384
sb.append(Base64Encoder.encode(baos.toByteArray(), true));
385
sb.append(System.getProperty("line.separator")); // NOI18N
386
return sb.toString();