OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 180 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
182 ilm 4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
17 ilm 5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.openoffice.generation.view;
15
 
73 ilm 16
import org.openconcerto.openoffice.ContentType;
17 ilm 17
import org.openconcerto.openoffice.Log;
18
import org.openconcerto.openoffice.ODSingleXMLDocument;
19
import org.openconcerto.openoffice.OOUtils;
20
import org.openconcerto.openoffice.generation.GenerationTask;
21
import org.openconcerto.openoffice.generation.ReportGeneration;
22
import org.openconcerto.openoffice.generation.TaskStatus;
23
import org.openconcerto.openoffice.generation.desc.ReportType;
24
import org.openconcerto.openoffice.generation.desc.ReportTypes;
25
import org.jopendocument.link.Component;
26
import org.jopendocument.link.OOConnexion;
27
import org.openconcerto.utils.EmailClient;
28
import org.openconcerto.utils.ExceptionHandler;
29
import org.openconcerto.utils.FileUtils;
30
import org.openconcerto.utils.model.ListComboBoxModel;
31
 
32
import java.awt.Desktop;
33
import java.awt.Dimension;
34
import java.awt.GridBagConstraints;
35
import java.awt.GridBagLayout;
36
import java.awt.Insets;
37
import java.awt.event.ActionEvent;
38
import java.awt.event.ActionListener;
39
import java.beans.PropertyChangeEvent;
40
import java.beans.PropertyChangeListener;
41
import java.io.File;
42
import java.io.FileNotFoundException;
43
import java.io.IOException;
44
import java.util.ArrayList;
45
import java.util.LinkedHashMap;
46
import java.util.List;
47
import java.util.Map;
48
import java.util.Map.Entry;
182 ilm 49
import java.util.concurrent.ExecutionException;
17 ilm 50
import java.util.concurrent.Future;
51
 
52
import javax.swing.AbstractAction;
53
import javax.swing.AbstractListModel;
54
import javax.swing.JButton;
55
import javax.swing.JComboBox;
56
import javax.swing.JComponent;
57
import javax.swing.JLabel;
58
import javax.swing.JList;
59
import javax.swing.JOptionPane;
60
import javax.swing.JPanel;
61
import javax.swing.JScrollPane;
62
import javax.swing.SwingUtilities;
180 ilm 63
import javax.swing.SwingWorker;
17 ilm 64
 
65
import org.jdom.JDOMException;
66
 
180 ilm 67
import net.jcip.annotations.GuardedBy;
68
 
17 ilm 69
/**
70
 * A panel to choose a ReportType, see the tasks of the generation, and optionnaly open the
71
 * document. NOTE: you have to call {@link #enableGeneration(boolean)} before being able to
72
 * generate, because it's set to false in the constructor.
144 ilm 73
 *
17 ilm 74
 * @author ILM Informatique 27 déc. 2003
75
 * @param <R> type of generation
76
 */
