From 7e5c0eb9a52cc7459e06445bc715993ce2745fb7 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Mon, 12 Jul 2010 01:05:25 -0400 Subject: [PATCH] Dependencies - Supports new "depends" attribute. - Supports "task depends:1,2". - Supports "task depends:-1,-2". - Supports id <--> uuid mapping in TDB. --- src/Att.cpp | 1 + src/TDB.cpp | 26 +++++++++++++++++++ src/TDB.h | 6 +++++ src/Task.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ src/Task.h | 5 ++++ src/command.cpp | 24 +++++++++++++++++- 6 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/Att.cpp b/src/Att.cpp index a19cb837b..7dc5b9dc8 100644 --- a/src/Att.cpp +++ b/src/Att.cpp @@ -63,6 +63,7 @@ static const char* modifiableNames[] = "recur", "until", "wait", + "depends", }; // Synonyms on the same line. diff --git a/src/TDB.cpp b/src/TDB.cpp index 1f32fed18..d5e81f0a3 100644 --- a/src/TDB.cpp +++ b/src/TDB.cpp @@ -304,6 +304,10 @@ int TDB::loadPending (std::vector & tasks, Filter& filter) task.id = mId++; mPending.push_back (task); + + // Maintain mapping for ease of link/dependency resolution. + mI2U[task.id] = task.get ("uuid"); + mU2I[task.get ("uuid")] = task.id; } ++line_number; @@ -425,6 +429,8 @@ int TDB::loadCompleted (std::vector & tasks, Filter& filter) void TDB::add (const Task& task) { mNew.push_back (task); + mI2U[task.id] = task.get ("uuid"); + mU2I[task.get ("uuid")] = task.id; } //////////////////////////////////////////////////////////////////////////////// @@ -1425,6 +1431,26 @@ void TDB::merge (const std::string& mergeFile) mods.clear(); } +//////////////////////////////////////////////////////////////////////////////// +std::string TDB::uuid (int id) const +{ + std::map ::const_iterator i; + if ((i = mI2U.find (id)) != mI2U.end ()) + return i->second; + + return ""; +} + +//////////////////////////////////////////////////////////////////////////////// +int TDB::id (const std::string& uuid) const +{ + std::map ::const_iterator i; + if ((i = mU2I.find (uuid)) != mU2I.end ()) + return i->second; + + return 0; +} + //////////////////////////////////////////////////////////////////////////////// FILE* TDB::openAndLock (const std::string& file) { diff --git a/src/TDB.h b/src/TDB.h index c65ad0570..add1c16b3 100644 --- a/src/TDB.h +++ b/src/TDB.h @@ -64,6 +64,9 @@ public: void undo (); void merge (const std::string&); + std::string uuid (int) const; + int id (const std::string&) const; + private: FILE* openAndLock (const std::string&); void writeUndo (const Task&, FILE*); @@ -79,6 +82,9 @@ private: std::vector mCompleted; // Contents of pending.data std::vector mNew; // Uncommitted new tasks std::vector mModified; // Uncommitted modified tasks + + std::map mI2U; // ID -> UUID map + std::map mU2I; // UUID -> ID map }; #endif diff --git a/src/Task.cpp b/src/Task.cpp index a955fca0d..ccf07e789 100644 --- a/src/Task.cpp +++ b/src/Task.cpp @@ -467,6 +467,72 @@ void Task::removeAnnotations () } } +//////////////////////////////////////////////////////////////////////////////// +void Task::addDependency (int id) +{ + std::string uuid = context.tdb.uuid (id); + if (uuid == "") + { + std::stringstream s; + s << "Could not create a dependency on task " << id << " - not found."; + throw s.str (); + } + + std::string depends = get ("depends"); + if (depends.length ()) + { + if (depends.find (uuid) == std::string::npos) + set ("depends", depends + "," + uuid); + } + else + set ("depends", uuid); +} + +//////////////////////////////////////////////////////////////////////////////// +void Task::removeDependency (int id) +{ + std::string uuid = context.tdb.uuid (id); + if (uuid == "") + { + std::stringstream s; + s << "Could not find a UUID for id " << id << "."; + throw s.str (); + } + + std::vector deps; + split (deps, get ("depends"), ','); + + std::vector ::iterator i; + i = std::find (deps.begin (), deps.end (), uuid); + if (i != deps.end ()) + { + deps.erase (i); + std::string combined; + join (combined, ",", deps); + set ("depends", combined); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void Task::getDependencies (std::vector & all) const +{ + std::vector deps; + split (deps, get ("depends"), ','); + + all.clear (); + + std::vector ::iterator i; + for (i = deps.begin (); i != deps.end (); ++i) + all.push_back (context.tdb.id (*i)); +} + +//////////////////////////////////////////////////////////////////////////////// +void Task::getDependencies (std::vector & all) const +{ + all.clear (); + split (all, get ("depends"), ','); +} + //////////////////////////////////////////////////////////////////////////////// int Task::getTagCount () { diff --git a/src/Task.h b/src/Task.h index 69f99e551..11a7b95ac 100644 --- a/src/Task.h +++ b/src/Task.h @@ -73,6 +73,11 @@ public: void addAnnotation (const std::string&); void removeAnnotations (); + void addDependency (int); + void removeDependency (int); + void getDependencies (std::vector &) const; + void getDependencies (std::vector &) const; + void validate () const; private: diff --git a/src/command.cpp b/src/command.cpp index 301c91ed5..552354381 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -99,6 +99,8 @@ int handleAdd (std::string &outs) !context.task.has ("recur")) throw std::string ("You cannot specify an until date for a non-recurring task."); + // TODO Resolve dependencies. + // Only valid tasks can be added. context.task.validate (); @@ -164,6 +166,8 @@ int handleLog (std::string &outs) foreach (tag, context.tagAdditions) context.task.addTag (*tag); + // TODO Resolve dependencies. + // Only valid tasks can be added. context.task.validate (); @@ -2231,7 +2235,25 @@ int deltaAttributes (Task& task) task.setStatus (Task::waiting); } - if (att->second.value () == "") + // Modifying dependencies requires adding/removing uuids. + else if (att->second.name () == "depends") + { + std::vector deps; + split (deps, att->second.value (), ','); + + std::vector ::iterator i; + for (i = deps.begin (); i != deps.end (); i++) + { + int id = atoi (i->c_str ()); + if (id < 0) + task.removeDependency (-id); + else + task.addDependency (id); + } + } + + // Now the generalized handling. + else if (att->second.value () == "") task.remove (att->second.name ()); else // One of the few places where the compound attribute name is used.