branch sync-timebundle created (now 44c6e82)
This is an automated email from the git hooks/post-receive script. New change to branch sync-timebundle in repository jtimer. See https://gitlab.nuiton.org/chorem/jtimer.git at 44c6e82 Remove default input This branch includes the following new commits: new ad79169 Add new version of timebundle plugin new fa131fd Refactoring of checkbox cell renderer and editor new 534767e Manage sub task times and project total sync new 5c38397 Add reload project option. Add doc new 44c6e82 Remove default input The 5 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 44c6e82a7cb6d5f74b1c9ef3b0ec195b9457228c Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Mar 7 17:03:46 2017 +0100 Remove default input commit 5c38397c9a701f39e6057f8645533205225dbf83 Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Mar 7 16:59:27 2017 +0100 Add reload project option. Add doc commit 534767e281d7db20e262cd1da83dbf470fb4d525 Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Mar 7 15:34:45 2017 +0100 Manage sub task times and project total sync commit fa131fdf539352df0e7ebf2f85d9e5332c58acd8 Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Mar 7 14:10:03 2017 +0100 Refactoring of checkbox cell renderer and editor commit ad7916980ef9c2ce5a654ce4ab644de3f59ddc91 Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Mar 7 11:52:59 2017 +0100 Add new version of timebundle 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 sync-timebundle in repository jtimer. See https://gitlab.nuiton.org/chorem/jtimer.git commit ad7916980ef9c2ce5a654ce4ab644de3f59ddc91 Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Mar 7 11:52:59 2017 +0100 Add new version of timebundle plugin --- pom.xml | 7 +- src/main/java/org/chorem/jtimer/JTimer.java | 106 ++++----- src/main/java/org/chorem/jtimer/JTimerFactory.java | 35 --- .../{data/TimerCore.java => JTimerService.java} | 76 +++--- .../org/chorem/jtimer/data/TimerDataManager.java | 8 +- .../java/org/chorem/jtimer/entities/TimerTask.java | 25 +- .../jtimer/plugin/timebundle/TimeBundleHelper.java | 136 ----------- .../jtimer/plugin/timebundle/TimeBundleSaver.java | 219 ------------------ .../plugin/timebundle/TimeBundleSynchronizer.java | 171 -------------- .../plugin/timebundle/TimeBundleVetoable.java | 56 ----- .../plugin/timebundle/TimerSyncCellRenderer.java | 46 ---- .../jtimer/plugin/timebundle/TimerSyncEditor.java | 245 -------------------- .../plugin/timebundle/TimerSyncTableModel.java | 203 ---------------- .../java/org/chorem/jtimer/plugins/Plugin.java | 30 +++ .../jtimer/plugins/timebundle/Constants.java | 27 +++ .../plugins/timebundle/LocalSynchronizer.java | 30 +++ .../plugins/timebundle/RemoteSynchronizer.java | 257 +++++++++++++++++++++ .../jtimer/plugins/timebundle/SyncIOSaver.java | 211 +++++++++++++++++ .../chorem/jtimer/plugins/timebundle/SyncView.java | 207 +++++++++++++++++ .../jtimer/plugins/timebundle/Synchronizer.java | 27 +++ .../plugins/timebundle/TimeBundlePlugin.java | 131 +++++++++++ .../plugins/timebundle/data/RemoteProject.java | 27 +++ .../jtimer/plugins/timebundle/data/RemoteTask.java | 85 +++++++ .../timebundle/model/RemoteTaskTreeModel.java | 121 ++++++++++ .../timebundle/model/RemoveTaskCellRenderer.java | 42 ++++ .../java/org/chorem/jtimer/ui/NewTaskView.java | 10 +- .../java/org/chorem/jtimer/ui/TimerTaskEditor.java | 14 +- .../java/org/chorem/jtimer/ui/TimerUIManager.java | 50 ++++ .../chorem/jtimer/ui/UserInterfaceListener.java | 37 +++ .../org/chorem/jtimer/ui/report/ReportView.java | 24 +- .../org/chorem/jtimer/ui/tasks/IdleDialog.java | 20 +- .../chorem/jtimer/ui/tasks/RefreshTreeTask.java | 10 +- .../org/chorem/jtimer/ui/tasks/RunTaskJob.java | 2 +- .../jtimer/ui/tree/CheckBoxTreeCellComponent.java | 28 ++- .../jtimer/ui/tree/CheckBoxTreeCellEditor.java | 22 +- .../jtimer/ui/tree/CheckBoxTreeCellRenderer.java | 13 +- .../org/chorem/jtimer/ui/tree/TaskTreeModel.java | 31 +-- .../jtimer/ui/treetable/ProjectsAndTasksModel.java | 8 +- .../ProjectsAndTasksRunningCellRenderer.java | 8 +- .../jtimer/ui/treetable/ProjectsAndTasksTable.java | 14 +- .../services/org.chorem.jtimer.plugins.Plugin | 1 + src/main/resources/log4j2.xml | 2 +- .../ui/report/resources/ReportView.properties | 1 - .../ui/report/resources/ReportView_fr.properties | 1 - .../java/org/chorem/jtimer/AbstractJTimerTest.java | 2 - .../org/chorem/jtimer/data/CommonVetoableTest.java | 49 ++-- .../chorem/jtimer/data/TimerDataManagerTest.java | 199 ++++++++-------- .../plugin/timebundle/TimeBundleHelperTest.java | 87 ------- .../plugin/timebundle/TimeBundleSaverTest.java | 66 ------ src/test/resources/testsync/41.task.sync | 8 - 50 files changed, 1622 insertions(+), 1613 deletions(-) diff --git a/pom.xml b/pom.xml index 08467bd..77060af 100644 --- a/pom.xml +++ b/pom.xml @@ -197,6 +197,11 @@ <version>2.8.0</version> </dependency> <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20160810</version> + </dependency> + <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.5</version> @@ -216,7 +221,7 @@ <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> - <version>6.10</version> + <version>6.11</version> <scope>test</scope> <exclusions> <exclusion> diff --git a/src/main/java/org/chorem/jtimer/JTimer.java b/src/main/java/org/chorem/jtimer/JTimer.java index f711d12..96ec4b8 100644 --- a/src/main/java/org/chorem/jtimer/JTimer.java +++ b/src/main/java/org/chorem/jtimer/JTimer.java @@ -23,15 +23,12 @@ package org.chorem.jtimer; 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; import org.chorem.jtimer.data.DataViolationException; -import org.chorem.jtimer.data.TimerCore; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; -import org.chorem.jtimer.plugin.timebundle.TimerSyncEditor; import org.chorem.jtimer.system.SystemInfo; import org.chorem.jtimer.system.SystemInfoFactory; import org.chorem.jtimer.ui.HelpFrame; @@ -106,8 +103,8 @@ public class JTimer extends SingleFrameApplication implements /** log. */ private static Log log = LogFactory.getLog(JTimer.class); - /** Timer core controller. */ - protected TimerCore core; + /** Timer service controller. */ + protected JTimerService service; /** Tree structure. */ protected ProjectsAndTasksTable projectsAndTasksTable; @@ -195,14 +192,14 @@ public class JTimer extends SingleFrameApplication implements // fix start in iconified mode ctxt.getSessionStorage().putProperty(JFrame.class, new WindowProperty2()); - // init timercore - core = new TimerCore(); + // init timerservice + service = new JTimerService(); // Systray mgr systrayManager = new SystrayManager(this); - core.getData().addDataEventListener(systrayManager); + service.getData().addDataEventListener(systrayManager); - IdleDialog.init(this, core); + IdleDialog.init(this, service); } /** @@ -255,6 +252,9 @@ public class JTimer extends SingleFrameApplication implements // set Menu Bar getMainFrame().setJMenuBar(createMenuBar()); + // post UI init + service.getUi().initUI(this); + // show main panel components show(createMainComponent()); @@ -281,9 +281,9 @@ public class JTimer extends SingleFrameApplication implements panel.add(scrollPaneProjectTreeTable, BorderLayout.CENTER); // status bar bottom - StatusBar sb = new StatusBar(this, core.getData()); + StatusBar sb = new StatusBar(this, service.getData()); // status bar ui will be notified from events - core.getData().addDataEventListener(sb); + service.getData().addDataEventListener(sb); panel.add(sb, BorderLayout.SOUTH); // taille par defaut au premier lancement de l'application @@ -300,7 +300,7 @@ public class JTimer extends SingleFrameApplication implements */ protected ProjectsAndTasksTable createTreeTable() { - projectsAndTasksTable = new ProjectsAndTasksTable(this, core); + projectsAndTasksTable = new ProjectsAndTasksTable(this, service); // name used in properties files projectsAndTasksTable.setName("projectslist"); @@ -366,9 +366,6 @@ 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", "closeTask", @@ -376,16 +373,13 @@ public class JTimer extends SingleFrameApplication implements "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"}; menuBar.add(createMenu("reportMenu", reportMenuActionNames)); - JMenu optionmMenu = createOptionMenu(); - menuBar.add(optionmMenu); + JMenu optionMenu = createOptionMenu(); + menuBar.add(optionMenu); String[] helpMenuActionNames = {"about"}; menuBar.add(createMenu("helpMenu", helpMenuActionNames)); @@ -488,8 +482,8 @@ public class JTimer extends SingleFrameApplication implements @Override protected void ready() { - // init core, load list, synchronization, etc... - boolean init = core.init(); + // init service, load list, synchronization, etc... + boolean init = service.init(); if (init) { // schedule tree refresh at midnight @@ -520,7 +514,7 @@ public class JTimer extends SingleFrameApplication implements @Override protected void shutdown() { log.debug("Shutdown called"); - core.exit(); + service.exit(); // save context // super, sauve le context des fenetres, etc... @@ -533,7 +527,7 @@ public class JTimer extends SingleFrameApplication implements protected void scheduleTreeRefresh() { // task used to refresh tree - java.util.TimerTask refreshTreeTask = new RefreshTreeTask(core); + java.util.TimerTask refreshTreeTask = new RefreshTreeTask(service); Timer timer = new Timer(); @@ -595,7 +589,7 @@ public class JTimer extends SingleFrameApplication implements p.setCreationDate(new Date()); try { - core.getData().addProject(p); + service.getData().addProject(p); } catch (DataViolationException e) { displayErrorMessage(e.getExceptionKey()); } @@ -623,7 +617,7 @@ public class JTimer extends SingleFrameApplication implements newProjectName = newProjectName.trim(); try { - core.getData().editProject(project, newProjectName); + service.getData().editProject(project, newProjectName); } catch (DataViolationException e) { displayErrorMessage(e.getExceptionKey()); } @@ -641,7 +635,7 @@ public class JTimer extends SingleFrameApplication implements // select task to add new task TimerTask selectedTask = projectsAndTasksTable.getSelectedElements().get(0); - NewTaskView newTaskPanel = new NewTaskView(this, core, selectedTask); + NewTaskView newTaskPanel = new NewTaskView(this, service, selectedTask); show(newTaskPanel); } @@ -664,23 +658,11 @@ public class JTimer extends SingleFrameApplication implements TimerTask task = projectsAndTasksTable.getSelectedTasks().get(0); - TimerTaskEditor editor = new TimerTaskEditor(this, task, core); + TimerTaskEditor editor = new TimerTaskEditor(this, task, service); show(editor); } /** - * Update task. - * Enabled when a task is selected - */ - @Action(enabledProperty = "selectedSingleElement") - public void editSync() { - TimerTask task = projectsAndTasksTable.getSelectedElements().get(0); - - TimerSyncEditor updater = new TimerSyncEditor(this, core, task); - show(updater); - } - - /** * Start selected task in tree. * * If it not already been running @@ -695,8 +677,8 @@ public class JTimer extends SingleFrameApplication implements // can't be null TimerTask task = projectsAndTasksTable.getSelectedTasks().get(0); - RunTaskJob jobToRun = new RunTaskJob(this, task, core.getData()); - core.getData().startTask(task); + RunTaskJob jobToRun = new RunTaskJob(this, task, service.getData()); + service.getData().startTask(task); return jobToRun; } @@ -707,11 +689,11 @@ public class JTimer extends SingleFrameApplication implements * @param taskPath task path to start (from root to task) */ public void startTask(String taskPath) { - TimerTask task = core.getData().getTaskForPath(taskPath); + TimerTask task = service.getData().getTaskForPath(taskPath); if (task != null) { - RunTaskJob jobToRun = new RunTaskJob(this, task, core.getData()); + RunTaskJob jobToRun = new RunTaskJob(this, task, service.getData()); getContext().getTaskService().execute(jobToRun); - core.getData().startTask(task); + service.getData().startTask(task); } else { if (log.isWarnEnabled()) { log.warn("Can't find task '" + taskPath + "'"); @@ -777,7 +759,7 @@ public class JTimer extends SingleFrameApplication implements // test if task is already running if (rtt != null) { rtt.wantToStop(); - core.getData().stopTask(task); + service.getData().stopTask(task); // re-enable/disable buttons setSelectedSingleRunningTask(false); @@ -805,7 +787,7 @@ public class JTimer extends SingleFrameApplication implements public void closeProject() { TimerProject project = projectsAndTasksTable.getSelectedProjects().get(0); - core.getData().changeProjectCloseState(project); + service.getData().changeProjectCloseState(project); } /** @@ -815,7 +797,7 @@ public class JTimer extends SingleFrameApplication implements public void closeTask() { TimerTask task = projectsAndTasksTable.getSelectedTasks().get(0); - core.getData().changeTaskCloseState(task); + service.getData().changeTaskCloseState(task); } /** @@ -843,7 +825,7 @@ public class JTimer extends SingleFrameApplication implements if (confirm == JOptionPane.YES_OPTION) { // approved for (TimerProject project : projects) { try { - core.getData().deleteProject(project); + service.getData().deleteProject(project); } catch (DataViolationException e) { displayErrorMessage(e.getExceptionKey()); } @@ -878,7 +860,7 @@ public class JTimer extends SingleFrameApplication implements for (TimerTask task : tasks) { try { stopTask(task); - core.getData().deleteTask(task); + service.getData().deleteTask(task); } catch (DataViolationException e) { displayErrorMessage(e.getExceptionKey()); } @@ -1035,11 +1017,11 @@ public class JTimer extends SingleFrameApplication implements try { // check if + negative increment still positive if (newTodayTime > 0) { - core.getData().changeTaskTime(selectedTask, now, + service.getData().changeTaskTime(selectedTask, now, newTodayTime); } else { // force to 0 - core.getData().changeTaskTime(selectedTask, now, 0L); + service.getData().changeTaskTime(selectedTask, now, 0L); } } catch (DataViolationException e) { displayErrorMessage(e.getExceptionKey()); @@ -1069,7 +1051,7 @@ public class JTimer extends SingleFrameApplication implements resourceMap.getString("input.mergeTaskTitle"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (confirm == JOptionPane.YES_OPTION) { try { - core.getData().mergeTasks(destinationTask, otherTasks); + service.getData().mergeTasks(destinationTask, otherTasks); } catch (DataViolationException e) { displayErrorMessage(e.getExceptionKey()); } @@ -1096,7 +1078,7 @@ public class JTimer extends SingleFrameApplication implements annotation = annotation.trim(); try { - core.getData().addAnnotation(selectedTask, new Date(), + service.getData().addAnnotation(selectedTask, new Date(), annotation); } catch (DataViolationException e) { displayErrorMessage(e.getExceptionKey()); @@ -1113,7 +1095,7 @@ public class JTimer extends SingleFrameApplication implements TimerTask selectedTask = projectsAndTasksTable.getSelectedTasks() .get(0); - AlertEditor alertEditor = new AlertEditor(this, core.getData(), + AlertEditor alertEditor = new AlertEditor(this, service.getData(), selectedTask); show(alertEditor); } @@ -1123,7 +1105,7 @@ public class JTimer extends SingleFrameApplication implements */ @Action public void makeReport() { - ReportView view = new ReportView(this, core); + ReportView view = new ReportView(this, service); show(view); } @@ -1334,6 +1316,10 @@ public class JTimer extends SingleFrameApplication implements firePropertyChange("selectedProjects", oldValue, selectedProjects); } + public List<TimerProject> getSelectedProject() { + return projectsAndTasksTable.getSelectedProjects(); + } + /** * Is selected task property. * @@ -1498,9 +1484,6 @@ 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()) { @@ -1509,9 +1492,6 @@ public class JTimer extends SingleFrameApplication implements "---", "addAnnotation", "editAlert", "increment1Task", "increment5Task", "increment30Task", "decrement1Task", "decrement5Task", "decrement30Task", "setToZero"}; - if (JTimerFactory.getSynchronizer() != null) { - actionNames = ArrayUtils.add(actionNames, 4, "editSync"); - } } if (isSelectedMultiplesTasks()) { @@ -1522,6 +1502,8 @@ public class JTimer extends SingleFrameApplication implements if (actionNames != null) { addActionToMenu(menu, actionNames); + service.getUi().initTreePopupMenu(menu); + menu.show(e.getComponent(), e.getX(), e.getY()); } } diff --git a/src/main/java/org/chorem/jtimer/JTimerFactory.java b/src/main/java/org/chorem/jtimer/JTimerFactory.java index dc2953e..6fc72ac 100644 --- a/src/main/java/org/chorem/jtimer/JTimerFactory.java +++ b/src/main/java/org/chorem/jtimer/JTimerFactory.java @@ -25,7 +25,6 @@ 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.plugin.timebundle.TimeBundleSynchronizer; /** * JTimer config class. @@ -44,9 +43,6 @@ public class JTimerFactory { /** Saver */ protected static Saver saver; - /** Synchronizer */ - protected static TimeBundleSynchronizer synchronizer; - /** * Constructeur. */ @@ -93,35 +89,4 @@ public class JTimerFactory { return saver; } - /** - * Get synchronizer manager - */ - public static TimeBundleSynchronizer getSynchronizer() { - if (synchronizer == null) { - - Class<?> synchronizerClass = JTimer.config.getIOSynchronizerClass(); - - if (synchronizerClass != null) { - // log - if (log.isInfoEnabled()) { - log.info("Using synchronizer class : " + synchronizerClass); - } - - 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); - } - } - } - } - return synchronizer; - } } diff --git a/src/main/java/org/chorem/jtimer/data/TimerCore.java b/src/main/java/org/chorem/jtimer/JTimerService.java similarity index 75% rename from src/main/java/org/chorem/jtimer/data/TimerCore.java rename to src/main/java/org/chorem/jtimer/JTimerService.java index ae4a4cc..342fb92 100644 --- a/src/main/java/org/chorem/jtimer/data/TimerCore.java +++ b/src/main/java/org/chorem/jtimer/JTimerService.java @@ -2,7 +2,7 @@ * #%L * jTimer * %% - * Copyright (C) 2007 - 2016 CodeLutin + * Copyright (C) 2007 - 2017 CodeLutin * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as @@ -20,19 +20,19 @@ * #L% */ -package org.chorem.jtimer.data; +package org.chorem.jtimer; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.chorem.jtimer.JTimer; -import org.chorem.jtimer.JTimerFactory; +import org.chorem.jtimer.data.CommonVetoable; +import org.chorem.jtimer.data.TimerDataManager; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.io.DataLockingException; import org.chorem.jtimer.io.Saver; -import org.chorem.jtimer.plugin.timebundle.TimeBundleSaver; -import org.chorem.jtimer.plugin.timebundle.TimeBundleSynchronizer; -import org.chorem.jtimer.plugin.timebundle.TimeBundleVetoable; +import org.chorem.jtimer.plugins.Plugin; +import org.chorem.jtimer.ui.TimerUIManager; import java.io.File; import java.io.IOException; @@ -40,9 +40,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.ServiceLoader; /** - * TimerCore + * JTimerService * * @author chatellier * @version $Revision$ @@ -50,24 +51,27 @@ import java.util.List; * Last update : $Date$ * By : $Author$ */ -public class TimerCore { +public class JTimerService { /** log. */ - private static Log log = LogFactory.getLog(TimerCore.class); + private static Log log = LogFactory.getLog(JTimerService.class); - /** Timer data. */ + /** Data Manager. */ protected TimerDataManager data; + /** UI Manager. */ + protected TimerUIManager ui; + /** saver io controller. */ protected Saver saver; - /** sync */ - protected TimeBundleSynchronizer synchronizer; + /** Plugin activated by configuration. */ + protected List<Plugin> activePlugins = new ArrayList<>(); /** * Constructor. */ - public TimerCore() { + public JTimerService() { // migrate if necessary try { @@ -78,8 +82,9 @@ public class TimerCore { } } - // init data + // init managers data = new TimerDataManager(); + ui = new TimerUIManager(); // add commmon vetoable CommonVetoable commonVetoable = new CommonVetoable(data); @@ -92,15 +97,21 @@ public class TimerCore { data.addDataEventListener(saver); } - //init sync - synchronizer = JTimerFactory.getSynchronizer(); - if (synchronizer != null) { - TimeBundleSaver saver = new TimeBundleSaver(); - data.addVetoableDataEventListener(new TimeBundleVetoable()); - data.addDataEventListener(saver); - data.addDataEventListener(synchronizer); - synchronizer.setSaver(saver); - } + ServiceLoader<Plugin> load = ServiceLoader.load(Plugin.class); + load.forEach(plugin -> { + // extract package name + String pluginPackage = plugin.getClass().getPackage().getName(); + pluginPackage = StringUtils.remove(pluginPackage, Plugin.class.getPackage().getName() + "."); + String pluginName = StringUtils.substringBefore(pluginPackage, "."); + + // loading + if (log.isInfoEnabled()) { + log.info("Loading plugin : " + pluginName); + } + activePlugins.add(plugin); + }); + + activePlugins.forEach(p -> p.register(this)); } /** @@ -149,7 +160,7 @@ public class TimerCore { // log if (log.isInfoEnabled()) { - log.info("Init core"); + log.info("Init service"); } try { @@ -169,18 +180,27 @@ public class TimerCore { } /** - * Get data. + * Get data manager. * - * @return timer data + * @return data manager */ public TimerDataManager getData() { return data; } /** + * Get UI manager. + * + * @return ui manager + */ + public TimerUIManager getUi() { + return ui; + } + + /** * Load project list from. */ - protected void load() { + public void load() { // log if (log.isInfoEnabled()) { diff --git a/src/main/java/org/chorem/jtimer/data/TimerDataManager.java b/src/main/java/org/chorem/jtimer/data/TimerDataManager.java index 79013d5..96ffd3c 100644 --- a/src/main/java/org/chorem/jtimer/data/TimerDataManager.java +++ b/src/main/java/org/chorem/jtimer/data/TimerDataManager.java @@ -2,7 +2,7 @@ * #%L * jTimer * %% - * Copyright (C) 2007 - 2016 CodeLutin + * Copyright (C) 2007 - 2017 CodeLutin * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as @@ -99,8 +99,7 @@ public class TimerDataManager { * * @param listener listener */ - public void addVetoableDataEventListener( - VetoableDataEventListener listener) { + public void addVetoableDataEventListener(VetoableDataEventListener listener) { vetoableDataEventListeners.add(listener); } @@ -109,8 +108,7 @@ public class TimerDataManager { * * @param listener listener */ - public void removeVetoableDataEventListener( - VetoableDataEventListener listener) { + public void removeVetoableDataEventListener(VetoableDataEventListener listener) { vetoableDataEventListeners.remove(listener); } diff --git a/src/main/java/org/chorem/jtimer/entities/TimerTask.java b/src/main/java/org/chorem/jtimer/entities/TimerTask.java index 19175ea..c761188 100644 --- a/src/main/java/org/chorem/jtimer/entities/TimerTask.java +++ b/src/main/java/org/chorem/jtimer/entities/TimerTask.java @@ -2,7 +2,7 @@ * #%L * jTimer * %% - * Copyright (C) 2007 - 2016 CodeLutin + * Copyright (C) 2007 - 2017 CodeLutin * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as @@ -27,7 +27,9 @@ import org.chorem.jtimer.utils.DailySortedMap; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import java.util.UUID; @@ -88,10 +90,8 @@ public class TimerTask implements Cloneable, */ protected List<TimerAlert> alerts; - /** - * Synchronization Info - */ - protected List<TimerSync> syncs; + /** Meta info for plugins. */ + protected Map<String, Object> meta = new HashMap<>(); /** * Constructor. @@ -102,7 +102,6 @@ public class TimerTask implements Cloneable, allDaysAnnotations = new TreeMap<>(); subTasks = new ArrayList<>(); alerts = new ArrayList<>(); - syncs = new ArrayList<>(); // wrong value to detect bug number = -1; } @@ -321,16 +320,8 @@ 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; + public Map<String, Object> getMeta() { + return meta; } @Override @@ -375,7 +366,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); + task.meta = new HashMap<>(meta); } catch (CloneNotSupportedException e) { throw new RuntimeException("Can't clone", e); } diff --git a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelper.java b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelper.java deleted file mode 100644 index b8baaaf..0000000 --- a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelper.java +++ /dev/null @@ -1,136 +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.plugin.timebundle; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import org.apache.commons.lang3.time.DateFormatUtils; -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.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 { - - protected static final String DATE_MIDNIGHT_PATTERN = "yyyy-MM-dd'T'00:00:00XXX"; - - /** - * 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 startPeriodString = DateFormatUtils.format(startDate, DATE_MIDNIGHT_PATTERN); - String endPeriodString = DateFormatUtils.format(endDate, DATE_MIDNIGHT_PATTERN); - - //get the times of the task - periodArray.addAll(getTimesAsJsonArray(task, startDate, endDate, sync.isWithAnnotations())); - //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())); - } - } - } - 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 - * @return a JsonArray of the periods - */ - public static JsonArray getTimesAsJsonArray(TimerTask task, Date startDate, Date endDate, boolean withAnnotations) { - 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 - String dateString = DateFormatUtils.format(entry.getKey(), DATE_MIDNIGHT_PATTERN); - //as jtimer has time entries only for a day, the id of the times will be the date in yyyy-mm-dd format - String idString = DateFormatUtils.format(entry.getKey(), "yyyy-MM-dd"); - JsonObject periodElement = new JsonObject(); - periodElement.addProperty("id", idString + "_" + task.getNumber()); - periodElement.addProperty("startDate", dateString); - //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 deleted file mode 100644 index f2f0fa4..0000000 --- a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSaver.java +++ /dev/null @@ -1,219 +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.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.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 (task.getSyncs() == null || task.getSyncs().isEmpty()) { - 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 deleted file mode 100644 index e42f59c..0000000 --- a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSynchronizer.java +++ /dev/null @@ -1,171 +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.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; - - protected TimeBundleSaver saver; - - /** - * 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); - boolean resultOk = synchronizeTaskOnURL(syncObject); - if (resultOk) { - timerSync.setLastSync(new Date()); - if (timerTask instanceof TimerProject) { - saver.modifyProject(TimerProject.class.cast(timerTask)); - } else { - saver.modifyTask(timerTask); - } - } - } - }); - } - - /** - * 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 boolean synchronizeTaskOnURL(JsonObject object) { - boolean result = false; - // 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); - int upDateValue = connection.getResponseCode(); - if (upDateValue >= 200 && upDateValue < 300) { - result = true; - } - } catch (IOException ex) { - if (log.isErrorEnabled()) { - log.error("Problem with the connection " + syncURL, ex); - } - } - } - return result; - } - - /** - * 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); - } - } - } - - /** - * Set saver. - * @param saver - * @deprecated since 1.5.1 this is ugly, need a proper centralized way to save a task modified by current class - */ - @Deprecated - public void setSaver(TimeBundleSaver saver) { - this.saver = saver; - } -} diff --git a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleVetoable.java b/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleVetoable.java deleted file mode 100644 index b911ed8..0000000 --- a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimeBundleVetoable.java +++ /dev/null @@ -1,56 +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.plugin.timebundle; - -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 { - - /** - * 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 deleted file mode 100644 index a9e124c..0000000 --- a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncCellRenderer.java +++ /dev/null @@ -1,46 +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.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 deleted file mode 100644 index 5740577..0000000 --- a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncEditor.java +++ /dev/null @@ -1,245 +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.plugin.timebundle; - -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; - -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 java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -/** - * 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 deleted file mode 100644 index d38f80a..0000000 --- a/src/main/java/org/chorem/jtimer/plugin/timebundle/TimerSyncTableModel.java +++ /dev/null @@ -1,203 +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.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/plugins/Plugin.java b/src/main/java/org/chorem/jtimer/plugins/Plugin.java new file mode 100644 index 0000000..e57e300 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/Plugin.java @@ -0,0 +1,30 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins; + +import org.chorem.jtimer.JTimerService; + +public abstract class Plugin { + + public abstract void register(JTimerService JTimerService); + +} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/Constants.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/Constants.java new file mode 100644 index 0000000..cabdf09 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/Constants.java @@ -0,0 +1,27 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle; + +public interface Constants { + String META = "timebundle"; +} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/LocalSynchronizer.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/LocalSynchronizer.java new file mode 100644 index 0000000..b70c2f9 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/LocalSynchronizer.java @@ -0,0 +1,30 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle; + +/** + * Synchroniser that fallback to local cache in case remote call are not working. + */ +public class LocalSynchronizer implements Synchronizer { + +} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java new file mode 100644 index 0000000..1e85162 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java @@ -0,0 +1,257 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.entities.TimerProject; +import org.chorem.jtimer.entities.TimerTask; +import org.chorem.jtimer.plugins.timebundle.data.RemoteProject; +import org.chorem.jtimer.plugins.timebundle.data.RemoteTask; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class RemoteSynchronizer implements Synchronizer { + + protected static Log log = LogFactory.getLog(RemoteSynchronizer.class); + + protected static final String DATE_MIDNIGHT_PATTERN = "yyyy-MM-dd'T'00:00:00XXX"; + + /** + * Fetch project from url such as : + * http://localhost:8080/api/v1/codelutin/contribute/a7e14ea2-fde7-4e80-aff3-03... + * + * As parse content: <pre> + * { + * project: { + * oid:"#49:0", + * parentTaskUuid:null, + * pathToProjectUuid:null, + * uuid:"01d57d63-dcd3-4a82-87e4-6698312c75c6", + * name:"jTimer", + * info:"", + * duration:0, + * startDate:"2017-03-02T13:14:09+01:00", + * endDate:null, + * contributionValidation:false, + * isPublic:false, + * openToContribution:false, + * logo:"" + * }, + * email:"chatellier@codelutin.com", + * tasks: { + * http://localhost:8080/api/v1/codelutin/contribute/a7e14ea2-fde7-4e80-aff3-03...: { + * oid:"#42:0", + * parentTaskUuid:null, + * pathToProjectUuid:null, + * uuid:"75ba149e-ac2b-470f-a44f-93d2a9bfe065", + * name:"service", + * info:"", + * duration:0, + * startDate:null, + * endDate:null, + * contributionValidation:false + * }, + * http://localhost:8080/api/v1/codelutin/contribute/a7e14ea2-fde7-4e80-aff3-03...: { + * oid:"#41:0", + * parentTaskUuid:null, + * pathToProjectUuid:null, + * uuid:"7ffb0a59-960f-483a-bd5b-1cde56aacb6d", + * name:"UI", + * info:"", + * duration:0, + * startDate:null, + * endDate:null, + * contributionValidation:false + * }, + * http://localhost:8080/api/v1/codelutin/contribute/a7e14ea2-fde7-4e80-aff3-03...: { + * oid:"#41:1", + * parentTaskUuid:null, + * pathToProjectUuid:null, + * uuid:"98132e1e-c792-4d81-99d3-5bc01dd9b8b6", + * name:"release", + * info:"", + * duration:0, + * startDate:null, + * endDate:null, + * contributionValidation:false + * }, + * http://localhost:8080/api/v1/codelutin/contribute/a7e14ea2-fde7-4e80-aff3-03398806525d/5195fa60-18cd-43af-8379-b893b523716a:{ + * oid:"#43:0", + * parentTaskUuid:null, + * pathToProjectUuid:null, + * uuid:"5195fa60-18cd-43af-8379-b893b523716a", + * name:"env", + * info:"", + * duration:0, + * startDate:null, + * endDate:null, + * contributionValidation:false + * } + * } + * } + * </pre> + * @param url + * @return + */ + public RemoteProject fetchProject(String url) { + RemoteProject project = null; + + try { + JSONObject json = new JSONObject(IOUtils.toString(new URL(url), Charset.forName("UTF-8"))); + JSONObject jsonProject = json.getJSONObject("project"); + + JSONObject jsonTasks = json.getJSONObject("tasks"); + Map<String, Object> mapTasks = jsonTasks.toMap(); + List<RemoteTask> tasks = mapTasks.entrySet().stream() + .map(entry -> { + RemoteTask task = new RemoteTask(); + task.setUrl(entry.getKey()); + + Map<String, String> value = (Map<String, String>) entry.getValue(); + task.setUuid(value.get("uuid")); + task.setName(value.get("name")); + return task; + }) + .collect(Collectors.toList()); + + project = new RemoteProject(); + project.setUuid(jsonProject.getString("uuid")); + project.setName(jsonProject.getString("name")); + project.setUrl(url); + project.setSubTasks(tasks); + } catch (IOException ex) { + + } + return project; + } + + /** + * Sync project by pushing all sub tasks times on: + * http://localhost:8080/api/v1/codelutin/contribute/a7e14ea2-fde7-4e80-aff3-03... + * + * with content:<pre> + * { + * "startDate": Date, + * "endDate": Date, + * "periods": [ // list of contributions + * { + * "id": String, // unique client id + * "startDate": Date, + * "duration": long, + * "info": String + * }, + * /....../ + * ] + * } + * </pre> + * @param timerProject + */ + public void sync(TimerProject timerProject) { + List<RemoteProject> projects = (List<RemoteProject>) timerProject.getMeta().get(Constants.META); + if (CollectionUtils.isNotEmpty(projects)) { + if (log.isDebugEnabled()) { + log.debug("Syncing timebundle project " + timerProject.getName()); + } + projects.stream() + .flatMap(tbProject -> tbProject.getSubTasks().stream()) + .forEach(this::syncTask); + } + } + + protected void syncTask(RemoteTask remoteTask) { + String url = remoteTask.getUrl(); + Set<TimerTask> syncTasks = remoteTask.getSyncTasks(); + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("startDate", DateFormatUtils.format(new Date(0), DATE_MIDNIGHT_PATTERN)); + jsonObject.put("endDate", DateFormatUtils.format(new Date(), DATE_MIDNIGHT_PATTERN)); + JSONArray periodArray = new JSONArray(); + syncTasks.forEach(timerTask -> { + timerTask.getAllDaysAndTimes().entrySet().forEach(dateLongEntry -> { + JSONObject period = new JSONObject(); + period.put("id", String.valueOf(timerTask.getNumber()) + DateFormatUtils.format(dateLongEntry.getKey(), DATE_MIDNIGHT_PATTERN)); + period.put("startDate", DateFormatUtils.format(dateLongEntry.getKey(), DATE_MIDNIGHT_PATTERN)); + period.put("duration", dateLongEntry.getValue() / 1000L); + periodArray.put(period); + }); + }); + jsonObject.put("periods", periodArray); + post(url, jsonObject); + } + + protected static void post(String url, JSONObject data) { + try { + URI uri = new URI(url); + HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection(); + conn.setDoOutput(true); + conn.setRequestMethod("POST"); + conn.addRequestProperty("Content-Type", "application/json"); + OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); + out.write(data.toString(2)); + out.close(); + System.out.println(conn.getResponseCode()); + } catch (IOException | URISyntaxException ex) { + log.error("ex", ex); + } + System.out.println("will send : " + data.toString(2)); + } + + public static void main(String... args) { + TimerProject project = new TimerProject("jTimer"); + List<RemoteProject> tbprojects = new ArrayList<>(); + RemoteProject tbProject = new RemoteProject(); + tbProject.setUrl("http://localhost:8080/api/v1/codelutin/contribute/3a526a69-59b9-45d2-bde2-09..."); + RemoteProject tbTask = new RemoteProject(); + tbTask.setUrl("http://localhost:8080/api/v1/codelutin/contribute/3a526a69-59b9-45d2-bde2-09..."); + + TimerTask task1 = new TimerTask(); + task1.setCreationDate(new Date()); + task1.setTime(new Date(), 42L); + tbTask.setSyncTasks(Collections.singleton(task1)); + tbProject.setSubTasks(Collections.singletonList(tbTask)); + tbprojects.add(tbProject); + project.getMeta().put(Constants.META, tbprojects); + + RemoteSynchronizer r = new RemoteSynchronizer(); + r.sync(project); + } +} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncIOSaver.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncIOSaver.java new file mode 100644 index 0000000..3fea5e3 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncIOSaver.java @@ -0,0 +1,211 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.JTimerConfig; +import org.chorem.jtimer.entities.TimerProject; +import org.chorem.jtimer.entities.TimerTask; +import org.chorem.jtimer.plugins.timebundle.data.RemoteProject; +import org.chorem.jtimer.plugins.timebundle.data.RemoteTask; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class SyncIOSaver { + + protected static Log log = LogFactory.getLog(SyncIOSaver.class); + + protected static final String TIMEBUNDLE_DATA_DIRECTORY = "timebundledatabeta1"; + + protected static final String TIMEBUNDLE_CACHE_DIRECTORY = "timebundlecachebeta1"; + + protected File dataDirectory; + + protected File cacheDirectory; + + public SyncIOSaver(JTimerConfig config) { + File homeDirectory = config.getHomeDirectory(); + dataDirectory = new File(homeDirectory, TIMEBUNDLE_DATA_DIRECTORY); + dataDirectory.mkdirs(); + cacheDirectory = new File(homeDirectory, TIMEBUNDLE_CACHE_DIRECTORY); + cacheDirectory.mkdirs(); + } + + public void saveProject(TimerProject project) { + if (log.isDebugEnabled()) { + log.debug("Saving timebundle project " + project.getName()); + } + try { + List<RemoteProject> remoteProjects = (List<RemoteProject>) project.getMeta().get(Constants.META); + if (CollectionUtils.isEmpty(remoteProjects)) { + clearSyncInfos(project); + } else { + saveSyncInfo(project, remoteProjects); + saveTaskLinks(project, remoteProjects); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected void saveSyncInfo(TimerProject project, List<RemoteProject> remoteProjects) throws IOException { + Gson gson = new Gson(); + String json = gson.toJson(remoteProjects); + File jsonFile = new File(dataDirectory, project.getNumber() + ".json"); + FileUtils.writeStringToFile(jsonFile, json, StandardCharsets.UTF_8); + } + + protected void saveTaskLinks(TimerProject project, List<RemoteProject> remoteProjects) throws IOException { + + Properties props = new Properties(); + remoteProjects.forEach(remoteProject -> saveTaskLinks(props, remoteProject.getSubTasks())); + + + File syncFile = new File(dataDirectory, project.getNumber() + ".sync"); + if (props.isEmpty()) { + syncFile.delete(); + } else { + try (FileWriter writer = new FileWriter(syncFile)) { + props.store(writer, null); + } + } + } + + private void saveTaskLinks(Properties props, List<RemoteTask> remoteTasks) { + if (remoteTasks != null) { + remoteTasks.forEach(remoteTask -> { + String values = remoteTask.getSyncTasks() + .stream() + .mapToInt(TimerTask::getNumber) + .mapToObj(Integer::toString) + .collect(Collectors.joining(",")); + if (!values.isEmpty()) { + props.put(remoteTask.getUuid(), values); + } + saveTaskLinks(props, remoteTask.getSubTasks()); + }); + } + } + + public void deleteProject(TimerProject project) { + clearSyncInfos(project); + } + + protected void clearSyncInfos(TimerProject project) { + if (log.isDebugEnabled()) { + log.debug("Clearing timebundle project " + project.getName()); + } + File jsonFile = new File(dataDirectory, project.getNumber() + ".json"); + jsonFile.delete(); + File syncFile = new File(dataDirectory, project.getNumber() + ".sync"); + syncFile.delete(); + } + + public void loadSyncInfos(Collection<TimerProject> projects) { + projects.forEach(this::loadProjectSyncInfos); + } + + protected void loadProjectSyncInfos(TimerProject project) { + File jsonFile = new File(dataDirectory, project.getNumber() + ".json"); + if (jsonFile.isFile()) { + try (JsonReader reader = new JsonReader(new FileReader(jsonFile))) { + // load remote + Gson gson = new Gson(); + Type REVIEW_TYPE = new TypeToken<List<RemoteProject>>() { + }.getType(); + List<RemoteProject> data = gson.fromJson(reader, REVIEW_TYPE); + project.getMeta().put(Constants.META, data); + Map<String, RemoteTask> remoteTaskById = new HashMap<>(); + data.forEach(remoteProject -> loadRemoteTaskMap(remoteTaskById, remoteProject.getSubTasks())); + + // load sync + File syncFile = new File(dataDirectory, project.getNumber() + ".sync"); + if (syncFile.isFile()) { + try (Reader syncReader = new FileReader(syncFile)) { + // load sync + Properties props = new Properties(); + props.load(syncReader); + Map<Integer, TimerTask> timerTaskByNumber = new HashMap<>(); + loadTimerTaskMap(timerTaskByNumber, project.getSubTasks()); + + // match remote and sync + remoteTaskById.entrySet().forEach(entry -> { + RemoteTask value = entry.getValue(); + String uuid = value.getUuid(); + String syncList = (String)props.get(uuid); + if (syncList != null) { + Set<TimerTask> syncTasks = Arrays.stream(syncList.split("\\s*,\\s*")) + .map(Integer::valueOf) + .map(timerTaskByNumber::get) + .collect(Collectors.toSet()); + value.setSyncTasks(syncTasks); + } + }); + } + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private void loadTimerTaskMap(Map<Integer, TimerTask> taskByNumber, List<TimerTask> subTasks) { + if (subTasks != null) { + Map<Integer, TimerTask> result = subTasks.stream() + .collect(Collectors.toMap(TimerTask::getNumber, Function.identity())); + taskByNumber.putAll(result); + subTasks.forEach(task -> loadTimerTaskMap(taskByNumber, task.getSubTasks())); + } + } + + private void loadRemoteTaskMap(Map<String, RemoteTask> remoteTaskById, List<RemoteTask> subTasks) { + if (subTasks != null) { + Map<String, RemoteTask> result = subTasks.stream() + .collect(Collectors.toMap(RemoteTask::getUuid, Function.identity())); + remoteTaskById.putAll(result); + subTasks.forEach(task -> loadRemoteTaskMap(remoteTaskById, task.getSubTasks())); + } + } +} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java new file mode 100644 index 0000000..b8e32c2 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java @@ -0,0 +1,207 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle; + +import org.chorem.jtimer.JTimerService; +import org.chorem.jtimer.entities.TimerProject; +import org.chorem.jtimer.entities.TimerTask; +import org.chorem.jtimer.plugins.timebundle.data.RemoteProject; +import org.chorem.jtimer.plugins.timebundle.data.RemoteTask; +import org.chorem.jtimer.plugins.timebundle.model.RemoteTaskTreeModel; +import org.chorem.jtimer.plugins.timebundle.model.RemoveTaskCellRenderer; +import org.chorem.jtimer.ui.tree.CheckBoxTreeCellEditor; +import org.chorem.jtimer.ui.tree.CheckBoxTreeCellRenderer; +import org.chorem.jtimer.ui.tree.TaskTreeModel; +import org.jdesktop.application.Application; +import org.jdesktop.application.FrameView; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.TreePath; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +public class SyncView extends FrameView implements TreeSelectionListener, WindowListener { + + protected RemoteSynchronizer synchronizer; + protected SyncIOSaver saver; + protected JTimerService service; + protected TimerProject timerProject; + + protected JTree remoteProjectTree = new JTree(); + protected RemoteTaskTreeModel projectModel; + + protected JTree projectsTree = new JTree(); + protected TaskTreeModel treeModel; + + public SyncView(Application application, JTimerService service, SyncIOSaver saver, + RemoteSynchronizer synchronizer, TimerProject timerProject) { + super(application); + this.service = service; + this.timerProject = timerProject; + this.synchronizer = synchronizer; + this.saver = saver; + + // modify frame name + getFrame().setName("timebundleFrame"); + getFrame().setTitle("Time bundle"); + + List<RemoteProject> timebundle = getRemoteProjects(); + projectModel = new RemoteTaskTreeModel(timebundle); + treeModel = new TaskTreeModel(Collections.singletonList(timerProject)); + + setComponent(getMainComponent()); + + this.getFrame().addWindowListener(this); + } + + protected List<RemoteProject> getRemoteProjects() { + return (List<RemoteProject>)this.timerProject.getMeta().computeIfAbsent(Constants.META, k -> new ArrayList<>()); + } + + /** + * Get main view component. + * + * @return main component + */ + protected JComponent getMainComponent() { + JPanel mainPanel = new JPanel(new GridBagLayout()); + + JLabel projectLabel = new JLabel("Remote projects:"); + mainPanel.add(projectLabel, new GridBagConstraints(0, 0, 1, 1, 0, 0, + GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(1, 1, 1, 1), 0, 0)); + + remoteProjectTree.setRootVisible(false); + remoteProjectTree.setShowsRootHandles(true); + remoteProjectTree.setModel(projectModel); + remoteProjectTree.setCellRenderer(new RemoveTaskCellRenderer()); + remoteProjectTree.addTreeSelectionListener(this); + JScrollPane projectScroll = new JScrollPane(remoteProjectTree); + mainPanel.add(projectScroll, new GridBagConstraints(0, 1, 1, 1, 1, 1, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(1, 1, 1, 1), 0, 0)); + + JButton addProject = new JButton("Add project"); + addProject.addActionListener(l -> addRemoteProject()); + mainPanel.add(addProject, new GridBagConstraints(0, 2, 1, 1, 0, 0, + GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(1, 1, 1, 1), 0, 0)); + + JLabel treeLabel = new JLabel("Local projects:"); + mainPanel.add(treeLabel, new GridBagConstraints(2, 0, 1, 1, 0, 0, + GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(1, 1, 1, 1), 0, 0)); + + projectsTree.setRootVisible(false); + projectsTree.setEditable(true); + projectsTree.setShowsRootHandles(true); + projectsTree.setModel(treeModel); + TreeSet<TimerTask> selection = new TreeSet<>(); + projectsTree.setCellRenderer(new CheckBoxTreeCellRenderer(service, projectsTree, selection, false)); + projectsTree.setCellEditor(new CheckBoxTreeCellEditor(service, projectsTree, selection, false)); + projectsTree.expandPath(new TreePath(treeModel.getRoot()).pathByAddingChild(timerProject)); + JScrollPane treeScroll = new JScrollPane(projectsTree); + mainPanel.add(treeScroll, new GridBagConstraints(2, 1, 1, 1, 1, 1, + GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(1, 1, 1, 1), 0, 0)); + + JButton syncAll = new JButton("Sync"); + syncAll.addActionListener(l -> syncProjects()); + mainPanel.add(syncAll, new GridBagConstraints(2, 2, 1, 1, 0, 0, + GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(1, 1, 1, 1), 0, 0)); + + return mainPanel; + } + + public void addRemoteProject() { + String url = (String)JOptionPane.showInputDialog(this.getFrame(), + "New remote project url", "Add remote project", + JOptionPane.INFORMATION_MESSAGE, null, + new Object[] {"http://localhost:8080/api/v1/codelutin/contribute/9370ee7e-7767-482c-b798-7a7262bc417f/#jTimer"}, + "http://localhost:8080/api/v1/codelutin/contribute/9370ee7e-7767-482c-b798-7a..."); + + RemoteProject project = synchronizer.fetchProject(url); + getRemoteProjects().add(project); + projectModel.addElement(project); + remoteProjectTree.expandPath(new TreePath(projectModel.getRoot()).pathByAddingChild(project)); + } + + public void syncProjects() { + synchronizer.sync(timerProject); + } + + @Override + public void valueChanged(TreeSelectionEvent treeSelectionEvent) { + RemoteTask task = (RemoteTask)treeSelectionEvent.getPath().getLastPathComponent(); + Set<TimerTask> selection = task.getSyncTasks(); + projectsTree.setCellRenderer(new CheckBoxTreeCellRenderer(service, projectsTree, selection, false)); + projectsTree.setCellEditor(new CheckBoxTreeCellEditor(service, projectsTree, selection, false)); + projectsTree.validate(); + } + + @Override + public void windowOpened(WindowEvent e) { + + } + + @Override + public void windowClosing(WindowEvent e) { + saver.saveProject(timerProject); + } + + @Override + public void windowClosed(WindowEvent e) { + + } + + @Override + public void windowIconified(WindowEvent e) { + + } + + @Override + public void windowDeiconified(WindowEvent e) { + + } + + @Override + public void windowActivated(WindowEvent e) { + + } + + @Override + public void windowDeactivated(WindowEvent e) { + + } +} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/Synchronizer.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/Synchronizer.java new file mode 100644 index 0000000..e15ba9f --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/Synchronizer.java @@ -0,0 +1,27 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle; + +public interface Synchronizer { + +} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/TimeBundlePlugin.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/TimeBundlePlugin.java new file mode 100644 index 0000000..efe5be8 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/TimeBundlePlugin.java @@ -0,0 +1,131 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.JTimer; +import org.chorem.jtimer.JTimerService; +import org.chorem.jtimer.data.DataEventListener; +import org.chorem.jtimer.data.TimerDataManager; +import org.chorem.jtimer.data.VetoableDataEventListener; +import org.chorem.jtimer.entities.TimerProject; +import org.chorem.jtimer.plugins.Plugin; +import org.chorem.jtimer.ui.TimerUIManager; +import org.chorem.jtimer.ui.UserInterfaceListener; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.KeyStroke; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class TimeBundlePlugin extends Plugin implements DataEventListener, VetoableDataEventListener, + UserInterfaceListener, PropertyChangeListener, Runnable { + + protected static Log log = LogFactory.getLog(TimeBundlePlugin.class); + + protected JTimerService service; + + protected JTimer application; + + protected Action timeBundleAction; + + protected RemoteSynchronizer synchronizer; + + protected SyncIOSaver saver; + + @Override + public void register(JTimerService service) { + this.service = service; + TimerDataManager data = service.getData(); + data.addDataEventListener(this); + data.addVetoableDataEventListener(this); + + TimerUIManager ui = service.getUi(); + ui.addUserInterfaceListener(this); + + synchronizer = new RemoteSynchronizer(); + saver = new SyncIOSaver(JTimer.config); + ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor(); + ses.scheduleAtFixedRate(this, 1, 60, TimeUnit.MINUTES); + } + + @Override + public void initUI(JTimer application) { + this.application = application; + + timeBundleAction = new AbstractAction("Time bundle") { + @Override + public void actionPerformed(ActionEvent e) { + TimerProject project = application.getSelectedProject().get(0); + SyncView view = new SyncView(application, service, saver, synchronizer, project); + application.show(view); + } + }; + timeBundleAction.setEnabled(false); + timeBundleAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F3, KeyEvent.SHIFT_MASK)); + + JMenuItem menuItem = new JMenuItem(timeBundleAction); + + JMenuBar jMenuBar = application.getMainFrame().getJMenuBar(); + JMenu menuProject = jMenuBar.getMenu(0); + menuProject.add(menuItem, 2); + + if (log.isInfoEnabled()) { + log.info("Time bundle UI registered"); + } + + application.addPropertyChangeListener(this); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("selectedSingleProject")) { + timeBundleAction.setEnabled((boolean)evt.getNewValue()); + } + } + + @Override + public void dataLoaded(Collection<TimerProject> projects) { + saver.loadSyncInfos(projects); + } + + @Override + public void run() { + if (log.isInfoEnabled()) { + log.info("Auto sync projects"); + } + List<TimerProject> projectsList = service.getData().getProjectsList(); + projectsList.forEach(synchronizer::sync); + } +} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/data/RemoteProject.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/data/RemoteProject.java new file mode 100644 index 0000000..3e33e01 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/data/RemoteProject.java @@ -0,0 +1,27 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle.data; + +public class RemoteProject extends RemoteTask { + +} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/data/RemoteTask.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/data/RemoteTask.java new file mode 100644 index 0000000..699cf88 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/data/RemoteTask.java @@ -0,0 +1,85 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle.data; + +import org.chorem.jtimer.entities.TimerTask; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class RemoteTask implements Serializable { + + protected String uuid; + + protected String name; + + protected String url; + + protected List<RemoteTask> subTasks; + + protected transient Set<TimerTask> syncTasks = new HashSet<>(); + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + public void setSyncTasks(Set<TimerTask> syncTasks) { + this.syncTasks = syncTasks; + } + + public Set<TimerTask> getSyncTasks() { + return syncTasks; + } + + public void setSubTasks(List<RemoteTask> subTasks) { + this.subTasks = subTasks; + } + + public List<RemoteTask> getSubTasks() { + return subTasks; + } +} + + diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/model/RemoteTaskTreeModel.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/model/RemoteTaskTreeModel.java new file mode 100644 index 0000000..00357c6 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/model/RemoteTaskTreeModel.java @@ -0,0 +1,121 @@ +/* + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle.model; + +import org.chorem.jtimer.plugins.timebundle.data.RemoteTask; +import org.jdesktop.swingx.tree.TreeModelSupport; + +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import java.util.List; + +/** + * Remote task tree data. + */ +public class RemoteTaskTreeModel implements TreeModel { + + protected TreeModelSupport modelSupport; + + protected List<? extends RemoteTask> projects; + + /** + * Constructor. + * + * @param projects root projects + */ + public RemoteTaskTreeModel(List<? extends RemoteTask> projects) { + this.projects = projects; + modelSupport = new TreeModelSupport(this); + } + + @Override + public Object getChild(Object parent, int index) { + RemoteTask child = getChildrenFor(parent).get(index); + return child; + } + + @Override + public int getChildCount(Object parent) { + List<? extends RemoteTask> childrenFor = getChildrenFor(parent); + int count = childrenFor != null ? childrenFor.size() : 0; + return count; + } + + /** + * Recupere la sous liste: projects si parent = root + * getSubTasks() sinon + * + * @param parent parent to task sublist + * @return children + */ + protected List<? extends RemoteTask> getChildrenFor(Object parent) { + + List<? extends RemoteTask> result; + + // get correct list + if (parent == getRoot()) { // case root node + result = projects; + } else { // not root node + result = RemoteTask.class.cast(parent).getSubTasks(); + } + + return result; + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + int count = getChildrenFor(parent).indexOf(parent); + return count; + } + + @Override + public boolean isLeaf(Object node) { + return getChildCount(node) == 0; + } + + @Override + public void addTreeModelListener(TreeModelListener l) { + modelSupport.addTreeModelListener(l); + } + + @Override + public Object getRoot() { + return this; + } + + @Override + public void removeTreeModelListener(TreeModelListener l) { + modelSupport.removeTreeModelListener(l); + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + + } + + public void addElement(RemoteTask task) { + // FIXME change this + modelSupport.fireNewRoot(); + } +} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/model/RemoveTaskCellRenderer.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/model/RemoveTaskCellRenderer.java new file mode 100644 index 0000000..d3211d7 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/model/RemoveTaskCellRenderer.java @@ -0,0 +1,42 @@ +/* + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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.plugins.timebundle.model; + +import org.chorem.jtimer.plugins.timebundle.data.RemoteTask; + +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; +import java.awt.Component; + +/** + * Remote task tree node cell renderer. + */ +public class RemoveTaskCellRenderer extends DefaultTreeCellRenderer { + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, + boolean leaf, int row, boolean hasFocus) { + Object value2 = value instanceof RemoteTask ? ((RemoteTask)value).getName() : value; + return super.getTreeCellRendererComponent(tree, value2, selected, expanded, leaf, row, hasFocus); + } +} diff --git a/src/main/java/org/chorem/jtimer/ui/NewTaskView.java b/src/main/java/org/chorem/jtimer/ui/NewTaskView.java index d8e4e12..39bd894 100644 --- a/src/main/java/org/chorem/jtimer/ui/NewTaskView.java +++ b/src/main/java/org/chorem/jtimer/ui/NewTaskView.java @@ -24,7 +24,7 @@ package org.chorem.jtimer.ui; import org.apache.commons.lang3.StringUtils; import org.chorem.jtimer.JTimer; import org.chorem.jtimer.data.DataViolationException; -import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerTask; import org.chorem.jtimer.ui.widget.DialogView; import org.jdesktop.application.Action; @@ -59,17 +59,17 @@ import java.util.Set; public class NewTaskView extends DialogView { protected JTimer parent; - protected TimerCore core; + protected JTimerService service; protected ActionMap actionMap; protected TimerTask selectedTask; protected JTextField newTaskField; protected JComboBox<String> newTaskTemplateBox; - public NewTaskView(JTimer parent, TimerCore core, TimerTask selectedTask) { + public NewTaskView(JTimer parent, JTimerService service, TimerTask selectedTask) { super(parent.getMainFrame(), parent); this.parent = parent; - this.core = core; + this.service = service; this.selectedTask = selectedTask; actionMap = parent.getContext().getActionMap(this); @@ -157,7 +157,7 @@ public class NewTaskView extends DialogView { t.setCreationDate(new Date()); try { - core.getData().addTask(selectedTask, t, taskTemplate); + service.getData().addTask(selectedTask, t, taskTemplate); parent.selectTask(t); } catch (DataViolationException e) { parent.displayErrorMessage(e.getExceptionKey()); diff --git a/src/main/java/org/chorem/jtimer/ui/TimerTaskEditor.java b/src/main/java/org/chorem/jtimer/ui/TimerTaskEditor.java index 1b1e34f..0e6dce5 100644 --- a/src/main/java/org/chorem/jtimer/ui/TimerTaskEditor.java +++ b/src/main/java/org/chorem/jtimer/ui/TimerTaskEditor.java @@ -25,8 +25,8 @@ package org.chorem.jtimer.ui; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.chorem.jtimer.JTimer; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.data.DataViolationException; -import org.chorem.jtimer.data.TimerCore; import org.chorem.jtimer.data.TimerDataManager; import org.chorem.jtimer.entities.TimerTask; import org.chorem.jtimer.entities.TimerTaskHelper; @@ -103,8 +103,8 @@ public class TimerTaskEditor extends DialogView { protected TimerTask task; /** task to edit */ protected TimerTask cloneTask; - /** timer core */ - protected TimerCore core; + /** timer service */ + protected JTimerService service; /** task manager */ protected TimerDataManager dataManager; @@ -136,9 +136,9 @@ public class TimerTaskEditor extends DialogView { * * @param parent * @param task - * @param core + * @param service */ - public TimerTaskEditor(JTimer parent, TimerTask task, TimerCore core) { + public TimerTaskEditor(JTimer parent, TimerTask task, JTimerService service) { super(parent.getMainFrame(), parent); this.parent = parent; @@ -154,8 +154,8 @@ public class TimerTaskEditor extends DialogView { this.task = task; cloneTask = task.clone(); - this.core = core; - dataManager = core.getData(); + this.service = service; + dataManager = service.getData(); // init resources map //ApplicationContext ctxt = parent.getContext(); diff --git a/src/main/java/org/chorem/jtimer/ui/TimerUIManager.java b/src/main/java/org/chorem/jtimer/ui/TimerUIManager.java new file mode 100644 index 0000000..8a52add --- /dev/null +++ b/src/main/java/org/chorem/jtimer/ui/TimerUIManager.java @@ -0,0 +1,50 @@ +/*- + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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; + +import org.chorem.jtimer.JTimer; + +import javax.swing.JPopupMenu; +import java.util.ArrayList; +import java.util.Collection; + +public class TimerUIManager { + + protected Collection<UserInterfaceListener> userInterfaceListeners = new ArrayList<>(); + + public void addUserInterfaceListener(UserInterfaceListener listener) { + userInterfaceListeners.add(listener); + } + + public void removeUserInterfaceListener(UserInterfaceListener listener) { + userInterfaceListeners.remove(listener); + } + + public void initUI(JTimer application) { + userInterfaceListeners.forEach(l -> l.initUI(application)); + } + + public void initTreePopupMenu(JPopupMenu menu) { + userInterfaceListeners.forEach(l -> l.initTreePopupMenu(menu)); + } + +} diff --git a/src/main/java/org/chorem/jtimer/ui/UserInterfaceListener.java b/src/main/java/org/chorem/jtimer/ui/UserInterfaceListener.java new file mode 100644 index 0000000..b416bff --- /dev/null +++ b/src/main/java/org/chorem/jtimer/ui/UserInterfaceListener.java @@ -0,0 +1,37 @@ +/* + * #%L + * jTimer + * %% + * Copyright (C) 2017 CodeLutin + * %% + * 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; + +import org.chorem.jtimer.JTimer; + +import javax.swing.JPopupMenu; + +public interface UserInterfaceListener { + + default void initUI(JTimer application) { + + } + + default void initTreePopupMenu(JPopupMenu menu) { + + } +} diff --git a/src/main/java/org/chorem/jtimer/ui/report/ReportView.java b/src/main/java/org/chorem/jtimer/ui/report/ReportView.java index bd70f45..79469fc 100644 --- a/src/main/java/org/chorem/jtimer/ui/report/ReportView.java +++ b/src/main/java/org/chorem/jtimer/ui/report/ReportView.java @@ -25,7 +25,7 @@ package org.chorem.jtimer.ui.report; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.chorem.jtimer.JTimer; -import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; import org.chorem.jtimer.ui.report.ReportGenerator.Type; @@ -88,8 +88,8 @@ public class ReportView extends FrameView implements DocumentListener { /** Class logger */ protected static Log log = LogFactory.getLog(ReportView.class); - /** Timer core. */ - protected TimerCore core; + /** Timer service. */ + protected JTimerService service; /** Unselected task set. */ protected Set<TimerTask> uncheckedTaskSet; @@ -136,9 +136,9 @@ public class ReportView extends FrameView implements DocumentListener { * Reposts constructor. * * @param application parent reference - * @param core core reference + * @param service service reference */ - public ReportView(Application application, TimerCore core) { + public ReportView(Application application, JTimerService service) { super(application); @@ -147,7 +147,7 @@ public class ReportView extends FrameView implements DocumentListener { getFrame().setName("reportFrame"); getFrame().setTitle(getResourceMap().getString("reportTitle")); - this.core = core; + this.service = service; this.uncheckedTaskSet = new HashSet<>(); // set title @@ -264,11 +264,12 @@ public class ReportView extends FrameView implements DocumentListener { GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); projectsTree = new JTree(); - projectsTree.setRootVisible(true); + projectsTree.setRootVisible(false); + projectsTree.setShowsRootHandles(true); projectsTree.setEditable(true); - projectsTree.setModel(new TaskTreeModel(core, getResourceMap().getString("reportProjectsList") + " :")); - projectsTree.setCellEditor(new CheckBoxTreeCellEditor(core, projectsTree, uncheckedTaskSet)); - projectsTree.setCellRenderer(new CheckBoxTreeCellRenderer(core, projectsTree, uncheckedTaskSet)); + projectsTree.setModel(new TaskTreeModel(service)); + projectsTree.setCellEditor(new CheckBoxTreeCellEditor(service, projectsTree, uncheckedTaskSet, true)); + projectsTree.setCellRenderer(new CheckBoxTreeCellRenderer(service, projectsTree, uncheckedTaskSet, true)); JScrollPane jspTable = new JScrollPane(projectsTree); panelProjects.add(jspTable, new GridBagConstraints(0, 1, 1, 1, 1, 1, @@ -408,8 +409,7 @@ public class ReportView extends FrameView implements DocumentListener { // get filtered project list // without non selected project and tasks - List<TimerProject> selectedProjects = getSelectedProjects(core - .getData().getProjectsList(), uncheckedTaskSet); + List<TimerProject> selectedProjects = getSelectedProjects(service.getData().getProjectsList(), uncheckedTaskSet); // make report String report = reportGenerator.getReportText(reportType, selectedProjects, datePickerFrom.getDate(), diff --git a/src/main/java/org/chorem/jtimer/ui/tasks/IdleDialog.java b/src/main/java/org/chorem/jtimer/ui/tasks/IdleDialog.java index 82624ea..c683cb3 100644 --- a/src/main/java/org/chorem/jtimer/ui/tasks/IdleDialog.java +++ b/src/main/java/org/chorem/jtimer/ui/tasks/IdleDialog.java @@ -26,7 +26,7 @@ import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.chorem.jtimer.JTimer; -import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerTask; import org.chorem.jtimer.ui.tree.ProjectsAndTasksTree; import org.chorem.jtimer.ui.tree.TaskTreeModel; @@ -105,8 +105,8 @@ public class IdleDialog extends JDialog { /** Parent application. */ protected SingleFrameApplication application; - /** Timer core. */ - protected TimerCore core; + /** Timer service. */ + protected JTimerService service; /** I18N resource map. */ protected ResourceMap resourceMap; @@ -147,13 +147,13 @@ public class IdleDialog extends JDialog { * Protected to force use of show static method. * * @param application parent application - * @param core timer core + * @param service timer service */ - protected IdleDialog(SingleFrameApplication application, TimerCore core) { + protected IdleDialog(SingleFrameApplication application, JTimerService service) { // don't make reference on other parent // windows, cause idle to unlock some task // if parent window is hidden by systray - this.core = core; + this.service = service; // init resources map this.application = application; @@ -284,7 +284,7 @@ public class IdleDialog extends JDialog { projectsTree = new ProjectsAndTasksTree(); projectsTree.setRootVisible(false); projectsTree.setShowsRootHandles(true); - projectsTree.setModel(new TaskTreeModel(core, null)); + projectsTree.setModel(new TaskTreeModel(service)); projectsTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); projectsTree.setCellRenderer(new ProjectsAndTasksCellRenderer()); projectsTree.addTreeSelectionListener(e -> { @@ -323,10 +323,10 @@ public class IdleDialog extends JDialog { * Init dialog idleDialog instance. * * @param parent parent reference - * @param core core reference + * @param service service reference */ - public static void init(SingleFrameApplication parent, TimerCore core) { - idleDialog = new IdleDialog(parent, core); + public static void init(SingleFrameApplication parent, JTimerService service) { + idleDialog = new IdleDialog(parent, service); } /** diff --git a/src/main/java/org/chorem/jtimer/ui/tasks/RefreshTreeTask.java b/src/main/java/org/chorem/jtimer/ui/tasks/RefreshTreeTask.java index 4be4c6b..774f28d 100644 --- a/src/main/java/org/chorem/jtimer/ui/tasks/RefreshTreeTask.java +++ b/src/main/java/org/chorem/jtimer/ui/tasks/RefreshTreeTask.java @@ -24,7 +24,7 @@ package org.chorem.jtimer.ui.tasks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.data.TimerDataManager; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; @@ -50,12 +50,12 @@ public class RefreshTreeTask extends java.util.TimerTask { protected TimerDataManager dataManager; /** - * Constructor with core. + * Constructor with service. * - * @param core core + * @param service service */ - public RefreshTreeTask(TimerCore core) { - dataManager = core.getData(); + public RefreshTreeTask(JTimerService service) { + dataManager = service.getData(); } /* diff --git a/src/main/java/org/chorem/jtimer/ui/tasks/RunTaskJob.java b/src/main/java/org/chorem/jtimer/ui/tasks/RunTaskJob.java index 7aed6ef..24f163f 100644 --- a/src/main/java/org/chorem/jtimer/ui/tasks/RunTaskJob.java +++ b/src/main/java/org/chorem/jtimer/ui/tasks/RunTaskJob.java @@ -49,7 +49,7 @@ import java.util.concurrent.atomic.AtomicLong; /** * RunTaskJob. * - * Notify every second the core controler. + * Notify every second the service controler. * * @author chatellier * @version $Revision$ diff --git a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellComponent.java b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellComponent.java index 2bdbb8e..0a08e49 100644 --- a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellComponent.java +++ b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellComponent.java @@ -22,7 +22,7 @@ package org.chorem.jtimer.ui.tree; -import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerTask; import javax.swing.JCheckBox; @@ -45,26 +45,31 @@ public abstract class CheckBoxTreeCellComponent extends JCheckBox { /** serialVersionUID */ private static final long serialVersionUID = 2497464481840318274L; - /** Timer core. */ - protected TimerCore core; + /** Timer service. */ + protected JTimerService service; /** Parent tree. */ protected JTree parentTree; /** Unchecked task set. */ - protected Set<TimerTask> uncheckedTaskSet; + protected Set<TimerTask> checkedTaskSet; + + /** To uncheck instead of checking. */ + protected boolean uncheck; /** * Constructor. * - * @param core core reference + * @param service service reference * @param parentTree tree reference - * @param uncheckedTaskList common unselected tasks list + * @param checkedTaskSet common unselected tasks list + * @param uncheck to uncheck instead of check by default */ - protected CheckBoxTreeCellComponent(TimerCore core, JTree parentTree, Set<TimerTask> uncheckedTaskList) { - this.core = core; + protected CheckBoxTreeCellComponent(JTimerService service, JTree parentTree, Set<TimerTask> checkedTaskSet, boolean uncheck) { + this.service = service; this.parentTree = parentTree; - this.uncheckedTaskSet = uncheckedTaskList; + this.checkedTaskSet = checkedTaskSet; + this.uncheck = uncheck; } /** @@ -78,8 +83,7 @@ public abstract class CheckBoxTreeCellComponent extends JCheckBox { * @param row row * @return check box component */ - protected Component getCheckBoxComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, - int row) { + protected Component getCheckBoxComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) { // if this is a task if (value instanceof TimerTask) { @@ -96,7 +100,7 @@ public abstract class CheckBoxTreeCellComponent extends JCheckBox { } // set checked ? - if (uncheckedTaskSet.contains(task)) { + if (checkedTaskSet.contains(task) == uncheck) { this.setSelected(false); } else { this.setSelected(true); diff --git a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellEditor.java b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellEditor.java index e260b25..b950161 100644 --- a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellEditor.java +++ b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellEditor.java @@ -24,7 +24,7 @@ package org.chorem.jtimer.ui.tree; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; @@ -66,12 +66,13 @@ public class CheckBoxTreeCellEditor extends CheckBoxTreeCellComponent implements /** * Constructor. * - * @param core core + * @param service service * @param parentTree tree - * @param uncheckedTaskList common unselected tasks list + * @param checkedTaskSet common unselected tasks list + * @param uncheck to uncheck instead of check by default */ - public CheckBoxTreeCellEditor(TimerCore core, JTree parentTree, Set<TimerTask> uncheckedTaskList) { - super(core, parentTree, uncheckedTaskList); + public CheckBoxTreeCellEditor(JTimerService service, JTree parentTree, Set<TimerTask> checkedTaskSet, boolean uncheck) { + super(service, parentTree, checkedTaskSet, uncheck); addItemListener(this); cellEditorListeners = new ArrayList<>(); @@ -130,9 +131,6 @@ public class CheckBoxTreeCellEditor extends CheckBoxTreeCellComponent implements return true; } - /* - * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent) - */ @Override public void itemStateChanged(ItemEvent e) { @@ -173,15 +171,15 @@ public class CheckBoxTreeCellEditor extends CheckBoxTreeCellComponent implements + lastPathComponent.getName()); } - if (select) { - uncheckedTaskSet.remove(lastPathComponent); + if (select == uncheck) { + checkedTaskSet.remove(lastPathComponent); } else { - uncheckedTaskSet.add(lastPathComponent); + checkedTaskSet.add(lastPathComponent); } // special case, root node selection if (parentTree.getModel().getRoot() == lastPathComponent) { - for (TimerProject project : core.getData().getProjectsList()) { + for (TimerProject project : service.getData().getProjectsList()) { TreePath subTreePath = treePath.pathByAddingChild(project); updateChildren(subTreePath, select); } diff --git a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellRenderer.java b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellRenderer.java index dcddacb..93f07ee 100644 --- a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellRenderer.java +++ b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellRenderer.java @@ -22,7 +22,7 @@ package org.chorem.jtimer.ui.tree; -import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerTask; import javax.swing.JTree; @@ -48,13 +48,14 @@ public class CheckBoxTreeCellRenderer extends CheckBoxTreeCellComponent /** * Constructor. * - * @param core core + * @param service service * @param parentTree tree - * @param uncheckedTaskList common unselected tasks list + * @param checkedTaskList common unselected tasks list + * @param uncheck to uncheck instead of check by default */ - public CheckBoxTreeCellRenderer(TimerCore core, JTree parentTree, - Set<TimerTask> uncheckedTaskList) { - super(core, parentTree, uncheckedTaskList); + public CheckBoxTreeCellRenderer(JTimerService service, JTree parentTree, + Set<TimerTask> checkedTaskList, boolean uncheck) { + super(service, parentTree, checkedTaskList, uncheck); } @Override diff --git a/src/main/java/org/chorem/jtimer/ui/tree/TaskTreeModel.java b/src/main/java/org/chorem/jtimer/ui/tree/TaskTreeModel.java index a74af61..8808349 100644 --- a/src/main/java/org/chorem/jtimer/ui/tree/TaskTreeModel.java +++ b/src/main/java/org/chorem/jtimer/ui/tree/TaskTreeModel.java @@ -24,7 +24,8 @@ package org.chorem.jtimer.ui.tree; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.JTimerService; +import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; import org.chorem.jtimer.entities.TimerTaskHelper; import org.jdesktop.swingx.tree.TreeModelSupport; @@ -37,7 +38,7 @@ import java.util.List; import java.util.stream.Collectors; /** - * Timer task tree model. + * Timer task tree data. * * @author chatellier * @version $Revision$ @@ -52,11 +53,8 @@ public class TaskTreeModel implements TreeModel { protected TreeModelSupport modelSupport; - /** Timer core. */ - protected TimerCore core; - - /** Tree model root. */ - protected TimerTask root; + /** Tree data root. */ + protected List<TimerProject> root; /** Show closed tasks. */ protected boolean showClosed; @@ -64,12 +62,15 @@ public class TaskTreeModel implements TreeModel { /** * Constructor. * - * @param core core - * @param rootName root node name + * @param service service + * @deprecated since 1.6 use constructor without service */ - public TaskTreeModel(TimerCore core, String rootName) { - this.core = core; - root = new TimerTask(rootName); + public TaskTreeModel(JTimerService service) { + this(service.getData().getProjectsList()); + } + + public TaskTreeModel(List<TimerProject> projects) { + root = projects; modelSupport = new TreeModelSupport(this); } @@ -94,7 +95,7 @@ public class TaskTreeModel implements TreeModel { */ protected List<TimerTask> getChildrenFor(Object parent) { - List<TimerTask> result = getFiteredSubListFor(parent); + List<TimerTask> result = getFilteredSubListFor(parent); return result; } @@ -106,13 +107,13 @@ public class TaskTreeModel implements TreeModel { * @param parent parent to task sublist * @return filtered list */ - protected List<TimerTask> getFiteredSubListFor(Object parent) { + protected List<TimerTask> getFilteredSubListFor(Object parent) { List<TimerTask> result = new ArrayList<>(); // get correct list if (parent == root) { // case root node - result.addAll(core.getData().getProjectsList()); + result.addAll(root); } else { // not root node TimerTask task = (TimerTask) parent; result.addAll(task.getSubTasks()); diff --git a/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksModel.java b/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksModel.java index 4ba03a3..9733bcd 100644 --- a/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksModel.java +++ b/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksModel.java @@ -25,8 +25,8 @@ package org.chorem.jtimer.ui.treetable; import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.data.DataEventListener; -import org.chorem.jtimer.data.TimerCore; import org.chorem.jtimer.data.TimerDataManager; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; @@ -100,16 +100,16 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements * Constructor. * * @param projectsAndTaskTable table - * @param core timer core + * @param service timer service * @param columnIdentifiers column identifiers */ - public ProjectsAndTasksModel(ProjectsAndTasksTable projectsAndTaskTable, TimerCore core, + public ProjectsAndTasksModel(ProjectsAndTasksTable projectsAndTaskTable, JTimerService service, List<String> columnIdentifiers) { super(new TimerProject("root")); // remember this.projectsAndTaskTable = projectsAndTaskTable; - this.dataManager = core.getData(); + this.dataManager = service.getData(); this.columnIdentifiers = columnIdentifiers; taskNameCache = new HashMap<>(); diff --git a/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksRunningCellRenderer.java b/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksRunningCellRenderer.java index c099566..37bd432 100644 --- a/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksRunningCellRenderer.java +++ b/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksRunningCellRenderer.java @@ -25,7 +25,7 @@ package org.chorem.jtimer.ui.treetable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.chorem.jtimer.data.DataEventListener; -import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerTask; import org.jdesktop.swingx.JXTreeTable; @@ -76,9 +76,9 @@ public class ProjectsAndTasksRunningCellRenderer extends ProjectsAndTasksCellRen * Constructor. * * @param treeTable Tree table reference for image observer - * @param core TimerCore + * @param service JTimerService */ - public ProjectsAndTasksRunningCellRenderer(JXTreeTable treeTable, TimerCore core) { + public ProjectsAndTasksRunningCellRenderer(JXTreeTable treeTable, JTimerService service) { this.treeTable = treeTable; // init @@ -91,7 +91,7 @@ public class ProjectsAndTasksRunningCellRenderer extends ProjectsAndTasksCellRen runningIcon.setImageObserver(nodeObserver); // be notified on events - core.getData().addDataEventListener(this); + service.getData().addDataEventListener(this); } @Override diff --git a/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksTable.java b/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksTable.java index aabb82d..95aec35 100644 --- a/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksTable.java +++ b/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksTable.java @@ -22,7 +22,7 @@ package org.chorem.jtimer.ui.treetable; -import org.chorem.jtimer.data.TimerCore; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; import org.chorem.jtimer.entities.TimerTaskHelper; @@ -71,9 +71,9 @@ public class ProjectsAndTasksTable extends JXTreeTable { * Constructor. * * @param application application - * @param core timer core + * @param service timer service */ - public ProjectsAndTasksTable(Application application, TimerCore core) { + public ProjectsAndTasksTable(Application application, JTimerService service) { // start with init i18n of table column name ApplicationContext ctxt = application.getContext(); @@ -86,10 +86,10 @@ public class ProjectsAndTasksTable extends JXTreeTable { columnIdentifiers.add(resourceMap.getString("totalTimeColumnName")); // set model - treeTableModel = new ProjectsAndTasksModel(this, core, columnIdentifiers); + treeTableModel = new ProjectsAndTasksModel(this, service, columnIdentifiers); // set renderer - treeCellRenderer = new ProjectsAndTasksRunningCellRenderer(this, core); + treeCellRenderer = new ProjectsAndTasksRunningCellRenderer(this, service); setTreeCellRenderer(treeCellRenderer); setTreeTableModel(treeTableModel); @@ -99,11 +99,11 @@ public class ProjectsAndTasksTable extends JXTreeTable { setBackground(Color.WHITE); //treeTableModel.setSortColumn("Projects & Tasks"); - core.getData().addDataEventListener(treeTableModel); + service.getData().addDataEventListener(treeTableModel); // enable drag n drop setDragEnabled(true); - setTransferHandler(new TimerTaskTransferHandler(application, core.getData())); + setTransferHandler(new TimerTaskTransferHandler(application, service.getData())); { // add action to collapse (left arrow) selected node Action action = new AbstractAction("collapseSelectedNode") { diff --git a/src/main/resources/META-INF/services/org.chorem.jtimer.plugins.Plugin b/src/main/resources/META-INF/services/org.chorem.jtimer.plugins.Plugin new file mode 100644 index 0000000..52aead6 --- /dev/null +++ b/src/main/resources/META-INF/services/org.chorem.jtimer.plugins.Plugin @@ -0,0 +1 @@ +org.chorem.jtimer.plugins.timebundle.TimeBundlePlugin \ No newline at end of file diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 1f9a53b..dd8ab84 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -30,7 +30,7 @@ <Loggers> <Logger name="org.chorem.jtimer" level="info"/> - <Logger name="org.chorem.jtimer.plugin.timebundle" level="debug"/> + <Logger name="org.chorem.jtimer.plugins.timebundle" level="debug"/> <Root level="warn"> <AppenderRef ref="Console"/> diff --git a/src/main/resources/org/chorem/jtimer/ui/report/resources/ReportView.properties b/src/main/resources/org/chorem/jtimer/ui/report/resources/ReportView.properties index 74ef97d..a24f7b0 100644 --- a/src/main/resources/org/chorem/jtimer/ui/report/resources/ReportView.properties +++ b/src/main/resources/org/chorem/jtimer/ui/report/resources/ReportView.properties @@ -35,7 +35,6 @@ reportIncludeTime=Include task times reportAnnotationsTime=with time reportIntermediateTotalTime=Include intermediate total times reportProjects=Projects -reportProjectsList=Projects list currentWeek.Action.icon = date_current.png currentWeek.Action.shortDescription = Current week diff --git a/src/main/resources/org/chorem/jtimer/ui/report/resources/ReportView_fr.properties b/src/main/resources/org/chorem/jtimer/ui/report/resources/ReportView_fr.properties index de9dc69..ab037ee 100644 --- a/src/main/resources/org/chorem/jtimer/ui/report/resources/ReportView_fr.properties +++ b/src/main/resources/org/chorem/jtimer/ui/report/resources/ReportView_fr.properties @@ -35,7 +35,6 @@ reportIncludeTime=Afficher les temps reportAnnotationsTime=avec l'heure reportIntermediateTotalTime=Ajout des temps totaux interm\u00E9diaires reportProjects=Projets -reportProjectsList=Liste des projets currentWeek.Action.shortDescription = Semaine courante diff --git a/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java b/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java index c731bc7..5e56ded 100644 --- a/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java +++ b/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java @@ -105,8 +105,6 @@ 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/data/CommonVetoableTest.java b/src/test/java/org/chorem/jtimer/data/CommonVetoableTest.java index 4f1f08c..2bdb3c9 100644 --- a/src/test/java/org/chorem/jtimer/data/CommonVetoableTest.java +++ b/src/test/java/org/chorem/jtimer/data/CommonVetoableTest.java @@ -23,6 +23,7 @@ package org.chorem.jtimer.data; import org.chorem.jtimer.AbstractJTimerTest; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; import org.testng.Assert; @@ -53,11 +54,11 @@ public class CommonVetoableTest extends AbstractJTimerTest { public void testAddProject() { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); // add new project TimerProject project = new TimerProject("jTimer"); @@ -73,11 +74,11 @@ public class CommonVetoableTest extends AbstractJTimerTest { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "IsisFish/UserInterface"); @@ -95,11 +96,11 @@ public class CommonVetoableTest extends AbstractJTimerTest { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findProject(projectsBefore, "Topia"); TimerTask task2 = findTask(projectsBefore, "jTimer/Add workspace support"); @@ -119,11 +120,11 @@ public class CommonVetoableTest extends AbstractJTimerTest { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "Chorem/Add webservice"); TimerTask task2 = findTask(projectsBefore, "jTimer"); @@ -147,11 +148,11 @@ public class CommonVetoableTest extends AbstractJTimerTest { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "jTimer/Add workspace support"); TimerTask task2 = findTask(projectsBefore, "jTimer/Interact with chorem services"); @@ -180,11 +181,11 @@ public class CommonVetoableTest extends AbstractJTimerTest { @Test(expectedExceptions = DataViolationException.class) public void testMergeTasks() { // first load all - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "Chorem"); TimerTask task2 = findTask(projectsBefore, "jTimer/Unit tests/UI tests"); diff --git a/src/test/java/org/chorem/jtimer/data/TimerDataManagerTest.java b/src/test/java/org/chorem/jtimer/data/TimerDataManagerTest.java index 018d073..8e8f625 100644 --- a/src/test/java/org/chorem/jtimer/data/TimerDataManagerTest.java +++ b/src/test/java/org/chorem/jtimer/data/TimerDataManagerTest.java @@ -23,6 +23,7 @@ package org.chorem.jtimer.data; import org.chorem.jtimer.AbstractJTimerTest; +import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerAlert; import org.chorem.jtimer.entities.TimerAlert.Type; import org.chorem.jtimer.entities.TimerProject; @@ -56,11 +57,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { public void testAddProject() { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "Test project"); @@ -71,12 +72,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { TimerProject project = new TimerProject("Test project"); project.setCreationDate(new Date()); dataManager.addProject(project); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerProject project1a = findProject(projectsAfter, "Test project"); @@ -91,11 +92,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { public void testEditProject() { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "jTimer"); @@ -104,12 +105,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // edit project dataManager.editProject(project1, "Edit Name"); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerProject project1a = findProject(projectsBefore, "Edit Name"); @@ -125,11 +126,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { public void testDeleteProject() { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "jTimer"); @@ -138,12 +139,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // delete project dataManager.deleteProject(project1); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); Assert.assertNull(findProject(projectsAfter, "jTimer")); @@ -158,11 +159,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "IsisFish/UserInterface"); @@ -173,12 +174,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { TimerTask newTask = new TimerTask("new task"); newTask.setCreationDate(new Date()); dataManager.addTask(task1, newTask, null); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "IsisFish/UserInterface"); TimerTask task2a = findTask(projectsAfter, "IsisFish/UserInterface/new task"); @@ -196,11 +197,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "IsisFish/UserInterface"); @@ -209,12 +210,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // edit task name dataManager.editTask(task1, "UI"); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "IsisFish/UI"); TimerTask task2a = findTask(projectsAfter, "IsisFish/UI/Debug"); @@ -232,11 +233,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "IsisFish"); TimerTask task1 = findTask(projectsBefore, "IsisFish/UserInterface"); @@ -247,12 +248,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // delete task dataManager.deleteTask(task1); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerProject project1a = findProject(projectsAfter, "IsisFish"); TimerTask task1a = findTask(projectsAfter, "IsisFish/UserInterface"); @@ -270,11 +271,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "Topia"); TimerTask task2 = findTask(projectsBefore, "jTimer/Add workspace support"); @@ -286,12 +287,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // move task 3 in jTimer/3 dataManager.moveTask(task1, Collections.singleton(task2)); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "Topia"); TimerTask task2a = findTask(projectsAfter, "jTimer/Add workspace support"); @@ -310,11 +311,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "Chorem"); TimerTask task1 = findTask(projectsBefore, "jTimer/Add workspace support"); @@ -336,12 +337,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // move task 3 in jTimer/3 dataManager.moveTask(project1, tasksToMove); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "Chorem/Add workspace support"); TimerTask task2a = findTask(projectsAfter, "Chorem/Interact with chorem services"); @@ -363,11 +364,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { * public void testMergeProjects() { // first load all - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "Topia"); TimerProject project2 = findProject(projectsBefore, "jTimer"); @@ -381,12 +382,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // merge projects dataManager.mergeProjects(project1, Collections.singletonList(project2)); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerProject project1a = findProject(projectsAfter, "Topia"); TimerProject project2a = findProject(projectsAfter, "jTimer"); @@ -403,11 +404,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { @Test public void testMergeTasks() { // first load all - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "jTimer/Refactoring"); TimerTask task2 = findTask(projectsBefore, "jTimer/Unit tests/UI tests"); @@ -425,12 +426,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // merge tasks dataManager.mergeTasks(task1, Collections.singletonList(task2)); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "jTimer/Refactoring"); TimerTask task2a = findTask(projectsAfter, "jTimer/Unit tests/UI tests"); @@ -453,11 +454,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { @Test public void testMergeTasksWithConflict() { // first load all - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "jTimer/Refactoring"); Assert.assertNotNull(task1); @@ -477,12 +478,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // merge tasks dataManager.mergeTasks(task1, Collections.singletonList(task2)); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "jTimer/Refactoring"); TimerTask task2a = findTask(projectsAfter, "jTimer/Unit tests/UI tests"); @@ -505,11 +506,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { public void testAddAlert() { // first load all // and make a move operation - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "jTimer/Refactoring"); @@ -525,12 +526,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { task1.addAlert(alert2); task1.addAlert(alert3); dataManager.modifyAlert(task1); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "jTimer/Refactoring"); @@ -564,11 +565,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { @Test public void testMergeTasksWithAlerts() { // first load all - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "IsisFish/Storage"); TimerTask task2 = findTask(projectsBefore, "IsisFish/Support"); @@ -586,12 +587,12 @@ public class TimerDataManagerTest extends AbstractJTimerTest { othersTask.add(task1); othersTask.add(task3); dataManager.mergeTasks(task2, othersTask); - core.exit(); + service.exit(); // second reload // and test reloaded data - //core.init(); - core.load(); + //service.init(); + service.load(); List<TimerProject> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "IsisFish/Storage"); TimerTask task2a = findTask(projectsAfter, "IsisFish/Support"); @@ -608,11 +609,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { */ @Test public void testGetTaskForPath() { - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); TimerTask task = dataManager.getTaskForPath("jTimer/Unit tests/UI tests"); Assert.assertEquals(task.getName(), "UI tests"); @@ -625,11 +626,11 @@ public class TimerDataManagerTest extends AbstractJTimerTest { */ @Test(expectedExceptions = IllegalArgumentException.class) public void testGetTaskForPathProject() { - TimerCore core = new TimerCore(); - TimerDataManager dataManager = core.getData(); + JTimerService service = new JTimerService(); + TimerDataManager dataManager = service.getData(); - //core.init(); - core.load(); + //service.init(); + service.load(); TimerTask task = dataManager.getTaskForPath("jTimer"); Assert.assertNotNull(task); diff --git a/src/test/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelperTest.java b/src/test/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelperTest.java deleted file mode 100644 index 5fcbe84..0000000 --- a/src/test/java/org/chorem/jtimer/plugin/timebundle/TimeBundleHelperTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * #%L - * jTimer - * %% - * Copyright (C) 2016 CodeLutin - * %% - * 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.DateFormatUtils; -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.util.Calendar; -import java.util.Date; - -/** - * Test related to TimeBundleHelper. - */ -public class TimeBundleHelperTest extends AbstractJTimerTest { - - /** - * Test json production - */ - @Test - public void taskToJSONFormatTest() { - TimerTask task = new TimerTask(); - task.setNumber(42); - task.setName("JsonBuilder Test"); - 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(1451602800000L)); - sync.setLastSync(new Date(1451602800000L)); - //date : 2016-01-18 - task.setTime(new Date(1453071600000L), 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(); - periodElement1.addProperty("id", "2016-01-18_42"); - periodElement1.addProperty("startDate", "2016-01-18T00:00:00+01:00"); - periodElement1.addProperty("duration", 452); - periodArray.add(periodElement1); - JsonObject periodElement2 = new JsonObject(); - periodElement2.addProperty("id", "2016-05-12_42"); - periodElement2.addProperty("startDate", "2016-05-12T00:00:00+02:00"); - periodElement2.addProperty("duration", 4533); - periodArray.add(periodElement2); - - objectToHave.addProperty("URL", "http://localhost:3000"); - objectToHave.addProperty("startDate", "2016-01-01T00:00:00+01:00"); - Date endDate = DateUtils.ceiling(new Date(), Calendar.DAY_OF_MONTH); - String endPeriodString = DateFormatUtils.format(endDate, TimeBundleHelper.DATE_MIDNIGHT_PATTERN); - - objectToHave.addProperty("endDate", endPeriodString); - objectToHave.add("periods", periodArray); - - //make a list of json objects from the task - JsonObject jsonObject = TimeBundleHelper.taskToJsonObject(task, sync); - Assert.assertEquals(jsonObject, objectToHave); - } -} diff --git a/src/test/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSaverTest.java b/src/test/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSaverTest.java deleted file mode 100644 index 06708ec..0000000 --- a/src/test/java/org/chorem/jtimer/plugin/timebundle/TimeBundleSaverTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/*- - * #%L - * jTimer - * %% - * Copyright (C) 2007 - 2016 CodeLutin - * %% - * 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.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; - -/** - * Test class for TimeBundleSaver. - */ -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/resources/testsync/41.task.sync b/src/test/resources/testsync/41.task.sync deleted file mode 100644 index 3a085bf..0000000 --- a/src/test/resources/testsync/41.task.sync +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "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>.
This is an automated email from the git hooks/post-receive script. New commit to branch sync-timebundle in repository jtimer. See https://gitlab.nuiton.org/chorem/jtimer.git commit fa131fdf539352df0e7ebf2f85d9e5332c58acd8 Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Mar 7 14:10:03 2017 +0100 Refactoring of checkbox cell renderer and editor --- .../chorem/jtimer/plugins/timebundle/SyncView.java | 20 ++- .../org/chorem/jtimer/ui/report/ReportView.java | 10 +- ...Editor.java => CheckBoxCellEditorRenderer.java} | 135 +++++++++++++++++---- .../jtimer/ui/tree/CheckBoxTreeCellComponent.java | 112 ----------------- .../jtimer/ui/tree/CheckBoxTreeCellRenderer.java | 71 ----------- 5 files changed, 128 insertions(+), 220 deletions(-) diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java index b8e32c2..e923067 100644 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java @@ -24,13 +24,11 @@ package org.chorem.jtimer.plugins.timebundle; import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerProject; -import org.chorem.jtimer.entities.TimerTask; import org.chorem.jtimer.plugins.timebundle.data.RemoteProject; import org.chorem.jtimer.plugins.timebundle.data.RemoteTask; import org.chorem.jtimer.plugins.timebundle.model.RemoteTaskTreeModel; import org.chorem.jtimer.plugins.timebundle.model.RemoveTaskCellRenderer; -import org.chorem.jtimer.ui.tree.CheckBoxTreeCellEditor; -import org.chorem.jtimer.ui.tree.CheckBoxTreeCellRenderer; +import org.chorem.jtimer.ui.tree.CheckBoxCellEditorRenderer; import org.chorem.jtimer.ui.tree.TaskTreeModel; import org.jdesktop.application.Application; import org.jdesktop.application.FrameView; @@ -53,8 +51,6 @@ import java.awt.event.WindowListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Set; -import java.util.TreeSet; public class SyncView extends FrameView implements TreeSelectionListener, WindowListener { @@ -128,9 +124,9 @@ public class SyncView extends FrameView implements TreeSelectionListener, Window projectsTree.setEditable(true); projectsTree.setShowsRootHandles(true); projectsTree.setModel(treeModel); - TreeSet<TimerTask> selection = new TreeSet<>(); - projectsTree.setCellRenderer(new CheckBoxTreeCellRenderer(service, projectsTree, selection, false)); - projectsTree.setCellEditor(new CheckBoxTreeCellEditor(service, projectsTree, selection, false)); + CheckBoxCellEditorRenderer checkBoxCellEditorRenderer = new CheckBoxCellEditorRenderer(service, projectsTree); + projectsTree.setCellRenderer(checkBoxCellEditorRenderer); + projectsTree.setCellEditor(checkBoxCellEditorRenderer); projectsTree.expandPath(new TreePath(treeModel.getRoot()).pathByAddingChild(timerProject)); JScrollPane treeScroll = new JScrollPane(projectsTree); mainPanel.add(treeScroll, new GridBagConstraints(2, 1, 1, 1, 1, 1, @@ -164,9 +160,11 @@ public class SyncView extends FrameView implements TreeSelectionListener, Window @Override public void valueChanged(TreeSelectionEvent treeSelectionEvent) { RemoteTask task = (RemoteTask)treeSelectionEvent.getPath().getLastPathComponent(); - Set<TimerTask> selection = task.getSyncTasks(); - projectsTree.setCellRenderer(new CheckBoxTreeCellRenderer(service, projectsTree, selection, false)); - projectsTree.setCellEditor(new CheckBoxTreeCellEditor(service, projectsTree, selection, false)); + CheckBoxCellEditorRenderer checkBoxCellEditorRenderer = new CheckBoxCellEditorRenderer(service, projectsTree); + checkBoxCellEditorRenderer.setCheckedTaskSet(task.getSyncTasks()); + checkBoxCellEditorRenderer.setDisableSubChecked(true); + projectsTree.setCellRenderer(checkBoxCellEditorRenderer); + projectsTree.setCellEditor(checkBoxCellEditorRenderer); projectsTree.validate(); } diff --git a/src/main/java/org/chorem/jtimer/ui/report/ReportView.java b/src/main/java/org/chorem/jtimer/ui/report/ReportView.java index 79469fc..2ae6c45 100644 --- a/src/main/java/org/chorem/jtimer/ui/report/ReportView.java +++ b/src/main/java/org/chorem/jtimer/ui/report/ReportView.java @@ -29,8 +29,7 @@ import org.chorem.jtimer.JTimerService; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; import org.chorem.jtimer.ui.report.ReportGenerator.Type; -import org.chorem.jtimer.ui.tree.CheckBoxTreeCellEditor; -import org.chorem.jtimer.ui.tree.CheckBoxTreeCellRenderer; +import org.chorem.jtimer.ui.tree.CheckBoxCellEditorRenderer; import org.chorem.jtimer.ui.tree.TaskTreeModel; import org.jdesktop.application.Action; import org.jdesktop.application.Application; @@ -268,8 +267,11 @@ public class ReportView extends FrameView implements DocumentListener { projectsTree.setShowsRootHandles(true); projectsTree.setEditable(true); projectsTree.setModel(new TaskTreeModel(service)); - projectsTree.setCellEditor(new CheckBoxTreeCellEditor(service, projectsTree, uncheckedTaskSet, true)); - projectsTree.setCellRenderer(new CheckBoxTreeCellRenderer(service, projectsTree, uncheckedTaskSet, true)); + CheckBoxCellEditorRenderer checkBoxCellEditorRenderer = new CheckBoxCellEditorRenderer(service, projectsTree); + checkBoxCellEditorRenderer.setCheckedTaskSet(uncheckedTaskSet); + checkBoxCellEditorRenderer.setUncheck(true); + projectsTree.setCellEditor(checkBoxCellEditorRenderer); + projectsTree.setCellRenderer(checkBoxCellEditorRenderer); JScrollPane jspTable = new JScrollPane(projectsTree); panelProjects.add(jspTable, new GridBagConstraints(0, 1, 1, 1, 1, 1, diff --git a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellEditor.java b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxCellEditorRenderer.java similarity index 55% rename from src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellEditor.java rename to src/main/java/org/chorem/jtimer/ui/tree/CheckBoxCellEditorRenderer.java index b950161..25114af 100644 --- a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellEditor.java +++ b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxCellEditorRenderer.java @@ -33,17 +33,20 @@ import javax.swing.JTree; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.tree.TreeCellEditor; +import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreePath; +import java.awt.Color; import java.awt.Component; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.Collection; import java.util.EventObject; +import java.util.HashSet; import java.util.Set; /** - * JCheckBox tree node cell editor. + * JCheckBox tree node cell component. * * @author chatellier * @version $Revision$ @@ -51,14 +54,28 @@ import java.util.Set; * Last update : $Date$ * By : $Author$ */ -public class CheckBoxTreeCellEditor extends CheckBoxTreeCellComponent implements - TreeCellEditor, ItemListener { +public class CheckBoxCellEditorRenderer extends JCheckBox implements TreeCellRenderer, TreeCellEditor, ItemListener { + + /** log */ + private static Log log = LogFactory.getLog(CheckBoxCellEditorRenderer.class); /** serialVersionUID */ private static final long serialVersionUID = 2497464481840318274L; - /** log */ - private static Log log = LogFactory.getLog(CheckBoxTreeCellEditor.class); + /** Timer service. */ + protected JTimerService service; + + /** Parent tree. */ + protected JTree parentTree; + + /** Unchecked task set. */ + protected Set<TimerTask> checkedTaskSet; + + /** To uncheck instead of checking. */ + protected boolean uncheck; + + /** Disable sub elements if parent is checked. */ + protected boolean disableSubChecked; /** {@link CellEditorListener}s */ protected Collection<CellEditorListener> cellEditorListeners; @@ -66,18 +83,68 @@ public class CheckBoxTreeCellEditor extends CheckBoxTreeCellComponent implements /** * Constructor. * - * @param service service - * @param parentTree tree - * @param checkedTaskSet common unselected tasks list - * @param uncheck to uncheck instead of check by default + * @param service service reference + * @param parentTree tree reference */ - public CheckBoxTreeCellEditor(JTimerService service, JTree parentTree, Set<TimerTask> checkedTaskSet, boolean uncheck) { - super(service, parentTree, checkedTaskSet, uncheck); - addItemListener(this); + public CheckBoxCellEditorRenderer(JTimerService service, JTree parentTree) { + this.service = service; + this.parentTree = parentTree; + this.checkedTaskSet = new HashSet<>(); + addItemListener(this); cellEditorListeners = new ArrayList<>(); } + public void setCheckedTaskSet(Set<TimerTask> checkedTaskSet) { + this.checkedTaskSet = checkedTaskSet; + } + + public void setUncheck(boolean uncheck) { + this.uncheck = uncheck; + } + + public void setDisableSubChecked(boolean disableSubChecked) { + this.disableSubChecked = disableSubChecked; + } + + /** + * Generic check box component. + * + * @param tree tree + * @param value value + * @param selected selected + * @param expanded expanded + * @param leaf leaf + * @param row row + * @return check box component + */ + protected Component getCheckBoxComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) { + + // if this is a task + if (value instanceof TimerTask) { + TimerTask task = (TimerTask) value; + + // task name should not be "null" + this.setText(task.getName()); + + // add color if task is closed + if (task.isClosed()) { + this.setForeground(Color.GRAY); + } else { + this.setForeground(Color.BLACK); + } + + // set checked ? + if (checkedTaskSet.contains(task) == uncheck) { + this.setSelected(false); + } else { + this.setSelected(true); + } + } + + return this; + } + @Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) { @@ -146,9 +213,9 @@ public class CheckBoxTreeCellEditor extends CheckBoxTreeCellComponent implements if (log.isDebugEnabled()) { log.debug("Current edition = " + editingPath); } - updateChildren(editingPath, checkbox.isSelected()); + updateChildren(editingPath, checkbox.isSelected(), false); - // too refresh selected/unselected checkbox + // to refresh selected/unselected checkbox parentTree.repaint(); } @@ -162,16 +229,14 @@ public class CheckBoxTreeCellEditor extends CheckBoxTreeCellComponent implements * @param treePath current tree path * @param select select (true) or deselect */ - protected void updateChildren(TreePath treePath, boolean select) { - TimerTask lastPathComponent = (TimerTask) treePath - .getLastPathComponent(); + protected void updateChildren(TreePath treePath, boolean select, boolean disableCurrent) { + TimerTask lastPathComponent = (TimerTask) treePath.getLastPathComponent(); if (log.isDebugEnabled()) { - log.debug("Recursive modification of = " - + lastPathComponent.getName()); + log.debug("Recursive modification of = " + lastPathComponent.getName()); } - if (select == uncheck) { + if (select == uncheck || disableCurrent) { checkedTaskSet.remove(lastPathComponent); } else { checkedTaskSet.add(lastPathComponent); @@ -181,13 +246,39 @@ public class CheckBoxTreeCellEditor extends CheckBoxTreeCellComponent implements if (parentTree.getModel().getRoot() == lastPathComponent) { for (TimerProject project : service.getData().getProjectsList()) { TreePath subTreePath = treePath.pathByAddingChild(project); - updateChildren(subTreePath, select); + updateChildren(subTreePath, select, disableSubChecked); } } else { for (TimerTask subtask : lastPathComponent.getSubTasks()) { TreePath subTreePath = treePath.pathByAddingChild(subtask); - updateChildren(subTreePath, select); + updateChildren(subTreePath, select, disableSubChecked); } } } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, + boolean leaf, int row, boolean hasFocus) { + boolean enable = true; + if (disableSubChecked && value instanceof TimerTask) { + TimerTask timerTask = (TimerTask)value; + + do { + timerTask = timerTask.getParent(); + if (timerTask != null) { + enable = !checkedTaskSet.contains(timerTask); + } + } while (timerTask != null && enable); + } + + Component c = getCheckBoxComponent(tree, value, selected, expanded, leaf, row); + + + // fix a strange bug where checkbox does have a "gray" background + c.setBackground(tree.getBackground()); + c.setFocusable(false); + c.setEnabled(enable); + + return c; + } } diff --git a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellComponent.java b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellComponent.java deleted file mode 100644 index 0a08e49..0000000 --- a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellComponent.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * #%L - * jTimer - * %% - * Copyright (C) 2009 - 2016 CodeLutin - * %% - * 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.tree; - -import org.chorem.jtimer.JTimerService; -import org.chorem.jtimer.entities.TimerTask; - -import javax.swing.JCheckBox; -import javax.swing.JTree; -import java.awt.Color; -import java.awt.Component; -import java.util.Set; - -/** - * JCheckBox tree node cell component. - * - * @author chatellier - * @version $Revision$ - * - * Last update : $Date$ - * By : $Author$ - */ -public abstract class CheckBoxTreeCellComponent extends JCheckBox { - - /** serialVersionUID */ - private static final long serialVersionUID = 2497464481840318274L; - - /** Timer service. */ - protected JTimerService service; - - /** Parent tree. */ - protected JTree parentTree; - - /** Unchecked task set. */ - protected Set<TimerTask> checkedTaskSet; - - /** To uncheck instead of checking. */ - protected boolean uncheck; - - /** - * Constructor. - * - * @param service service reference - * @param parentTree tree reference - * @param checkedTaskSet common unselected tasks list - * @param uncheck to uncheck instead of check by default - */ - protected CheckBoxTreeCellComponent(JTimerService service, JTree parentTree, Set<TimerTask> checkedTaskSet, boolean uncheck) { - this.service = service; - this.parentTree = parentTree; - this.checkedTaskSet = checkedTaskSet; - this.uncheck = uncheck; - } - - /** - * Generic check box component. - * - * @param tree tree - * @param value value - * @param selected selected - * @param expanded expanded - * @param leaf leaf - * @param row row - * @return check box component - */ - protected Component getCheckBoxComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) { - - // if this is a task - if (value instanceof TimerTask) { - TimerTask task = (TimerTask) value; - - // task name should not be "null" - this.setText(task.getName()); - - // add color if task is closed - if (task.isClosed()) { - this.setForeground(Color.GRAY); - } else { - this.setForeground(Color.BLACK); - } - - // set checked ? - if (checkedTaskSet.contains(task) == uncheck) { - this.setSelected(false); - } else { - this.setSelected(true); - } - } - - return this; - } -} diff --git a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellRenderer.java b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellRenderer.java deleted file mode 100644 index 93f07ee..0000000 --- a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellRenderer.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * #%L - * jTimer - * %% - * Copyright (C) 2009 - 2011 CodeLutin - * %% - * 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.tree; - -import org.chorem.jtimer.JTimerService; -import org.chorem.jtimer.entities.TimerTask; - -import javax.swing.JTree; -import javax.swing.tree.TreeCellRenderer; -import java.awt.Component; -import java.util.Set; - -/** - * JCheckBox tree node cell renderer. - * - * @author chatellier - * @version $Revision$ - * - * Last update : $Date$ - * By : $Author$ - */ -public class CheckBoxTreeCellRenderer extends CheckBoxTreeCellComponent - implements TreeCellRenderer { - - /** serialVersionUID */ - private static final long serialVersionUID = 2497464481840318274L; - - /** - * Constructor. - * - * @param service service - * @param parentTree tree - * @param checkedTaskList common unselected tasks list - * @param uncheck to uncheck instead of check by default - */ - public CheckBoxTreeCellRenderer(JTimerService service, JTree parentTree, - Set<TimerTask> checkedTaskList, boolean uncheck) { - super(service, parentTree, checkedTaskList, uncheck); - } - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, - boolean leaf, int row, boolean hasFocus) { - Component c = getCheckBoxComponent(tree, value, selected, expanded, leaf, row); - - // fix a strange bug where checkbox does have a "gray" background - c.setBackground(tree.getBackground()); - - return c; - } -} -- 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 sync-timebundle in repository jtimer. See https://gitlab.nuiton.org/chorem/jtimer.git commit 534767e281d7db20e262cd1da83dbf470fb4d525 Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Mar 7 15:34:45 2017 +0100 Manage sub task times and project total sync --- .../plugins/timebundle/RemoteSynchronizer.java | 54 +++++++++------------- .../jtimer/plugins/timebundle/SyncIOSaver.java | 41 +++++++++------- .../chorem/jtimer/plugins/timebundle/SyncView.java | 7 +-- 3 files changed, 47 insertions(+), 55 deletions(-) diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java index 1e85162..7ab4853 100644 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java @@ -41,8 +41,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; @@ -198,6 +196,9 @@ public class RemoteSynchronizer implements Synchronizer { } protected void syncTask(RemoteTask remoteTask) { + if (log.isDebugEnabled()) { + log.debug("Syncing timebundle task " + remoteTask.getName()); + } String url = remoteTask.getUrl(); Set<TimerTask> syncTasks = remoteTask.getSyncTasks(); @@ -205,19 +206,22 @@ public class RemoteSynchronizer implements Synchronizer { jsonObject.put("startDate", DateFormatUtils.format(new Date(0), DATE_MIDNIGHT_PATTERN)); jsonObject.put("endDate", DateFormatUtils.format(new Date(), DATE_MIDNIGHT_PATTERN)); JSONArray periodArray = new JSONArray(); - syncTasks.forEach(timerTask -> { - timerTask.getAllDaysAndTimes().entrySet().forEach(dateLongEntry -> { - JSONObject period = new JSONObject(); - period.put("id", String.valueOf(timerTask.getNumber()) + DateFormatUtils.format(dateLongEntry.getKey(), DATE_MIDNIGHT_PATTERN)); - period.put("startDate", DateFormatUtils.format(dateLongEntry.getKey(), DATE_MIDNIGHT_PATTERN)); - period.put("duration", dateLongEntry.getValue() / 1000L); - periodArray.put(period); - }); - }); + syncTasks.forEach(timerTask -> addTaskTime(periodArray, timerTask)); jsonObject.put("periods", periodArray); post(url, jsonObject); } + protected void addTaskTime(JSONArray periodArray, TimerTask timerTask) { + timerTask.getAllDaysAndTimes().entrySet().forEach(dateLongEntry -> { + JSONObject period = new JSONObject(); + period.put("id", String.valueOf(timerTask.getNumber()) + DateFormatUtils.format(dateLongEntry.getKey(), DATE_MIDNIGHT_PATTERN)); + period.put("startDate", DateFormatUtils.format(dateLongEntry.getKey(), DATE_MIDNIGHT_PATTERN)); + period.put("duration", dateLongEntry.getValue() / 1000L); + periodArray.put(period); + }); + timerTask.getSubTasks().forEach(subTask -> addTaskTime(periodArray, subTask)); + } + protected static void post(String url, JSONObject data) { try { URI uri = new URI(url); @@ -228,30 +232,14 @@ public class RemoteSynchronizer implements Synchronizer { OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); out.write(data.toString(2)); out.close(); - System.out.println(conn.getResponseCode()); + if (log.isDebugEnabled()) { + log.debug("Sync url " + url + "(" + conn.getResponseCode() + ")"); + } } catch (IOException | URISyntaxException ex) { log.error("ex", ex); } - System.out.println("will send : " + data.toString(2)); - } - - public static void main(String... args) { - TimerProject project = new TimerProject("jTimer"); - List<RemoteProject> tbprojects = new ArrayList<>(); - RemoteProject tbProject = new RemoteProject(); - tbProject.setUrl("http://localhost:8080/api/v1/codelutin/contribute/3a526a69-59b9-45d2-bde2-09..."); - RemoteProject tbTask = new RemoteProject(); - tbTask.setUrl("http://localhost:8080/api/v1/codelutin/contribute/3a526a69-59b9-45d2-bde2-09..."); - - TimerTask task1 = new TimerTask(); - task1.setCreationDate(new Date()); - task1.setTime(new Date(), 42L); - tbTask.setSyncTasks(Collections.singleton(task1)); - tbProject.setSubTasks(Collections.singletonList(tbTask)); - tbprojects.add(tbProject); - project.getMeta().put(Constants.META, tbprojects); - - RemoteSynchronizer r = new RemoteSynchronizer(); - r.sync(project); + /*if (log.isDebugEnabled()) { + log.debug("will send : " + data.toString(2)); + }*/ } } diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncIOSaver.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncIOSaver.java index 3fea5e3..716d566 100644 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncIOSaver.java +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncIOSaver.java @@ -23,6 +23,7 @@ package org.chorem.jtimer.plugins.timebundle; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import org.apache.commons.collections4.CollectionUtils; @@ -47,6 +48,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.function.Function; @@ -56,9 +58,7 @@ public class SyncIOSaver { protected static Log log = LogFactory.getLog(SyncIOSaver.class); - protected static final String TIMEBUNDLE_DATA_DIRECTORY = "timebundledatabeta1"; - - protected static final String TIMEBUNDLE_CACHE_DIRECTORY = "timebundlecachebeta1"; + protected static final String TIMEBUNDLE_DATA_DIRECTORY = "timebundlebeta1"; protected File dataDirectory; @@ -68,8 +68,6 @@ public class SyncIOSaver { File homeDirectory = config.getHomeDirectory(); dataDirectory = new File(homeDirectory, TIMEBUNDLE_DATA_DIRECTORY); dataDirectory.mkdirs(); - cacheDirectory = new File(homeDirectory, TIMEBUNDLE_CACHE_DIRECTORY); - cacheDirectory.mkdirs(); } public void saveProject(TimerProject project) { @@ -90,7 +88,7 @@ public class SyncIOSaver { } protected void saveSyncInfo(TimerProject project, List<RemoteProject> remoteProjects) throws IOException { - Gson gson = new Gson(); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); String json = gson.toJson(remoteProjects); File jsonFile = new File(dataDirectory, project.getNumber() + ".json"); FileUtils.writeStringToFile(jsonFile, json, StandardCharsets.UTF_8); @@ -99,8 +97,7 @@ public class SyncIOSaver { protected void saveTaskLinks(TimerProject project, List<RemoteProject> remoteProjects) throws IOException { Properties props = new Properties(); - remoteProjects.forEach(remoteProject -> saveTaskLinks(props, remoteProject.getSubTasks())); - + remoteProjects.forEach(remoteProject -> saveTaskLinks(props, remoteProject.getSubTasks(), project)); File syncFile = new File(dataDirectory, project.getNumber() + ".sync"); if (props.isEmpty()) { @@ -112,18 +109,26 @@ public class SyncIOSaver { } } - private void saveTaskLinks(Properties props, List<RemoteTask> remoteTasks) { + private void saveTaskLinks(Properties props, List<RemoteTask> remoteTasks, TimerProject project) { if (remoteTasks != null) { remoteTasks.forEach(remoteTask -> { - String values = remoteTask.getSyncTasks() - .stream() - .mapToInt(TimerTask::getNumber) - .mapToObj(Integer::toString) - .collect(Collectors.joining(",")); - if (!values.isEmpty()) { - props.put(remoteTask.getUuid(), values); + if (remoteTask.getSyncTasks() != null) { + Set<TimerTask> syncTasks = remoteTask.getSyncTasks(); + String values; + if (syncTasks.contains(project)) { + values = "-1"; // FIXME HACK + } else { + values = syncTasks + .stream() + .mapToInt(TimerTask::getNumber) + .mapToObj(Integer::toString) + .collect(Collectors.joining(",")); + } + if (!values.isEmpty()) { + props.put(remoteTask.getUuid(), values); + } } - saveTaskLinks(props, remoteTask.getSubTasks()); + saveTaskLinks(props, remoteTask.getSubTasks(), project); }); } } @@ -168,6 +173,7 @@ public class SyncIOSaver { props.load(syncReader); Map<Integer, TimerTask> timerTaskByNumber = new HashMap<>(); loadTimerTaskMap(timerTaskByNumber, project.getSubTasks()); + timerTaskByNumber.put(-1, project); // FIXME HACK // match remote and sync remoteTaskById.entrySet().forEach(entry -> { @@ -178,6 +184,7 @@ public class SyncIOSaver { Set<TimerTask> syncTasks = Arrays.stream(syncList.split("\\s*,\\s*")) .map(Integer::valueOf) .map(timerTaskByNumber::get) + .filter(Objects::nonNull) // HACK .collect(Collectors.toSet()); value.setSyncTasks(syncTasks); } diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java index e923067..2c31658 100644 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java @@ -141,11 +141,8 @@ public class SyncView extends FrameView implements TreeSelectionListener, Window } public void addRemoteProject() { - String url = (String)JOptionPane.showInputDialog(this.getFrame(), - "New remote project url", "Add remote project", - JOptionPane.INFORMATION_MESSAGE, null, - new Object[] {"http://localhost:8080/api/v1/codelutin/contribute/9370ee7e-7767-482c-b798-7a7262bc417f/#jTimer"}, - "http://localhost:8080/api/v1/codelutin/contribute/9370ee7e-7767-482c-b798-7a..."); + String url = JOptionPane.showInputDialog(this.getFrame(), + "New remote project url", "Add remote project"); RemoteProject project = synchronizer.fetchProject(url); getRemoteProjects().add(project); -- 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 sync-timebundle in repository jtimer. See https://gitlab.nuiton.org/chorem/jtimer.git commit 5c38397c9a701f39e6057f8645533205225dbf83 Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Mar 7 16:59:27 2017 +0100 Add reload project option. Add doc --- src/main/java/org/chorem/jtimer/JTimer.java | 1 + src/main/java/org/chorem/jtimer/JTimerConfig.java | 12 ++- src/main/java/org/chorem/jtimer/JTimerService.java | 16 +++- .../plugins/timebundle/LocalSynchronizer.java | 30 ------ .../plugins/timebundle/RemoteSynchronizer.java | 28 +++++- .../chorem/jtimer/plugins/timebundle/SyncView.java | 103 +++++++++++++++------ .../jtimer/plugins/timebundle/Synchronizer.java | 27 ------ .../jtimer/plugins/timebundle/data/RemoteTask.java | 15 +++ .../timebundle/model/RemoteTaskTreeModel.java | 13 ++- .../jtimer/ui/tree/CheckBoxCellEditorRenderer.java | 2 +- src/site/rst/configuration.rst | 9 ++ src/site/rst/timebundle.rst | 30 ------ 12 files changed, 159 insertions(+), 127 deletions(-) diff --git a/src/main/java/org/chorem/jtimer/JTimer.java b/src/main/java/org/chorem/jtimer/JTimer.java index 96ec4b8..b2eb5ab 100644 --- a/src/main/java/org/chorem/jtimer/JTimer.java +++ b/src/main/java/org/chorem/jtimer/JTimer.java @@ -194,6 +194,7 @@ public class JTimer extends SingleFrameApplication implements // init timerservice service = new JTimerService(); + service.initPlugins(config); // Systray mgr systrayManager = new SystrayManager(this); diff --git a/src/main/java/org/chorem/jtimer/JTimerConfig.java b/src/main/java/org/chorem/jtimer/JTimerConfig.java index bc1f2c2..4084dca 100644 --- a/src/main/java/org/chorem/jtimer/JTimerConfig.java +++ b/src/main/java/org/chorem/jtimer/JTimerConfig.java @@ -38,6 +38,7 @@ import java.nio.file.DirectoryStream; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -178,11 +179,12 @@ public class JTimerConfig { } /** - * Returns the synchronizer class - * @return + * Returns the activated plugins. + * + * @return activated plugins */ - public Class<?> getIOSynchronizerClass() { - return appConfig.getOptionAsClass(JTimerOption.IO_SYNC_CLASS.key); + public List<String> getPlugins() { + return appConfig.getOptionAsList(JTimerOption.PLUGINS.key).getOption(); } /** @@ -297,7 +299,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", null), + PLUGINS("jtimer.plugins", null), UI_IDLE_TIME("jtimer.ui.idletime", "300"), UI_SHOW_CLOSED("jtimer.ui.showclosed", "false"), diff --git a/src/main/java/org/chorem/jtimer/JTimerService.java b/src/main/java/org/chorem/jtimer/JTimerService.java index 342fb92..0a7ee51 100644 --- a/src/main/java/org/chorem/jtimer/JTimerService.java +++ b/src/main/java/org/chorem/jtimer/JTimerService.java @@ -96,7 +96,11 @@ public class JTimerService { data.addVetoableDataEventListener(saver); data.addDataEventListener(saver); } + } + + public void initPlugins(JTimerConfig config) { + List<String> configPlugins = config.getPlugins(); ServiceLoader<Plugin> load = ServiceLoader.load(Plugin.class); load.forEach(plugin -> { // extract package name @@ -105,10 +109,16 @@ public class JTimerService { String pluginName = StringUtils.substringBefore(pluginPackage, "."); // loading - if (log.isInfoEnabled()) { - log.info("Loading plugin : " + pluginName); + if (configPlugins.contains(pluginName)) { + if (log.isInfoEnabled()) { + log.info("Loading plugin : " + pluginName); + } + activePlugins.add(plugin); + } else { + if (log.isInfoEnabled()) { + log.info("Plugin disabled : " + pluginName); + } } - activePlugins.add(plugin); }); activePlugins.forEach(p -> p.register(this)); diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/LocalSynchronizer.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/LocalSynchronizer.java deleted file mode 100644 index b70c2f9..0000000 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/LocalSynchronizer.java +++ /dev/null @@ -1,30 +0,0 @@ -/*- - * #%L - * jTimer - * %% - * Copyright (C) 2017 CodeLutin - * %% - * 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.plugins.timebundle; - -/** - * Synchroniser that fallback to local cache in case remote call are not working. - */ -public class LocalSynchronizer implements Synchronizer { - -} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java index 7ab4853..2d76ad5 100644 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/RemoteSynchronizer.java @@ -22,7 +22,8 @@ package org.chorem.jtimer.plugins.timebundle; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ListUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.time.DateFormatUtils; import org.apache.commons.logging.Log; @@ -47,7 +48,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -public class RemoteSynchronizer implements Synchronizer { +public class RemoteSynchronizer { protected static Log log = LogFactory.getLog(RemoteSynchronizer.class); @@ -162,6 +163,29 @@ public class RemoteSynchronizer implements Synchronizer { return project; } + public void reloadProject(RemoteProject project) { + RemoteProject newProject = fetchProject(project.getUrl()); + merge(project, newProject); + } + + public void merge(RemoteTask currentTask, RemoteTask newTask) { + currentTask.setName(newTask.getName()); + currentTask.setUrl(newTask.getUrl()); + + if (currentTask.getSubTasks() != null && newTask.getSubTasks() != null) { + List<RemoteTask> currentTasks = ListUtils.retainAll(currentTask.getSubTasks(), newTask.getSubTasks()); // still existing tasks + currentTasks.forEach(subTask -> { + RemoteTask newSubTask = newTask.getSubTasks().stream() + .filter(task -> task.getUuid().equals(subTask.getUuid())) + .findFirst().orElse(null); + merge(subTask, newSubTask); + }); + List<RemoteTask> newTasks = ListUtils.removeAll(newTask.getSubTasks(), currentTask.getSubTasks()); + currentTasks.addAll(newTasks); + currentTask.setSubTasks(currentTasks); + } + } + /** * Sync project by pushing all sub tasks times on: * http://localhost:8080/api/v1/codelutin/contribute/a7e14ea2-fde7-4e80-aff3-03... diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java index 2c31658..17be4eb 100644 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java @@ -43,8 +43,10 @@ import javax.swing.JTree; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.awt.GridLayout; import java.awt.Insets; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; @@ -60,10 +62,13 @@ public class SyncView extends FrameView implements TreeSelectionListener, Window protected TimerProject timerProject; protected JTree remoteProjectTree = new JTree(); - protected RemoteTaskTreeModel projectModel; + protected RemoteTaskTreeModel remoteProjectModel; protected JTree projectsTree = new JTree(); - protected TaskTreeModel treeModel; + protected TaskTreeModel projectModel; + + protected JButton removeProject; + protected JButton reloadProject; public SyncView(Application application, JTimerService service, SyncIOSaver saver, RemoteSynchronizer synchronizer, TimerProject timerProject) { @@ -78,8 +83,8 @@ public class SyncView extends FrameView implements TreeSelectionListener, Window getFrame().setTitle("Time bundle"); List<RemoteProject> timebundle = getRemoteProjects(); - projectModel = new RemoteTaskTreeModel(timebundle); - treeModel = new TaskTreeModel(Collections.singletonList(timerProject)); + remoteProjectModel = new RemoteTaskTreeModel(timebundle); + projectModel = new TaskTreeModel(Collections.singletonList(timerProject)); setComponent(getMainComponent()); @@ -104,39 +109,53 @@ public class SyncView extends FrameView implements TreeSelectionListener, Window remoteProjectTree.setRootVisible(false); remoteProjectTree.setShowsRootHandles(true); - remoteProjectTree.setModel(projectModel); + remoteProjectTree.setModel(remoteProjectModel); remoteProjectTree.setCellRenderer(new RemoveTaskCellRenderer()); remoteProjectTree.addTreeSelectionListener(this); + remoteProjectTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); JScrollPane projectScroll = new JScrollPane(remoteProjectTree); mainPanel.add(projectScroll, new GridBagConstraints(0, 1, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(1, 1, 1, 1), 0, 0)); - JButton addProject = new JButton("Add project"); - addProject.addActionListener(l -> addRemoteProject()); - mainPanel.add(addProject, new GridBagConstraints(0, 2, 1, 1, 0, 0, - GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(1, 1, 1, 1), 0, 0)); - JLabel treeLabel = new JLabel("Local projects:"); - mainPanel.add(treeLabel, new GridBagConstraints(2, 0, 1, 1, 0, 0, + mainPanel.add(treeLabel, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(1, 1, 1, 1), 0, 0)); projectsTree.setRootVisible(false); projectsTree.setEditable(true); projectsTree.setShowsRootHandles(true); - projectsTree.setModel(treeModel); + projectsTree.setModel(projectModel); + projectsTree.setEnabled(false); + projectsTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); CheckBoxCellEditorRenderer checkBoxCellEditorRenderer = new CheckBoxCellEditorRenderer(service, projectsTree); projectsTree.setCellRenderer(checkBoxCellEditorRenderer); projectsTree.setCellEditor(checkBoxCellEditorRenderer); - projectsTree.expandPath(new TreePath(treeModel.getRoot()).pathByAddingChild(timerProject)); + projectsTree.expandPath(new TreePath(projectModel.getRoot()).pathByAddingChild(timerProject)); JScrollPane treeScroll = new JScrollPane(projectsTree); - mainPanel.add(treeScroll, new GridBagConstraints(2, 1, 1, 1, 1, 1, + mainPanel.add(treeScroll, new GridBagConstraints(1, 1, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(1, 1, 1, 1), 0, 0)); + JPanel buttonPanel = new JPanel(new GridLayout(0, 4)); + JButton addProject = new JButton("Add project"); + addProject.addActionListener(l -> addRemoteProject()); + buttonPanel.add(addProject); + + reloadProject = new JButton("Reload project"); + reloadProject.addActionListener(l -> reloadRemoteProject()); + reloadProject.setEnabled(false); + buttonPanel.add(reloadProject); + + removeProject = new JButton("Remove project"); + removeProject.addActionListener(l -> removeRemoteProject()); + removeProject.setEnabled(false); + buttonPanel.add(removeProject); + JButton syncAll = new JButton("Sync"); syncAll.addActionListener(l -> syncProjects()); - mainPanel.add(syncAll, new GridBagConstraints(2, 2, 1, 1, 0, 0, - GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(1, 1, 1, 1), 0, 0)); + buttonPanel.add(syncAll); + mainPanel.add(buttonPanel, new GridBagConstraints(0, 2, 2, 1, 0, 0, + GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(1, 1, 1, 1), 0, 0)); return mainPanel; } @@ -144,10 +163,26 @@ public class SyncView extends FrameView implements TreeSelectionListener, Window String url = JOptionPane.showInputDialog(this.getFrame(), "New remote project url", "Add remote project"); - RemoteProject project = synchronizer.fetchProject(url); - getRemoteProjects().add(project); - projectModel.addElement(project); - remoteProjectTree.expandPath(new TreePath(projectModel.getRoot()).pathByAddingChild(project)); + RemoteProject remoteProject = synchronizer.fetchProject(url); + if (remoteProject != null) { + getRemoteProjects().add(remoteProject); + remoteProjectModel.addElement(remoteProject); + remoteProjectTree.expandPath(new TreePath(remoteProjectModel.getRoot()).pathByAddingChild(remoteProject)); + } + } + + public void removeRemoteProject() { + TreePath selectionPath = remoteProjectTree.getSelectionPath(); + RemoteProject remoteProject = (RemoteProject)selectionPath.getLastPathComponent(); + remoteProjectModel.removeElement(remoteProject); + getRemoteProjects().remove(remoteProject); + } + + public void reloadRemoteProject() { + TreePath selectionPath = remoteProjectTree.getSelectionPath(); + RemoteProject remoteProject = (RemoteProject)selectionPath.getLastPathComponent(); + synchronizer.reloadProject(remoteProject); + remoteProjectModel.changeElement(remoteProject); } public void syncProjects() { @@ -156,13 +191,27 @@ public class SyncView extends FrameView implements TreeSelectionListener, Window @Override public void valueChanged(TreeSelectionEvent treeSelectionEvent) { - RemoteTask task = (RemoteTask)treeSelectionEvent.getPath().getLastPathComponent(); - CheckBoxCellEditorRenderer checkBoxCellEditorRenderer = new CheckBoxCellEditorRenderer(service, projectsTree); - checkBoxCellEditorRenderer.setCheckedTaskSet(task.getSyncTasks()); - checkBoxCellEditorRenderer.setDisableSubChecked(true); - projectsTree.setCellRenderer(checkBoxCellEditorRenderer); - projectsTree.setCellEditor(checkBoxCellEditorRenderer); - projectsTree.validate(); + projectsTree.setEnabled(false); + removeProject.setEnabled(false); + reloadProject.setEnabled(false); + + TreePath newLeadSelectionPath = treeSelectionEvent.getNewLeadSelectionPath(); + if (newLeadSelectionPath != null) { + RemoteTask task = (RemoteTask) newLeadSelectionPath.getLastPathComponent(); + if (task instanceof RemoteProject) { + removeProject.setEnabled(true); + reloadProject.setEnabled(true); + } else { + CheckBoxCellEditorRenderer checkBoxCellEditorRenderer = new CheckBoxCellEditorRenderer(service, projectsTree); + checkBoxCellEditorRenderer.setCheckedTaskSet(task.getSyncTasks()); + checkBoxCellEditorRenderer.setDisableSubChecked(true); + projectsTree.setCellRenderer(checkBoxCellEditorRenderer); + projectsTree.setCellEditor(checkBoxCellEditorRenderer); + projectsTree.validate(); + projectsTree.setEnabled(true); + } + } + } @Override diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/Synchronizer.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/Synchronizer.java deleted file mode 100644 index e15ba9f..0000000 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/Synchronizer.java +++ /dev/null @@ -1,27 +0,0 @@ -/*- - * #%L - * jTimer - * %% - * Copyright (C) 2017 CodeLutin - * %% - * 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.plugins.timebundle; - -public interface Synchronizer { - -} diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/data/RemoteTask.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/data/RemoteTask.java index 699cf88..0a98ffd 100644 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/data/RemoteTask.java +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/data/RemoteTask.java @@ -80,6 +80,21 @@ public class RemoteTask implements Serializable { public List<RemoteTask> getSubTasks() { return subTasks; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RemoteTask that = (RemoteTask) o; + + return uuid != null ? uuid.equals(that.uuid) : that.uuid == null; + } + + @Override + public int hashCode() { + return uuid != null ? uuid.hashCode() : 0; + } } diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/model/RemoteTaskTreeModel.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/model/RemoteTaskTreeModel.java index 00357c6..22dca99 100644 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/model/RemoteTaskTreeModel.java +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/model/RemoteTaskTreeModel.java @@ -22,6 +22,7 @@ package org.chorem.jtimer.plugins.timebundle.model; +import org.chorem.jtimer.plugins.timebundle.data.RemoteProject; import org.chorem.jtimer.plugins.timebundle.data.RemoteTask; import org.jdesktop.swingx.tree.TreeModelSupport; @@ -85,8 +86,8 @@ public class RemoteTaskTreeModel implements TreeModel { @Override public int getIndexOfChild(Object parent, Object child) { - int count = getChildrenFor(parent).indexOf(parent); - return count; + int index = getChildrenFor(parent).indexOf(parent); + return index; } @Override @@ -118,4 +119,12 @@ public class RemoteTaskTreeModel implements TreeModel { // FIXME change this modelSupport.fireNewRoot(); } + + public void removeElement(RemoteProject remoteProject) { + modelSupport.fireChildRemoved(new TreePath(getRoot()), projects.indexOf(remoteProject), remoteProject); + } + + public void changeElement(RemoteProject remoteProject) { + modelSupport.fireChildChanged(new TreePath(getRoot()), projects.indexOf(remoteProject), remoteProject); + } } diff --git a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxCellEditorRenderer.java b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxCellEditorRenderer.java index 25114af..0cb22e8 100644 --- a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxCellEditorRenderer.java +++ b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxCellEditorRenderer.java @@ -277,7 +277,7 @@ public class CheckBoxCellEditorRenderer extends JCheckBox implements TreeCellRen // fix a strange bug where checkbox does have a "gray" background c.setBackground(tree.getBackground()); c.setFocusable(false); - c.setEnabled(enable); + c.setEnabled(tree.isEnabled() && enable); return c; } diff --git a/src/site/rst/configuration.rst b/src/site/rst/configuration.rst index c5b78d6..79093fc 100644 --- a/src/site/rst/configuration.rst +++ b/src/site/rst/configuration.rst @@ -58,3 +58,12 @@ For example, in file ``templatename1.yml``:: sub database task2: meetings: releases: + +Plugin: TimeBundle +------------------ + +Synchronization with time bundle is currently under developpement and can be enabled as a plugin. + +To enable it, modify configuration with following line:: + + jtimer.plugins=timebundle diff --git a/src/site/rst/timebundle.rst b/src/site/rst/timebundle.rst deleted file mode 100644 index 386d032..0000000 --- a/src/site/rst/timebundle.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. - -.. * #%L -.. * jTimer -.. * %% -.. * Copyright (C) 2016 CodeLutin -.. * %% -.. * 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 -- 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 sync-timebundle in repository jtimer. See https://gitlab.nuiton.org/chorem/jtimer.git commit 44c6e82a7cb6d5f74b1c9ef3b0ec195b9457228c Author: Eric Chatellier <chatellier@codelutin.com> Date: Tue Mar 7 17:03:46 2017 +0100 Remove default input --- src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java index 17be4eb..898cabf 100644 --- a/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java +++ b/src/main/java/org/chorem/jtimer/plugins/timebundle/SyncView.java @@ -161,7 +161,7 @@ public class SyncView extends FrameView implements TreeSelectionListener, Window public void addRemoteProject() { String url = JOptionPane.showInputDialog(this.getFrame(), - "New remote project url", "Add remote project"); + "New remote project url"); RemoteProject remoteProject = synchronizer.fetchProject(url); if (remoteProject != null) { -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
participants (1)
-
chorem.org scm