77
public abstract class BaseGenerationRapport<R extends ReportGeneration<?>> extends JPanel {
78
 
79
    static public enum FileAction {
80
        DO_NOTHING("ne rien faire"), OPEN("ouvrir le fichier"), MAIL("envoyer par courriel");
81
 
82
        private final String label;
83
 
144 ilm 84
        private FileAction(final String label) {
17 ilm 85
            this.label = label;
86
        }
87
 
88
        @Override
89
        public String toString() {
90
            return this.label;
91
        }
92
    }
93
 
144 ilm 94
    private JComboBox<ReportType> typeRapportComboSelection;
17 ilm 95
 
96
    private JButton genererButton;
144 ilm 97
    private JComboBox<FileAction> fileActionCombo;
17 ilm 98
 
144 ilm 99
    private JList<GenerationTask> tasksView;
174 ilm 100
    private final JLabel status;
17 ilm 101
 
180 ilm 102
    @GuardedBy("EDT")
103
    private SwingWorker<Void, Void> generationWorker;
17 ilm 104
 
105
    public BaseGenerationRapport() throws JDOMException, IOException {
180 ilm 106
        this.generationWorker = null;
174 ilm 107
        this.status = new JLabel();
17 ilm 108
        this.setStatus("Inactif");
109
    }
110
 
111
    protected Map<String, JComponent> getEntries() {
112
        return new LinkedHashMap<String, JComponent>();
113
    }
114
 
115
    protected File getReportDir() {
116
        return null;
117
    }
118
 
174 ilm 119
    protected final void uiInit() throws JDOMException, IOException {
17 ilm 120
        setLayout(new GridBagLayout());
144 ilm 121
        final GridBagConstraints c = new GridBagConstraints();
17 ilm 122
        c.ipadx = 40;
123
        c.insets = new Insets(2, 5, 0, 0);
124
        c.gridx = 0;
125
        c.gridy = 0;
126
        c.gridheight = 1;
127
        c.gridwidth = 1;
128
        c.fill = GridBagConstraints.HORIZONTAL;
129
 
130
        final Map<String, JComponent> allEntries = new LinkedHashMap<String, JComponent>();
144 ilm 131
        this.typeRapportComboSelection = new JComboBox<>(new ListComboBoxModel<ReportType>(this.getTypes().getTypes()));
17 ilm 132
        allEntries.put("Type de rapport", this.typeRapportComboSelection);
133
        allEntries.putAll(this.getEntries());
134
 
135
        for (final Map.Entry<String, JComponent> e : allEntries.entrySet()) {
136
            c.gridx = 0;
137
            c.gridy++;
138
            this.add(new JLabel(e.getKey()), c);
139
            c.gridx++;
140
            c.weightx = 1;
141
            this.add(e.getValue(), c);
142
            c.weightx = 0;
143
        }
144
        final File reportDir = getReportDir();
145
        if (reportDir != null) {
146
            c.gridx = 1;
147
            c.gridy++;
148
            c.weightx = 1;
149
            this.add(new JButton(new AbstractAction("Ouvrir le dossier des rapports") {
150
                @Override
144 ilm 151
                public void actionPerformed(final ActionEvent e) {
17 ilm 152
                    try {
153
                        Desktop.getDesktop().browse(reportDir.toURI());
144 ilm 154
                    } catch (final Exception e1) {
17 ilm 155
                        JOptionPane.showMessageDialog(BaseGenerationRapport.this, "Impossible d'ouvrir le dossier", "Erreur", JOptionPane.ERROR_MESSAGE);
156
                    }
157
                }
158
            }), c);
159
            c.weightx = 0;
160
        }
161
 
162
        c.gridx = 0;
163
        c.gridy++;
164
        this.genererButton = new JButton("Générer le rapport et");
165
        this.add(this.genererButton, c);
166
        this.genererButton.setEnabled(false);
167
        this.genererButton.addActionListener(new GenerateAction());
168
 
169
        c.gridx++;
144 ilm 170
        this.fileActionCombo = new JComboBox<>(getAllowedActions());
17 ilm 171
        this.fileActionCombo.setSelectedItem(FileAction.OPEN);
172
        this.add(this.fileActionCombo, c);
173
        this.fileActionCombo.setEnabled(false);
174
 
175
        c.gridx = 0;
176
        c.gridy++;
177
        c.gridwidth = GridBagConstraints.REMAINDER;
178
        this.add(this.status, c);
179
 
180
        c.gridy++;
144 ilm 181
        this.tasksView = new JList<>(new TasksModel());
17 ilm 182
        this.tasksView.setCellRenderer(new GenerationTaskView());
183
        c.fill = GridBagConstraints.BOTH;
184
        c.weighty = 1;
144 ilm 185
        final JScrollPane scroll = new JScrollPane(this.tasksView);
17 ilm 186
        scroll.setPreferredSize(new Dimension(200, 400));
187
        this.add(scroll, c);
188
    }
189
 
190
    /**
191
     * Permet de modifier la liste des tâches.
144 ilm 192
     *
17 ilm 193
     * @author Sylvain CUAZ
194
     */
144 ilm 195
    private static final class TasksModel extends AbstractListModel<GenerationTask> implements PropertyChangeListener {
17 ilm 196
        private List<GenerationTask> tasks;
197
 
198
        {
199
            this.tasks = new ArrayList<GenerationTask>(15);
200
        }
201
 
144 ilm 202
        @Override
17 ilm 203
        public int getSize() {
204
            return this.tasks.size();
205
        }
206
 
144 ilm 207
        @Override
208
        public GenerationTask getElementAt(final int index) {
17 ilm 209
            return this.tasks.get(index);
210
        }
211
 
144 ilm 212
        void add(final GenerationTask task) {
17 ilm 213
            this.tasks.add(task);
214
            task.addPropertyChangeListener(this);
215
            this.fireIntervalAdded(this, this.tasks.size(), this.tasks.size());
216
        }
217
 
218
        void clear() {
219
            for (final GenerationTask t : this.tasks) {
220
                t.removePropertyChangeListener(this);
221
            }
222
            this.fireIntervalRemoved(this, 0, this.tasks.size());
223
            this.tasks.clear();
224
        }
225
 
226
        /*
227
         * Une tache a changé.
228
         */
144 ilm 229
        @Override
230
        public void propertyChange(final PropertyChangeEvent evt) {
231
            final int index = this.tasks.indexOf(evt.getSource());
17 ilm 232
            this.fireContentsChanged(this, index, index);
233
        }
234
    }
235
 
236
    /**
237
     * Interrompt la génération.
238
     */
239
    public final void interrupt() {
240
        // pas de threads active, quand pas génération
180 ilm 241
        if (this.generationWorker != null)
242
            this.generationWorker.cancel(true);
17 ilm 243
    }
244
 
245
    class GenerateAction implements ActionListener {
246
        public GenerateAction() {
247
        }
248
 
144 ilm 249
        @Override
250
        public void actionPerformed(final ActionEvent e) {
17 ilm 251
            enableGeneration(false);
252
            final FileAction sel = (FileAction) BaseGenerationRapport.this.fileActionCombo.getSelectedItem();
253
            // "génération..."
180 ilm 254
            BaseGenerationRapport.this.generationWorker = new SwingWorker<Void, Void>() {
17 ilm 255
                @Override
180 ilm 256
                protected Void doInBackground() throws Exception {
17 ilm 257
                    generate(sel);
180 ilm 258
                    return null;
259
                }
260
 
261
                @Override
262
                protected void done() {
17 ilm 263
                    // toujours le faire, même si interrompu
180 ilm 264
                    enableGeneration(true);
17 ilm 265
                }
180 ilm 266
            };
267
            BaseGenerationRapport.this.generationWorker.execute();
17 ilm 268
        }
269
    }
270
 
144 ilm 271
    protected final void enableGeneration(final boolean b) {
17 ilm 272
        this.fileActionCombo.setEnabled(b);
273
        this.genererButton.setEnabled(b);
274
    }
275
 
276
    protected final void generate(final FileAction sel) {
277
        final ReportType type = (ReportType) this.typeRapportComboSelection.getSelectedItem();
278
        final R rg = this.createGeneration(type);
279
        rg.addTaskListener(new PropertyChangeListener() {
144 ilm 280
            @Override
281
            public void propertyChange(final PropertyChangeEvent evt) {
17 ilm 282
                final TaskStatus st = (TaskStatus) evt.getOldValue();
283
                if (st.getState().equals(TaskStatus.State.NOT_STARTED))
284
                    addTask((GenerationTask) evt.getSource());
285
            }
286
 
287
        });
288
 
289
        if (Thread.currentThread().isInterrupted())
290
            return;
291
 
292
        Map<String, ODSingleXMLDocument> report = null;
293
        try {
294
            report = this.generate(rg);
295
            if (report != null) {
144 ilm 296
                for (final Entry<String, ODSingleXMLDocument> e : report.entrySet()) {
17 ilm 297
                    try {
73 ilm 298
                        final ODSingleXMLDocument singleDoc = e.getValue();
299
                        final File reportFile = singleDoc.saveToPackageAs(this.getFile(rg, e.getKey()));
17 ilm 300
                        if (sel == FileAction.OPEN)
301
                            OOUtils.open(reportFile);
302
                        else if (sel == FileAction.MAIL) {
303
                            final File fileOutPDF = FileUtils.addSuffix(reportFile, ".pdf");
304
                            try {
305
                                final OOConnexion conn = OOConnexion.create();
306
                                if (conn == null)
307
                                    throw new IllegalStateException("OpenOffice n'a pu être trouvé");
308
                                final Component doc = conn.loadDocument(reportFile, true);
73 ilm 309
                                final String filter = singleDoc.getPackage().getContentType().getType() == ContentType.SPREADSHEET ? "calc_pdf_Export" : "writer_pdf_Export";
310
                                final Future<File> pdf = doc.saveToPDF(fileOutPDF, filter);
17 ilm 311
                                doc.close();
312
                                conn.closeConnexion();
313
                                // can wait for the pdf since we're not in the EDT
314
                                EmailClient.getPreferred().compose(getEmailRecipient(rg), "Rapport", null, pdf.get());
144 ilm 315
                            } catch (final Exception exn) {
17 ilm 316
                                exn.printStackTrace();
317
                                ExceptionHandler.handle("Impossible de charger le document OpenOffice dans le logiciel de courriel", exn);
318
                            }
319
                        }
144 ilm 320
                    } catch (final FileNotFoundException exn) {
17 ilm 321
                        // TODO tester pb de droits
322
                        ExceptionHandler.handle(BaseGenerationRapport.this, "Le fichier est déjà ouvert, veuillez le refermer avant de générer.", exn);
144 ilm 323
                    } catch (final IOException exn) {
17 ilm 324
                        ExceptionHandler.handle(BaseGenerationRapport.this, "Impossible de sauver le rapport", exn);
325
                    }
326
                }
327
            }
144 ilm 328
        } catch (final Throwable e) {
182 ilm 329
            // generate() always use a Future
330
            this.handleReportException(e instanceof ExecutionException ? e.getCause() : e);
17 ilm 331
        }
332
    }
333
 
144 ilm 334
    private Map<String, ODSingleXMLDocument> generate(final org.openconcerto.openoffice.generation.ReportGeneration<?> rg) throws Throwable {
17 ilm 335
        clearTasks();
336
        setStatus("Génération en cours...");
337
 
338
        if (Thread.currentThread().isInterrupted())
339
            return null;
340
 
341
        final long start = System.currentTimeMillis();
342
        Map<String, ODSingleXMLDocument> report = null;
343
        String s;
344
        Throwable t = null;
345
        try {
346
            report = rg.generateMulti();
347
            if (report == null) {
348
                s = "Génération interrompue.";
349
            } else {
350
                s = "Rapport généré en " + (System.currentTimeMillis() - start) / 1000 + " secondes.";
351
            }
144 ilm 352
        } catch (final Throwable e) {
17 ilm 353
            s = "Erreur de génération.";
354
            t = e;
355
        }
356
 
357
        setStatus(s);
358
        if (t == null)
359
            return report;
360
        else
361
            throw t;
362
    }
363
 
364
    private final void addTask(final GenerationTask task) {
365
        SwingUtilities.invokeLater(new Runnable() {
144 ilm 366
            @Override
17 ilm 367
            public void run() {
368
                ((TasksModel) BaseGenerationRapport.this.tasksView.getModel()).add(task);
369
            }
370
        });
371
 
372
    }
373
 
374
    private final void clearTasks() {
375
        SwingUtilities.invokeLater(new Runnable() {
144 ilm 376
            @Override
17 ilm 377
            public void run() {
378
                ((TasksModel) BaseGenerationRapport.this.tasksView.getModel()).clear();
379
            }
380
        });
381
    }
382
 
383
    private final void setStatus(final String status) {
384
        Log.get().info(this + " status: " + status);
385
        SwingUtilities.invokeLater(new Runnable() {
144 ilm 386
            @Override
17 ilm 387
            public void run() {
388
                BaseGenerationRapport.this.status.setText(status);
389
            }
390
        });
391
    }
392
 
144 ilm 393
    @Override
17 ilm 394
    public String toString() {
395
        return "Generation panel";
396
    }
397
 
398
    abstract protected ReportTypes getTypes() throws JDOMException, IOException;
399
 
400
    abstract protected R createGeneration(final ReportType type);
401
 
182 ilm 402
    protected void handleReportException(Throwable t) {
403
        ExceptionHandler.handle(this, "Impossible de générer le rapport", t);
404
    }
405
 
17 ilm 406
    abstract protected File getFile(final R rg, String docID);
407
 
408
    protected String getEmailRecipient(final R rg) {
409
        return null;
410
    }
411
 
412
    protected FileAction[] getAllowedActions() {
413
        return FileAction.values();
414
    }
415
}