~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to javacvs/cvsmodule/src/org/netbeans/modules/versioning/system/cvss/ui/actions/diff/ExportDiffAction.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
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]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
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
 *
 
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.
 
40
 */
 
41
 
 
42
package org.netbeans.modules.versioning.system.cvss.ui.actions.diff;
 
43
 
 
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;
 
67
 
 
68
import javax.swing.*;
 
69
import java.io.*;
 
70
import java.util.*;
 
71
import java.util.List;
 
72
import java.awt.event.ActionListener;
 
73
import java.awt.event.ActionEvent;
 
74
import java.awt.*;
 
75
 
 
76
/**
 
77
 * Exports diff to file:
 
78
 *
 
79
 * <ul>
 
80
 * <li>for components that implements {@link DiffSetupSource} interface
 
81
 * exports actually displayed diff.
 
82
 *
 
83
 * <li>for DataNodes <b>local</b> differencies between the current
 
84
 * working copy and BASE repository version.
 
85
 * </ul>
 
86
 *  
 
87
 * @author Petr Kuzel
 
88
 */
 
89
public class ExportDiffAction extends AbstractSystemAction {
 
90
    
 
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;
 
98
 
 
99
    public ExportDiffAction() {
 
100
        setIcon(null);
 
101
        putValue("noIconInMenu", Boolean.TRUE); // NOI18N
 
102
    }
 
103
 
 
104
    protected String getBaseName(Node [] activatedNodes) {
 
105
        return "CTL_MenuItem_ExportDiff";  // NOI18N
 
106
    }
 
107
 
 
108
    /**
 
109
     * First look for DiffSetupSource name then for super (context name).
 
110
     */
 
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
 
117
                                            setupName);
 
118
            }
 
119
        }
 
120
        return super.getName();
 
121
    }
 
122
 
 
123
    protected int getFileEnabledStatus() {
 
124
        return enabledForStatus;
 
125
    }
 
126
 
 
127
    public boolean enable(Node[] nodes) {
 
128
        TopComponent activated = TopComponent.getRegistry().getActivated();
 
129
        if (activated instanceof DiffSetupSource) {
 
130
            return true;
 
131
        }
 
132
        return  super.enable(nodes) && 
 
133
                Lookup.getDefault().lookup(DiffProvider.class) != null;
 
134
    }
 
135
 
 
136
    public void performCvsAction(final Node[] nodes) {
 
137
 
 
138
        // reevaluate fast enablement logic guess
 
139
 
 
140
        boolean noop;
 
141
        TopComponent activated = TopComponent.getRegistry().getActivated();
 
142
        if (activated instanceof DiffSetupSource) {
 
143
            noop = ((DiffSetupSource) activated).getSetups().isEmpty();
 
144
        } else {
 
145
            Context context = getContext(nodes);
 
146
            File [] files = DiffExecutor.getModifiedFiles(context, FileInformation.STATUS_LOCAL_CHANGE);
 
147
            noop = files.length == 0;
 
148
        }
 
149
        if (noop) {
 
150
            NotifyDescriptor msg = new NotifyDescriptor.Message(NbBundle.getMessage(ExportDiffAction.class, "BK3001"), NotifyDescriptor.INFORMATION_MESSAGE);
 
151
            DialogDisplayer.getDefault().notify(msg);
 
152
            return;
 
153
        }
 
154
 
 
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);
 
162
 
 
163
        }        
 
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
 
168
            }
 
169
            public String getDescription() {
 
170
                return NbBundle.getMessage(ExportDiffAction.class, "BK3002");
 
171
            }
 
172
        });
 
173
 
 
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);
 
180
 
 
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
 
194
                    }
 
195
 
 
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) {
 
201
                            return;
 
202
                        }
 
203
                    }
 
204
 
 
205
                    CvsModuleConfig.getDefault().getPreferences().put("ExportDiff.saveFolder", destination.getParent());
 
206
 
 
207
                    final File out = destination;
 
208
                    RequestProcessor.getDefault().post(new Runnable() {
 
209
                        public void run() {
 
210
                            async(nodes, out);
 
211
                        }
 
212
                    });
 
