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>.