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