213
                }
 
214
                dialog.dispose();
 
215
            }
 
216
        });
 
217
        dialog.setVisible(true);
 
218
 
 
219
    }
 
220
 
 
221
    protected boolean asynchronous() {
 
222
        return false;
 
223
    }
 
224
    
 
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));
 
230
        try {
 
231
 
 
232
            // prepare setups and common parent - root
 
233
 
 
234
            List<Setup> setups;
 
235
 
 
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()); 
 
243
                }
 
244
            } else {
 
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);
 
251
                    setups.add(setup);
 
252
                }
 
253
            }
 
254
 
 
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
 
262
 
 
263
 
 
264
            Collections.sort(setups, new Comparator<Setup>() {
 
265
                public int compare(Setup o1, Setup o2) {
 
266
                    return o1.getBaseFile().compareTo(o2.getBaseFile());
 
267
                }
 
268
            });
 
269
            Iterator it = setups.iterator();
 
270
            int i = 0;
 
271
            while (it.hasNext()) {
 
272
                Setup setup = (Setup) it.next();
 
273
                File file = setup.getBaseFile();
 
274
                group.progress(file.getName());
 
275
 
 
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);
 
280
                i++;
 
281
            }
 
282
 
 
283
            exportedFiles = i;
 
284
            success = true;
 
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
 
289
        } finally {
 
290
            group.executed();
 
291
            if (out != null) {
 
292
                try {
 
293
                    out.flush();
 
294
                    out.close();
 
295
                } catch (IOException alreadyClsoed) {
 
296
                }
 
297
            }
 
298
            if (success) {
 
299
                StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(ExportDiffAction.class, "BK3004", new Integer(exportedFiles)));
 
300
                if (exportedFiles == 0) {
 
301
                    destination.delete();
 
302
                } else {
 
303
                    Utils.openFile(destination);
 
304
                }
 
305
            } else {
 
306
                destination.delete();
 
307
            }
 
308
 
 
309
        }
 
310
    }
 
311
 
 
312
    private static String getIndexPath(File file) {
 
313
        AdminHandler ah = CvsVersioningSystem.getInstance().getAdminHandler();
 
314
        try {
 
315
            return ah.getRepositoryForDirectory(file.getParent(), "").substring(1) + "/" + file.getName();
 
316
        } catch (IOException e) {
 
317
            return null;
 
318
        }
 
319
    }
 
320
 
 
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);
 
325
 
 
326
        Reader r1 = null;
 
327
        Reader r2 = null;
 
328
        Difference[] differences;
 
329
 
 
330
        try {
 
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);
 
336
        } finally {
 
337
            if (r1 != null) try { r1.close(); } catch (Exception e) {}
 
338
            if (r2 != null) try { r2.close(); } catch (Exception e) {}
 
339
        }
 
340
 
 
341
        File file = setup.getBaseFile();
 
342
        try {
 
343
            InputStream is;
 
344
            if (!CvsVersioningSystem.getInstance().isText(file) && differences.length == 0) {
 
345
                // assume the file is binary 
 
346
                is = new ByteArrayInputStream(exportBinaryFile(file).getBytes("utf8"));  // NOI18N
 
347
            } else {
 
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
 
355
                    null,
 
356
                    null,
 
357
                    r1,
 
358
                    r2,
 
359
                    differences
 
360
                );
 
361
                info.setContextMode(true, 3);
 
362
                String diffText = TextDiffVisualizer.differenceToUnifiedDiffText(info);
 
363
                is = new ByteArrayInputStream(diffText.getBytes("utf8"));  // NOI18N
 
364
            }
 
365
            while(true) {
 
366
                int i = is.read();
 
367
                if (i == -1) break;
 
368
                out.write(i);
 
369
            }
 
370
        } finally {
 
371
            if (r1 != null) try { r1.close(); } catch (Exception e) {}
 
372
            if (r2 != null) try { r2.close(); } catch (Exception e) {}
 
373
        }
 
374
    }
 
375
 
 
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));
 
381
        }
 
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();
 
387
    }
 
388
}