View Javadoc
1   package fr.ifremer.quadrige2.ui.swing.common.synchro;
2   
3   /*
4    * #%L
5    * Reef DB :: UI
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2013 - 2014 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import com.google.common.base.Charsets;
27  import com.google.common.base.Preconditions;
28  import com.google.common.collect.Multimap;
29  import com.google.common.collect.Sets;
30  import com.google.common.io.Files;
31  import fr.ifremer.quadrige2.core.config.Quadrige2Configuration;
32  import fr.ifremer.quadrige2.core.dao.technical.DateVersions;
33  import fr.ifremer.quadrige2.core.dao.technical.Dates;
34  import fr.ifremer.quadrige2.core.dao.technical.hibernate.ConfigurationHelper;
35  import fr.ifremer.quadrige2.core.exception.Quadrige2TechnicalException;
36  import fr.ifremer.quadrige2.synchro.vo.SynchroExportContextVO;
37  import fr.ifremer.quadrige2.synchro.vo.SynchroImportContextVO;
38  import fr.ifremer.quadrige2.synchro.vo.SynchroProgressionStatus;
39  import fr.ifremer.quadrige2.ui.swing.common.ApplicationUIContext;
40  import fr.ifremer.quadrige2.ui.swing.common.model.BeanPropertyChangeListener;
41  import fr.ifremer.quadrige2.ui.swing.common.model.ProgressionModel;
42  import org.apache.commons.collections4.CollectionUtils;
43  import org.apache.commons.io.IOUtils;
44  import org.apache.commons.lang3.StringUtils;
45  import org.apache.commons.logging.Log;
46  import org.apache.commons.logging.LogFactory;
47  import org.jdesktop.beans.AbstractBean;
48  import org.nuiton.i18n.I18n;
49  import org.nuiton.updater.ApplicationUpdater;
50  import org.nuiton.util.DateUtil;
51  
52  import java.beans.PropertyChangeListener;
53  import java.io.*;
54  import java.nio.charset.Charset;
55  import java.util.Collection;
56  import java.util.Date;
57  import java.util.Properties;
58  import java.util.Set;
59  
60  import static org.nuiton.i18n.I18n.t;
61  
62  /**
63   * <p>SynchroUIContext class.</p>
64   *
65   * @author Ludovic Pecquot <ludovic.pecquot@e-is.pro>
66   */
67  public class SynchroUIContext extends AbstractBean implements Closeable, BeanPropertyChangeListener {
68  
69      private static final Log log = LogFactory.getLog(SynchroUIContext.class);
70  
71      private static final String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSX";
72  
73      private static final String IMPORT_DIRECTORY = "import";
74      private static final String EXPORT_DIRECTORY = "export";
75      private static final String IMPORT_PROPERTIES = "import.properties";
76      private static final String EXPORT_PROPERTIES = "export.properties";
77  
78      private static final String PROPERTY_UI_DATA_START_DATE = "importDataStartDate";
79      private static final String PROPERTY_UI_DATA_END_DATE = "importDataEndDate";
80  
81      private static final String PROPERTY_DATA_DATE_RANGE = "yyyyMMdd";
82  
83      private final ApplicationUIContext mainContext;
84  
85      private SynchroDirection direction;
86  
87      private String title;
88      /** Constant <code>PROPERTY_SYNCHRO_TITLE="title"</code> */
89      public static final String PROPERTY_SYNCHRO_TITLE = "title";
90  
91      private SynchroImportContextVO synchroImportContext;
92  
93      private SynchroExportContextVO synchroExportContext;
94  
95      private SynchroProgressionStatus status;
96      /** Constant <code>PROPERTY_PROGRESSION_STATUS="status"</code> */
97      public static final String PROPERTY_PROGRESSION_STATUS = "status";
98  
99      private ProgressionModel progressionModel;
100     /** Constant <code>PROPERTY_PROGRESSION_MODEL="progressionModel"</code> */
101     public static final String PROPERTY_PROGRESSION_MODEL = "progressionModel";
102 
103     private boolean serverSide;
104     /** Constant <code>PROPERTY_SERVER_SIDE="serverSide"</code> */
105     public static final String PROPERTY_SERVER_SIDE = "serverSide";
106 
107     private String transferFilename;
108     /** Constant <code>PROPERTY_TRANSFER_FILENAME="transfertFilename"</code> */
109     public static final String PROPERTY_TRANSFER_FILENAME = "transfertFilename";
110 
111     private File workingDirectory;
112     /** Constant <code>PROPERTY_WORKING_DIRECTORY="workingDirectory"</code> */
113     public static final String PROPERTY_WORKING_DIRECTORY = "workingDirectory";
114 
115     /**
116      * <p>Constructor for SynchroUIContext.</p>
117      *
118      * @param mainContext a {@link fr.ifremer.quadrige2.ui.swing.common.ApplicationUIContext} object.
119      */
120     public SynchroUIContext(ApplicationUIContext mainContext) {
121         this.mainContext = mainContext;
122         synchroImportContext = new SynchroImportContextVO();
123         synchroExportContext = new SynchroExportContextVO();
124 
125         // create new progression model
126         setProgressionModel(new ProgressionModel());
127         getProgressionModel().setTotal(100);
128 
129         if (getStatus() == null) {
130             setStatus(SynchroProgressionStatus.NOT_STARTED);
131         }
132     }
133 
134     /**
135      * <p>Getter for the field <code>direction</code>.</p>
136      *
137      * @return a {@link fr.ifremer.quadrige2.ui.swing.common.synchro.SynchroDirection} object.
138      */
139     public SynchroDirection getDirection() {
140         return direction;
141     }
142 
143     /**
144      * <p>Setter for the field <code>direction</code>.</p>
145      *
146      * @param direction a {@link fr.ifremer.quadrige2.ui.swing.common.synchro.SynchroDirection} object.
147      */
148     public void setDirection(SynchroDirection direction) {
149         this.direction = direction;
150         updateTitle();
151     }
152 
153     /**
154      * <p>Getter for the field <code>title</code>.</p>
155      *
156      * @return a {@link String} object.
157      */
158     public String getTitle() {
159         return title;
160     }
161 
162     /**
163      * <p>Setter for the field <code>title</code>.</p>
164      *
165      * @param title a {@link String} object.
166      */
167     public void setTitle(String title) {
168         String oldValue = getTitle();
169         this.title = title;
170         firePropertyChange(PROPERTY_SYNCHRO_TITLE, oldValue, title);
171     }
172 
173     /**
174      * <p>Getter for the field <code>status</code>.</p>
175      *
176      * @return a {@link fr.ifremer.quadrige2.synchro.vo.SynchroProgressionStatus} object.
177      */
178     public final SynchroProgressionStatus getStatus() {
179         return status;
180     }
181 
182     /**
183      * <p>isRunningStatus.</p>
184      *
185      * @return a boolean.
186      */
187     public final boolean isRunningStatus() {
188         return status == SynchroProgressionStatus.RUNNING
189                 || status == SynchroProgressionStatus.WAITING_EXECUTION;
190     }
191 
192     /**
193      * <p>Setter for the field <code>status</code>.</p>
194      *
195      * @param status a {@link fr.ifremer.quadrige2.synchro.vo.SynchroProgressionStatus} object.
196      */
197     public final void setStatus(SynchroProgressionStatus status) {
198         this.status = status;
199         firePropertyChange(PROPERTY_PROGRESSION_STATUS, null, status);
200     }
201 
202     /**
203      * <p>Getter for the field <code>progressionModel</code>.</p>
204      *
205      * @return a {@link fr.ifremer.quadrige2.ui.swing.common.model.ProgressionModel} object.
206      */
207     public final ProgressionModel getProgressionModel() {
208         return progressionModel;
209     }
210 
211     /**
212      * <p>Setter for the field <code>progressionModel</code>.</p>
213      *
214      * @param progressionModel a {@link fr.ifremer.quadrige2.ui.swing.common.model.ProgressionModel} object.
215      */
216     public final void setProgressionModel(ProgressionModel progressionModel) {
217         Object oldValue = getProgressionModel();
218         this.progressionModel = progressionModel;
219         firePropertyChange(PROPERTY_PROGRESSION_MODEL, oldValue, progressionModel);
220     }
221 
222     /**
223      * <p>isServerSide.</p>
224      *
225      * @return a boolean.
226      */
227     public boolean isServerSide() {
228         return serverSide;
229     }
230 
231     /**
232      * <p>Setter for the field <code>serverSide</code>.</p>
233      *
234      * @param serverSide a boolean.
235      */
236     public void setServerSide(boolean serverSide) {
237         boolean oldValue = isServerSide();
238         this.serverSide = serverSide;
239         firePropertyChange(PROPERTY_SERVER_SIDE, oldValue, serverSide);
240     }
241 
242     /**
243      * <p>Getter for the field <code>workingDirectory</code>.</p>
244      *
245      * @return a {@link File} object.
246      */
247     public File getWorkingDirectory() {
248         return workingDirectory;
249     }
250 
251     /**
252      * <p>Setter for the field <code>workingDirectory</code>.</p>
253      *
254      * @param workingDirectory a {@link File} object.
255      */
256     public void setWorkingDirectory(File workingDirectory) {
257         this.workingDirectory = workingDirectory;
258     }
259 
260     /**
261      * <p>Getter for the field <code>transferFilename</code>.</p>
262      *
263      * @return a {@link String} object.
264      */
265     public String getTransferFilename() {
266         return transferFilename;
267     }
268 
269     /**
270      * <p>Setter for the field <code>transferFilename</code>.</p>
271      *
272      * @param transfertFilename a {@link String} object.
273      */
274     public void setTransferFilename(String transfertFilename) {
275         this.transferFilename = transfertFilename;
276     }
277 
278     /**
279      * <p>Getter for the field <code>synchroImportContext</code>.</p>
280      *
281      * @return a {@link fr.ifremer.quadrige2.synchro.vo.SynchroImportContextVO} object.
282      */
283     public SynchroImportContextVO getSynchroImportContext() {
284         return synchroImportContext;
285     }
286 
287     /**
288      * <p>Setter for the field <code>synchroImportContext</code>.</p>
289      *
290      * @param synchroImportContext a {@link fr.ifremer.quadrige2.synchro.vo.SynchroImportContextVO} object.
291      */
292     public void setSynchroImportContext(SynchroImportContextVO synchroImportContext) {
293         Preconditions.checkNotNull(synchroImportContext);
294         this.synchroImportContext = synchroImportContext;
295     }
296 
297     /**
298      * <p>Getter for the field <code>synchroExportContext</code>.</p>
299      *
300      * @return a {@link fr.ifremer.quadrige2.synchro.vo.SynchroExportContextVO} object.
301      */
302     public SynchroExportContextVO getSynchroExportContext() {
303         return synchroExportContext;
304     }
305 
306     /**
307      * <p>Setter for the field <code>synchroExportContext</code>.</p>
308      *
309      * @param synchroExportContext a {@link fr.ifremer.quadrige2.synchro.vo.SynchroExportContextVO} object.
310      */
311     public void setSynchroExportContext(SynchroExportContextVO synchroExportContext) {
312         Preconditions.checkNotNull(synchroExportContext);
313         this.synchroExportContext = synchroExportContext;
314     }
315 
316     /*
317      * DELEGATE METHODS FOR IMPORT CONTEXT
318      */
319     /**
320      * <p>getImportJobId.</p>
321      *
322      * @return a {@link String} object.
323      */
324     public String getImportJobId() {
325         return synchroImportContext.getJobId();
326     }
327 
328     /**
329      * <p>setImportJobId.</p>
330      *
331      * @param jobId a {@link String} object.
332      */
333     public void setImportJobId(String jobId) {
334         synchroImportContext.setJobId(jobId);
335     }
336 
337     /**
338      * <p>getImportReferentialUpdateDate.</p>
339      *
340      * @return a {@link Date} object.
341      */
342     public Date getImportReferentialUpdateDate() {
343         return synchroImportContext.getReferentialUpdateDate();
344     }
345 
346     /**
347      * <p>setImportReferentialUpdateDate.</p>
348      *
349      * @param referentialUpdateDate a {@link Date} object.
350      */
351     public void setImportReferentialUpdateDate(Date referentialUpdateDate) {
352         synchroImportContext.setReferentialUpdateDate(referentialUpdateDate);
353     }
354 
355     /**
356      * <p>getImportDataUpdateDate.</p>
357      *
358      * @return a {@link Date} object.
359      */
360     public Date getImportDataUpdateDate() {
361         return synchroImportContext.getDataUpdateDate();
362     }
363 
364     /**
365      * <p>setImportDataUpdateDate.</p>
366      *
367      * @param dataUpdateDate a {@link Date} object.
368      */
369     public void setImportDataUpdateDate(Date dataUpdateDate) {
370         synchroImportContext.setDataUpdateDate(dataUpdateDate);
371     }
372 
373     /**
374      * <p>getImportDataStartDate.</p>
375      *
376      * @return a {@link Date} object.
377      */
378     public Date getImportDataStartDate() {
379         return synchroImportContext.getDataStartDate();
380     }
381 
382     /**
383      * <p>setImportDataStartDate.</p>
384      *
385      * @param dataStartDate a {@link Date} object.
386      */
387     public void setImportDataStartDate(Date dataStartDate) {
388         synchroImportContext.setDataStartDate(dataStartDate);
389         firePropertyChange(PROPERTY_UI_DATA_START_DATE, null, null);
390         if (dataStartDate != null && getImportDataEndDate() != null && dataStartDate.after(getImportDataEndDate())) {
391             setImportDataEndDate(dataStartDate);
392         }
393     }
394 
395     /**
396      * <p>getImportDataEndDate.</p>
397      *
398      * @return a {@link Date} object.
399      */
400     public Date getImportDataEndDate() {
401         return synchroImportContext.getDataEndDate();
402     }
403 
404     /**
405      * <p>setImportDataEndDate.</p>
406      *
407      * @param dataEndDate a {@link Date} object.
408      */
409     public void setImportDataEndDate(Date dataEndDate) {
410         synchroImportContext.setDataEndDate(dataEndDate);
411         firePropertyChange(PROPERTY_UI_DATA_END_DATE, null, null);
412         if (dataEndDate != null && getImportDataStartDate() != null && dataEndDate.before(getImportDataStartDate())) {
413             setImportDataStartDate(dataEndDate);
414         }
415     }
416 
417     /**
418      * <p>isImportReferential.</p>
419      *
420      * @return a boolean.
421      */
422     public boolean isImportReferential() {
423         return synchroImportContext.isWithReferential();
424     }
425 
426     /**
427      * <p>setImportReferential.</p>
428      *
429      * @param referential a boolean.
430      */
431     public void setImportReferential(boolean referential) {
432         synchroImportContext.setWithReferential(referential);
433     }
434 
435     /**
436      * <p>isImportData.</p>
437      *
438      * @return a boolean.
439      */
440     public boolean isImportData() {
441         return synchroImportContext.isWithData();
442     }
443 
444     /**
445      * <p>setImportData.</p>
446      *
447      * @param data a boolean.
448      */
449     public void setImportData(boolean data) {
450         synchroImportContext.setWithData(data);
451         updateTitle();
452     }
453 
454     /**
455      * <p>setImportReferentialPkIncludes.</p>
456      *
457      * @param referentialPkIncludes a {@link Multimap} object.
458      */
459     public void setImportReferentialPkIncludes(Multimap<String, String> referentialPkIncludes) {
460         synchroImportContext.setReferentialPkIncludes(referentialPkIncludes);
461     }
462 
463     /**
464      * <p>setImportDataPkIncludes.</p>
465      *
466      * @param dataPkIncludes a {@link Multimap} object.
467      */
468     public void setImportDataPkIncludes(Multimap<String, String> dataPkIncludes) {
469         synchroImportContext.setDataPkIncludes(dataPkIncludes);
470     }
471 
472     /**
473      * <p>setForceDuplication.</p>
474      *
475      * @param forceDuplication a boolean.
476      */
477     public void setForceDuplication(boolean forceDuplication) {
478         synchroImportContext.setForceDuplication(forceDuplication);
479     }
480 
481     /**
482      * <p>getImportDataProgramCodes.</p>
483      *
484      * @return a {@link Set} object.
485      */
486     public Set<String> getImportDataProgramCodes() {
487         return synchroImportContext.getDataProgramCodes();
488     }
489 
490     /**
491      * <p>setImportDataProgramCodes.</p>
492      *
493      * @param programCodes a {@link Set} object.
494      */
495     public void setImportDataProgramCodes(Set<String> programCodes) {
496         synchroImportContext.setDataProgramCodes(programCodes);
497     }
498 
499     /**
500      * <p>setImportReferentialProgramCodes.</p>
501      *
502      * @param programCodes a {@link Set} object.
503      */
504     public void setImportReferentialProgramCodes(Set<String> programCodes) {
505         synchroImportContext.setReferentialProgramCodes(programCodes);
506     }
507 
508     /**
509      * <p>getImportReferentialProgramCodes.</p>
510      *
511      * @return a {@link Set} object.
512      */
513     public Set<String> getImportReferentialProgramCodes() {
514         return synchroImportContext.getReferentialProgramCodes();
515     }
516 
517     /**
518      * <p>getExportDataProgramCodes.</p>
519      *
520      * @return a {@link Set} object.
521      */
522     public Set<String> getExportDataProgramCodes() {
523         return synchroExportContext.getDataProgramCodes();
524     }
525 
526     /**
527      * <p>setExportDataProgramCodes.</p>
528      *
529      * @param programCodes a {@link Set} object.
530      */
531     public void setExportDataProgramCodes(Set<String> programCodes) {
532         synchroExportContext.setDataProgramCodes(programCodes);
533     }
534 
535     /**
536      * <p>getExportReferentialProgramCodes.</p>
537      *
538      * @return a {@link Set} object.
539      */
540     public Set<String> getExportReferentialProgramCodes() {
541         return synchroExportContext.getReferentialProgramCodes();
542     }
543 
544     /**
545      * <p>setExportReferentialProgramCodes.</p>
546      *
547      * @param programCodes a {@link Set} object.
548      */
549     public void setExportReferentialProgramCodes(Set<String> programCodes) {
550         synchroExportContext.setReferentialProgramCodes(programCodes);
551     }
552 
553     /**
554      * <p>getImportReferentialPkIncludes.</p>
555      *
556      * @return a {@link Multimap} object.
557      */
558     public Multimap<String, String> getImportReferentialPkIncludes() {
559         return synchroImportContext.getReferentialPkIncludes();
560     }
561 
562     /**
563      * <p>getImportDataPkIncludes.</p>
564      *
565      * @return a {@link Multimap} object.
566      */
567     public Multimap<String, String> getImportDataPkIncludes() {
568         return synchroImportContext.getDataPkIncludes();
569     }
570 
571     /**
572      * <p>isForceDuplication.</p>
573      *
574      * @return a boolean.
575      */
576     public boolean isForceDuplication() {
577         return synchroImportContext.isForceDuplication();
578     }
579 
580     /**
581      * <p>setImportDataForceEditedRowOverride.</p>
582      *
583      * @param forceEditedDataOverride a boolean.
584      */
585     public void setImportDataForceEditedRowOverride(boolean forceEditedDataOverride) {
586         synchroImportContext.setDataForceEditedRowOverride(forceEditedDataOverride);
587     }
588 
589     /**
590      * <p>isImportDataForceEditedRowOverride.</p>
591      *
592      * @return a boolean.
593      */
594     public boolean isImportDataForceEditedRowOverride() {
595         return synchroImportContext.isDataForceEditedRowOverride();
596     }
597 
598     /* DELEGATE METHODS FOR EXPORT CONTEXT */
599     /**
600      * <p>getExportJobId.</p>
601      *
602      * @return a {@link String} object.
603      */
604     public String getExportJobId() {
605         return synchroExportContext.getJobId();
606     }
607 
608     /**
609      * <p>setExportJobId.</p>
610      *
611      * @param jobId a {@link String} object.
612      */
613     public void setExportJobId(String jobId) {
614         synchroExportContext.setJobId(jobId);
615     }
616 
617     /**
618      * <p>getExportFileName.</p>
619      *
620      * @return a {@link String} object.
621      */
622     public String getExportFileName() {
623         return synchroExportContext.getFileName();
624     }
625 
626     /**
627      * <p>setExportFileName.</p>
628      *
629      * @param fileName a {@link String} object.
630      */
631     public void setExportFileName(String fileName) {
632         synchroExportContext.setFileName(fileName);
633     }
634 
635     /* Import context methods */
636     /**
637      * <p>getImportDirectory.</p>
638      *
639      * @return a {@link File} object.
640      */
641     public File getImportDirectory() {
642         return new File(mainContext.getConfiguration().getSynchroUserDirectory(), IMPORT_DIRECTORY);
643     }
644 
645     /**
646      * <p>loadImportContext.</p>
647      */
648     public void loadImportContext() {
649 
650         // Read the actual db version to calculate referential update date
651         {
652             File dbVersionFile = ApplicationUpdater.getVersionFile(mainContext.getConfiguration().getDbDirectory());
653             String dbVersion = null;
654             if (dbVersionFile.exists()) {
655                 try {
656                     dbVersion = Files.asCharSource(dbVersionFile, Charset.defaultCharset()).readFirstLine();
657                 } catch (IOException ex) {
658                     throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.read.file", dbVersionFile.getAbsolutePath()), ex);
659                 }
660             }
661             // if version.appup is missing, the next import will be a full referential import
662             Date importReferentialUpdateDate = DateVersions.safeConvertVersion2Date(dbVersion,
663                     Quadrige2Configuration.getInstance().getDbTimezone());
664             setImportReferentialUpdateDate(importReferentialUpdateDate);
665         }
666 
667         File file = getImportContextFile();
668         if (file.exists()) {
669             Properties p = new Properties();
670             Reader reader = null;
671             try {
672                 reader = Files.newReader(file, Charsets.UTF_8);
673                 p.load(reader);
674             } catch (IOException ex) {
675                 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.read.file", file.getAbsolutePath()), ex);
676             } finally {
677                 IOUtils.closeQuietly(reader);
678             }
679 
680             // load last status
681             setStatus(SynchroProgressionStatus.valueOf(
682                     p.getProperty(PROPERTY_PROGRESSION_STATUS, SynchroProgressionStatus.NOT_STARTED.name())));
683 
684             // load job id
685             setImportJobId(p.getProperty(SynchroImportContextVO.PROPERTY_JOB_ID));
686 
687             // load flags
688             setImportReferential(Boolean.parseBoolean(p.getProperty(SynchroImportContextVO.PROPERTY_WITH_REFERENTIAL)));
689             setImportData(Boolean.parseBoolean(p.getProperty(SynchroImportContextVO.PROPERTY_WITH_DATA)));
690 
691             // load dates
692             setImportDataUpdateDate(Dates.safeParseDate(p.getProperty(SynchroImportContextVO.PROPERTY_DATA_UPDATE_DATE), DATE_PATTERN));
693             setImportDataStartDate(Dates.safeParseDate(p.getProperty(SynchroImportContextVO.PROPERTY_DATA_START_DATE), DATE_PATTERN));
694             setImportDataEndDate(Dates.safeParseDate(p.getProperty(SynchroImportContextVO.PROPERTY_DATA_END_DATE), DATE_PATTERN));
695 
696             // load programs
697             Collection<String> programCodes = ConfigurationHelper.getList(SynchroImportContextVO.PROPERTY_DATA_PROGRAM_CODES, p);
698             if (CollectionUtils.isEmpty(programCodes)) {
699                 setImportDataProgramCodes(null);
700             } else {
701                 setImportDataProgramCodes(Sets.newHashSet(programCodes));
702             }
703 
704             // if programs codes is present, load specific update date
705             // load from computed property key like dataUpdateDate_<PROG_CD>
706             if (CollectionUtils.isNotEmpty(programCodes)) {
707                 String keyPrefix = SynchroImportContextVO.PROPERTY_DATA_UPDATE_DATE + "_";
708 
709                 // if data date range is present, load specific update date
710                 // load from computed property key like dataUpdateDate_20091001_20091231_<PROG_CD>
711                 if (getImportDataStartDate() != null && getImportDataEndDate() != null) {
712                     keyPrefix += DateUtil.formatDate(getImportDataStartDate(), PROPERTY_DATA_DATE_RANGE)
713                             + "_"
714                             + DateUtil.formatDate(getImportDataEndDate(), PROPERTY_DATA_DATE_RANGE)
715                             + "_";
716                 }
717 
718                 Date minUpdateDate = null;
719                 for (String programCode : programCodes) {
720                     String key = keyPrefix + programCode;
721                     Date programUpdateDate = Dates.safeParseDate(p.getProperty(key), DATE_PATTERN);
722                     if (programUpdateDate == null) {
723                         minUpdateDate = null;
724                         break;
725                     }
726                     if (minUpdateDate == null || programUpdateDate.before(minUpdateDate)) {
727                         minUpdateDate = programUpdateDate;
728                     }
729                 }
730                 setImportDataUpdateDate(minUpdateDate);
731             }
732 
733             // if data date range is present, load specific update date
734             // load from computed property key like dataUpdateDate_20091001_20091231
735             else if (getImportDataStartDate() != null && getImportDataEndDate() != null) {
736                 String key = SynchroImportContextVO.PROPERTY_DATA_UPDATE_DATE + "_"
737                         + DateUtil.formatDate(getImportDataStartDate(), PROPERTY_DATA_DATE_RANGE) + "_"
738                         + DateUtil.formatDate(getImportDataEndDate(), PROPERTY_DATA_DATE_RANGE);
739                 setImportDataUpdateDate(Dates.safeParseDate(p.getProperty(key), DATE_PATTERN));
740             }
741 
742             // load files
743             setTransferFilename(p.getProperty(PROPERTY_TRANSFER_FILENAME));
744             String workingDirectoryPath = p.getProperty(PROPERTY_WORKING_DIRECTORY);
745             if (StringUtils.isNotBlank(workingDirectoryPath)) {
746                 setWorkingDirectory(new File(workingDirectoryPath));
747             }
748         }
749     }
750 
751     /**
752      * <p>saveImportContext.</p>
753      */
754     public void saveImportContext() {
755         saveImportContext(false, false);
756     }
757 
758     /**
759      * <p>saveImportContext.</p>
760      *
761      * @param saveDataDates a boolean.
762      * @param saveVersionAppup a boolean.
763      */
764     public void saveImportContext(boolean saveDataDates, boolean saveVersionAppup) {
765 
766         File file = getImportContextFile();
767 
768         // Load previous properties
769         Properties p = new Properties();
770         if (file.exists() && file.isFile()) {
771             Reader reader = null;
772             try {
773                 reader = Files.newReader(file, Charsets.UTF_8);
774                 p.load(reader);
775             } catch (IOException ex) {
776                 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.read.file", file.getAbsolutePath()), ex);
777             } finally {
778                 IOUtils.closeQuietly(reader);
779             }
780         }
781 
782         // save status
783         p.put(PROPERTY_PROGRESSION_STATUS, getStatus().name());
784 
785         // save job id
786         if (getImportJobId() != null) {
787             p.put(SynchroImportContextVO.PROPERTY_JOB_ID, getImportJobId());
788         } else {
789             p.remove(SynchroImportContextVO.PROPERTY_JOB_ID);
790         }
791 
792         // save flags
793         p.put(SynchroImportContextVO.PROPERTY_WITH_REFERENTIAL, Boolean.toString(isImportReferential()));
794         p.put(SynchroImportContextVO.PROPERTY_WITH_DATA, Boolean.toString(isImportData()));
795 
796         // save dates
797         // SynchroImportContextVO.PROPERTY_REFERENTIAL_UPDATE_DATE property in context file is not used
798         ConfigurationHelper.setDate(
799                 SynchroImportContextVO.PROPERTY_REFERENTIAL_UPDATE_DATE,
800                 getImportReferentialUpdateDate(), DATE_PATTERN, true,
801                 p);
802         ConfigurationHelper.setDate(
803                 SynchroImportContextVO.PROPERTY_DATA_UPDATE_DATE,
804                 getImportDataUpdateDate(), DATE_PATTERN, true,
805                 p);
806         ConfigurationHelper.setDate(
807                 SynchroImportContextVO.PROPERTY_DATA_START_DATE,
808                 getImportDataStartDate(), DATE_PATTERN, true,
809                 p);
810         ConfigurationHelper.setDate(
811                 SynchroImportContextVO.PROPERTY_DATA_END_DATE,
812                 getImportDataEndDate(), DATE_PATTERN, true,
813                 p);
814 
815         // save pkIncludes
816         ConfigurationHelper.setMultimap(
817                 SynchroImportContextVO.PROPERTY_REFERENTIAL_PK_INCLUDES,
818                 getImportReferentialPkIncludes(),
819                 p);
820         ConfigurationHelper.setMultimap(
821                 SynchroImportContextVO.PROPERTY_DATA_PK_INCLUDES,
822                 getImportDataPkIncludes(),
823                 p);
824 
825         // save programs
826         ConfigurationHelper.setList(
827                 SynchroImportContextVO.PROPERTY_DATA_PROGRAM_CODES,
828                 getImportDataProgramCodes(),
829                 p);
830 
831         // save status
832         p.put(PROPERTY_PROGRESSION_STATUS, getStatus().name());
833 
834         if (saveDataDates
835                 && CollectionUtils.isNotEmpty(getImportDataProgramCodes())
836                 && getImportDataUpdateDate() != null) {
837             String keyPrefix = SynchroImportContextVO.PROPERTY_DATA_UPDATE_DATE + "_";
838 
839             // save specific data update date if date range is present
840             if (getImportDataStartDate() != null
841                     && getImportDataEndDate() != null) {
842                 keyPrefix += DateUtil.formatDate(getImportDataStartDate(), PROPERTY_DATA_DATE_RANGE) + "_"
843                         + DateUtil.formatDate(getImportDataEndDate(), PROPERTY_DATA_DATE_RANGE) + "_";
844             }
845 
846             for (String programCode : getImportDataProgramCodes()) {
847                 ConfigurationHelper.setDate(
848                         keyPrefix + programCode,
849                         getImportDataUpdateDate(), DATE_PATTERN, true,
850                         p);
851             }
852 
853         }
854 
855         // save files status
856         ConfigurationHelper.setString(
857                 PROPERTY_TRANSFER_FILENAME,
858                 getTransferFilename(),
859                 p);
860         ConfigurationHelper.setFile(
861                 PROPERTY_WORKING_DIRECTORY,
862                 getWorkingDirectory(),
863                 p);
864 
865         // replace the version.appup file content only
866         if (saveVersionAppup && isImportReferential() && getImportReferentialUpdateDate() != null) {
867 
868             File target = mainContext.getConfiguration().getDbDirectory();
869             File versionFile = ApplicationUpdater.getVersionFile(target);
870             String newVersion = DateVersions.convertDate2Version(
871                     getImportReferentialUpdateDate(),
872                     Quadrige2Configuration.getInstance().getDbTimezone()
873                 ).toString();
874             if (log.isInfoEnabled()) {
875                 log.info("Replace content of file " + versionFile + " with " + newVersion);
876             }
877             try {
878                 ApplicationUpdater.storeVersionFile(target, newVersion);
879             } catch (IOException e) {
880                 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.write.file", versionFile));
881             }
882         }
883 
884         // Write to properties file
885         Writer writer = null;
886         try {
887             if (!file.exists()) {
888                 Files.createParentDirs(file);
889                 file.createNewFile();
890             }
891             writer = Files.newWriter(file, Charsets.UTF_8);
892             p.store(writer, null);
893         } catch (IOException ex) {
894             throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.write.file", file.getAbsolutePath()), ex);
895         } finally {
896             IOUtils.closeQuietly(writer);
897         }
898 
899     }
900 
901     /**
902      * <p>resetImportContext.</p>
903      */
904     public void resetImportContext() {
905 
906         setWorkingDirectory(null);
907         setTransferFilename(null);
908         setImportJobId(null);
909         setStatus(SynchroProgressionStatus.NOT_STARTED);
910         setImportDataPkIncludes(null);
911         setForceDuplication(false);
912         setImportDataForceEditedRowOverride(false);
913         setDirection(null);
914 
915     }
916 
917     /* Export Context Methods */
918     /**
919      * <p>getExportDirectory.</p>
920      *
921      * @return a {@link File} object.
922      */
923     public File getExportDirectory() {
924         return new File(mainContext.getConfiguration().getSynchroUserDirectory(), EXPORT_DIRECTORY);
925     }
926 
927     /**
928      * <p>loadExportContext.</p>
929      */
930     public void loadExportContext() {
931 
932         File file = getExportContextFile();
933         if (file.exists()) {
934             Properties p = new Properties();
935             Reader reader = null;
936             try {
937                 reader = Files.newReader(file, Charsets.UTF_8);
938                 p.load(reader);
939             } catch (IOException ex) {
940                 throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.read.file", file.getAbsolutePath()), ex);
941             } finally {
942                 IOUtils.closeQuietly(reader);
943             }
944 
945             // load last status
946             setStatus(SynchroProgressionStatus.valueOf(
947                     p.getProperty(PROPERTY_PROGRESSION_STATUS, SynchroProgressionStatus.NOT_STARTED.name())));
948 
949             // load job id
950             setExportJobId(p.getProperty(SynchroExportContextVO.PROPERTY_JOB_ID));
951 
952             // load programs
953             Collection<String> programCodes = ConfigurationHelper.getList(SynchroExportContextVO.PROPERTY_DATA_PROGRAM_CODES, p);
954             if (CollectionUtils.isEmpty(programCodes)) {
955                 setExportDataProgramCodes(null);
956             } else {
957                 setExportDataProgramCodes(Sets.newHashSet(programCodes));
958             }
959 
960             // load files
961             setExportFileName(p.getProperty(SynchroExportContextVO.PROPERTY_FILE_NAME));
962 
963             setTransferFilename(p.getProperty(PROPERTY_TRANSFER_FILENAME));
964             String workingDirectoryPath = p.getProperty(PROPERTY_WORKING_DIRECTORY);
965             if (StringUtils.isNotBlank(workingDirectoryPath)) {
966                 setWorkingDirectory(new File(workingDirectoryPath));
967             }
968 
969         }
970     }
971 
972     /**
973      * <p>saveExportContext.</p>
974      */
975     public void saveExportContext() {
976 
977         File file = getExportContextFile();
978         Properties p = new Properties();
979 
980         // save status
981         p.put(PROPERTY_PROGRESSION_STATUS, getStatus().name());
982 
983         // save job id
984         if (getExportJobId() != null) {
985             p.put(SynchroExportContextVO.PROPERTY_JOB_ID, getExportJobId());
986         }
987 
988         // save programs
989         ConfigurationHelper.setList(
990                 SynchroExportContextVO.PROPERTY_DATA_PROGRAM_CODES,
991                 getExportDataProgramCodes(),
992                 p);
993 
994         // save files status
995         if (getExportFileName() != null) {
996             p.put(SynchroExportContextVO.PROPERTY_FILE_NAME, getExportFileName());
997         }
998         if (getTransferFilename() != null) {
999             p.put(PROPERTY_TRANSFER_FILENAME, getTransferFilename());
1000         }
1001         if (getWorkingDirectory() != null) {
1002             p.put(PROPERTY_WORKING_DIRECTORY, getWorkingDirectory().getAbsolutePath());
1003         }
1004 
1005         // Write to properties file
1006         Writer writer = null;
1007         try {
1008             if (!file.exists()) {
1009                 Files.createParentDirs(file);
1010                 file.createNewFile();
1011             }
1012             writer = Files.newWriter(file, Charsets.UTF_8);
1013             p.store(writer, null);
1014         } catch (IOException ex) {
1015             throw new Quadrige2TechnicalException(I18n.t("quadrige2.error.write.file", file.getAbsolutePath()), ex);
1016         } finally {
1017             IOUtils.closeQuietly(writer);
1018         }
1019     }
1020 
1021     /**
1022      * <p>resetExportContext.</p>
1023      */
1024     public void resetExportContext() {
1025 
1026         setExportFileName(null);
1027         setWorkingDirectory(null);
1028         setTransferFilename(null);
1029         setExportJobId(null);
1030         setStatus(SynchroProgressionStatus.NOT_STARTED);
1031         setDirection(null);
1032 
1033     }
1034 
1035     /* Private methods */
1036     private File getImportContextFile() {
1037         return new File(mainContext.getConfiguration().getSynchroUserDirectory(), IMPORT_PROPERTIES);
1038     }
1039 
1040     private File getExportContextFile() {
1041         return new File(mainContext.getConfiguration().getSynchroUserDirectory(), EXPORT_PROPERTIES);
1042     }
1043 
1044     private void updateTitle() {
1045         String newTitle = null;
1046         if (direction != null) {
1047 
1048             if (direction == SynchroDirection.IMPORT && isImportData()) {
1049                 newTitle = t("quadrige2.synchro.import.data");
1050             } else {
1051                 newTitle = direction.getLabel();
1052             }
1053         }
1054 
1055         setTitle(newTitle);
1056     }
1057 
1058     /** {@inheritDoc} */
1059     @Override
1060     public void close() throws IOException {
1061         // remove listeners
1062         PropertyChangeListener[] listeners = getPropertyChangeListeners();
1063         for (PropertyChangeListener listener : listeners) {
1064             if (log.isDebugEnabled()) {
1065                 log.debug("Remove listener: " + listener);
1066             }
1067             removePropertyChangeListener(listener);
1068         }
1069     }
1070 
1071     /** {@inheritDoc} */
1072     @Override
1073     public void firePropertyChanged(String propertyName, Object oldValue, Object newValue) {
1074         firePropertyChange(propertyName, oldValue, newValue);
1075     }
1076 
1077     @Override
1078     public void fireIndexedPropertyChanged(String propertyName, int index, Object oldValue, Object newValue) {
1079         fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
1080     }
1081 }