branch 1.6-database-h2 created (now 250b866)
This is an automated email from the git hooks/post-receive script. New change to branch 1.6-database-h2 in repository jtimer. See https://gitlab.nuiton.org/chorem/jtimer.git at 250b866 refs #142: Begin h2 storage refactoring This branch includes the following new commits: new 250b866 refs #142: Begin h2 storage refactoring The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit 250b866c0b0ef50fae29aa636ac557dcdbeda29e Author: Eric Chatellier <chatellier@codelutin.com> Date: Thu May 31 14:16:38 2018 +0200 refs #142: Begin h2 storage refactoring -- 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 1.6-database-h2 in repository jtimer. See https://gitlab.nuiton.org/chorem/jtimer.git commit 250b866c0b0ef50fae29aa636ac557dcdbeda29e Author: Eric Chatellier <chatellier@codelutin.com> Date: Thu May 31 14:16:38 2018 +0200 refs #142: Begin h2 storage refactoring --- pom.xml | 6 + src/main/java/org/chorem/jtimer/JTimer.java | 7 +- src/main/java/org/chorem/jtimer/JTimerConfig.java | 11 ++ src/main/java/org/chorem/jtimer/JTimerUtils.java | 54 +++++ .../org/chorem/jtimer/data/CommonVetoable.java | 7 +- .../java/org/chorem/jtimer/data/TimerCore.java | 38 ++-- .../org/chorem/jtimer/data/TimerDataManager.java | 31 +-- src/main/java/org/chorem/jtimer/db/Database.java | 60 ++++++ .../java/org/chorem/jtimer/db/DbDataMigration.java | 67 +++++++ .../java/org/chorem/jtimer/db/DbException.java | 36 ++++ src/main/java/org/chorem/jtimer/db/DbManager.java | 219 +++++++++++++++++++++ .../java/org/chorem/jtimer/db/DbMigration.java | 106 ++++++++++ src/main/java/org/chorem/jtimer/db/DbUtils.java | 45 +++++ .../java/org/chorem/jtimer/db/NodeAndCount.java | 55 ++++++ .../java/org/chorem/jtimer/entities/TimerTask.java | 13 +- src/main/java/org/chorem/jtimer/ui/StatusBar.java | 7 +- .../org/chorem/jtimer/ui/report/ReportView.java | 4 +- .../chorem/jtimer/ui/tasks/RefreshTreeTask.java | 5 +- .../jtimer/ui/tree/CheckBoxTreeCellEditor.java | 5 +- .../org/chorem/jtimer/ui/tree/TaskTreeModel.java | 5 +- .../ui/treetable/ProjectsAndTasksCellRenderer.java | 5 +- .../jtimer/ui/treetable/ProjectsAndTasksModel.java | 69 ++++--- .../java/org/chorem/jtimer/AbstractJTimerTest.java | 50 +++++ .../org/chorem/jtimer/data/CommonVetoableTest.java | 11 +- .../chorem/jtimer/data/TimerDataManagerTest.java | 49 ++--- 25 files changed, 855 insertions(+), 110 deletions(-) diff --git a/pom.xml b/pom.xml index 806e98c..9493bad 100644 --- a/pom.xml +++ b/pom.xml @@ -212,6 +212,12 @@ <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>1.4.197</version> + <scope>runtime</scope> + </dependency> <!-- tests dependencies --> <dependency> diff --git a/src/main/java/org/chorem/jtimer/JTimer.java b/src/main/java/org/chorem/jtimer/JTimer.java index ab54d3e..f9a3e31 100644 --- a/src/main/java/org/chorem/jtimer/JTimer.java +++ b/src/main/java/org/chorem/jtimer/JTimer.java @@ -158,8 +158,7 @@ public class JTimer extends SingleFrameApplication implements public static void main(String[] args) { if (log.isInfoEnabled()) { - log.info("Starting " + JTimer.class.getSimpleName() + " at " - + new Date()); + log.info("Starting " + JTimer.class.getSimpleName() + " at " + new Date()); } // load configuration and run actions @@ -187,9 +186,7 @@ public class JTimer extends SingleFrameApplication implements // native init Optional<SystemInfo> systemInfo = SystemInfoFactory.getSystemInfo(); - if (systemInfo.isPresent()) { - systemInfo.get().systemInit(); - } + systemInfo.ifPresent(SystemInfo::systemInit); // fix start in iconified mode ctxt.getSessionStorage().putProperty(JFrame.class, new WindowProperty2()); diff --git a/src/main/java/org/chorem/jtimer/JTimerConfig.java b/src/main/java/org/chorem/jtimer/JTimerConfig.java index 50db0ab..a7f88ed 100644 --- a/src/main/java/org/chorem/jtimer/JTimerConfig.java +++ b/src/main/java/org/chorem/jtimer/JTimerConfig.java @@ -138,6 +138,16 @@ public class JTimerConfig { return appConfig.getOptionAsFile(JTimerOption.DATA_DIRECTORY.key); } + /** + * Get jtimer data directory. + * + * @since 1.6 + * @return jtimer 1.6 database directory + */ + public File getDatabaseDirectory() { + return appConfig.getOptionAsFile(JTimerOption.DATABASE_DIRECTORY.key); + } + /** * Get jtimer backup directory. * @@ -280,6 +290,7 @@ public class JTimerConfig { HOME_DIRECTORY("jtimer.home.directory", "${user.home}/.jtimer"), DATA_DIRECTORY("jtimer.data.directory", "${jtimer.home.directory}/data"), + DATABASE_DIRECTORY("jtimer.database.directory", "${jtimer.home.directory}/db"), BACKUP_DIRECTORY("jtimer.backup.directory", "${jtimer.home.directory}/backups"), TEMPLATE_DIRECTORY("jtimer.templates.directory", "${jtimer.home.directory}/templates"), diff --git a/src/main/java/org/chorem/jtimer/JTimerUtils.java b/src/main/java/org/chorem/jtimer/JTimerUtils.java new file mode 100644 index 0000000..9d1f6b6 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/JTimerUtils.java @@ -0,0 +1,54 @@ +/* + * #%L + * jTimer + * %% + * Copyright (C) 2018 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; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.Map; +import java.util.TreeMap; + +public class JTimerUtils { + + public static Date toDate(LocalDate localDate) { + return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()); + } + + public static Date toDate(LocalDateTime localDateTime) { + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + + public static LocalDate toLocalDate(Date date) { + return new java.sql.Date(date.getTime()).toLocalDate(); + } + + public static LocalDateTime toLocalDateTime(Date date) { + return new java.sql.Timestamp(date.getTime()).toLocalDateTime(); + } + + public static <E> Map<Date, E> toDateMap(Map<LocalDateTime, E> map) { + Map<Date, E> result = new TreeMap<>(); + map.forEach((k, v) -> result.put(toDate(k), v)); + return result; + } +} diff --git a/src/main/java/org/chorem/jtimer/data/CommonVetoable.java b/src/main/java/org/chorem/jtimer/data/CommonVetoable.java index d392114..c70c1e8 100644 --- a/src/main/java/org/chorem/jtimer/data/CommonVetoable.java +++ b/src/main/java/org/chorem/jtimer/data/CommonVetoable.java @@ -103,12 +103,14 @@ public class CommonVetoable implements VetoableDataEventListener { public void checkAddProject(TimerProject project) { // check duplicated project name + /* + FIXME db if (isSameTaskName(project, manager.getProjectsList())) { if (log.isDebugEnabled()) { log.debug("Duplicated name, checkAddProject won't pass"); } throw new DataViolationException("Can't add project", DUPLICATED_PROJECT_VIOLATION); - } + }*/ } @Override @@ -127,12 +129,13 @@ public class CommonVetoable implements VetoableDataEventListener { public void checkModifyProject(TimerProject project) { // check duplicated project name + /*FIXME db if (isSameTaskName(project, manager.getProjectsList())) { if (log.isDebugEnabled()) { log.debug("Duplicated name, checkModifyProject won't pass"); } throw new DataViolationException("Can't modify project", DUPLICATED_PROJECT_VIOLATION); - } + }*/ } @Override diff --git a/src/main/java/org/chorem/jtimer/data/TimerCore.java b/src/main/java/org/chorem/jtimer/data/TimerCore.java index b63347d..0b02efb 100644 --- a/src/main/java/org/chorem/jtimer/data/TimerCore.java +++ b/src/main/java/org/chorem/jtimer/data/TimerCore.java @@ -27,6 +27,10 @@ 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.db.Database; +import org.chorem.jtimer.db.DbDataMigration; +import org.chorem.jtimer.db.DbManager; +import org.chorem.jtimer.db.DbMigration; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.io.DataLockingException; import org.chorem.jtimer.io.Saver; @@ -55,6 +59,10 @@ public class TimerCore { /** Timer data. */ protected TimerDataManager data; + /** Database. */ + protected Database database; + protected DbManager dbManager; + /** saver io controller. */ protected Saver saver; @@ -72,19 +80,26 @@ public class TimerCore { } } + database = new Database(); + dbManager = new DbManager(database); + DbMigration dbMigration = new DbMigration(database); + dbMigration.init(); + DbDataMigration dbDataMigration = new DbDataMigration(dbManager); + dbDataMigration.init(); + // init data - data = new TimerDataManager(); + data = new TimerDataManager(dbManager); // add commmon vetoable CommonVetoable commonVetoable = new CommonVetoable(data); data.addVetoableDataEventListener(commonVetoable); // init saver implementation - saver = JTimerFactory.getFileSaver(); + /*saver = JTimerFactory.getFileSaver(); if (saver != null) { data.addVetoableDataEventListener(saver); data.addDataEventListener(saver); - } + }*/ } /** @@ -126,10 +141,10 @@ public class TimerCore { * Init Load and launch synchronization. * * @return true if init has gone successfully - */ + **/ public boolean init() { - boolean initSucceded = false; + /*boolean initSucceded = false; // log if (log.isInfoEnabled()) { @@ -149,7 +164,8 @@ public class TimerCore { } } - return initSucceded; + return initSucceded;*/ + return true; } /** @@ -167,7 +183,7 @@ public class TimerCore { protected void load() { // log - if (log.isInfoEnabled()) { + /*if (log.isInfoEnabled()) { log.info("Load local data"); } @@ -177,14 +193,14 @@ public class TimerCore { List<TimerProject> projectsList = new ArrayList<>(projects); Collections.sort(projectsList); - data.addAllProjects(projectsList); + data.addAllProjects(projectsList);*/ } /** * Save. - */ + **/ public void exit() { - if (log.isInfoEnabled()) { + /*if (log.isInfoEnabled()) { log.info("Exiting application"); } // unlock fs directory @@ -194,6 +210,6 @@ public class TimerCore { if (log.isErrorEnabled()) { log.error("Error on unlocking", e); } - } + }*/ } } diff --git a/src/main/java/org/chorem/jtimer/data/TimerDataManager.java b/src/main/java/org/chorem/jtimer/data/TimerDataManager.java index 79013d5..4b3d30d 100644 --- a/src/main/java/org/chorem/jtimer/data/TimerDataManager.java +++ b/src/main/java/org/chorem/jtimer/data/TimerDataManager.java @@ -26,6 +26,8 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.chorem.jtimer.JTimer; +import org.chorem.jtimer.db.DbManager; +import org.chorem.jtimer.db.NodeAndCount; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; @@ -54,8 +56,8 @@ public class TimerDataManager { /** log. */ private static Log log = LogFactory.getLog(TimerDataManager.class); - /** Project list. */ - protected List<TimerProject> projectList; + /** Project list. * + protected List<TimerProject> projectList;*/ /** For change notification */ protected Collection<DataEventListener> dataEventListeners; @@ -63,13 +65,16 @@ public class TimerDataManager { /** For change notification */ protected Collection<VetoableDataEventListener> vetoableDataEventListeners; + protected DbManager dbManager; + /** * Constructor. */ - public TimerDataManager() { + public TimerDataManager(DbManager dbManager) { + this.dbManager = dbManager; // init data list - projectList = new ArrayList<>(); + //projectList = new ArrayList<>(); // init support list dataEventListeners = new ArrayList<>(); @@ -126,7 +131,7 @@ public class TimerDataManager { vetoableDataEventListener.checkAddProject(project); } - projectList.add(project); + //projectList.add(project); // fire data event for (DataEventListener dataEventListener : dataEventListeners) { @@ -195,7 +200,7 @@ public class TimerDataManager { * Add many projects. * * @param projects project collection - */ + * public void addAllProjects(Collection<TimerProject> projects) { if (projects != null) { projectList.clear(); @@ -206,7 +211,7 @@ public class TimerDataManager { dataEventListener.dataLoaded(projects); } } - } + }*/ /** * Get projects list. @@ -215,8 +220,12 @@ public class TimerDataManager { * * @return list of projects */ - public List<TimerProject> getProjectsList() { - return projectList; + public List<NodeAndCount> getProjectsList() { + return dbManager.getProjectsAndSubTaskCount(); + } + + public List<NodeAndCount> getSubTasks(TimerTask parent) { + return dbManager.getTasksAndSubTaskCount(parent); } /** @@ -299,7 +308,7 @@ public class TimerDataManager { vetoableDataEventListener.checkDeleteProject(project); } - projectList.remove(project); + //projectList.remove(project); // send notification for (DataEventListener dataEventListener : dataEventListeners) { @@ -639,7 +648,7 @@ public class TimerDataManager { throw new IllegalArgumentException("Can't parse argument '" + taskPath + "' as task"); } - return findTask(projectList, paths); + return null; // FIXME findTask(projectList, paths); } /** diff --git a/src/main/java/org/chorem/jtimer/db/Database.java b/src/main/java/org/chorem/jtimer/db/Database.java new file mode 100644 index 0000000..ebe6c62 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/db/Database.java @@ -0,0 +1,60 @@ +/* + * #%L + * jTimer + * %% + * Copyright (C) 2018 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.db; + +import org.chorem.jtimer.JTimer; +import org.chorem.jtimer.JTimerConfig; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class Database { + + protected String connectionUrl; + protected String connectionUsername; + protected String connectionPassword; + + public Database() { + JTimerConfig config = JTimer.config; + + connectionUrl = String.format("jdbc:h2:%s/jtimer", config.getDatabaseDirectory()); + connectionUsername = "sa"; + connectionPassword = ""; + + try { + Class.forName("org.h2.Driver"); + } catch (ClassNotFoundException ex) { + throw new DbException("Can't load driver", ex); + } + } + + public Connection getConnection() { + Connection conn; + try { + conn = DriverManager.getConnection(connectionUrl, connectionUsername, connectionPassword); + } catch (SQLException ex) { + throw new DbException("Can't open database", ex); + } + return conn; + } +} diff --git a/src/main/java/org/chorem/jtimer/db/DbDataMigration.java b/src/main/java/org/chorem/jtimer/db/DbDataMigration.java new file mode 100644 index 0000000..8c8b876 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/db/DbDataMigration.java @@ -0,0 +1,67 @@ +/* + * #%L + * jTimer + * %% + * Copyright (C) 2018 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.db; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.JTimerFactory; +import org.chorem.jtimer.entities.TimerProject; +import org.chorem.jtimer.entities.TimerTask; +import org.chorem.jtimer.io.Saver; + +import java.util.Collection; + +public class DbDataMigration { + + private static Log log = LogFactory.getLog(DbDataMigration.class); + + protected DbManager dbm; + + public DbDataMigration(DbManager dbm) { + this.dbm = dbm; + } + + public void init() { + long projectCount = dbm.getProjectCount(); + if (projectCount == 0) { + performDataMigration(); + } + } + + protected void performDataMigration() { + if (log.isInfoEnabled()) { + log.info("Performing data migration"); + } + Saver fileSaver = JTimerFactory.getFileSaver(); + Collection<TimerProject> projects = fileSaver.load(); + projects.forEach(this::migrateTask); + } + + protected void migrateTask(TimerTask timerTask) { + if (log.isDebugEnabled()) { + log.debug("migration task " + timerTask.getName()); + } + dbm.createTask(timerTask); + dbm.updateTask(timerTask); + timerTask.getSubTasks().forEach(this::migrateTask); + } +} diff --git a/src/main/java/org/chorem/jtimer/db/DbException.java b/src/main/java/org/chorem/jtimer/db/DbException.java new file mode 100644 index 0000000..7917958 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/db/DbException.java @@ -0,0 +1,36 @@ +/* + * #%L + * jTimer + * %% + * Copyright (C) 2018 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.db; + +public class DbException extends RuntimeException { + public DbException(String message) { + super(message); + } + + public DbException(String message, Throwable cause) { + super(message, cause); + } + + public DbException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/chorem/jtimer/db/DbManager.java b/src/main/java/org/chorem/jtimer/db/DbManager.java new file mode 100644 index 0000000..d5ee9e6 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/db/DbManager.java @@ -0,0 +1,219 @@ +/* + * #%L + * jTimer + * %% + * Copyright (C) 2018 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.db; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.JTimerUtils; +import org.chorem.jtimer.entities.TimerProject; +import org.chorem.jtimer.entities.TimerTask; + +import java.awt.image.DataBufferUShort; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.UUID; + +public class DbManager { + private static Log log = LogFactory.getLog(DbDataMigration.class); + + protected Database db; + + public DbManager(Database db) { + this.db = db; + } + + public long getProjectCount() { + long taskCount = 0; + Connection conn = db.getConnection(); + try (Statement st = conn.createStatement()) { + ResultSet resultSet = st.executeQuery("SELECT count(*) from task"); + if (resultSet.next()) { + taskCount = resultSet.getLong(1); + } + } catch(SQLException ex) { + DbUtils.rollback(conn); + throw new DbException(ex); + } finally { + DbUtils.close(conn); + } + + return taskCount; + } + + public List<NodeAndCount> getProjectsAndSubTaskCount() { + log.info("Compute tree of : null"); + long before = System.currentTimeMillis(); + List<NodeAndCount> tasks = new ArrayList<>(); + Connection conn = db.getConnection(); + try { + try (PreparedStatement st = conn.prepareStatement("SELECT id, name, created, " + + " SELECT count(*) FROM task t2 where t2.parent = t1.id as subtasks" + + " from task t1 WHERE parent is null and t1.closed = false order by t1.name;")) { + ResultSet resultSet = st.executeQuery(); + while (resultSet.next()) { + TimerProject task = new TimerProject(); + task.setUuid((UUID) resultSet.getObject(1)); + task.setName(resultSet.getString(2)); + task.setCreationDate(JTimerUtils.toDate(resultSet.getObject(3, LocalDateTime.class))); + tasks.add(new NodeAndCount(task, resultSet.getLong("subtasks"))); + } + } + + try (PreparedStatement st = conn.prepareStatement("WITH RECURSIVE descendants(ID, PARENT) AS(" + + " SELECT ID, PARENT FROM task tr WHERE ID = ?" + + " UNION ALL" + + " SELECT S2.ID, S2.PARENT FROM descendants S1 INNER JOIN task S2 ON S1.id = S2.parent" + + ") " + + + "SELECT t.*, (select sum(ti.duration) " + + " from time ti where ti.task IN (" + + " SELECT ID FROM descendants" + + " )) total," + + " (select sum(ti2.duration) " + + " from time ti2 where ti2.task IN (" + + " SELECT ID FROM descendants" + + " )" + + " AND ti2.day = ?) today " + + "from task t " + + "where t.id = ? ")) { + + for (NodeAndCount task : tasks) { + st.setObject(1, task.getTimerTask().getUuid()); + st.setObject(2, LocalDate.now()); + st.setObject(3, task.getTimerTask().getUuid()); + ResultSet resultSet = st.executeQuery(); + if (resultSet.next()) { + long today = resultSet.getLong("today"); + long total = resultSet.getLong("total"); + task.setTotalTime(total); + task.setTodayTime(today); + } + } + } + } catch(SQLException ex) { + DbUtils.rollback(conn); + throw new DbException(ex); + } finally { + DbUtils.close(conn); + } + + long after = System.currentTimeMillis(); + log.info("Take " + (after - before) + " ms"); + + return tasks; + } + + public List<NodeAndCount> getTasksAndSubTaskCount(TimerTask parent) { + log.info("Compute tree of : " + parent.getName()); + List<NodeAndCount> tasks = new ArrayList<>(); + Connection conn = db.getConnection(); + try (PreparedStatement st = conn.prepareStatement("SELECT id, name, created, " + + " SELECT count(*) FROM task t2 where t2.parent = t1.id as subtasks" + + " from task t1 WHERE parent = ?;")) { + st.setObject(1, parent.getUuid()); + ResultSet resultSet = st.executeQuery(); + while (resultSet.next()) { + TimerTask task = new TimerTask(); + task.setUuid((UUID)resultSet.getObject(1)); + task.setName(resultSet.getString(2)); + task.setCreationDate(JTimerUtils.toDate(resultSet.getObject(3, LocalDateTime.class))); + tasks.add(new NodeAndCount(task, resultSet.getLong("subtasks"))); + } + } catch(SQLException ex) { + DbUtils.rollback(conn); + throw new DbException(ex); + } finally { + DbUtils.close(conn); + } + + return tasks; + } + + public void createTask(TimerTask timerTask) { + Connection conn = db.getConnection(); + try (PreparedStatement st = conn.prepareStatement("INSERT INTO task(id, name, created, closed, parent) values(?, ?, ?, ?, ?)")) { + st.setObject(1, timerTask.getUuid()); + st.setString(2, timerTask.getName()); + st.setObject(3, timerTask.getCreationDate()); + st.setBoolean(4, timerTask.isClosed()); + st.setObject(5, Optional.ofNullable(timerTask.getParent()).map(TimerTask::getUuid).orElse(null)); + st.executeUpdate(); + + conn.commit(); + } catch(SQLException ex) { + DbUtils.rollback(conn); + throw new DbException(ex); + } finally { + DbUtils.close(conn); + } + } + + public void updateTask(TimerTask timerTask) { + Connection conn = db.getConnection(); + + try { + try (PreparedStatement st = conn.prepareStatement("UPDATE task set name = ?, created = ?, closed = ?, parent = ? WHERE id = ?")) { + st.setString(1, timerTask.getName()); + st.setObject(2, JTimerUtils.toLocalDateTime(timerTask.getCreationDate())); + st.setBoolean(3, timerTask.isClosed()); + st.setObject(4, Optional.ofNullable(timerTask.getParent()).map(TimerTask::getUuid).orElse(null)); + st.setObject(5, timerTask.getUuid()); + st.executeUpdate(); + } + + try (PreparedStatement st = conn.prepareStatement("DELETE FROM time WHERE task = ?")) { + st.setObject(1, timerTask.getUuid()); + st.executeUpdate(); + } + + try (PreparedStatement st = conn.prepareStatement("INSERT INTO time(id, day, duration, task) VALUES(?, ?, ?, ?)")) { + for (Map.Entry<Date, Long> dateLongEntry : timerTask.getAllDaysAndTimes().entrySet()) { + st.setObject(1, UUID.randomUUID()); + st.setObject(2, JTimerUtils.toLocalDate(dateLongEntry.getKey())); + st.setObject(3, dateLongEntry.getValue()); + st.setObject(4, timerTask.getUuid()); + st.executeUpdate(); + } + conn.commit(); + } + } catch (SQLException ex) { + DbUtils.rollback(conn); + throw new DbException(ex); + } finally { + DbUtils.close(conn); + } + } +} diff --git a/src/main/java/org/chorem/jtimer/db/DbMigration.java b/src/main/java/org/chorem/jtimer/db/DbMigration.java new file mode 100644 index 0000000..671e20d --- /dev/null +++ b/src/main/java/org/chorem/jtimer/db/DbMigration.java @@ -0,0 +1,106 @@ +/* + * #%L + * jTimer + * %% + * Copyright (C) 2018 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.db; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.JTimer; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class DbMigration { + + /** log. */ + private static Log log = LogFactory.getLog(DbMigration.class); + + protected Database db; + + public DbMigration(Database db) { + this.db = db; + } + + public void init() { + Connection conn = db.getConnection(); + try (Statement st = conn.createStatement()) { + int currentVersion = 0; + try { + ResultSet rs = st.executeQuery("SELECT current_version FROM database_version"); + if (rs.next()) { + currentVersion = rs.getInt("current_version"); + } + rs.close(); + } catch (SQLException ex) { + // database is empty + st.executeUpdate("CREATE TABLE database_version (current_version INT NOT NULL)"); + st.executeUpdate("INSERT INTO database_version VALUES(0)"); + } + performMigration(currentVersion + 1); + } catch (SQLException ex) { + DbUtils.rollback(conn); + throw new DbException(ex); + } finally { + DbUtils.close(conn); + } + } + + protected void performMigration(int targetVersion) { + switch (targetVersion) { + case 1: + perform("CREATE TABLE task (id UUID PRIMARY KEY," + + "name VARCHAR(255) NOT NULL," + + "created timestamp not null," + + "closed boolean not null," + + "parent UUID," + + "foreign key (parent) references task(id))"); + case 2: + perform("create table time (id UUID PRIMARY KEY," + + "day DATE not null," + + "duration int null," + + "task UUID," + + "foreign key (task) references task(id))"); + case 3: + perform("CREATE INDEX task_parent_idx ON task(parent)"); + case 4: + perform("CREATE INDEX time_task_idx ON time(task)"); + } + } + + protected void perform(String sql) { + Connection conn = db.getConnection(); + try (Statement statement = conn.createStatement()) { + if (log.isInfoEnabled()) { + log.info("Database migration : " + sql); + } + statement.executeUpdate(sql); + statement.executeUpdate("update database_version set current_version = current_version + 1"); + conn.commit(); + } catch (SQLException ex) { + DbUtils.rollback(conn); + throw new DbException(ex); + } finally { + DbUtils.close(conn); + } + } +} diff --git a/src/main/java/org/chorem/jtimer/db/DbUtils.java b/src/main/java/org/chorem/jtimer/db/DbUtils.java new file mode 100644 index 0000000..253d296 --- /dev/null +++ b/src/main/java/org/chorem/jtimer/db/DbUtils.java @@ -0,0 +1,45 @@ +/* + * #%L + * jTimer + * %% + * Copyright (C) 2018 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.db; + +import java.sql.Connection; +import java.sql.SQLException; + +public class DbUtils { + + public static void close(Connection conn) { + try { + conn.close(); + } catch (SQLException ex) { + throw new DbException(ex); + } + } + + public static void rollback(Connection conn) { + try { + conn.rollback(); + } catch (SQLException ex) { + throw new DbException(ex); + } + } + +} diff --git a/src/main/java/org/chorem/jtimer/db/NodeAndCount.java b/src/main/java/org/chorem/jtimer/db/NodeAndCount.java new file mode 100644 index 0000000..8b4a94f --- /dev/null +++ b/src/main/java/org/chorem/jtimer/db/NodeAndCount.java @@ -0,0 +1,55 @@ +package org.chorem.jtimer.db; + +import org.chorem.jtimer.entities.TimerTask; + +public class NodeAndCount { + + protected TimerTask timerTask; + + protected long count; + + protected long todayTime; + + protected long totalTime; + + public NodeAndCount() { + + } + + public NodeAndCount(TimerTask timerTask, long count) { + this.timerTask = timerTask; + this.count = count; + } + + public TimerTask getTimerTask() { + return timerTask; + } + + public void setTimerTask(TimerTask timerTask) { + this.timerTask = timerTask; + } + + public long getCount() { + return count; + } + + public void setCount(long count) { + this.count = count; + } + + public long getTodayTime() { + return todayTime; + } + + public void setTodayTime(long todayTime) { + this.todayTime = todayTime; + } + + public long getTotalTime() { + return totalTime; + } + + public void setTotalTime(long totalTime) { + this.totalTime = totalTime; + } +} diff --git a/src/main/java/org/chorem/jtimer/entities/TimerTask.java b/src/main/java/org/chorem/jtimer/entities/TimerTask.java index 11dab70..51d6781 100644 --- a/src/main/java/org/chorem/jtimer/entities/TimerTask.java +++ b/src/main/java/org/chorem/jtimer/entities/TimerTask.java @@ -41,14 +41,13 @@ import java.util.UUID; * Last update : $Date$ * By : $Author$ */ -public class TimerTask implements Cloneable, - Comparable<TimerTask>, Serializable { +public class TimerTask implements Cloneable, Comparable<TimerTask>, Serializable { /** serialVersionUID */ private static final long serialVersionUID = -7590755569706702695L; /** Task uuid used to managed task equality. */ - protected String uuid = UUID.randomUUID().toString(); + protected UUID uuid = UUID.randomUUID(); /** Task number. */ protected int number; @@ -111,6 +110,14 @@ public class TimerTask implements Cloneable, this.name = name; } + public UUID getUuid() { + return uuid; + } + + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + /** * Get task number. * diff --git a/src/main/java/org/chorem/jtimer/ui/StatusBar.java b/src/main/java/org/chorem/jtimer/ui/StatusBar.java index d31436a..404e005 100644 --- a/src/main/java/org/chorem/jtimer/ui/StatusBar.java +++ b/src/main/java/org/chorem/jtimer/ui/StatusBar.java @@ -103,17 +103,18 @@ public class StatusBar extends JPanel implements DataEventListener { } /** - * Udpate today time in status bar. + * Update today time in status bar. */ protected void updateTodayTime() { // refresh time - long duration = 0L; + /*long duration = 0L; + FIXME db for (TimerProject p : dataManager.getProjectsList()) { duration += TimerTaskHelper.getTotalTime(p, new Date()); } lblTime.setText(resourceMap .getString("todayTotalMessage", DurationFormatUtils - .formatDuration(duration, "HH:mm:ss"))); + .formatDuration(duration, "HH:mm:ss")));*/ } @Override 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..d5370cb 100644 --- a/src/main/java/org/chorem/jtimer/ui/report/ReportView.java +++ b/src/main/java/org/chorem/jtimer/ui/report/ReportView.java @@ -408,8 +408,8 @@ 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 = null; // FIXME db getSelectedProjects(core + //.getData().getProjectsList(), uncheckedTaskSet); // make report String report = reportGenerator.getReportText(reportType, selectedProjects, datePickerFrom.getDate(), 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..5a88949 100644 --- a/src/main/java/org/chorem/jtimer/ui/tasks/RefreshTreeTask.java +++ b/src/main/java/org/chorem/jtimer/ui/tasks/RefreshTreeTask.java @@ -66,12 +66,13 @@ public class RefreshTreeTask extends java.util.TimerTask { log.info("Refresh tree"); - List<TimerProject> projects = dataManager.getProjectsList(); + // FIXME Db + /*List<TimerProject> projects = dataManager.getProjectsList(); // don't call changeTaskTime on projects for (TimerProject project : projects) { refreshTasks(project.getSubTasks()); - } + }*/ } /** 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..953be1d 100644 --- a/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellEditor.java +++ b/src/main/java/org/chorem/jtimer/ui/tree/CheckBoxTreeCellEditor.java @@ -180,7 +180,8 @@ public class CheckBoxTreeCellEditor extends CheckBoxTreeCellComponent implements } // special case, root node selection - if (parentTree.getModel().getRoot() == lastPathComponent) { + // FIXME db + /*if (parentTree.getModel().getRoot() == lastPathComponent) { for (TimerProject project : core.getData().getProjectsList()) { TreePath subTreePath = treePath.pathByAddingChild(project); updateChildren(subTreePath, select); @@ -190,6 +191,6 @@ public class CheckBoxTreeCellEditor extends CheckBoxTreeCellComponent implements TreePath subTreePath = treePath.pathByAddingChild(subtask); updateChildren(subTreePath, select); } - } + }*/ } } 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..b520477 100644 --- a/src/main/java/org/chorem/jtimer/ui/tree/TaskTreeModel.java +++ b/src/main/java/org/chorem/jtimer/ui/tree/TaskTreeModel.java @@ -111,12 +111,13 @@ public class TaskTreeModel implements TreeModel { List<TimerTask> result = new ArrayList<>(); // get correct list - if (parent == root) { // case root node + // FIXME db + /*if (parent == root) { // case root node result.addAll(core.getData().getProjectsList()); } else { // not root node TimerTask task = (TimerTask) parent; result.addAll(task.getSubTasks()); - } + }*/ // filter list, if only show closed if (!showClosed) { diff --git a/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksCellRenderer.java b/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksCellRenderer.java index 583e34a..58a2e30 100644 --- a/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksCellRenderer.java +++ b/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksCellRenderer.java @@ -22,6 +22,7 @@ package org.chorem.jtimer.ui.treetable; +import org.chorem.jtimer.db.NodeAndCount; import org.chorem.jtimer.entities.TimerTask; import javax.swing.JTree; @@ -50,8 +51,8 @@ public class ProjectsAndTasksCellRenderer extends DefaultTreeCellRenderer { Object localValue = value; // if this is a task - if (value instanceof TimerTask) { - TimerTask task = (TimerTask) value; + if (value instanceof NodeAndCount) { + TimerTask task = ((NodeAndCount) value).getTimerTask(); // task name should not be "null" localValue = task.getName(); 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..8cd5dd6 100644 --- a/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksModel.java +++ b/src/main/java/org/chorem/jtimer/ui/treetable/ProjectsAndTasksModel.java @@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory; import org.chorem.jtimer.data.DataEventListener; import org.chorem.jtimer.data.TimerCore; import org.chorem.jtimer.data.TimerDataManager; +import org.chorem.jtimer.db.NodeAndCount; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; import org.chorem.jtimer.entities.TimerTaskHelper; @@ -36,13 +37,13 @@ import org.jdesktop.swingx.treetable.AbstractTreeTableModel; import javax.swing.SwingUtilities; import javax.swing.table.TableColumn; import javax.swing.tree.TreePath; +import javax.xml.soap.Node; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; /** * Cette classe gere l'affichage d'une liste de project dans l'arbre/table @@ -90,11 +91,8 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements /** Tree column identifiers. */ protected List<String> columnIdentifiers; - /** Show closed task property. Default to false. */ - protected boolean showClosedTask; - protected Map<TimerTask, String> taskNameCache; - protected Map<Object, List<TimerTask>> subTasksCache; + protected Map<Object, List<NodeAndCount>> subTasksCache; /** * Constructor. @@ -132,19 +130,19 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements // default value, but normally never visible String value = "n/a"; - if (object instanceof TimerTask) { - TimerTask task = (TimerTask) object; + if (object instanceof NodeAndCount) { + NodeAndCount nodeAndCount = (NodeAndCount)object; switch (column) { case 0: - value = task.getName(); + value = nodeAndCount.getTimerTask().getName(); break; case 1: - long duration = TimerTaskHelper.getTotalTime(task, new Date()); + long duration = nodeAndCount.getTodayTime(); value = DurationFormatUtils.formatDuration(duration, "HH:mm:ss"); break; case 2: - long totalDuration = TimerTaskHelper.getAllTotalTime(task); + long totalDuration = nodeAndCount.getTotalTime(); value = DurationFormatUtils.formatDuration(totalDuration, "HH:mm:ss"); break; } @@ -168,14 +166,19 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements @Override public Object getChild(Object parent, int index) { - TimerTask t = getFiteredSubListFor(parent).get(index); + NodeAndCount t = getFiteredSubListFor(parent).get(index); return t; } @Override public int getChildCount(Object parent) { - int childCount = getFiteredSubListFor(parent).size(); + int childCount; + if (parent == root) { + childCount = getFiteredSubListFor(parent).size(); + } else { + childCount = (int)((NodeAndCount)parent).getCount(); + } return childCount; } @@ -186,7 +189,7 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements * @param parent parent to task sublist * @return filtered list */ - protected List<TimerTask> getFiteredSubListFor(Object parent) { + protected List<NodeAndCount> getFiteredSubListFor(Object parent) { return getFiteredSubListFor(parent, false); } @@ -198,36 +201,29 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements * @param noCache disable use of cached result and result caching * @return filtered list */ - protected List<TimerTask> getFiteredSubListFor(Object parent, boolean noCache) { + protected List<NodeAndCount> getFiteredSubListFor(Object parent, boolean noCache) { - List<TimerTask> result = subTasksCache.get(parent); + List<NodeAndCount> result = subTasksCache.get(parent); if (result == null || noCache) { result = new ArrayList<>(); // get correct list if (parent == root) { // case root node - List<TimerProject> projects = dataManager.getProjectsList(); + List<NodeAndCount> projects = dataManager.getProjectsList(); result.addAll(projects); } else { // not root node - TimerTask task = (TimerTask) parent; - result.addAll(task.getSubTasks()); - } - - // filter list, if only show closed - if (!showClosedTask) { - result = result.stream() - .filter(task -> !task.isClosed()) - .collect(Collectors.toList()); + List<NodeAndCount> tasks = dataManager.getSubTasks(((NodeAndCount)parent).getTimerTask()); + result.addAll(tasks); } // Since sort is not supported by the table, do a manual sorting. - result = TimerTaskHelper.sortTask(result); + //result = TimerTaskHelper.sortTask(result); if (!noCache) { // cache tasks name - for (TimerTask task : result) { + /*for (TimerTask task : result) { taskNameCache.put(task, task.getName()); - } + }*/ subTasksCache.put(parent, result); } @@ -239,8 +235,9 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements @Override public int getIndexOfChild(Object parent, Object child) { - int childIndex = getFiteredSubListFor(parent).indexOf(child); - return childIndex; + //int childIndex = getFiteredSubListFor(parent).indexOf(child); + // FIXME + return -1; } @Override @@ -274,10 +271,10 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements boolean updated = false; // get childreen without cache in case of add operation // delete operation MUST use cached result - List<TimerTask> subTask = getFiteredSubListFor(pathLastComponent, operation == OPERATION_ADD); + List<NodeAndCount> subTask = getFiteredSubListFor(pathLastComponent, operation == OPERATION_ADD); int childCount = subTask.size(); for (int childIndex = 0; !updated && childIndex < childCount; ++childIndex) { - TimerTask taskUO = subTask.get(childIndex); + TimerTask taskUO = subTask.get(childIndex).getTimerTask(); if (task.equals(taskUO)) { @@ -398,7 +395,7 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements notifyTaskChanged(task, OPERATION_MODIFY); } - @Override + /*@Override public void changeClosedState(TimerTask task) { if (showClosedTask) { @@ -419,7 +416,7 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements notifyTaskChanged(task, OPERATION_ADD); } } - } + }*/ /** * Change closed task property. @@ -427,9 +424,9 @@ public class ProjectsAndTasksModel extends AbstractTreeTableModel implements * @param showClosedTask closed task property */ public void setShowClosed(boolean showClosedTask) { - this.showClosedTask = showClosedTask; + //this.showClosedTask = showClosedTask; // TODO echatellier 20120309 add better diff code - dataLoaded(null); + //dataLoaded(null); } @Override diff --git a/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java b/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java index 5e56ded..23b8a2f 100644 --- a/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java +++ b/src/test/java/org/chorem/jtimer/AbstractJTimerTest.java @@ -26,6 +26,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.HiddenFileFilter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.chorem.jtimer.db.NodeAndCount; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; import org.chorem.jtimer.io.Saver; @@ -36,6 +37,7 @@ import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Collection; +import java.util.List; import java.util.Locale; import java.util.Properties; @@ -207,6 +209,54 @@ public abstract class AbstractJTimerTest { return foundProject; } + /** + * Get A specific task with is path in tree. + * + * @param tasks tasks list to search into + * @param path path to search + * @return found task, or <tt>null</tt> if not found + */ + public static TimerTask findTask(List<NodeAndCount> tasks, String path) { + + String[] paths = path.split("/"); + String current = paths[0]; + + TimerTask foundTask = null; + for (NodeAndCount subtaskAndCount : tasks) { + if (subtaskAndCount.getTimerTask().getName().equals(current)) { + foundTask = subtaskAndCount.getTimerTask(); + } + } + + if (foundTask != null && paths.length > 1) { + // +1 == / + String newPath = path.substring(current.length() + 1); + // FIXME db foundTask = findTask(foundTask.getSubTasks(), newPath); + + } + + return foundTask; + } + + /** + * Get a specific project in project list. + * + * @param projects projects list + * @param path path to search + * @return found project, or <tt>null</tt> if not found + */ + public static TimerProject findProject(List<NodeAndCount> projects, String path) { + + TimerProject foundProject = null; + for (NodeAndCount projectAndCount : projects) { + if (projectAndCount.getTimerTask().getName().equals(path)) { + foundProject = (TimerProject)projectAndCount.getTimerTask(); + } + } + + return foundProject; + } + /** * Get projects count. * diff --git a/src/test/java/org/chorem/jtimer/data/CommonVetoableTest.java b/src/test/java/org/chorem/jtimer/data/CommonVetoableTest.java index 4f1f08c..6bce1f1 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.db.NodeAndCount; import org.chorem.jtimer.entities.TimerProject; import org.chorem.jtimer.entities.TimerTask; import org.testng.Assert; @@ -78,7 +79,7 @@ public class CommonVetoableTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "IsisFish/UserInterface"); // add a new task @@ -100,7 +101,7 @@ public class CommonVetoableTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findProject(projectsBefore, "Topia"); TimerTask task2 = findTask(projectsBefore, "jTimer/Add workspace support"); @@ -124,7 +125,7 @@ public class CommonVetoableTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "Chorem/Add webservice"); TimerTask task2 = findTask(projectsBefore, "jTimer"); @@ -152,7 +153,7 @@ public class CommonVetoableTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "jTimer/Add workspace support"); TimerTask task2 = findTask(projectsBefore, "jTimer/Interact with chorem services"); TimerTask task3 = findTask(projectsBefore, "jTimer/Refactoring"); @@ -185,7 +186,7 @@ public class CommonVetoableTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> 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..ce2d96b 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.db.NodeAndCount; import org.chorem.jtimer.entities.TimerAlert; import org.chorem.jtimer.entities.TimerAlert.Type; import org.chorem.jtimer.entities.TimerProject; @@ -61,7 +62,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "Test project"); Assert.assertNull(project1); @@ -77,7 +78,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerProject project1a = findProject(projectsAfter, "Test project"); Assert.assertNotNull(project1a); @@ -96,7 +97,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "jTimer"); Assert.assertNotNull(project1); @@ -110,7 +111,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerProject project1a = findProject(projectsBefore, "Edit Name"); Assert.assertNull(findProject(projectsAfter, "jTimer")); @@ -130,7 +131,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "jTimer"); Assert.assertNotNull(project1); @@ -144,7 +145,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); Assert.assertNull(findProject(projectsAfter, "jTimer")); Assert.assertEquals(projectsAfter.size(), 5); @@ -163,7 +164,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "IsisFish/UserInterface"); Assert.assertNotNull(task1); @@ -179,7 +180,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "IsisFish/UserInterface"); TimerTask task2a = findTask(projectsAfter, "IsisFish/UserInterface/new task"); @@ -201,7 +202,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "IsisFish/UserInterface"); Assert.assertNotNull(task1); @@ -215,7 +216,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "IsisFish/UI"); TimerTask task2a = findTask(projectsAfter, "IsisFish/UI/Debug"); @@ -237,7 +238,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "IsisFish"); TimerTask task1 = findTask(projectsBefore, "IsisFish/UserInterface"); @@ -253,7 +254,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerProject project1a = findProject(projectsAfter, "IsisFish"); TimerTask task1a = findTask(projectsAfter, "IsisFish/UserInterface"); @@ -275,7 +276,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "Topia"); TimerTask task2 = findTask(projectsBefore, "jTimer/Add workspace support"); TimerTask task3 = findTask(projectsBefore, "Topia/Add workspace support"); @@ -292,7 +293,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "Topia"); TimerTask task2a = findTask(projectsAfter, "jTimer/Add workspace support"); TimerTask task3a = findTask(projectsAfter, "Topia/Add workspace support"); @@ -315,7 +316,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerProject project1 = findProject(projectsBefore, "Chorem"); TimerTask task1 = findTask(projectsBefore, "jTimer/Add workspace support"); TimerTask task2 = findTask(projectsBefore, "jTimer/Interact with chorem services"); @@ -342,7 +343,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "Chorem/Add workspace support"); TimerTask task2a = findTask(projectsAfter, "Chorem/Interact with chorem services"); TimerTask task3a = findTask(projectsAfter, "Chorem/Refactoring"); @@ -408,7 +409,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "jTimer/Refactoring"); TimerTask task2 = findTask(projectsBefore, "jTimer/Unit tests/UI tests"); TimerTask task3 = findTask(projectsBefore, "jTimer/Unit tests"); @@ -431,7 +432,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "jTimer/Refactoring"); TimerTask task2a = findTask(projectsAfter, "jTimer/Unit tests/UI tests"); TimerTask task3a = findTask(projectsAfter, "jTimer/Unit tests"); @@ -458,7 +459,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "jTimer/Refactoring"); Assert.assertNotNull(task1); @@ -483,7 +484,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "jTimer/Refactoring"); TimerTask task2a = findTask(projectsAfter, "jTimer/Unit tests/UI tests"); @@ -510,7 +511,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "jTimer/Refactoring"); Assert.assertNotNull(task1); @@ -531,7 +532,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "jTimer/Refactoring"); Assert.assertNotNull(task1a); @@ -569,7 +570,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { //core.init(); core.load(); - List<TimerProject> projectsBefore = dataManager.getProjectsList(); + List<NodeAndCount> projectsBefore = dataManager.getProjectsList(); TimerTask task1 = findTask(projectsBefore, "IsisFish/Storage"); TimerTask task2 = findTask(projectsBefore, "IsisFish/Support"); TimerTask task3 = findTask(projectsBefore, "IsisFish/UserInterface"); @@ -592,7 +593,7 @@ public class TimerDataManagerTest extends AbstractJTimerTest { // and test reloaded data //core.init(); core.load(); - List<TimerProject> projectsAfter = dataManager.getProjectsList(); + List<NodeAndCount> projectsAfter = dataManager.getProjectsList(); TimerTask task1a = findTask(projectsAfter, "IsisFish/Storage"); TimerTask task2a = findTask(projectsAfter, "IsisFish/Support"); TimerTask task3a = findTask(projectsAfter, "IsisFish/UserInterface"); -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
participants (1)
-
chorem.org scm