branch timebundle created (now df7f10f)
This is an automated email from the git hooks/post-receive script. New change to branch timebundle in repository jtimer. See https://gitlab.nuiton.org/chorem/jtimer.git at df7f10f Refactor time bundle code into dedicated plugin. This branch includes the following new commits: new df7f10f Refactor time bundle code into dedicated plugin. The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit df7f10fe33e431c480a54d6eb1337a847a0de50c Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Aug 2 16:50:41 2016 +0200 Refactor time bundle code into dedicated plugin. -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
This is an automated email from the git hooks/post-receive script. New commit to branch timebundle in repository jtimer. See https://gitlab.nuiton.org/chorem/jtimer.git commit df7f10fe33e431c480a54d6eb1337a847a0de50c Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Aug 2 16:50:41 2016 +0200 Refactor time bundle code into dedicated plugin. --- src/main/java/org/chorem/jtimer/JTimer.java | 55 +-- src/main/java/org/chorem/jtimer/JTimerConfig.java | 21 +- src/main/java/org/chorem/jtimer/JTimerFactory.java | 41 +- .../org/chorem/jtimer/data/DataEventListener.java | 20 +- .../java/org/chorem/jtimer/data/TimerCore.java | 9 +- .../org/chorem/jtimer/data/TimerDataManager.java | 68 ++-- .../java/org/chorem/jtimer/entities/SyncInfo.java | 187 --------- .../java/org/chorem/jtimer/entities/TimerSync.java | 151 +++++++ .../java/org/chorem/jtimer/entities/TimerTask.java | 179 +------- .../chorem/jtimer/entities/TimerTaskHelper.java | 102 ----- .../org/chorem/jtimer/entities/package-info.java | 2 +- .../java/org/chorem/jtimer/io/AbstractSaver.java | 83 ---- .../io/{AbstractSaver.java => BackupUtils.java} | 55 +-- .../chorem/jtimer/io/GTimerIncrementalSaver.java | 140 +------ .../chorem/jtimer/io/TimerTaskSynchronizer.java | 299 -------------- .../jtimer/plugin/timebundle/TimeBundleHelper.java | 140 +++++++ .../jtimer/plugin/timebundle/TimeBundleSaver.java | 220 ++++++++++ .../plugin/timebundle/TimeBundleSynchronizer.java | 148 +++++++ .../plugin/timebundle/TimeBundleVetoable.java | 61 +++ .../plugin/timebundle/TimerSyncCellRenderer.java | 46 +++ .../jtimer/plugin/timebundle/TimerSyncEditor.java | 245 +++++++++++ .../plugin/timebundle/TimerSyncTableModel.java | 203 +++++++++ .../jtimer/ui/report/TimerTaskSyncInfoEditor.java | 453 --------------------- src/main/resources/log4j2.xml | 1 + .../resources/TimerSyncEditor.properties} | 4 +- .../resources/TimerSyncEditor_fr.properties} | 2 +- .../plugin/timebundle/resources/dialog-close.png | Bin 0 -> 1382 bytes .../org/chorem/jtimer/resources/JTimer.properties | 8 +- .../chorem/jtimer/resources/JTimer_fr.properties | 7 +- .../jtimer/ui/resources/TimerTaskEditor.properties | 2 +- .../ui/resources/TimerTaskEditor_fr.properties | 2 +- src/site/rst/timebundle.rst | 30 ++ src/site/site.xml | 5 +- .../java/org/chorem/jtimer/AbstractJTimerTest.java | 2 + .../jtimer/entities/TimerTaskHelperTest.java | 62 +-- .../org/chorem/jtimer/entities/TimerTaskTest.java | 42 -- .../org/chorem/jtimer/io/AbstractSaverTest.java | 12 +- .../jtimer/io/GTimerIncrementalSaverTest.java | 23 +- .../plugin/timebundle/TImeBundleSaverTest.java | 45 ++ .../timebundle/TimeBundleHelperTest.java} | 42 +- src/test/resources/testdata/41.task.sync | 8 - src/test/resources/testsync/41.task.sync | 8 + 42 files changed, 1477 insertions(+), 1756 deletions(-) diff --git a/src/main/java/org/chorem/jtimer/JTimer.java b/src/main/java/org/chorem/jtimer/JTimer.java index 3981915..48faf5e 100644 --- a/src/main/java/org/chorem/jtimer/JTimer.java +++ b/src/main/java/org/chorem/jtimer/JTimer.java @@ -59,6 +59,7 @@ import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreePath; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -74,7 +75,7 @@ import org.chorem.jtimer.ui.StatusBar; import org.chorem.jtimer.ui.TimerTaskEditor; import org.chorem.jtimer.ui.alert.AlertEditor; import org.chorem.jtimer.ui.report.ReportView; -import org.chorem.jtimer.ui.report.TimerTaskSyncInfoEditor; +import org.chorem.jtimer.plugin.timebundle.TimerSyncEditor; import org.chorem.jtimer.ui.systray.SystrayManager; import org.chorem.jtimer.ui.tasks.IdleDialog; import org.chorem.jtimer.ui.tasks.RefreshTreeTask; @@ -366,13 +367,19 @@ public class JTimer extends SingleFrameApplication implements String[] projectMenuActionNames = { "newProject", "editProject", "closeProject", "deleteProject", "---", "quit"}; + if (JTimerFactory.getSynchronizer() != null) { + projectMenuActionNames = ArrayUtils.add(projectMenuActionNames, 2, "editSync"); + } menuBar.add(createMenu("projectMenu", projectMenuActionNames)); - String[] taskMenuActionNames = { "newTask", "editTask", "updateTask", "closeTask", + String[] taskMenuActionNames = { "newTask", "editTask", "closeTask", "deleteTask", "---", "startTask", "stopAllTasks", "---", "addAnnotation", "editAlert", "increment1Task", "increment5Task", "increment30Task", "decrement1Task", "decrement5Task", "decrement30Task", "setToZero", "mergeTasks" }; + if (JTimerFactory.getSynchronizer() != null) { + taskMenuActionNames = ArrayUtils.add(taskMenuActionNames, 2, "editSync"); + } menuBar.add(createMenu("taskMenu", taskMenuActionNames)); String[] reportMenuActionNames = { "makeReport" }; @@ -666,11 +673,11 @@ public class JTimer extends SingleFrameApplication implements * Update task. * Enabled when a task is selected */ - @Action(enabledProperty = "selectedSingleTask") - public void updateTask() { - TimerTask task = projectsAndTasksTable.getSelectedTasks().get(0); + @Action(enabledProperty = "selectedSingleElement") + public void editSync() { + TimerTask task = projectsAndTasksTable.getSelectedElements().get(0); - TimerTaskSyncInfoEditor updater = new TimerTaskSyncInfoEditor(this, core, task); + TimerSyncEditor updater = new TimerSyncEditor(this, core, task); show(updater); } @@ -1054,24 +1061,10 @@ public class JTimer extends SingleFrameApplication implements TimerTask destinationTask = tasks.get(0); List<TimerTask> otherTasks = tasks.subList(1, tasks.size()); - //check for syncInfos, if there are some in the tasks being merged, warn with a message - String mergeMessage = resourceMap.getString("input.mergeTaskMessage", tasks.size(), - destinationTask.getName()); - boolean syncInfoInMergedTasks = false; - for (TimerTask task : otherTasks) { - if (!task.getSynchronizingInfoList().isEmpty()) { - syncInfoInMergedTasks = true; - } - } - if (syncInfoInMergedTasks) { - mergeMessage = resourceMap.getString("input.mergeTaskWithSyncInfoMessage", tasks.size(), destinationTask.getName()); - } - int confirm = JOptionPane.showConfirmDialog(getMainFrame(), mergeMessage - , resourceMap - .getString("input.mergeTaskTitle"), - JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + int confirm = JOptionPane.showConfirmDialog(getMainFrame(), + resourceMap.getString("input.mergeTaskMessage", tasks.size(), destinationTask.getName()), + resourceMap.getString("input.mergeTaskTitle"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (confirm == JOptionPane.YES_OPTION) { - try { core.getData().mergeTasks(destinationTask, otherTasks); } catch (DataViolationException e) { @@ -1436,9 +1429,7 @@ public class JTimer extends SingleFrameApplication implements systrayManager.postIdleDetect(); } - /* - * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) - */ + @Override public void mouseClicked(MouseEvent e) { if (log.isDebugEnabled()) { @@ -1496,14 +1487,20 @@ public class JTimer extends SingleFrameApplication implements if (isSelectedSingleProject()) { actionNames = new String[] { "newTask", "---", "newProject", "editProject", "closeProject", "deleteProject" }; + if (JTimerFactory.getSynchronizer() != null) { + actionNames = ArrayUtils.add(actionNames, 4, "editSync"); + } } if (isSelectedSingleTask()) { actionNames = new String[] { "startTask", "---", - "newTask", "editTask", "updateTask", "closeTask", "deleteTask", + "newTask", "editTask", "closeTask", "deleteTask", "---", "addAnnotation", "editAlert", "increment1Task", "increment5Task", "increment30Task", "decrement1Task", "decrement5Task", "decrement30Task", "setToZero" }; + if (JTimerFactory.getSynchronizer() != null) { + actionNames = ArrayUtils.add(actionNames, 4, "editSync"); + } } if (isSelectedMultiplesTasks()) { @@ -1519,15 +1516,19 @@ public class JTimer extends SingleFrameApplication implements } } + @Override public void mouseEntered(MouseEvent e) { } + @Override public void mouseExited(MouseEvent e) { } + @Override public void mousePressed(MouseEvent e) { } + @Override public void mouseReleased(MouseEvent e) { } } diff --git a/src/main/java/org/chorem/jtimer/JTimerConfig.java b/src/main/java/org/chorem/jtimer/JTimerConfig.java index 758cb43..778b5cd 100644 --- a/src/main/java/org/chorem/jtimer/JTimerConfig.java +++ b/src/main/java/org/chorem/jtimer/JTimerConfig.java @@ -120,6 +120,16 @@ public class JTimerConfig { /** * Get jtimer data directory. + * + * @since 1.5.1 + * @return jtimer 1.5.1 sync directory + */ + public File getHomeDirectory() { + return appConfig.getOptionAsFile(JTimerOption.HOME_DIRECTORY.key); + } + + /** + * Get jtimer data directory. * * @since 1.5 * @return jtimer 1.5 data directory @@ -176,14 +186,6 @@ public class JTimerConfig { } /** - * Returns timezone for sync - * @return timezone - */ - public String getIOSyncTimeZone() { - return appConfig.getOption(JTimerOption.IO_SYNC_TIMEZONE.key); - } - - /** * Return user idle time threshold in seconds. * * @return idle time threshold @@ -295,8 +297,7 @@ public class JTimerConfig { GTIMER_BACKUP_DIRECTORY("jtimer.io.backup.directory", "${jtimer.io.saver.directory}/backups"), IO_SAVER_AUTOSAVEDELAY("jtimer.io.saver.autosavedelay", "300"), - IO_SYNC_CLASS("jtimer.io.synchronizer.class", "org.chorem.jtimer.io.TimerTaskSynchronizer"), - IO_SYNC_TIMEZONE("jtimer.io.synchronizer.timezone", "+01:00"), + IO_SYNC_CLASS("jtimer.io.synchronizer.class", null), UI_IDLE_TIME("jtimer.ui.idletime", "300"), UI_SHOW_CLOSED("jtimer.ui.showclosed", "false"), diff --git a/src/main/java/org/chorem/jtimer/JTimerFactory.java b/src/main/java/org/chorem/jtimer/JTimerFactory.java index de4aef4..5689b49 100644 --- a/src/main/java/org/chorem/jtimer/JTimerFactory.java +++ b/src/main/java/org/chorem/jtimer/JTimerFactory.java @@ -25,7 +25,7 @@ package org.chorem.jtimer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.chorem.jtimer.io.Saver; -import org.chorem.jtimer.io.TimerTaskSynchronizer; +import org.chorem.jtimer.plugin.timebundle.TimeBundleSynchronizer; /** * JTimer config class. @@ -45,7 +45,7 @@ public class JTimerFactory { protected static Saver saver; /** Synchronizer */ - protected static TimerTaskSynchronizer synchronizer; + protected static TimeBundleSynchronizer synchronizer; /** * Constructeur. @@ -96,30 +96,29 @@ public class JTimerFactory { /** * Get synchronizer manager */ - public static TimerTaskSynchronizer getSynchronizer(){ + public static TimeBundleSynchronizer getSynchronizer(){ if (synchronizer == null) { Class<?> synchronizerClass = JTimer.config.getIOSynchronizerClass(); - // log - if (log.isInfoEnabled()) { - log.info("Using synchronizer class : " + synchronizerClass); - } - - try { - // get instance - synchronizer = (TimerTaskSynchronizer) synchronizerClass.newInstance(); - - //set timezone to synchronizer - synchronizer.setTimezone(JTimer.config.getIOSyncTimeZone()); - - } catch (InstantiationException e) { - if (log.isErrorEnabled()) { - log.error("Can't instanciate class : " + synchronizerClass, e); + if (synchronizerClass != null) { + // log + if (log.isInfoEnabled()) { + log.info("Using synchronizer class : " + synchronizerClass); } - } catch (IllegalAccessException e) { - if (log.isErrorEnabled()) { - log.error("Can't access class : " + synchronizerClass, e); + + try { + // get instance + synchronizer = (TimeBundleSynchronizer) synchronizerClass.newInstance(); + + } catch (InstantiationException e) { + if (log.isErrorEnabled()) { + log.error("Can't instanciate class : " + synchronizerClass, e); + } + } catch (IllegalAccessException e) { + if (log.isErrorEnabled()) { + log.error("Can't access class : " + synchronizerClass, e); + } } } } diff --git a/src/main/java/org/chorem/jtimer/data/DataEventListener.java b/src/main/java/org/chorem/jtimer/data/DataEventListener.java index 8d20530..f26e548 100644 --- a/src/main/java/org/chorem/jtimer/data/DataEventListener.java +++ b/src/main/java/org/chorem/jtimer/data/DataEventListener.java @@ -27,7 +27,7 @@ import java.util.Date; import java.util.EventListener; import java.util.List; -import org.chorem.jtimer.entities.SyncInfo; +import org.chorem.jtimer.entities.TimerSync; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; @@ -190,23 +190,5 @@ public interface DataEventListener extends EventListener { default void dataLoaded(Collection<TimerProject> projects) { } - - /** - * SyncInfo changed - * @param task TimerTask - * @param syncInfo SyncInfo - */ - default void syncInfoChanged(TimerTask task, SyncInfo syncInfo) { - - } - - /** - * SyncInfo deleted - * @param task TimerTask - * @param syncInfo SyncInfo - */ - default void syncInfoDeleted(TimerTask task, SyncInfo syncInfo) { - - } } diff --git a/src/main/java/org/chorem/jtimer/data/TimerCore.java b/src/main/java/org/chorem/jtimer/data/TimerCore.java index c6be19f..44fec8a 100644 --- a/src/main/java/org/chorem/jtimer/data/TimerCore.java +++ b/src/main/java/org/chorem/jtimer/data/TimerCore.java @@ -36,7 +36,9 @@ import org.chorem.jtimer.JTimerFactory; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.io.DataLockingException; import org.chorem.jtimer.io.Saver; -import org.chorem.jtimer.io.TimerTaskSynchronizer; +import org.chorem.jtimer.plugin.timebundle.TimeBundleSaver; +import org.chorem.jtimer.plugin.timebundle.TimeBundleSynchronizer; +import org.chorem.jtimer.plugin.timebundle.TimeBundleVetoable; /** * TimerCore @@ -59,7 +61,7 @@ public class TimerCore { protected Saver saver; /** sync */ - protected TimerTaskSynchronizer synchronizer; + protected TimeBundleSynchronizer synchronizer; /** * Constructor. @@ -92,9 +94,10 @@ public class TimerCore { //init sync synchronizer = JTimerFactory.getSynchronizer(); if (synchronizer != null) { + data.addVetoableDataEventListener(new TimeBundleVetoable()); + data.addDataEventListener(new TimeBundleSaver()); data.addDataEventListener(synchronizer); } - } /** diff --git a/src/main/java/org/chorem/jtimer/data/TimerDataManager.java b/src/main/java/org/chorem/jtimer/data/TimerDataManager.java index 6d03164..b8bd106 100644 --- a/src/main/java/org/chorem/jtimer/data/TimerDataManager.java +++ b/src/main/java/org/chorem/jtimer/data/TimerDataManager.java @@ -36,7 +36,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.chorem.jtimer.JTimer; -import org.chorem.jtimer.entities.SyncInfo; +import org.chorem.jtimer.entities.TimerSync; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; @@ -366,7 +366,10 @@ public class TimerDataManager { * * @param project project to edit * @param newProjectName new project name + * + * @deprecated since 1.5.1 this is duplicated with modifyProject */ + @Deprecated public void editProject(TimerProject project, String newProjectName) { @@ -390,7 +393,10 @@ public class TimerDataManager { * * @param task task to edit * @param newTaskName new task name + * + * @deprecated since 1.5.1 this is duplicated with modifyTask */ + @Deprecated public void editTask(TimerTask task, String newTaskName) { // fire vetoable event @@ -410,6 +416,32 @@ public class TimerDataManager { } /** + * Modify project. + * + * @param project project to edit + */ + public void modifyProject(TimerProject project) { + + // send notification + for (DataEventListener dataEventListener : dataEventListeners) { + dataEventListener.modifyProject(project); + } + } + + /** + * Modify task. + * + * @param task task to edit + */ + public void modifyTask(TimerTask task) { + + // send notification + for (DataEventListener dataEventListener : dataEventListeners) { + dataEventListener.modifyTask(task); + } + } + + /** * Move task. * * @param destination task to move to @@ -636,38 +668,4 @@ public class TimerDataManager { return foundTask; } - - /** - * Edit Synchronization Information - * @param task : the task to edit - * @param info : the SyncInfo that was added - */ - public void syncInfoChanged(TimerTask task, SyncInfo info) { - - task.addSyncInfo(info); - - for (DataEventListener dataEventListener : dataEventListeners) { - dataEventListener.syncInfoChanged(task, info); - } - if (log.isDebugEnabled()) { - log.debug("SyncInfo changed: " + info.getSyncURL()); - } - } - - /** - * When a syncInfo is deleted - * @param task : the task to edit - * @param info : the SyncInfo to remove - */ - public void deleteSyncInfo(TimerTask task, SyncInfo info) { - if (task.getSynchronizingInfoList().contains(info)) { - task.removeSyncInfo(info); - } - for (DataEventListener dataEventListener : dataEventListeners) { - dataEventListener.syncInfoDeleted(task, info); - } - if (log.isDebugEnabled()) { - log.debug("SyncInfo deleted: " + info.getSyncURL()); - } - } } diff --git a/src/main/java/org/chorem/jtimer/entities/SyncInfo.java b/src/main/java/org/chorem/jtimer/entities/SyncInfo.java deleted file mode 100644 index f31f121..0000000 --- a/src/main/java/org/chorem/jtimer/entities/SyncInfo.java +++ /dev/null @@ -1,187 +0,0 @@ -/*- - * #%L - * jTimer - * %% - * Copyright (C) 2016 CodeLutin, Charlène Servantie - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -package org.chorem.jtimer.entities; - -import java.util.Date; - -/** - * Class to structure synchronization info - * Created by servantie on 13/06/16. - */ -public class SyncInfo { - - /** - * the url to synchronize on - */ - protected String syncURL; - - /** - * the last time the synchronization happened successfully - */ - protected Date lastSync; - - /** - * true if the synchronization is active - */ - protected boolean isActiveSync; - - /** - * true if annotations included - */ - protected boolean isWithAnnotations; - - /** - * constructor with all parameters - * - * @param syncURL the url to sync to - * @param lastSync the last time it was synced successfully - * @param isActiveSync if true, will sync auto - * @param isWithAnnotations if true, will add annotations - */ - public SyncInfo(String syncURL, Date lastSync, boolean isActiveSync, boolean isWithAnnotations) { - this.syncURL = syncURL; - this.lastSync = lastSync; - this.isActiveSync = isActiveSync; - this.isWithAnnotations = isWithAnnotations; - } - - /** - * Constructor with just the url, at creation the sync is considered active by default, - * and annotations are not sent by default - * - * @param syncURL the url to sync to - */ - public SyncInfo(String syncURL) { - this.syncURL = syncURL; - this.isActiveSync = true; - this.isWithAnnotations = false; - this.lastSync = new Date(0); - } - - /** - * Returns the time of the last sync - * - * @return LocalDateTime - */ - public Date getLastSync() { - return lastSync; - } - - /** - * Returns a boolean if sync should send to this url - * - * @return aboolean - */ - public boolean isActiveSync() { - return isActiveSync; - } - - /** - * Sets the setActiveSync boolean - * - * @param isActive boolean - */ - public void setActiveSync(boolean isActive) { - isActiveSync = isActive; - } - - /** - * Returns a boolean for the inclusion of annotations - * in the sync - * - * @return a boolean - */ - public boolean isWithAnnotations() { - return isWithAnnotations; - } - - /** - * Sets the isWithAnnotations boolean - * - * @param hasAnnotations boolean - */ - public void setWithAnnotations(boolean hasAnnotations) { - isWithAnnotations = hasAnnotations; - } - - /** - * Set the syncURL - * - * @param syncURL String - */ - public void setSyncURL(String syncURL) { - this.syncURL = syncURL; - } - - /** - * Get the syncURL - * - * @return String - */ - public String getSyncURL() { - return syncURL; - } - - /** - * Set Sync time - * - * @param syncTime Date - */ - public void setLastSync(Date syncTime) { - lastSync = syncTime; - } - - /** - * Overrides the toString() method to return the url (for combobox) - */ - @Override - public String toString() { - return syncURL; - } - - /** - * Override the equals - */ - @Override - public boolean equals(Object o) { - if (o instanceof SyncInfo) { - SyncInfo objInfo = (SyncInfo) o; - if (objInfo.getSyncURL() != null) { - if (objInfo.getSyncURL().equals(this.getSyncURL())) { - return true; - } - } - } - return false; - } - - /** - * Overriding hashcode - */ - @Override - public int hashCode() { - int hash = syncURL.hashCode(); - return hash; - } -} - diff --git a/src/main/java/org/chorem/jtimer/entities/TimerSync.java b/src/main/java/org/chorem/jtimer/entities/TimerSync.java new file mode 100644 index 0000000..dfd15a9 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/entities/TimerSync.java @@ -0,0 +1,151 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2016 CodeLutin, Charlène Servantie + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +package org.chorem.jtimer.entities; + +import java.util.Date; + +/** + * Class to structure synchronization info. + */ +public class TimerSync { + + /** + * the url to synchronize on + */ + protected String url; + + /** + * the last time the synchronization happened successfully + */ + protected Date lastSync; + + /** + * true if the synchronization is active + */ + protected boolean active; + + /** + * true if annotations included + */ + protected boolean withAnnotations; + + /** + * constructor with all parameters + * + * @param url the url to sync to + * @param lastSync the last time it was synced successfully + * @param active if true, will sync auto + * @param withAnnotations if true, will add annotations + */ + public TimerSync(String url, Date lastSync, boolean active, boolean withAnnotations) { + this.url = url; + this.lastSync = lastSync; + this.active = active; + this.withAnnotations = withAnnotations; + } + + /** + * Constructor with just the url, at creation the sync is considered active by default, + * and annotations are not sent by default + * + * @param url the url to sync to + */ + public TimerSync(String url) { + this(url, new Date(0), true, false); + } + + /** + * Returns the time of the last sync + * + * @return LocalDateTime + */ + public Date getLastSync() { + return lastSync; + } + + /** + * Returns a boolean if sync should send to this url + * + * @return a boolean + */ + public boolean isActive() { + return active; + } + + /** + * Sets the setActive boolean + * + * @param active boolean + */ + public void setActive(boolean active) { + this.active = active; + } + + /** + * Returns a boolean for the inclusion of annotations + * in the sync + * + * @return a boolean + */ + public boolean isWithAnnotations() { + return withAnnotations; + } + + /** + * Sets the withAnnotations boolean + * + * @param withAnnotations boolean + */ + public void setWithAnnotations(boolean withAnnotations) { + this.withAnnotations = withAnnotations; + } + + /** + * Set the url + * + * @param url String + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * Get the url + * + * @return String + */ + public String getUrl() { + return url; + } + + /** + * Set Sync time + * + * @param syncTime Date + */ + public void setLastSync(Date syncTime) { + lastSync = syncTime; + } + +} + diff --git a/src/main/java/org/chorem/jtimer/entities/TimerTask.java b/src/main/java/org/chorem/jtimer/entities/TimerTask.java index f59ddd7..0043d52 100644 --- a/src/main/java/org/chorem/jtimer/entities/TimerTask.java +++ b/src/main/java/org/chorem/jtimer/entities/TimerTask.java @@ -91,7 +91,7 @@ public class TimerTask implements Cloneable, /** * Synchronization Info */ - protected List<SyncInfo> synchronisingInfoList; + protected List<TimerSync> syncs; /** * Constructor. @@ -102,7 +102,7 @@ public class TimerTask implements Cloneable, allDaysAnnotations = new TreeMap<>(); subTasks = new ArrayList<>(); alerts = new ArrayList<>(); - synchronisingInfoList = new ArrayList<>(); + syncs = new ArrayList<>(); // wrong value to detect bug number = -1; } @@ -224,168 +224,6 @@ public class TimerTask implements Cloneable, } /** - * Get task's last sync time associated to the url - * If there is no last sync, will return a very far past time - * - * @param urlString the url to get the last sync from - * @return the date of the sync - */ - public Date getLastSync(String urlString) { - //to avoid null, a default time to return if errors - Date resultTime = new Date(0); - if (!synchronisingInfoList.isEmpty()) { - for (SyncInfo sync : synchronisingInfoList) { - if (urlString.equals(sync.getSyncURL())) { - resultTime = sync.lastSync; - } - } - } - return resultTime; - } - - /** - * Sets task's last sync time on one url - * (if the url doesn't exist in the task's sync info, adds it) - * - * @param syncDate : date of the sync - * @param syncURL : the url that has a change of syncdate - */ - public void setLastSync(Date syncDate, String syncURL) { - boolean urlinList = false; - if (!synchronisingInfoList.isEmpty()) { - for (SyncInfo sync : synchronisingInfoList) { - if (sync.syncURL.equals(syncURL)) { - sync.setLastSync(syncDate); - urlinList = true; - } - } - } - if (!urlinList) { - synchronisingInfoList.add(new SyncInfo(syncURL)); - } - } - - /** - * Get task's sync URL List - * returns an empty list if there is no info - * - * @return the URL List - */ - public List<String> getSynchronizingURLList() { - List<String> resultList = new ArrayList<>(); - if (!synchronisingInfoList.isEmpty()) { - for (SyncInfo sync : synchronisingInfoList) { - resultList.add(sync.getSyncURL()); - } - } - return resultList; - } - - /** - * Returns only the URLs that are active - * - * @return a List<String> with only active urls - */ - public List<String> getActiveSynchronizingURLList() { - List<String> resultList = new ArrayList<>(); - if (!synchronisingInfoList.isEmpty()) { - for (SyncInfo sync : synchronisingInfoList) { - if (sync.isActiveSync) { - resultList.add(sync.getSyncURL()); - } - } - } - return resultList; - } - - /** - * Adds a new synchronization info if the url isn't already present in the list - * sync activity true by default, annotations false by default - * - * @param url a string - * @param time a LocalDateTime - */ - public void addSyncInfo(String url, Date time) { - if (!url.isEmpty() && !getSynchronizingURLList().contains(url)) { - synchronisingInfoList.add(new SyncInfo(url, time, true, false)); - } - } - - /** - * Adds a new synchronization info with just url provided - * (default isActive = true and isWithAnnotations false) - * - * @param url a String - */ - public void addSyncInfo(String url) { - if (!url.isEmpty()) { - addSyncInfo(url, new Date(0)); - } - } - - /** - * Adds a SyncInfo to the task - * - * @param info a SyncInfo - */ - - public void addSyncInfo(SyncInfo info) { - if (!getSynchronizingInfoList().contains(info) && !info.getSyncURL().isEmpty()) { - synchronisingInfoList.add(info); - } - } - - - /** - * Removes a synchronization info attached to a url if it exists - * - * @param info : the sync info to remove - */ - public void removeSyncInfo(SyncInfo info) { - synchronisingInfoList.remove(info); - } - - /** - * Returns all the synchronization info as a List, removes the empty ones first if they exist - * - * @return a list of TimerTask.SyncInfo - */ - public List<SyncInfo> getSynchronizingInfoList() { - removeSyncInfo(new SyncInfo("")); - return synchronisingInfoList; - } - - - /** - * Returns the sync info matching a url - * (if the url is not in the list, returns a new SyncInfo with the url and adds it to the list) - * - * @param urlString - * @return a SyncInfo matching the url - */ - public SyncInfo getSynchronizingInfo(String urlString) { - for (SyncInfo sync : synchronisingInfoList) { - if (sync.getSyncURL().equals(urlString)) { - return sync; - } - } - SyncInfo newInfo = new SyncInfo(urlString); - synchronisingInfoList.add(newInfo); - return newInfo; - - } - - /** - * sets the activity of a sync info - * - * @param isActive a boolean - * @param syncInfo the SyncInfo - */ - public void setIsActive(boolean isActive, SyncInfo syncInfo) { - syncInfo.setActiveSync(isActive); - } - - /** * Add task's subtask. * * Also add parent reference. @@ -483,6 +321,18 @@ public class TimerTask implements Cloneable, this.alerts = alerts; } + public void addSync(TimerSync sync) { + syncs.add(sync); + } + + public List<TimerSync> getSyncs() { + return syncs; + } + + public void setSyncs(List<TimerSync> syncs) { + this.syncs = syncs; + } + @Override public String toString() { return name + subTasks.toString(); @@ -525,6 +375,7 @@ public class TimerTask implements Cloneable, task.allDaysTimes = new DailySortedMap<>(allDaysTimes); task.allDaysAnnotations = new TreeMap<>(allDaysAnnotations); task.subTasks = new ArrayList<>(subTasks); + task.syncs = new ArrayList<>(syncs); } catch (CloneNotSupportedException e) { throw new RuntimeException("Can't clone", e); } diff --git a/src/main/java/org/chorem/jtimer/entities/TimerTaskHelper.java b/src/main/java/org/chorem/jtimer/entities/TimerTaskHelper.java index a3343b0..e6f8eb2 100644 --- a/src/main/java/org/chorem/jtimer/entities/TimerTaskHelper.java +++ b/src/main/java/org/chorem/jtimer/entities/TimerTaskHelper.java @@ -22,10 +22,6 @@ package org.chorem.jtimer.entities; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import java.time.LocalDate; -import java.time.ZoneId; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; @@ -37,7 +33,6 @@ import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; -import org.apache.commons.lang3.time.DateUtils; /** * Helper to remove process code from entity @@ -334,101 +329,4 @@ public class TimerTaskHelper { return components; } - - /** - * makes a JSONObject corresponding to one sync with all the times since lastSync - * @param task - * @param sync - * @param timezone - * @return - */ - public static JsonObject taskToJsonObject(TimerTask task, SyncInfo sync, String timezone) { - JsonObject resultingObject = new JsonObject(); - JsonArray periodArray = new JsonArray(); - Date startDate = sync.getLastSync(); - Date endDate = DateUtils.ceiling(new Date(), Calendar.DAY_OF_MONTH); - String timestamp = "T00:00:00" + timezone; - SortedMap<Date, Long> dates = task.getAllDaysAndTimes().subMap(startDate, endDate); - LocalDate startPeriodDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); - String startPeriodString = startPeriodDate.toString() + timestamp; - LocalDate endPeriodDate = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); - String endPeriodString = endPeriodDate.toString() + timestamp; - //get the times of the task - periodArray.addAll(getTimesAsJsonArray(task, startDate, endDate, sync.isWithAnnotations(), timestamp)); - //if there are subtasks, get the times of the subtasks that have no syncInfo - if (!task.getSubTasks().isEmpty()) { - for (TimerTask subtask : getAllSubTasks(task)) { - if (subtask.getSynchronizingInfoList().isEmpty()) { - periodArray.addAll(getTimesAsJsonArray(subtask, startDate, endDate, sync.isWithAnnotations(), timestamp)); - } - } - } - resultingObject.addProperty("URL", sync.getSyncURL()); - resultingObject.addProperty("startDate", startPeriodString); - resultingObject.addProperty("endDate", endPeriodString); - resultingObject.add("periods", periodArray); - - return resultingObject; - - } - - /** - * Returns all the subtasks of a task (including the subtaks of subtasks) - * @param task - * @return a List<TimerTask> - */ - public static List<TimerTask> getAllSubTasks(TimerTask task) { - List<TimerTask> subTasksList = new ArrayList<>(); - if (!task.getSubTasks().isEmpty()) { - for (TimerTask subTask : task.getSubTasks()) { - subTasksList.add(subTask); - if (!subTask.getSubTasks().isEmpty()) { - subTasksList.addAll(getAllSubTasks(subTask)); - } - } - } - return subTasksList; - } - - /*** - * Returns a JsonArray of all the times of a task, according to format - * the id is made of the date of the time and the id of the task (as in the number given - * by jTimer) - * id, startdate, duration, info (if annotations is true) - * - * @param task - * @param startDate - * @param endDate - * @param withAnnotations - * @param timestamp - * @return a JsonArray of the periods - */ - public static JsonArray getTimesAsJsonArray(TimerTask task, Date startDate, Date endDate, boolean withAnnotations, String timestamp) { - JsonArray periodArray = new JsonArray(); - SortedMap<Date, Long> dates = task.getAllDaysAndTimes().subMap(startDate, endDate); - for (SortedMap.Entry<Date, Long> entry : dates.entrySet()) { - //adding id, startDate and duration - //converting Date to LocalDate (to ease the .toString()) - LocalDate date = entry.getKey().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); - String dateString = date.toString(); - //as jtimer has time entries only for a day, the id of the times will be the date in yyyy-mm-dd format - JsonObject periodElement = new JsonObject(); - periodElement.addProperty("id", dateString + "_" + task.getNumber()); - periodElement.addProperty("startDate", dateString + timestamp); - //TimerTaskHelper.getTotalTime(task, entry.getKey()) to get the total time of the task and all the subtasks - //entry.getValue() to get only the time of the task divided by 1000 (jtimer stores milliseconds) - periodElement.addProperty("duration", entry.getValue()/1000); - if (withAnnotations && !(getAnnotation(task, entry.getKey()).isEmpty())) { - StringBuilder annotationBuilder = new StringBuilder(); - for (String s : getAnnotation(task, entry.getKey())) { - annotationBuilder.append(s); - annotationBuilder.append(","); - } - periodElement.addProperty("info", annotationBuilder.toString()); - } - periodArray.add(periodElement); - } - return periodArray; - - } } diff --git a/src/main/java/org/chorem/jtimer/entities/package-info.java b/src/main/java/org/chorem/jtimer/entities/package-info.java index 7b740e0..d214e1d 100644 --- a/src/main/java/org/chorem/jtimer/entities/package-info.java +++ b/src/main/java/org/chorem/jtimer/entities/package-info.java @@ -2,7 +2,7 @@ * #%L * jTimer * %% - * Copyright (C) 2007 - 2011 CodeLutin, Chatellier Eric + * Copyright (C) 2007 - 2016 CodeLutin, Chatellier Eric * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as diff --git a/src/main/java/org/chorem/jtimer/io/AbstractSaver.java b/src/main/java/org/chorem/jtimer/io/AbstractSaver.java index f8d0ed7..1b0f528 100644 --- a/src/main/java/org/chorem/jtimer/io/AbstractSaver.java +++ b/src/main/java/org/chorem/jtimer/io/AbstractSaver.java @@ -22,16 +22,8 @@ package org.chorem.jtimer.io; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.TimerTask; -import org.apache.commons.io.FileUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - /** * Abstract saver class. * @@ -45,79 +37,4 @@ import org.apache.commons.logging.LogFactory; */ public abstract class AbstractSaver extends TimerTask implements Saver { - /** log. */ - private static Log log = LogFactory.getLog(GTimerIncrementalSaver.class); - - /** Backup file extension. */ - public static final String BACKUP_EXTENSION = ".tmp"; - - /** - * Make to backup of file if exists. - * - * Copy file to filename + ".tmp" - * - * @param file file to backup - * @return backup file or {@code null} if input file doesn't exist - * @throws IOException - */ - protected File makeBackupFile(File file) throws IOException { - - // if not exist, don't do anything - if (!file.exists()) { - return null; - } - - File backupFile = new File(file.getAbsoluteFile() + BACKUP_EXTENSION); - - if (log.isDebugEnabled()) { - log.debug("Backuping file " + file.getName() + " to " + backupFile.getName()); - } - - backupFile.delete(); - FileUtils.copyFile(file, backupFile); - return backupFile; - } - - /** - * Rename backup file to original file name; - * - * @param backupFile backup file - * @return if backup file has been restored - */ - protected boolean restoreBackupFile(File backupFile) { - - String fileName = backupFile.getAbsolutePath(); - if (!fileName.endsWith(BACKUP_EXTENSION)) { - throw new IllegalArgumentException("Not a valid backup file" + backupFile); - } - - fileName = fileName.substring(0, fileName.length() - BACKUP_EXTENSION.length()); - File file = new File(fileName); - - if (log.isDebugEnabled()) { - log.debug("Renaming " + backupFile.getName() + " to " + file.getName()); - } - - boolean done; - try { - Files.move(backupFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); - done = true; - } catch (IOException ex) { - done = false; - } - return done; - } - - /** - * Delete backup file. - * - * This function NEVER throw IOException. - * - * @param backupFile backup file (can be {@code null}) - */ - protected void deleteBackupFile(File backupFile) { - if (backupFile != null) { - backupFile.delete(); - } - } } diff --git a/src/main/java/org/chorem/jtimer/io/AbstractSaver.java b/src/main/java/org/chorem/jtimer/io/BackupUtils.java similarity index 62% copy from src/main/java/org/chorem/jtimer/io/AbstractSaver.java copy to src/main/java/org/chorem/jtimer/io/BackupUtils.java index f8d0ed7..58d0305 100644 --- a/src/main/java/org/chorem/jtimer/io/AbstractSaver.java +++ b/src/main/java/org/chorem/jtimer/io/BackupUtils.java @@ -1,66 +1,35 @@ -/* - * #%L - * jTimer - * %% - * Copyright (C) 2009 - 2016 CodeLutin, Chatellier Eric - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - package org.chorem.jtimer.io; +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import java.util.TimerTask; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** - * Abstract saver class. - * - * Contains methods that can be used by various savers. - * - * @author chatellier - * @version $Revision$ - * - * Last update : $Date$ - * By : $Author$ + * Saver with backup support. */ -public abstract class AbstractSaver extends TimerTask implements Saver { +public class BackupUtils { /** log. */ - private static Log log = LogFactory.getLog(GTimerIncrementalSaver.class); + private static Log log = LogFactory.getLog(BackupUtils.class); /** Backup file extension. */ public static final String BACKUP_EXTENSION = ".tmp"; /** * Make to backup of file if exists. - * + * <p> * Copy file to filename + ".tmp" * * @param file file to backup * @return backup file or {@code null} if input file doesn't exist * @throws IOException */ - protected File makeBackupFile(File file) throws IOException { + public static File makeBackupFile(File file) throws IOException { // if not exist, don't do anything if (!file.exists()) { @@ -84,7 +53,7 @@ public abstract class AbstractSaver extends TimerTask implements Saver { * @param backupFile backup file * @return if backup file has been restored */ - protected boolean restoreBackupFile(File backupFile) { + public static boolean restoreBackupFile(File backupFile) { String fileName = backupFile.getAbsolutePath(); if (!fileName.endsWith(BACKUP_EXTENSION)) { @@ -110,12 +79,12 @@ public abstract class AbstractSaver extends TimerTask implements Saver { /** * Delete backup file. - * + * <p> * This function NEVER throw IOException. * * @param backupFile backup file (can be {@code null}) */ - protected void deleteBackupFile(File backupFile) { + public static void deleteBackupFile(File backupFile) { if (backupFile != null) { backupFile.delete(); } diff --git a/src/main/java/org/chorem/jtimer/io/GTimerIncrementalSaver.java b/src/main/java/org/chorem/jtimer/io/GTimerIncrementalSaver.java index e31a847..495dae6 100644 --- a/src/main/java/org/chorem/jtimer/io/GTimerIncrementalSaver.java +++ b/src/main/java/org/chorem/jtimer/io/GTimerIncrementalSaver.java @@ -71,7 +71,7 @@ import org.apache.commons.logging.LogFactory; import org.chorem.jtimer.JTimer; import org.chorem.jtimer.data.DataEventListener; import org.chorem.jtimer.data.DataViolationException; -import org.chorem.jtimer.entities.SyncInfo; +import org.chorem.jtimer.entities.TimerSync; import org.chorem.jtimer.entities.TimerAlert; import org.chorem.jtimer.entities.TimerAlert.Type; import org.chorem.jtimer.entities.TimerProject; @@ -121,9 +121,6 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, /** Extension alert. */ protected static final String GTIMER_ALERT_EXTENSION = "alert"; - /** Extension sync. */ - protected static final String JTIMER_SYNC_EXTENSION = "sync"; - /** Empty gtimer project name. */ protected static final String GTIMER_EMPTY_PROJECT_NAME = "No project"; @@ -355,7 +352,6 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, parseAnnotations(task); parseAlerts(task); - parseSyncInfo(task); } } catch (NumberFormatException e) { if (log.isWarnEnabled()) { @@ -594,39 +590,6 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, } } - /** Try and find sync information and load it - * - * @param task task to load sync info - */ - protected void parseSyncInfo(TimerTask task) throws IOException { - int taskNumber = task.getNumber(); - File syncTaskFile = new File(dataSaveDirectory + File.separator + taskNumber +"."+ GTIMER_TASK_EXTENSION +"."+ JTIMER_SYNC_EXTENSION); - - if (syncTaskFile.exists()) { - if (log.isDebugEnabled()) { - log.debug("Synchronization information found for task " + task.getName()); - } - try (BufferedReader parseIn = new BufferedReader(new FileReader(syncTaskFile))) { - JsonParser parser = new JsonParser(); - Gson gson = new Gson(); - JsonElement element = parser.parse(parseIn); - if (element.isJsonArray()) { - JsonArray infoArray = (JsonArray) element; - for (JsonElement obj : infoArray) { - JsonObject res = (JsonObject) obj; - SyncInfo sync = gson.fromJson(res, SyncInfo.class); - task.addSyncInfo(sync); - } - } - } - } else { - if (log.isDebugEnabled()) { - log.debug("Synchronization information not found for task " + task.getName()); - } - } - } - - /** * Find task alert and load it. * @@ -919,7 +882,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, try (Writer out = new OutputStreamWriter(new FileOutputStream(projectfile), "ISO-8859-1")) { // first try to make backup - backupfile = makeBackupFile(projectfile); + backupfile = BackupUtils.makeBackupFile(projectfile); // get creation date long mscreatedtime = project.getCreationDate().getTime(); @@ -931,7 +894,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, out.write("Created: " + createdTime + "\n"); out.write("Options: " + (project.isClosed() ? "1" : "0") + "\n"); - deleteBackupFile(backupfile); + BackupUtils.deleteBackupFile(backupfile); } catch (IOException e) { if (log.isDebugEnabled()) { log.error("Can't save project information, restore backup file", e); @@ -939,7 +902,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, // can be null if backup throw the exception if (backupfile != null) { - restoreBackupFile(backupfile); + BackupUtils.restoreBackupFile(backupfile); } } } @@ -1016,7 +979,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, try (Writer out = new OutputStreamWriter(new FileOutputStream(taskfile), "ISO-8859-1")) { // first make backup - backupfile = makeBackupFile(taskfile); + backupfile = BackupUtils.makeBackupFile(taskfile); // Format: 1.2 // Name: Test Tache 1.2 @@ -1050,7 +1013,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, } } - deleteBackupFile(backupfile); + BackupUtils.deleteBackupFile(backupfile); } catch (IOException e) { if (log.isErrorEnabled()) { log.error("Can't save task", e); @@ -1058,7 +1021,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, // can be null if backup throw the exception if (backupfile != null) { - restoreBackupFile(backupfile); + BackupUtils.restoreBackupFile(backupfile); } } } @@ -1082,7 +1045,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, try (Writer out = new OutputStreamWriter(new FileOutputStream(annotationTaskFile), "ISO-8859-1")) { // first make backup - backupfile = makeBackupFile(annotationTaskFile); + backupfile = BackupUtils.makeBackupFile(annotationTaskFile); // save time of each day for (Entry<Date, String> entry : task.getAllDaysAnnotations() @@ -1094,7 +1057,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, out.write(gtimerTS + " " + entry.getValue() + "\n"); } - deleteBackupFile(backupfile); + BackupUtils.deleteBackupFile(backupfile); } catch (IOException e) { if (log.isErrorEnabled()) { log.debug("Can't save task", e); @@ -1102,7 +1065,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, // can be null if backup throw the exception if (backupfile != null) { - restoreBackupFile(backupfile); + BackupUtils.restoreBackupFile(backupfile); } } } else { @@ -1110,50 +1073,6 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, } } - protected void saveSynchronizationInfo(TimerTask task) { - - int taskNumber = task.getNumber(); - - File synchronizationTaskFile = new File(dataSaveDirectory + File.separator - + taskNumber + "." + GTIMER_TASK_EXTENSION + "." + JTIMER_SYNC_EXTENSION); - - if ((task.getSynchronizingURLList() != null) || (!task.getSynchronizingURLList().isEmpty())) { - File backupfile = null; - - try (Writer out = new OutputStreamWriter(new FileOutputStream(synchronizationTaskFile), "ISO-8859-1")) { - - // first make backup - backupfile = makeBackupFile(synchronizationTaskFile); - - //make a json object for the syncInfo - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - gson.newJsonWriter(out); - JsonArray infoArray = new JsonArray(); - for (SyncInfo sync : task.getSynchronizingInfoList()) { - JsonElement res= gson.toJsonTree(sync); - infoArray.add(res); - } - out.write(gson.toJson(infoArray)); - - out.close(); - deleteBackupFile(backupfile); - if (log.isDebugEnabled()) { - log.debug("Saving sync on task : " + task.getName()); - } - } - catch (IOException e) { - if (log.isErrorEnabled()) { - log.debug("Can't save task synchronization information", e); - } - - // can be null if backup throws the exception - if (backupfile != null) { - restoreBackupFile(backupfile); - } - } - } - } - /** * Save task alerts. * @@ -1172,7 +1091,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, try (Writer out = new OutputStreamWriter(new FileOutputStream(alertTaskFile), "ISO-8859-1")) { // first make backup - backupfile = makeBackupFile(alertTaskFile); + backupfile = BackupUtils.makeBackupFile(alertTaskFile); out.write("Format: " + GTIMER_FILE_VERSION + "\n"); @@ -1192,7 +1111,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, } } - deleteBackupFile(backupfile); + BackupUtils.deleteBackupFile(backupfile); } catch (IOException e) { if (log.isErrorEnabled()) { log.debug("Can't save task", e); @@ -1200,7 +1119,7 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, // can be null if backup throw the exception if (backupfile != null) { - restoreBackupFile(backupfile); + BackupUtils.restoreBackupFile(backupfile); } } } else { @@ -1313,14 +1232,6 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, + alertFileToDelete.getPath() + ")"); } } - //and delete sync information file - File syncFileToDelete = new File(dataSaveDirectory + File.separator + fileNumber+ "."+ GTIMER_TASK_EXTENSION + "." + JTIMER_SYNC_EXTENSION); - if (syncFileToDelete.exists()) { - syncFileToDelete.delete(); - if (log.isDebugEnabled()) { - log.debug("Synchronization file deleted for " + taskOrProject.getName() + "(" + syncFileToDelete.getPath() +")"); - } - } } // second, go recursively on subtasks @@ -1366,7 +1277,6 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, // Potentiellement aussi lors d'un move. saveTaskAnnotation(task); saveAlerts(task); - saveSynchronizationInfo(task); // fix a bug with the gtimer subtask // save format du to composed task name @@ -1446,7 +1356,6 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, // remove task from running task and force save runningTasks.remove(task); saveTask(task); - saveSynchronizationInfo(task); } @Override @@ -1465,7 +1374,6 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, protected void saveRunningTasks() { synchronized (runningTasks) { runningTasks.forEach(this::saveTask); - runningTasks.forEach(this::saveSynchronizationInfo); } } @@ -1495,26 +1403,4 @@ public class GTimerIncrementalSaver extends AbstractSaver implements Saver, throw new DataViolationException("Can't add task", "vetoable.saver.invalid.characters"); } } - - /** when a syncInfo is deleted make a save - * - * @param task - * @param info - */ - @Override - public void syncInfoDeleted(TimerTask task, SyncInfo info) { - saveSynchronizationInfo(task); - } - - /** - * When a syncInfo is modified - * @param task TimerTask - * @param info - */ - @Override - public void syncInfoChanged(TimerTask task, SyncInfo info) { - saveSynchronizationInfo(task); - } - - } diff --git a/src/main/java/org/chorem/jtimer/io/TimerTaskSynchronizer.java b/src/main/java/org/chorem/jtimer/io/TimerTaskSynchronizer.java deleted file mode 100644 index 158ee15..0000000 --- a/src/main/java/org/chorem/jtimer/io/TimerTaskSynchronizer.java +++ /dev/null @@ -1,299 +0,0 @@ -/*- - * #%L - * jTimer - * %% - * Copyright (C) 2016 CodeLutin, Charlène Servantie - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ - -package org.chorem.jtimer.io; - -import com.google.gson.JsonObject; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.ProtocolException; -import java.net.URL; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Timer; -import org.apache.commons.lang3.time.DateUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.chorem.jtimer.data.DataEventListener; -import org.chorem.jtimer.entities.SyncInfo; -import org.chorem.jtimer.entities.TimerProject; -import org.chorem.jtimer.entities.TimerTask; -import org.chorem.jtimer.entities.TimerTaskHelper; - -/** - * - * Class dealing with synchronization of tasks - * - * Created by servantie on 01/06/16. - */ -public class TimerTaskSynchronizer implements DataEventListener { - - /** log */ - private static Log log = LogFactory.getLog(TimerTaskSynchronizer.class); - - /** timer to schedule syncs */ - protected Timer timer; - - /** Tasks to sync */ - protected Collection<TimerProject> projectsToSync; - - /** Timezone, defaulted to GMT +1 */ - protected String timezone = "+01:00"; - - /** - * TimerTaskSynchronizer constructor - */ - public TimerTaskSynchronizer() { - log.info("Starting synchronizer"); - } - - /** - * Change the timezone - * @param timezone - */ - public void setTimezone(String timezone) { - if (!timezone.isEmpty()) { - this.timezone = timezone; - } - } - - /** - * Does a sync attempt at start on tasks that have sync infos - * fixme:this sync doesn't trigger listener changes on the change of lastSync - */ - public void syncAtStart() { - if (log.isDebugEnabled()) { - log.debug("Sync At Start"); - } - - for (TimerProject project : projectsToSync) { - if (log.isDebugEnabled()) { - log.info("project : " + project.getName()); - } - - List<TimerTask> taskList = project.getSubTasks(); - for (TimerTask task : taskList) { - if (!task.getSynchronizingInfoList().isEmpty()) { - if (log.isDebugEnabled()) { - log.info("task : " + task.getName()); - } - synchronizeTask(task); - } - } - } - } - - - /** - * Synchronize for the first sync at startup - * (tests current date against lastSync date, - * syncs if the date is not the same day) - * @param task - * - */ - public void synchronizeTask(TimerTask task) { - for (SyncInfo syncInfo : task.getSynchronizingInfoList()) { - if (log.isDebugEnabled()) { - log.debug("Synchronization info : " + syncInfo.getSyncURL()); - } - //if the last sync is not on the same day - if (!DateUtils.isSameDay(new Date(), syncInfo.getLastSync())) { - String syncURL = syncInfo.getSyncURL(); - if (log.isDebugEnabled()) { - log.debug("Sync hasn't been done today for " + syncURL + " " + task.getName() ); - } - JsonObject syncObject = TimerTaskHelper.taskToJsonObject(task, syncInfo, timezone); - int syncResult = synchronizeTaskOnURL(syncObject); - if (syncResult > 199 && syncResult < 300) { - if (log.isDebugEnabled()) { - log.debug("Synchronization successful on : " + syncURL); - } - Calendar cal = Calendar.getInstance(); - syncInfo.setLastSync(cal.getTime()); - - } - else if (syncResult == 0) { - if (log.isDebugEnabled()) { - log.debug("Error in connection " + syncURL); - } - } - } - } - } - - /** - * Synchronizes one Task (calls to synchronizeTaskOnURL - * for each syncURLList element of the task) - * - * @param task - */ - public void synchronizeSingleTask(TimerTask task) { - - List<JsonObject> jsonObjectList = new ArrayList<>(); - for (SyncInfo syncInfo : task.getSynchronizingInfoList()) { - JsonObject syncObject = TimerTaskHelper.taskToJsonObject(task, syncInfo, timezone); - jsonObjectList.add(syncObject); - } - - for (JsonObject object : jsonObjectList) { - int syncResult = synchronizeTaskOnURL(object); - boolean successfulSync = false; - String syncURL = object.get("URL").getAsString(); - //syncResult is the HTTP Code received as an answer to the sync - if (syncResult > 199 && syncResult < 300) { - successfulSync = true; - } - //sync successful -> change the last sync time - if (successfulSync) { - if (log.isDebugEnabled()) { - log.debug("Sync successful on " + syncURL); - log.debug(LocalDateTime.now()); - } - Calendar cal = Calendar.getInstance(); - task.getSynchronizingInfo(syncURL).setLastSync(cal.getTime()); - } else { - if (log.isDebugEnabled()) { - log.debug("Error in connection " + syncURL); - log.debug("Error code received " + syncResult); - } - } - } - } - - /** - * Sends one synchronization (one JSON object) - * @param object the object to sync - * @return an int (http response code or 0 in case of a problem) - */ - public static int synchronizeTaskOnURL(JsonObject object) { - int upDateValue = 0; - //if it's an empty object, no synchronization is possible - if (!object.equals(new JsonObject())) { - String updateJsonString = object.toString(); - String syncURL = object.get("URL").getAsString(); - String charset = "UTF-8"; - HttpURLConnection connection; - URL url; - byte[] postDataBytes; - try { - url = new URL(syncURL); - connection = (HttpURLConnection) url.openConnection(); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(true); - connection.setRequestProperty("Content-Length", Integer.toString(updateJsonString.length())); - connection.setRequestProperty("Accept-Charset", charset); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setRequestMethod("POST"); - postDataBytes = updateJsonString.getBytes(charset); - connection.getOutputStream().write(postDataBytes); - upDateValue = connection.getResponseCode(); - } catch (MalformedURLException e) { - if (log.isErrorEnabled()) { - log.error("URL malformed : " + syncURL + " Code : " + upDateValue); - } - } catch (ProtocolException e) { - if (log.isErrorEnabled()) { - log.error("Protocol error."+ " Code : " + upDateValue); - } - } catch (UnsupportedEncodingException e) { - if (log.isErrorEnabled()) { - log.error("Problem with encoding " + syncURL + " Code : " + upDateValue); - } - } catch (IOException e) { - if (log.isErrorEnabled()) { - log.error("Problem with the connection " + syncURL + " Code : " + upDateValue); - } - }catch (IllegalArgumentException e) { - if (log.isErrorEnabled()) { - log.error("Port value not valid " + syncURL + " Code : " + upDateValue); - } - } - } - return upDateValue; - } - - /** - * Tasks are synchronized when stopped - * @param task modified task - */ - @Override - public void stopTask(TimerTask task) { - if (!task.getSynchronizingInfoList().isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("synchronizing task : " + task.getName()); - } - synchronizeSingleTask(task); - } else if (!task.getParent().getSynchronizingInfoList().isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("synchronizing parent task : " + task.getParent().getName()); - } - synchronizeSingleTask(task.getParent()); - } - } - - /** - * When tasktime is modified, if it's before the last sync, - * the task is synchronized - */ - @Override - public void setTaskTime(TimerTask task, Date date, Long time) { - //check if it isn't the current day, to avoid sync every second - if (!DateUtils.isSameDay(new Date(), date)) { - if (!task.getSynchronizingInfoList().isEmpty()) { - for (SyncInfo syncInfo : task.getSynchronizingInfoList()) { - if (!DateUtils.isSameDay(date, syncInfo.getLastSync())) { - syncInfo.setLastSync(date); - } - } - synchronizeSingleTask(task); - } - //if the task has a parent task with a synchronizing info, then set that sync info - //to that time and try to sync - else if (!task.getParent().getSynchronizingInfoList().isEmpty()) { - TimerTask parentTask = task.getParent(); - for (SyncInfo syncInfo : parentTask.getSynchronizingInfoList()) { - if (!DateUtils.isSameDay(date, syncInfo.getLastSync())) { - syncInfo.setLastSync(date); - } - } - synchronizeSingleTask(parentTask); - } - } - } - - /** - * When data is loaded, get the data to work on and synchronize - */ - @Override - public void dataLoaded(Collection<TimerProject> projects) { - projectsToSync = projects; - syncAtStart(); - } - -} diff --git a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelper.java b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelper.java new file mode 100644 index 0000000..8082470 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelper.java @@ -0,0 +1,140 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2016 CodeLutin, Charlène Servantie, Eric Chatellier + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ +package org.chorem.jtimer.plugin.timebundle; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import org.apache.commons.lang3.time.DateUtils; +import org.chorem.jtimer.entities.TimerSync; +import org.chorem.jtimer.entities.TimerTask; +import org.chorem.jtimer.entities.TimerTaskHelper; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.SortedMap; + +/** + * Helper for time bundle. + */ +public class TimeBundleHelper { + + /** + * Returns all the subtasks of a task (including the subtaks of subtasks). + * + * @param task + * @return a List<TimerTask> + */ + public static List<TimerTask> getAllSubTasks(TimerTask task) { + List<TimerTask> subTasksList = new ArrayList<>(); + if (task.getSubTasks() != null) { + for (TimerTask subTask : task.getSubTasks()) { + subTasksList.add(subTask); + subTasksList.addAll(getAllSubTasks(subTask)); + } + } + return subTasksList; + } + + /** + * makes a JSONObject corresponding to one sync with all the times since lastSync. + * + * @param task + * @param sync + * @return + */ + public static JsonObject taskToJsonObject(TimerTask task, TimerSync sync) { + JsonObject resultingObject = new JsonObject(); + JsonArray periodArray = new JsonArray(); + Date startDate = sync.getLastSync(); + Date endDate = DateUtils.ceiling(new Date(), Calendar.DAY_OF_MONTH); + String timestamp = "T00:00:00+00:00"; // why +1 ? + SortedMap<Date, Long> dates = task.getAllDaysAndTimes().subMap(startDate, endDate); + LocalDate startPeriodDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + String startPeriodString = startPeriodDate.toString() + timestamp; + LocalDate endPeriodDate = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + String endPeriodString = endPeriodDate.toString() + timestamp; + //get the times of the task + periodArray.addAll(getTimesAsJsonArray(task, startDate, endDate, sync.isWithAnnotations(), timestamp)); + //if there are subtasks, get the times of the subtasks that have no syncInfo + if (!task.getSubTasks().isEmpty()) { + for (TimerTask subtask : getAllSubTasks(task)) { + if (subtask.getSyncs().isEmpty()) { + periodArray.addAll(getTimesAsJsonArray(subtask, startDate, endDate, sync.isWithAnnotations(), timestamp)); + } + } + } + resultingObject.addProperty("URL", sync.getUrl()); + resultingObject.addProperty("startDate", startPeriodString); + resultingObject.addProperty("endDate", endPeriodString); + resultingObject.add("periods", periodArray); + + return resultingObject; + + } + + /*** + * Returns a JsonArray of all the times of a task, according to format + * the id is made of the date of the time and the id of the task (as in the number given + * by jTimer) + * id, startdate, duration, info (if annotations is true) + * + * @param task + * @param startDate + * @param endDate + * @param withAnnotations + * @param timestamp + * @return a JsonArray of the periods + */ + public static JsonArray getTimesAsJsonArray(TimerTask task, Date startDate, Date endDate, boolean withAnnotations, String timestamp) { + JsonArray periodArray = new JsonArray(); + SortedMap<Date, Long> dates = task.getAllDaysAndTimes().subMap(startDate, endDate); + for (SortedMap.Entry<Date, Long> entry : dates.entrySet()) { + //adding id, startDate and duration + //converting Date to LocalDate (to ease the .toString()) + LocalDate date = entry.getKey().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + String dateString = date.toString(); + //as jtimer has time entries only for a day, the id of the times will be the date in yyyy-mm-dd format + JsonObject periodElement = new JsonObject(); + periodElement.addProperty("id", dateString + "_" + task.getNumber()); + periodElement.addProperty("startDate", dateString + timestamp); + //TimerTaskHelper.getTotalTime(task, entry.getKey()) to get the total time of the task and all the subtasks + //entry.getValue() to get only the time of the task divided by 1000 (jtimer stores milliseconds) + periodElement.addProperty("duration", entry.getValue() / 1000); + if (withAnnotations && !(TimerTaskHelper.getAnnotation(task, entry.getKey()).isEmpty())) { + StringBuilder annotationBuilder = new StringBuilder(); + for (String s : TimerTaskHelper.getAnnotation(task, entry.getKey())) { + annotationBuilder.append(s); + annotationBuilder.append(","); + } + periodElement.addProperty("info", annotationBuilder.toString()); + } + periodArray.add(periodElement); + } + return periodArray; + + } + +} diff --git a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSaver.java b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSaver.java new file mode 100644 index 0000000..862fc50 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSaver.java @@ -0,0 +1,220 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2016 CodeLutin, Charlène Servantie, Eric Chatellier + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ +package org.chorem.jtimer.plugin.timebundle; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.JTimer; +import org.chorem.jtimer.data.DataEventListener; +import org.chorem.jtimer.entities.TimerProject; +import org.chorem.jtimer.entities.TimerSync; +import org.chorem.jtimer.entities.TimerTask; +import org.chorem.jtimer.io.BackupUtils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.Collection; +import java.util.List; + +/** + * Saver to handle sync related info. + */ +public class TimeBundleSaver implements DataEventListener { + + private static Log log = LogFactory.getLog(TimeBundleSaver.class); + + protected static final String JTIMER_SYNC_DIRECTORY = "timebundle"; + + protected static final String JTIMER_PROJECT_EXTENSION = ".project.sync"; + + protected static final String JTIMER_TASK_EXTENSION = ".task.sync"; + + protected File syncDataDirectory; + + public TimeBundleSaver() { + syncDataDirectory = new File(JTimer.config.getHomeDirectory(), JTIMER_SYNC_DIRECTORY); + syncDataDirectory.mkdirs(); + } + + @Override + public void dataLoaded(Collection<TimerProject> projects) { + projects.forEach(this::addProjectSync); + } + + @Override + public void modifyProject(TimerProject project) { + saveProjectSync(project); + } + + @Override + public void modifyTask(TimerTask task) { + saveTaskSync(task); + } + + @Override + public void addProject(TimerProject project) { + saveProjectSync(project); + } + + @Override + public void addTask(TimerTask task) { + saveTaskSync(task); + } + + @Override + public void deleteProject(TimerProject project) { + int projectNumber = project.getNumber(); + File projectSyncFile = new File(syncDataDirectory, projectNumber + JTIMER_PROJECT_EXTENSION); + if (projectSyncFile.exists()) { + projectSyncFile.delete(); + if (log.isDebugEnabled()) { + log.debug("Synchronization file deleted for " + project.getName()); + } + } + } + + @Override + public void deleteTask(TimerTask task) { + int taskNumber = task.getNumber(); + File taskSyncFile = new File(syncDataDirectory, taskNumber + JTIMER_TASK_EXTENSION); + if (taskSyncFile.exists()) { + taskSyncFile.delete(); + if (log.isDebugEnabled()) { + log.debug("Synchronization file deleted for " + task.getName()); + } + } + } + + protected void addProjectSync(TimerProject timerProject) { + int projectNumber = timerProject.getNumber(); + File projectSyncFile = new File(syncDataDirectory, projectNumber + JTIMER_PROJECT_EXTENSION); + parseFileSync(timerProject, projectSyncFile); + timerProject.getSubTasks().forEach(this::addTaskSync); + } + + protected void addTaskSync(TimerTask timerTask) { + int taskNumber = timerTask.getNumber(); + File taskSyncFile = new File(syncDataDirectory, taskNumber + JTIMER_TASK_EXTENSION); + parseFileSync(timerTask, taskSyncFile); + timerTask.getSubTasks().forEach(this::addTaskSync); + } + + protected void parseFileSync(TimerTask projectOrTask, File syncFile) { + if (syncFile.exists()) { + if (log.isDebugEnabled()) { + log.debug("Add sync for project " + projectOrTask.getName()); + } + + try (Reader parseIn = new BufferedReader(new FileReader(syncFile))) { + JsonParser parser = new JsonParser(); + Gson gson = new Gson(); + JsonElement element = parser.parse(parseIn); + JsonArray infoArray = (JsonArray) element; + for (JsonElement obj : infoArray) { + JsonObject res = (JsonObject) obj; + TimerSync sync = gson.fromJson(res, TimerSync.class); + projectOrTask.addSync(sync); + } + } catch (IOException ex) { + if (log.isErrorEnabled()) { + log.error("Can't parse sync file", ex); + } + } + } else if (log.isTraceEnabled()) { + log.trace("Sync not found for " + projectOrTask.getName()); + } + } + + protected void saveProjectSync(TimerProject timerProject) { + int projectNumber = timerProject.getNumber(); + File projectSyncFile = new File(syncDataDirectory, projectNumber + JTIMER_PROJECT_EXTENSION); + saveFileSync(timerProject, projectSyncFile); + } + + protected void saveTaskSync(TimerTask timerTask) { + int taskNumber = timerTask.getNumber(); + File taskSyncFile = new File(syncDataDirectory, taskNumber + JTIMER_TASK_EXTENSION); + saveFileSync(timerTask, taskSyncFile); + } + + protected void saveFileSync(TimerTask task, File taskSyncFile) { + + if (CollectionUtils.isEmpty(task.getSyncs())) { + taskSyncFile.delete(); + + } else { + File backupfile = null; + + try (Writer out = new OutputStreamWriter(new FileOutputStream(taskSyncFile), "ISO-8859-1")) { + + // first make backup + backupfile = BackupUtils.makeBackupFile(taskSyncFile); + + //make a json object for the syncInfo + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + out.write(gson.toJson(task.getSyncs())); + + out.close(); + BackupUtils.deleteBackupFile(backupfile); + if (log.isDebugEnabled()) { + log.debug("Saving sync on task : " + task.getName()); + } + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.debug("Can't save task synchronization information", e); + } + + // can be null if backup throws the exception + if (backupfile != null) { + BackupUtils.restoreBackupFile(backupfile); + } + } + } + } + + @Override + public void postMergeTasks(TimerTask destinationTask, List<TimerTask> otherTasks) { + otherTasks.forEach(task -> { + task.getSyncs().forEach(sync -> { + boolean urlExists = destinationTask.getSyncs().stream() // + .anyMatch(timerSync -> timerSync.getUrl().equals(sync.getUrl())); + if (!urlExists) { + destinationTask.getSyncs().add(sync); + } + }); + }); + modifyTask(destinationTask); + } +} diff --git a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSynchronizer.java b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSynchronizer.java new file mode 100644 index 0000000..abd5fba --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSynchronizer.java @@ -0,0 +1,148 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2016 CodeLutin, Charlène Servantie, Eric Chatellier + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ +package org.chorem.jtimer.plugin.timebundle; + +import com.google.gson.JsonObject; +import org.apache.commons.lang3.time.DateUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.data.DataEventListener; +import org.chorem.jtimer.entities.TimerProject; +import org.chorem.jtimer.entities.TimerSync; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Collection; +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; + +/** + * Synchronizer background task. + */ +public class TimeBundleSynchronizer extends TimerTask implements DataEventListener { + + /** log */ + private static Log log = LogFactory.getLog(TimeBundleSynchronizer.class); + + /** timer to schedule syncs */ + protected Timer timer; + + protected Collection<TimerProject> projects; + + /** + * TimeBundleSynchronizer constructor + */ + public TimeBundleSynchronizer() { + log.info("Starting synchronizer"); + timer = new Timer(); + } + + @Override + public void dataLoaded(Collection<TimerProject> projects) { + this.projects = projects; + timer.schedule(this, 0, 60 * 60 * 1000); + } + + @Override + public void run() { + projects.forEach(this::synchronizerTask); + } + + protected void synchronizerTask(org.chorem.jtimer.entities.TimerTask timerTask) { + synchronizerProjectOrTask(timerTask); + timerTask.getSubTasks().forEach(this::synchronizerTask); + } + + protected void synchronizerProjectOrTask(org.chorem.jtimer.entities.TimerTask timerTask) { + timerTask.getSyncs().forEach(timerSync -> { + if (!DateUtils.isSameDay(new Date(), timerSync.getLastSync())) { + if (log.isDebugEnabled()) { + log.debug("Synchronizing task " + timerTask.getName() + "..."); + } + + JsonObject syncObject = TimeBundleHelper.taskToJsonObject(timerTask, timerSync); + synchronizeTaskOnURL(syncObject); + } + }); + } + + /** + * Sends one synchronization (one JSON object) + * @param object the object to sync + * @return an int (http response code or 0 in case of a problem) + */ + protected int synchronizeTaskOnURL(JsonObject object) { + int upDateValue = 0; + // if it's an empty object, no synchronization is possible + if (!object.equals(new JsonObject())) { + String updateJsonString = object.toString(); + String syncURL = object.get("URL").getAsString(); + String charset = "UTF-8"; + HttpURLConnection connection; + URL url; + byte[] postDataBytes; + try { + url = new URL(syncURL); + connection = (HttpURLConnection) url.openConnection(); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + connection.setRequestProperty("Content-Length", Integer.toString(updateJsonString.length())); + connection.setRequestProperty("Accept-Charset", charset); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestMethod("POST"); + postDataBytes = updateJsonString.getBytes(charset); + connection.getOutputStream().write(postDataBytes); + upDateValue = connection.getResponseCode(); + } catch (IOException ex) { + if (log.isErrorEnabled()) { + log.error("Problem with the connection " + syncURL + " Code : " + upDateValue, ex); + } + } + } + return upDateValue; + } + + /** + * When tasktime is modified, if it's before the last sync, + * the task is synchronized + */ + @Override + public void setTaskTime(org.chorem.jtimer.entities.TimerTask task, Date date, Long time) { + //check if it isn't the current day, to avoid sync every second + if (!DateUtils.isSameDay(new Date(), date)) { + if (!task.getSyncs().isEmpty()) { + for (TimerSync timerSync : task.getSyncs()) { + if (!DateUtils.isSameDay(date, timerSync.getLastSync())) { + timerSync.setLastSync(date); + } + } + } + + if (task.getParent() != null) { + setTaskTime(task.getParent(), date, time); + } + } + } +} diff --git a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleVetoable.java b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleVetoable.java new file mode 100644 index 0000000..b4f34e5 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleVetoable.java @@ -0,0 +1,61 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2016 CodeLutin, Charlène Servantie, Eric Chatellier + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ +package org.chorem.jtimer.plugin.timebundle; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.data.DataEventListener; +import org.chorem.jtimer.data.DataViolationException; +import org.chorem.jtimer.data.VetoableDataEventListener; +import org.chorem.jtimer.entities.TimerSync; +import org.chorem.jtimer.entities.TimerTask; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Vetoable for forbidden action on synchronized tasks. + */ +public class TimeBundleVetoable implements VetoableDataEventListener { + + private static Log log = LogFactory.getLog(TimeBundleVetoable.class); + + /** + * Merge project violation key. + */ + protected static final String INVALID_SYNC_LIST_VIOLATION = "vetoable.timebundle.invalid.sync.list"; + + @Override + public void checkMergeTasks(TimerTask destinationTask, List<TimerTask> otherTasks) { + Set<String> allUrls = otherTasks.stream() // + .flatMap(t -> t.getSyncs().stream()) // + .map(TimerSync::getUrl) // + .collect(Collectors.toSet()); + Set<String> destUrls = destinationTask.getSyncs().stream() // + .map(TimerSync::getUrl) // + .collect(Collectors.toSet()); + if (!allUrls.equals(destUrls)) { + throw new DataViolationException("Can't merge tasks", INVALID_SYNC_LIST_VIOLATION); + } + } +} diff --git a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncCellRenderer.java b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncCellRenderer.java new file mode 100644 index 0000000..87efc37 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncCellRenderer.java @@ -0,0 +1,46 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2016 CodeLutin, Charlène Servantie, Eric Chatellier + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ +package org.chorem.jtimer.plugin.timebundle; + +import javax.swing.table.DefaultTableCellRenderer; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Renderer for last sync date column. + */ +public class TimerSyncCellRenderer extends DefaultTableCellRenderer { + + private Date dateValue; + private SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); + private String valueToString = ""; + + @Override + public void setValue(Object value) { + if ((value != null)) { + dateValue = (Date) value; + valueToString = dateFormat.format(dateValue); + value = valueToString; + super.setValue(value); + } + } +} diff --git a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncEditor.java b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncEditor.java new file mode 100644 index 0000000..4ece14f --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncEditor.java @@ -0,0 +1,245 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2016 CodeLutin, Charlène Servantie, Eric Chatellier + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ +package org.chorem.jtimer.plugin.timebundle; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.TableColumnModel; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.entities.TimerProject; +import org.chorem.jtimer.entities.TimerSync; +import org.chorem.jtimer.entities.TimerTask; +import org.jdesktop.application.Application; +import org.jdesktop.application.FrameView; + +/** + * UI to modify Synchronization Info of a task. + */ +public class TimerSyncEditor extends FrameView implements ActionListener { + + /** + * Class logger + */ + protected static Log log = LogFactory.getLog(TimerSyncEditor.class); + + /** + * Timer core. + */ + protected TimerCore core; + + /** + * button to add a URL + */ + protected JButton addButton; + + /** + * button to delete a URL + */ + protected JButton deleteButton; + + /** + * button to test a url + */ + protected JButton testSyncUrlButton; + + /** + * JTable of URLs + */ + protected JTable urlJTable; + + /** + * task to update + */ + protected TimerTask task; + + /** + * timezone + */ + protected String timezone; + + /** + * UpdaterView constructor. + * + * @param application parent reference + * @param core core reference + * @param task the task to update + */ + public TimerSyncEditor(Application application, TimerCore core, TimerTask task) { + + super(application); + // modify frame name + getFrame().setName("syncFrame"); + getFrame().setTitle(getResourceMap().getString("syncTitle")); + + this.core = core; + this.task = task; + timezone = "+01:00"; + + setComponent(getMainComponent()); + } + + /** + * Get main view component. + * + * @return main component + */ + protected JComponent getMainComponent() { + + JPanel configComponent = new JPanel(); + configComponent.setLayout(new BorderLayout(5, 5)); + + JPanel urlPanel = new JPanel(); + urlPanel.setLayout(new BorderLayout(5,5)); + + urlJTable = new JTable(new TimerSyncTableModel(this, task)); + //try to have coherent width of columns + TableColumnModel tableColModel = urlJTable.getColumnModel(); + tableColModel.getColumn(0).setPreferredWidth(200); + tableColModel.getColumn(1).setPreferredWidth(40); + tableColModel.getColumn(2).setPreferredWidth(60); + tableColModel.getColumn(3).setPreferredWidth(200); + + //render the date properly + tableColModel.getColumn(3).setCellRenderer(new TimerSyncCellRenderer()); + + JScrollPane scrollTablePane = new JScrollPane(urlJTable); + configComponent.add(scrollTablePane, BorderLayout.CENTER); + + //panel to hold the buttons + JPanel buttonsPane = new JPanel(new BorderLayout()); + JPanel buttonPane = new JPanel(); + buttonPane.setAlignmentX(Component.CENTER_ALIGNMENT); + + addButton = new JButton(getResourceMap().getString("addButton")); + addButton.addActionListener(this); + addButton.setActionCommand("addURL"); + + deleteButton = new JButton(getResourceMap().getString("deleteButton")); + deleteButton.addActionListener(this); + deleteButton.setActionCommand("deleteURL"); + + testSyncUrlButton = new JButton(getResourceMap().getString("testSyncButton")); + testSyncUrlButton.addActionListener(this); + testSyncUrlButton.setActionCommand("testURL"); + + buttonPane.add(addButton); + buttonPane.add(testSyncUrlButton); + buttonPane.add(deleteButton); + // button to close + JButton closeButton = new JButton(); + closeButton.setAction(getContext().getActionMap(this).get("closeView")); + + //adding components to the main one + buttonsPane.add(buttonPane, BorderLayout.CENTER); + buttonsPane.add(closeButton, BorderLayout.SOUTH); + configComponent.add(buttonsPane, BorderLayout.SOUTH); + + // color fix on linux ? + configComponent.setBackground(urlPanel.getBackground()); + // set minimum size to prevent "packed size" (too big) + configComponent.setMinimumSize(new Dimension(200, 200)); + + return configComponent; + } + + /** + * Close action. + */ + @org.jdesktop.application.Action + public void closeView() { + TimerSyncTableModel model = (TimerSyncTableModel) urlJTable.getModel(); + task.setSyncs(model.getTimerSyncList()); + if (task instanceof TimerProject) { + core.getData().modifyProject(TimerProject.class.cast(task)); + } else { + core.getData().modifyTask(task); + } + getApplication().hide(this); + } + + /** + * Method to display an error message + * + * @param errorMessage the message to display + * @param titleBar the title of the frame + */ + public static void errorBox(String errorMessage, String titleBar) { + JOptionPane.showMessageDialog(null, errorMessage, titleBar, JOptionPane.ERROR_MESSAGE); + } + + /** + * Method to display an info message + * + * @param infoMessage the message to display + * @param titleBar the title of the frame + */ + public static void infoBox(String infoMessage, String titleBar) { + JOptionPane.showMessageDialog(null, infoMessage, titleBar, JOptionPane.INFORMATION_MESSAGE); + } + + @Override + public void actionPerformed(ActionEvent actionEvent) { + + String actionCommand = actionEvent.getActionCommand(); + TimerSyncTableModel model = (TimerSyncTableModel) urlJTable.getModel(); + + if ("addURL".equals(actionCommand)) { + model.add(new TimerSync("")); + } + else if ("deleteURL".equals(actionCommand)) { + TimerSync timerSync = model.getValueAt(urlJTable.getSelectedRow()); + model.remove(timerSync); + } + /*else if ("testURL".equals(actionCommand)) { + //if the test button has been clicked, test the sync on the URL and returns a message to the user + //tests only if the info exists + if (task.getSynchronizingInfoList().contains(infoToUse)) { + JsonObject testObject = TimerTaskHelper.taskToJsonObject(task, infoToUse, timezone); + int responseCode = TimerTaskSynchronizer.synchronizeTaskOnURL(testObject); + + if (responseCode > 199 && responseCode < 300) { + infoBox(getResourceMap().getString("testSyncSuccessMessage"), getResourceMap().getString("testSyncSuccessTitle")); + Calendar cal = Calendar.getInstance(); + infoToUse.setLastSync(cal.getTime()); + core.getData().syncInfoChanged(task, infoToUse); + } else { + errorBox(getResourceMap().getString("testSyncFailureMessage"), getResourceMap().getString("testSyncFailureTitle")); + } + + if (log.isDebugEnabled()) { + log.debug("Response code : " + responseCode); + } + } + } */ + } +} diff --git a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncTableModel.java b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncTableModel.java new file mode 100644 index 0000000..94f100e --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncTableModel.java @@ -0,0 +1,203 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2016 CodeLutin, Charlène Servantie, Eric Chatellier + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ +package org.chorem.jtimer.plugin.timebundle; + +import org.chorem.jtimer.entities.TimerSync; +import org.chorem.jtimer.entities.TimerTask; +import org.jdesktop.application.View; + +import javax.swing.table.AbstractTableModel; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Class to deal with the syncs in a table. + */ +public class TimerSyncTableModel extends AbstractTableModel { + + /** the syncInfo list. */ + protected List<TimerSync> timerSyncList; + + /** headers of the columns. */ + protected String[] columnHeaders; + + public TimerSyncTableModel(View view, TimerTask task) { + timerSyncList = new ArrayList<>(task.getSyncs()); + columnHeaders = new String[] { + view.getResourceMap().getString("syncURLHeader"), + view.getResourceMap().getString("activeHeader"), + view.getResourceMap().getString("withAnnotationsHeader"), + view.getResourceMap().getString("lastSyncHeader") + }; + } + + /** + * Gets the column headers + * + * @param col the column + * @return a string for the header + */ + @Override + public String getColumnName(int col) { + return columnHeaders[col]; + } + + + @Override + public int getRowCount() { + return timerSyncList.size(); + } + + /** + * returns the column number (url, activity, annotations and last sync, so 4) + */ + @Override + public int getColumnCount() { + return columnHeaders.length; + } + + /** + * Returns the value at rowIndex, columnIndex + * column 0 is url String, column 1 is activity boolean, + * column 2 is annotations boolean, column 3 is lastSync Date + * + * @param rowIndex + * @param columnIndex + * @return an Object + */ + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + Object value = ""; + TimerSync timerSync = timerSyncList.get(rowIndex); + switch (columnIndex) { + case 0: + value = timerSync.getUrl(); + break; + case 1: + value = timerSync.isActive(); + break; + case 2: + value = timerSync.isWithAnnotations(); + break; + case 3: + value = timerSync.getLastSync(); + break; + } + return value; + } + + /** + * When the data is changed + * + * @param value an object -a string- + * @param rowIndex the row selected + * @param columnIndex the column modified + */ + @Override + public void setValueAt(Object value, int rowIndex, int columnIndex) { + TimerSync timerSync = timerSyncList.get(rowIndex); + // if the url changes + switch (columnIndex) { + case 0: + timerSync.setUrl((String) value); + break; + case 1: + timerSync.setActive((Boolean) value); + break; + case 2: + timerSync.setWithAnnotations((Boolean) value); + break; + } + } + + /** + * To mark the url and checkbox cells as editable but not the date + * + * @param rowIndex + * @param columnIndex + * @return + */ + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + // the sync date is not editable + return columnIndex < 3; + } + + /** + * Returns the class of objects in the columns + * + * @param columnIndex + * @return the class + */ + + @Override + public Class<?> getColumnClass(int columnIndex) { + Class<?> result; + switch (columnIndex) { + case 0: + result = String.class; + break; + case 1: + case 2: + result = Boolean.class; + break; + case 3: + result = Date.class; + break; + default: + result = Object.class; + break; + } + + return result; + } + + /** + * Adds a syncInfo + * + * @param sync + */ + public void add(TimerSync sync) { + timerSyncList.add(sync); + fireTableRowsInserted(timerSyncList.indexOf(sync), timerSyncList.indexOf(sync)); + } + + /** + * removes a syncInfo + * + * @param sync + */ + public void remove(TimerSync sync) { + int index = timerSyncList.indexOf(sync); + timerSyncList.remove(sync); + fireTableRowsDeleted(index, index); + } + + public TimerSync getValueAt(int selectedRow) { + return timerSyncList.get(selectedRow); + } + + public List<TimerSync> getTimerSyncList() { + return timerSyncList; + } +} diff --git a/src/main/java/org/chorem/jtimer/ui/report/TimerTaskSyncInfoEditor.java b/src/main/java/org/chorem/jtimer/ui/report/TimerTaskSyncInfoEditor.java deleted file mode 100644 index 81b8701..0000000 --- a/src/main/java/org/chorem/jtimer/ui/report/TimerTaskSyncInfoEditor.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * #%L - * jTimer - * %% - * Copyright (C) 2008 - 2016 CodeLutin, Chatellier Eric - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/gpl-3.0.html>. - * #L% - */ -package org.chorem.jtimer.ui.report; - -import com.google.gson.JsonObject; -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableColumnModel; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.chorem.jtimer.data.TimerCore; -import org.chorem.jtimer.entities.SyncInfo; -import org.chorem.jtimer.entities.TimerTask; -import org.chorem.jtimer.entities.TimerTaskHelper; -import org.chorem.jtimer.io.TimerTaskSynchronizer; -import org.jdesktop.application.Application; -import org.jdesktop.application.FrameView; - -/** - * UI to modify Synchronization Info of a task - * - * Created by servantie on 13/05/16. - */ -public class TimerTaskSyncInfoEditor extends FrameView implements ActionListener { - - /** - * Class logger - */ - protected static Log log = LogFactory.getLog(TimerTaskSyncInfoEditor.class); - - /** - * Timer core. - */ - protected TimerCore core; - - /** - * button to add a URL - */ - protected JButton addButton; - - /** - * button to delete a URL - */ - protected JButton deleteButton; - - /** - * button to test a url - */ - protected JButton testSyncUrlButton; - - /** - * JTable of URLs - */ - protected JTable urlJTable; - - /** - * task to update - */ - protected TimerTask task; - - /** - * timezone - */ - protected String timezone; - - /** - * UpdaterView constructor. - * - * @param application parent reference - * @param core core reference - * @param task the task to update - */ - public TimerTaskSyncInfoEditor(Application application, TimerCore core, TimerTask task) { - - super(application); - // modify frame name - getFrame().setName("syncFrame"); - getFrame().setTitle(getResourceMap().getString("syncTitle")); - - this.core = core; - this.task = task; - timezone = "+01:00"; - - setComponent(getMainComponent()); - } - - /** - * Get main view component. - * - * @return main component - */ - protected JComponent getMainComponent() { - - JPanel configComponent = new JPanel(); - configComponent.setLayout(new BorderLayout(5, 5)); - - JPanel urlPanel = new JPanel(); - urlPanel.setLayout(new BorderLayout(5,5)); - - urlJTable = new JTable(new SyncInfoTableModel()); - //try to have coherent width of columns - TableColumnModel tableColModel = urlJTable.getColumnModel(); - tableColModel.getColumn(0).setPreferredWidth(200); - tableColModel.getColumn(1).setPreferredWidth(40); - tableColModel.getColumn(2).setPreferredWidth(60); - tableColModel.getColumn(3).setPreferredWidth(200); - - //render the date properly - tableColModel.getColumn(3).setCellRenderer(new DateRenderer()); - - JScrollPane scrollTablePane = new JScrollPane(urlJTable); - configComponent.add(scrollTablePane, BorderLayout.CENTER); - - //panel to hold the buttons - JPanel buttonsPane = new JPanel(new BorderLayout()); - JPanel buttonPane = new JPanel(); - buttonPane.setAlignmentX(Component.CENTER_ALIGNMENT); - - addButton = new JButton(getResourceMap().getString("addButton")); - addButton.addActionListener(this); - addButton.setActionCommand("addURL"); - - deleteButton = new JButton(getResourceMap().getString("deleteButton")); - deleteButton.addActionListener(this); - deleteButton.setActionCommand("deleteURL"); - - testSyncUrlButton = new JButton(getResourceMap().getString("testSyncButton")); - testSyncUrlButton.addActionListener(this); - testSyncUrlButton.setActionCommand("testURL"); - - buttonPane.add(addButton); - buttonPane.add(testSyncUrlButton); - buttonPane.add(deleteButton); - // button to close - JButton closeButton = new JButton(); - closeButton.setAction(getContext().getActionMap(this).get("closeView")); - - //adding components to the main one - buttonsPane.add(buttonPane, BorderLayout.CENTER); - buttonsPane.add(closeButton, BorderLayout.SOUTH); - configComponent.add(buttonsPane, BorderLayout.SOUTH); - - // color fix on linux ? - configComponent.setBackground(urlPanel.getBackground()); - // set minimum size to prevent "packed size" (too big) - configComponent.setMinimumSize(new Dimension(200, 200)); - - return configComponent; - } - - /** - * Close action. - */ - @org.jdesktop.application.Action - public void closeView() { - getApplication().hide(this); - } - - /** - * Method to display an error message - * - * @param errorMessage the message to display - * @param titleBar the title of the frame - */ - public static void errorBox(String errorMessage, String titleBar) { - JOptionPane.showMessageDialog(null, errorMessage, titleBar, JOptionPane.ERROR_MESSAGE); - } - - /** - * Method to display an info message - * - * @param infoMessage the message to display - * @param titleBar the title of the frame - */ - public static void infoBox(String infoMessage, String titleBar) { - JOptionPane.showMessageDialog(null, infoMessage, titleBar, JOptionPane.INFORMATION_MESSAGE); - } - - /** - * Class to deal with the SyncInfos in a table - */ - protected class SyncInfoTableModel extends AbstractTableModel { - - /** - * the syncInfo list - */ - protected List<SyncInfo> syncInfoList; - - /** - * headers of the columns - */ - protected String[] columnHeaders = {getResourceMap().getString("syncURLHeader"), getResourceMap().getString("isActiveHeader"), getResourceMap().getString("isWithAnnotationsHeader"), getResourceMap().getString("lastSyncHeader")}; - - - public SyncInfoTableModel() { - syncInfoList = task.getSynchronizingInfoList(); - } - - - /** - * Gets the column headers - * - * @param col the column - * @return a string for the header - */ - @Override - public String getColumnName(int col) { - return columnHeaders[col]; - } - - - @Override - public int getRowCount() { - return syncInfoList.size(); - } - - /** - * returns the column number (url, activity, annotations and last sync, so 4) - */ - @Override - public int getColumnCount() { - return 4; - } - - /** - * Returns the value at rowIndex, columnIndex - * column 0 is url String, column 1 is activity boolean, - * column 2 is annotations boolean, column 3 is lastSync Date - * - * @param rowIndex - * @param columnIndex - * @return an Object - */ - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - Object value = ""; - SyncInfo syncInfo = syncInfoList.get(rowIndex); - switch (columnIndex) { - case 0: - value = syncInfo.getSyncURL(); - break; - case 1: - value = syncInfo.isActiveSync(); - break; - case 2: - value = syncInfo.isWithAnnotations(); - break; - case 3: - value = syncInfo.getLastSync(); - break; - } - return value; - } - - /** - * When the data is changed - * - * @param value an object -a string- - * @param rowIndex the row selected - * @param columnIndex the column modified - */ - @Override - public void setValueAt(Object value, int rowIndex, int columnIndex) { - SyncInfo syncInfo = syncInfoList.get(rowIndex); - //if the url changes - switch (columnIndex) { - case 0: - syncInfo.setSyncURL((String) value); - break; - case 1: - syncInfo.setActiveSync((Boolean) value); - break; - case 2: - syncInfo.setWithAnnotations((Boolean) value); - break; - } - core.getData().syncInfoChanged(task, syncInfo); - } - - /** - * To mark the url and checkbox cells as editable but not the date - * - * @param rowIndex - * @param columnIndex - * @return - */ - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - //the sync date is not editable - if (columnIndex >= 3) { - return false; - } else { - return true; - } - } - - /** - * Returns the class of objects in the columns - * - * @param columnIndex - * @return the class - */ - - @Override - public Class<?> getColumnClass(int columnIndex) { - if (columnIndex == 0) { - return String.class; - } else if (columnIndex == 1 || columnIndex == 2) { - return Boolean.class; - } else if (columnIndex == 3) { - return Date.class; - } - return Object.class; - } - - /** - * Adds a syncInfo - * - * @param sync - */ - public void add(SyncInfo sync) { - if (!syncInfoList.contains(sync)) { - syncInfoList.add(sync); - fireTableRowsInserted(syncInfoList.indexOf(sync), syncInfoList.indexOf(sync)); - } - } - - /** - * removes a syncInfo - * - * @param sync - */ - public void remove(SyncInfo sync) { - if (syncInfoList.contains(sync)) { - syncInfoList.remove(sync); - fireTableRowsDeleted(syncInfoList.indexOf(sync), syncInfoList.indexOf(sync)); - } - } - } - - /** - * Class to render dates with a readable format - * (dd/MM/yy HH:mm:ss) - */ - protected class DateRenderer extends DefaultTableCellRenderer { - - private Date dateValue; - private SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yy HH:mm:ss"); - private String valueToString = ""; - - @Override - public void setValue(Object value) { - if ((value != null)) { - dateValue = (Date) value; - valueToString = dateFormat.format(dateValue); - value = valueToString; - super.setValue(value); - } - } - } - - @Override - public void actionPerformed(ActionEvent actionEvent) { - - String actionCommand = actionEvent.getActionCommand(); - SyncInfo infoToUse; - SyncInfoTableModel model = (SyncInfoTableModel) urlJTable.getModel(); - - if ("addURL".equals(actionCommand)) { - model.add(new SyncInfo("")); - } else if (!task.getSynchronizingInfoList().isEmpty()) { - //first check that a row is selected - if (urlJTable.getSelectedRow() != -1) { - infoToUse = task.getSynchronizingInfo((String) urlJTable.getModel().getValueAt(urlJTable.getSelectedRow(), 0)); - if ("deleteURL".equals(actionCommand)) { - if (!((SyncInfoTableModel) urlJTable.getModel()).syncInfoList.isEmpty()) { - //if the delete button has been clicked, delete the task (if it exists) - if ((task.getSynchronizingInfoList().contains(infoToUse))) { - task.removeSyncInfo(infoToUse); - model.remove(infoToUse); - core.getData().deleteSyncInfo(task, infoToUse); - urlJTable.repaint(); - } else { - errorBox(getResourceMap().getString("deleteErrorMessage"), getResourceMap().getString("deleteErrorTitle")); - } - - } else { - errorBox(getResourceMap().getString("deleteErrorMessage"), getResourceMap().getString("deleteErrorTitle")); - } - - } else if ("testURL".equals(actionCommand)) { - //if the test button has been clicked, test the sync on the URL and returns a message to the user - //tests only if the info exists - if (task.getSynchronizingInfoList().contains(infoToUse)) { - JsonObject testObject = TimerTaskHelper.taskToJsonObject(task, infoToUse, timezone); - int responseCode = TimerTaskSynchronizer.synchronizeTaskOnURL(testObject); - - if (responseCode > 199 && responseCode < 300) { - infoBox(getResourceMap().getString("testSyncSuccessMessage"), getResourceMap().getString("testSyncSuccessTitle")); - Calendar cal = Calendar.getInstance(); - infoToUse.setLastSync(cal.getTime()); - core.getData().syncInfoChanged(task, infoToUse); - } else { - errorBox(getResourceMap().getString("testSyncFailureMessage"), getResourceMap().getString("testSyncFailureTitle")); - } - - if (log.isDebugEnabled()) { - log.debug("Response code : " + responseCode); - } - } - } - } - } else { - if (log.isDebugEnabled()) { - log.debug("Action performed. Action: " + actionCommand); - } - } - } -} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 39ee61a..d82c2d6 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -30,6 +30,7 @@ <Loggers> <Logger name="org.chorem.jtimer" level="info"/> + <Logger name="org.chorem.jtimer.plugin.timebundle" level="debug"/> <Root level="warn"> <AppenderRef ref="Console" /> diff --git a/src/main/resources/org/chorem/jtimer/ui/report/resources/TimerTaskSyncInfoEditor.properties b/src/main/resources/org/chorem/jtimer/plugin/timebundle/resources/TimerSyncEditor.properties similarity index 93% rename from src/main/resources/org/chorem/jtimer/ui/report/resources/TimerTaskSyncInfoEditor.properties rename to src/main/resources/org/chorem/jtimer/plugin/timebundle/resources/TimerSyncEditor.properties index f311f08..b2b24ff 100644 --- a/src/main/resources/org/chorem/jtimer/ui/report/resources/TimerTaskSyncInfoEditor.properties +++ b/src/main/resources/org/chorem/jtimer/plugin/timebundle/resources/TimerSyncEditor.properties @@ -41,6 +41,6 @@ testSyncFailureMessage = Test failed to synchronize, check the URL ? testSyncFailureTitle = Synchronization test failure syncURLHeader = URL -isActiveHeader = is Active -isWithAnnotationsHeader = Annotations +activeHeader = is Active +withAnnotationsHeader = Annotations lastSyncHeader = Last Synchronization \ No newline at end of file diff --git a/src/main/resources/org/chorem/jtimer/ui/report/resources/TimerTaskSyncInfoEditor_fr.properties b/src/main/resources/org/chorem/jtimer/plugin/timebundle/resources/TimerSyncEditor_fr.properties similarity index 93% rename from src/main/resources/org/chorem/jtimer/ui/report/resources/TimerTaskSyncInfoEditor_fr.properties rename to src/main/resources/org/chorem/jtimer/plugin/timebundle/resources/TimerSyncEditor_fr.properties index 3c5ee10..2140def 100644 --- a/src/main/resources/org/chorem/jtimer/ui/report/resources/TimerTaskSyncInfoEditor_fr.properties +++ b/src/main/resources/org/chorem/jtimer/plugin/timebundle/resources/TimerSyncEditor_fr.properties @@ -39,7 +39,7 @@ testSyncSuccessTitle = Test r\u00E9ussi testSyncFailureMessage = Test \u00E9chou\u00E9, v\u00E9rifier l'URL ? testSyncFailureTitle = Echec du test de synchronisation -isActiveSync.Action.text = Synchronisation Active +active.Action.text = Synchronisation Active syncURLHeader = URL isActiveHeader = Active diff --git a/src/main/resources/org/chorem/jtimer/plugin/timebundle/resources/dialog-close.png b/src/main/resources/org/chorem/jtimer/plugin/timebundle/resources/dialog-close.png new file mode 100644 index 0000000..48ef771 Binary files /dev/null and b/src/main/resources/org/chorem/jtimer/plugin/timebundle/resources/dialog-close.png differ diff --git a/src/main/resources/org/chorem/jtimer/resources/JTimer.properties b/src/main/resources/org/chorem/jtimer/resources/JTimer.properties index e841b89..53bf3d7 100644 --- a/src/main/resources/org/chorem/jtimer/resources/JTimer.properties +++ b/src/main/resources/org/chorem/jtimer/resources/JTimer.properties @@ -62,9 +62,9 @@ editTask.Action.text = &Edit Task editTask.Action.accelerator = F2 editTask.Action.shortDescription = Edit task -updateTask.Action.text = &Edit Synchronization Info -updateTask.Action.accelerator = F5 -updateTask.Action.shortDescription = Edit Synchronization Info +editSync.Action.text = &Edit Synchronization +editSync.Action.accelerator = F5 +editSync.Action.shortDescription = Edit Synchronization closeTask.Action.text = &Open/Close Task closeTask.Action.accelerator = control O @@ -171,7 +171,6 @@ input.deleteTaskTitle=Confirm input.deleteTaskMessage=Do you want to delete task "%s" ? input.deleteTasksMessage=Do you want to delete the %d selected tasks ? input.mergeTaskMessage=Do you want to merge selected %d tasks\ninto "%s" ? -input.mergeTaskWithSyncInfoMessage = Do you want to merge selected %d tasks\ninto "%s" ? \nSynchronization Information will be lost ! input.mergeTaskTitle=Merge input.addAnnotationTitle=Add annotation input.addAnnotationMessage=Annotation for task "%s" : @@ -187,6 +186,7 @@ vetoable.common.merge.invalid.types=Can't merge project and task ! vetoable.common.move.invalid.types=Can't move project ! vetoable.saver.empty.name=Empty task name ! vetoable.saver.invalid.characters=Task name contains invalid characters ! +vetoable.timebundle.invalid.sync.list=Can't merge tasks with non matching sync urls ! # Start fail i18n startFail.title=Error diff --git a/src/main/resources/org/chorem/jtimer/resources/JTimer_fr.properties b/src/main/resources/org/chorem/jtimer/resources/JTimer_fr.properties index 0f3aa4b..c34b822 100644 --- a/src/main/resources/org/chorem/jtimer/resources/JTimer_fr.properties +++ b/src/main/resources/org/chorem/jtimer/resources/JTimer_fr.properties @@ -41,9 +41,8 @@ newTask.Action.shortDescription = Cr\u00E9ation d'une nouvelle t\u00E2che editTask.Action.text = \u00C9dition de la t\u00E2ch&e editTask.Action.shortDescription = \u00C9dition de la t\u00E2che - -updateTask.Action.text = &\u00C9dition de la synchronisation -updateTask.Action.shortDescription = Edition de la synchronisation +editSync.Action.text = &\u00C9dition de la synchronisation +editSync.Action.shortDescription = Edition de la synchronisation closeTask.Action.text = &Ouvrir/Fermer la t\u00E2che closeTask.Action.shortDescription = Ouvrir ou fermer la t\u00E2che @@ -135,7 +134,6 @@ input.deleteTaskTitle=Confirmation input.deleteTaskMessage=Voulez-vous supprimer la t\u00E2che "%s" ? input.deleteTasksMessage=Voulez-vous supprimer les %d t\u00E2ches s\u00E9lectionn\u00E9es ? input.mergeTaskMessage=Voulez-vous fusionner les %d t\u00E2ches s\u00E9lectionn\u00E9es\ndans la t\u00E2ches "%s" ? -input.mergeTaskWithSyncInfoMessage = Voulez-vous fusionner les %d t\u00E2ches s\u00E9lectionn\u00E9es\ndans la t\u00E2che "%s" ?\n Les informations de synchronisation seront perdues ! input.mergeTaskTitle=Fusionner input.addAnnotationTitle=Ajouter une annotation @@ -152,6 +150,7 @@ vetoable.common.merge.invalid.types=Impossible de fusionner un projet et une t\u vetoable.common.move.invalid.types=Impossible de d\u00E9placer un projet ! vetoable.saver.empty.name=Le nom est vide ! vetoable.saver.invalid.characters=Le nom contient des caract\u00E8res invalide ! +vetoable.timebundle.invalid.sync.list=Impossible de fusionner des t\u00E2ches n'ayant pas les m\u00EAmes adresses de synchronisation ! # Start fail i18n startFail.title=Erreur diff --git a/src/main/resources/org/chorem/jtimer/ui/resources/TimerTaskEditor.properties b/src/main/resources/org/chorem/jtimer/ui/resources/TimerTaskEditor.properties index 027a244..0f20572 100644 --- a/src/main/resources/org/chorem/jtimer/ui/resources/TimerTaskEditor.properties +++ b/src/main/resources/org/chorem/jtimer/ui/resources/TimerTaskEditor.properties @@ -29,5 +29,5 @@ cancel.Action.shortDescription = Revert changes and quit label.time.text = Time: label.annotation.text = Annotation: -label.syncURL.text = URL: +label.url.text = URL: label.title.text = Title: \ No newline at end of file diff --git a/src/main/resources/org/chorem/jtimer/ui/resources/TimerTaskEditor_fr.properties b/src/main/resources/org/chorem/jtimer/ui/resources/TimerTaskEditor_fr.properties index 9f6b39f..3b2c4e9 100644 --- a/src/main/resources/org/chorem/jtimer/ui/resources/TimerTaskEditor_fr.properties +++ b/src/main/resources/org/chorem/jtimer/ui/resources/TimerTaskEditor_fr.properties @@ -29,5 +29,5 @@ cancel.Action.shortDescription = Annuler et quitter label.time.text = Temps: label.annotation.text = Note: -label.syncURL.text = URL: +label.url.text = URL: label.title.text = Titre: \ No newline at end of file diff --git a/src/site/rst/timebundle.rst b/src/site/rst/timebundle.rst new file mode 100644 index 0000000..7c3ce42 --- /dev/null +++ b/src/site/rst/timebundle.rst @@ -0,0 +1,30 @@ +.. - +.. * #%L +.. * jTimer +.. * %% +.. * Copyright (C) 2016 CodeLutin, Chatellier Eric +.. * %% +.. * This program is free software: you can redistribute it and/or modify +.. * it under the terms of the GNU General Public License as +.. * published by the Free Software Foundation, either version 3 of the +.. * License, or (at your option) any later version. +.. * +.. * This program is distributed in the hope that it will be useful, +.. * but WITHOUT ANY WARRANTY; without even the implied warranty of +.. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.. * GNU General Public License for more details. +.. * +.. * You should have received a copy of the GNU General Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/gpl-3.0.html>. +.. * #L% +.. - + +Time bundle +=========== + +Synchronization with time bundle is currently under developpement and disabled by default. + +To enable it, modify configuration with following line:: + + jtimer.io.synchronizer.class=org.chorem.jtimer.plugin.timebundle.TimeBundleSynchronizer diff --git a/src/site/site.xml b/src/site/site.xml index 5f40b98..00585dd 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -21,9 +21,9 @@ #L% --> <project name="${project.name}" - xmlns="http://maven.apache.org/DECORATION/1.6.0" + xmlns="http://maven.apache.org/DECORATION/1.7.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/DECORATION/1.6.0 http://maven.apache.org/xsd/decoration-1.6.0.xsd"> + xsi:schemaLocation="http://maven.apache.org/DECORATION/1.7.0 http://maven.apache.org/xsd/decoration-1.7.0.xsd"> <custom> <fluidoSkin> @@ -89,6 +89,7 @@ <item name="Swing framework" href="devel/saf.html"/> <item name="File format" href="devel/fileformat.html"/> </item> + <item name="Time bundle" href="timebundle.html"/> </menu> <menu ref="reports"/> diff --git a/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java b/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java index 4cfc75c..551be0c 100644 --- a/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java +++ b/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java @@ -105,6 +105,8 @@ public abstract class AbstractJTimerTest { // copy .svn folders FileUtils.copyDirectory(new File("src/test/resources/testdata"), new File(testDataDirectory, "data"), HiddenFileFilter.VISIBLE); + FileUtils.copyDirectory(new File("src/test/resources/testsync"), + new File(testDataDirectory, "timebundle"), HiddenFileFilter.VISIBLE); // force null, to force new instance JTimerFactory.saver = null; diff --git a/src/test/java/org/chorem/jtimer/entities/TimerTaskHelperTest.java b/src/test/java/org/chorem/jtimer/entities/TimerTaskHelperTest.java index a65d238..88eb68f 100644 --- a/src/test/java/org/chorem/jtimer/entities/TimerTaskHelperTest.java +++ b/src/test/java/org/chorem/jtimer/entities/TimerTaskHelperTest.java @@ -21,18 +21,12 @@ */ package org.chorem.jtimer.entities; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import org.apache.commons.lang3.time.DateUtils; import org.chorem.jtimer.AbstractJTimerTest; import org.testng.Assert; import org.testng.annotations.Test; +import java.util.List; + /** * Test related to TimerTaskHelper. */ @@ -55,56 +49,4 @@ public class TimerTaskHelperTest extends AbstractJTimerTest { Assert.assertEquals(components.get(1), task1); Assert.assertEquals(components.get(2), task2); } - - /** - * Test json production - */ - @Test - public void taskToJSONFormatTest() { - TimerTask task = new TimerTask(); - task.setName("JsonBuilder Test"); - SyncInfo sync = new SyncInfo("http://localhost:3000"); - task.addSyncInfo(sync); - //set the creation date at same date as first timing date (to fit test object) - task.setCreationDate(new Date(1462831200000L)); - sync.setLastSync(new Date(1462831200000L)); - //date : 2016-05-10 - task.setTime(new Date(1462831200000L), 452000L); - //date : 2016-05-12 - task.setTime(new Date(1463004000000L), 4533000L); - - - //create json object - JsonObject objectToHave = new JsonObject(); - JsonArray periodArray = new JsonArray(); - JsonObject periodElement1 = new JsonObject(); - //id is date+_+task.number, here -1 - periodElement1.addProperty("id", "2016-05-10_-1"); - periodElement1.addProperty("startDate", "2016-05-10T00:00:00+01:00"); - periodElement1.addProperty("duration", 452); - periodArray.add(periodElement1); - JsonObject periodElement2 = new JsonObject(); - periodElement2.addProperty("id", "2016-05-12_-1"); - periodElement2.addProperty("startDate", "2016-05-12T00:00:00+01:00"); - periodElement2.addProperty("duration", 4533); - periodArray.add(periodElement2); - - objectToHave.addProperty("URL", "http://localhost:3000"); - //startDate corresponds to lastSync in taksToJsonObject - objectToHave.addProperty("startDate", "2016-05-10T00:00:00+01:00"); - //endDate is nextday (from currenttime) - Date endDate = DateUtils.ceiling(new Date(), Calendar.DAY_OF_MONTH); - String timestamp = "T00:00:00+01:00"; - LocalDate endPeriodDate = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); - String endPeriodString = endPeriodDate.toString() + timestamp; - - objectToHave.addProperty("endDate", endPeriodString); - objectToHave.add("periods", periodArray); - - - //make a list of json objects from the task - JsonObject jsonObject = TimerTaskHelper.taskToJsonObject(task, sync, "+01:00"); - //compare it - Assert.assertEquals(jsonObject, objectToHave); - } } diff --git a/src/test/java/org/chorem/jtimer/entities/TimerTaskTest.java b/src/test/java/org/chorem/jtimer/entities/TimerTaskTest.java index 4d809cb..87cc0e1 100644 --- a/src/test/java/org/chorem/jtimer/entities/TimerTaskTest.java +++ b/src/test/java/org/chorem/jtimer/entities/TimerTaskTest.java @@ -116,46 +116,4 @@ public class TimerTaskTest extends AbstractJTimerTest { Assert.assertEquals(task.getSubTasks().size(), 1); Assert.assertEquals(clonedTask.getSubTasks().size(), 0); } - - /** - * Test that no URL is present twice for a task - */ - @Test - public void addSyncInfoTest() { - TimerTask task = new TimerTask(); - String url1 = "localhost/test"; - String url2 = "localhost/test"; - - task.addSyncInfo(url1); - task.addSyncInfo(url2); - - Assert.assertEquals(task.getSynchronizingInfoList().size(), 1); - - String url3 = "localhost"; - task.addSyncInfo(url3); - - Assert.assertEquals(task.getSynchronizingInfoList().size(), 2); - } - - /** - * Test that removal of URL works ? - */ - @Test - public void removeSyncInfoTest() { - TimerTask task = new TimerTask(); - - SyncInfo info1 = new SyncInfo("localhost/test"); - SyncInfo info2 = new SyncInfo("localhost/other"); - - task.addSyncInfo(info1); - task.addSyncInfo(info2); - - Assert.assertTrue(task.getSynchronizingInfoList().size() == 2); - - task.removeSyncInfo(info1); - - Assert.assertTrue(task.getSynchronizingInfoList().size() == 1); - Assert.assertTrue(task.getSynchronizingInfoList().contains(info2)); - - } } diff --git a/src/test/java/org/chorem/jtimer/io/AbstractSaverTest.java b/src/test/java/org/chorem/jtimer/io/AbstractSaverTest.java index ddc0d96..c387b13 100644 --- a/src/test/java/org/chorem/jtimer/io/AbstractSaverTest.java +++ b/src/test/java/org/chorem/jtimer/io/AbstractSaverTest.java @@ -52,7 +52,7 @@ public class AbstractSaverTest extends AbstractJTimerTest { File file = File.createTempFile("test", ".test"); AbstractSaver saver = new GTimerIncrementalSaver(); - File backupFile = saver.makeBackupFile(file); + File backupFile = BackupUtils.makeBackupFile(file); Assert.assertNotNull(backupFile); Assert.assertTrue(backupFile.isFile()); @@ -69,7 +69,7 @@ public class AbstractSaverTest extends AbstractJTimerTest { FileUtils.writeStringToFile(file, "oldcontent", StandardCharsets.UTF_8); AbstractSaver saver = new GTimerIncrementalSaver(); - File backupFile = saver.makeBackupFile(file); + File backupFile = BackupUtils.makeBackupFile(file); Assert.assertNotNull(backupFile); Assert.assertTrue(backupFile.isFile()); @@ -78,7 +78,7 @@ public class AbstractSaverTest extends AbstractJTimerTest { Assert.assertEquals(FileUtils.readFileToString(file, StandardCharsets.UTF_8), "newcontent"); Assert.assertEquals(FileUtils.readFileToString(backupFile, StandardCharsets.UTF_8), "oldcontent"); - boolean result = saver.restoreBackupFile(backupFile); + boolean result = BackupUtils.restoreBackupFile(backupFile); Assert.assertTrue(result); Assert.assertFalse(backupFile.isFile()); @@ -95,9 +95,9 @@ public class AbstractSaverTest extends AbstractJTimerTest { File file = File.createTempFile("test", ".test"); AbstractSaver saver = new GTimerIncrementalSaver(); - File backupFile = saver.makeBackupFile(file); - - saver.deleteBackupFile(backupFile); + File backupFile = BackupUtils.makeBackupFile(file); + + BackupUtils.deleteBackupFile(backupFile); Assert.assertFalse(backupFile.isFile()); } diff --git a/src/test/java/org/chorem/jtimer/io/GTimerIncrementalSaverTest.java b/src/test/java/org/chorem/jtimer/io/GTimerIncrementalSaverTest.java index 6dc4d8f..a4c8a19 100644 --- a/src/test/java/org/chorem/jtimer/io/GTimerIncrementalSaverTest.java +++ b/src/test/java/org/chorem/jtimer/io/GTimerIncrementalSaverTest.java @@ -36,7 +36,7 @@ import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.io.FileUtils; import org.chorem.jtimer.AbstractJTimerTest; -import org.chorem.jtimer.entities.SyncInfo; +import org.chorem.jtimer.entities.TimerSync; import org.chorem.jtimer.entities.TimerAlert; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; @@ -417,25 +417,4 @@ public class GTimerIncrementalSaverTest extends AbstractJTimerTest { } }*/ } - - /** - * Test that synchronization Info is properly parsed - * @throws IOException - */ - @Test - public void parseSyncInfoTest() throws IOException{ - - GTimerIncrementalSaver gsaver = (GTimerIncrementalSaver) testSaver; - - TimerTask task = new TimerTask(); - task.setNumber(41); - - gsaver.parseSyncInfo(task); - - SyncInfo syncInfo = new SyncInfo("http://localhost:3000"); - - Assert.assertNotNull(task.getSynchronizingInfoList()); - Assert.assertTrue(task.getSynchronizingInfoList().contains(syncInfo)); - - } } diff --git a/src/test/java/org/chorem/jtimer/plugin/timebundle/TImeBundleSaverTest.java b/src/test/java/org/chorem/jtimer/plugin/timebundle/TImeBundleSaverTest.java new file mode 100644 index 0000000..4dac2cf --- /dev/null +++ b/src/test/java/org/chorem/jtimer/plugin/timebundle/TImeBundleSaverTest.java @@ -0,0 +1,45 @@ +package org.chorem.jtimer.plugin.timebundle; + +import org.chorem.jtimer.AbstractJTimerTest; +import org.chorem.jtimer.entities.TimerProject; +import org.chorem.jtimer.entities.TimerTask; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.Collections; + +/** + * Created by chatellier on 02/08/16. + */ +public class TImeBundleSaverTest extends AbstractJTimerTest { + + protected TimeBundleSaver saver; + + @BeforeMethod + @Override + public void beforeTest() throws IOException { + super.beforeTest(); + saver = new TimeBundleSaver(); + } + + /** + * Test that synchronization Info is properly parsed + * @throws IOException + */ + @Test + public void parseSyncInfoTest() throws IOException{ + + TimerTask task = new TimerTask(); + task.setNumber(41); + TimerProject project = new TimerProject(); + project.setNumber(1); + project.addTask(task); + saver.dataLoaded(Collections.singletonList(project)); + + Assert.assertNotNull(task.getSyncs()); + Assert.assertEquals(task.getSyncs().get(0).getUrl(), "http://localhost:3000"); + + } +} diff --git a/src/test/java/org/chorem/jtimer/entities/TimerTaskHelperTest.java b/src/test/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelperTest.java similarity index 78% copy from src/test/java/org/chorem/jtimer/entities/TimerTaskHelperTest.java copy to src/test/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelperTest.java index a65d238..3bdb517 100644 --- a/src/test/java/org/chorem/jtimer/entities/TimerTaskHelperTest.java +++ b/src/test/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelperTest.java @@ -19,42 +19,26 @@ * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ -package org.chorem.jtimer.entities; +package org.chorem.jtimer.plugin.timebundle; import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.Calendar; -import java.util.Date; -import java.util.List; import org.apache.commons.lang3.time.DateUtils; import org.chorem.jtimer.AbstractJTimerTest; +import org.chorem.jtimer.entities.TimerSync; +import org.chorem.jtimer.entities.TimerTask; import org.testng.Assert; import org.testng.annotations.Test; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Calendar; +import java.util.Date; + /** - * Test related to TimerTaskHelper. + * Test related to TimeBundleHelper. */ -public class TimerTaskHelperTest extends AbstractJTimerTest { - - /** - * Test get path from parent. - */ - @Test - public void getPathFromParentTest() { - TimerProject project = new TimerProject(); - TimerTask task1 = new TimerTask(); - TimerTask task2 = new TimerTask(); - - project.addTask(task1); - task1.addTask(task2); - - List<TimerTask> components = TimerTaskHelper.getPathFromParent(task2); - Assert.assertEquals(components.get(0), project); - Assert.assertEquals(components.get(1), task1); - Assert.assertEquals(components.get(2), task2); - } +public class TimeBundleHelperTest extends AbstractJTimerTest { /** * Test json production @@ -63,8 +47,8 @@ public class TimerTaskHelperTest extends AbstractJTimerTest { public void taskToJSONFormatTest() { TimerTask task = new TimerTask(); task.setName("JsonBuilder Test"); - SyncInfo sync = new SyncInfo("http://localhost:3000"); - task.addSyncInfo(sync); + TimerSync sync = new TimerSync("http://localhost:3000"); + task.addSync(sync); //set the creation date at same date as first timing date (to fit test object) task.setCreationDate(new Date(1462831200000L)); sync.setLastSync(new Date(1462831200000L)); @@ -103,7 +87,7 @@ public class TimerTaskHelperTest extends AbstractJTimerTest { //make a list of json objects from the task - JsonObject jsonObject = TimerTaskHelper.taskToJsonObject(task, sync, "+01:00"); + JsonObject jsonObject = TimeBundleHelper.taskToJsonObject(task, sync); //compare it Assert.assertEquals(jsonObject, objectToHave); } diff --git a/src/test/resources/testdata/41.task.sync b/src/test/resources/testdata/41.task.sync deleted file mode 100644 index 759598c..0000000 --- a/src/test/resources/testdata/41.task.sync +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "syncURL": "http://localhost:3000", - "lastSync": "Jan 1, 1970 1:00:00 AM", - "isActiveSync": true, - "isWithAnnotations": false - } -] \ No newline at end of file diff --git a/src/test/resources/testsync/41.task.sync b/src/test/resources/testsync/41.task.sync new file mode 100644 index 0000000..3a085bf --- /dev/null +++ b/src/test/resources/testsync/41.task.sync @@ -0,0 +1,8 @@ +[ + { + "url": "http://localhost:3000", + "lastSync": "Jan 1, 1970 1:00:00 AM", + "active": true, + "withAnnotations": false + } +] \ No newline at end of file -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
participants (1)
-
chorem.org scm