taskwarrior/src/DOM.cpp
2016-01-09 22:24:55 -05:00

394 lines
12 KiB
C++

////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <DOM.h>
#include <sstream>
#include <map>
#include <stdlib.h>
#include <Variant.h>
#include <Context.h>
#include <Nibbler.h>
#include <ISO8601.h>
#include <text.h>
#include <i18n.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
// DOM Supported References:
//
// Configuration:
// rc.<name>
//
// System:
// context.program
// context.args
// context.width
// context.height
// system.version
// system.os
//
bool DOM::get (const std::string& name, Variant& value)
{
// Special case, blank refs cause problems.
if (name == "")
return false;
int len = name.length ();
Nibbler n (name);
// rc. --> context.config
if (len > 3 &&
! name.compare (0, 3, "rc.", 3))
{
std::string key = name.substr (3);
auto c = context.config.find (key);
if (c != context.config.end ())
{
value = Variant (c->second);
return true;
}
return false;
}
// context.*
if (len > 8 &&
! name.compare (0, 8, "context.", 8))
{
if (name == "context.program")
{
value = Variant (context.cli2.getBinary ());
return true;
}
else if (name == "context.args")
{
std::string commandLine;
for (auto& arg : context.cli2._original_args)
{
if (commandLine != "")
commandLine += " ";
commandLine += arg.attribute("raw");
}
value = Variant (commandLine);
return true;
}
else if (name == "context.width")
{
value = Variant (static_cast<int> (context.terminal_width
? context.terminal_width
: context.getWidth ()));
return true;
}
else if (name == "context.height")
{
value = Variant (static_cast<int> (context.terminal_height
? context.terminal_height
: context.getHeight ()));
return true;
}
else
throw format (STRING_DOM_UNREC, name);
}
// TODO stats.<name>
// system. --> Implement locally.
if (len > 7 &&
! name.compare (0, 7, "system.", 7))
{
// Taskwarrior version number.
if (name == "system.version")
{
value = Variant (VERSION);
return true;
}
// OS type.
else if (name == "system.os")
{
#if defined (DARWIN)
value = Variant ("Darwin");
#elif defined (SOLARIS)
value = Variant ("Solaris");
#elif defined (CYGWIN)
value = Variant ("Cygwin");
#elif defined (HAIKU)
value = Variant ("Haiku");
#elif defined (OPENBSD)
value = Variant ("OpenBSD");
#elif defined (FREEBSD)
value = Variant ("FreeBSD");
#elif defined (NETBSD)
value = Variant ("NetBSD");
#elif defined (LINUX)
value = Variant ("Linux");
#elif defined (KFREEBSD)
value = Variant ("GNU/kFreeBSD");
#elif defined (GNUHURD)
value = Variant ("GNU/Hurd");
#else
value = Variant (STRING_DOM_UNKNOWN);
#endif
return true;
}
else
throw format (STRING_DOM_UNREC, name);
}
// Empty string if nothing is found.
return false;
}
////////////////////////////////////////////////////////////////////////////////
// DOM Supported References:
//
// Relative or absolute attribute:
// <attribute>
// <id>.<attribute>
// <uuid>.<attribute>
//
// Single tag:
// tags.<word>
//
// Date type:
// <date>.year
// <date>.month
// <date>.day
// <date>.week
// <date>.weekday
// <date>.julian
// <date>.hour
// <date>.minute
// <date>.second
//
// Annotations (entry is a date):
// annotations.<N>.entry
// annotations.<N>.description
//
// This code emphasizes speed, hence 'id' and 'urgecny' being evaluated first
// as special cases.
bool DOM::get (const std::string& name, const Task& task, Variant& value)
{
// Special case, blank refs cause problems.
if (name == "")
return false;
// Quickly deal with the most common cases.
if (task.data.size () && name == "id")
{
value = Variant (static_cast<int> (task.id));
return true;
}
if (task.data.size () && name == "urgency")
{
value = Variant (task.urgency_c ());
return true;
}
// split name on '.'
std::vector <std::string> elements;
split (elements, name, '.');
Task ref (task);
Nibbler n (elements[0]);
n.save ();
int id;
std::string uuid;
// If elements[0] is a UUID, load that task (if necessary), and clobber ref.
if (n.getPartialUUID (uuid) && n.depleted ())
{
if (uuid != ref.get ("uuid"))
context.tdb2.get (uuid, ref);
// Eat elements[0]/UUID.
elements.erase (elements.begin ());
}
else
{
// If elements[0] is a ID, load that task (if necessary), and clobber ref.
if (n.getInt (id) && n.depleted ())
{
if (id != ref.id)
context.tdb2.get (id, ref);
// Eat elements[0]/ID.
elements.erase (elements.begin ());
}
}
auto size = elements.size ();
std::string canonical;
if ((size == 1 || size == 2) && context.cli2.canonicalize (canonical, "attribute", elements[0]))
{
// Now that 'ref' is the contextual task, and any ID/UUID is chopped off the
// elements vector, DOM resolution is now simple.
if (ref.data.size () && size == 1 && canonical == "id")
{
value = Variant (static_cast<int> (ref.id));
return true;
}
if (ref.data.size () && size == 1 && canonical == "urgency")
{
value = Variant (ref.urgency_c ());
return true;
}
Column* column = context.columns[canonical];
if (ref.data.size () && size == 1 && column)
{
if (column->is_uda () && ! ref.has (canonical))
{
value = Variant ("");
return true;
}
if (column->type () == "date")
{
auto numeric = ref.get_date (canonical);
if (numeric == 0)
value = Variant ("");
else
value = Variant (numeric, Variant::type_date);
}
else if (column->type () == "duration" || canonical == "recur")
{
auto period = ref.get (canonical);
ISO8601p iso;
std::string::size_type cursor = 0;
if (iso.parse (period, cursor))
value = Variant ((time_t) iso, Variant::type_duration);
else
value = Variant ((time_t) ISO8601p (ref.get (canonical)), Variant::type_duration);
}
else if (column->type () == "numeric")
value = Variant (ref.get_float (canonical));
else
value = Variant (ref.get (canonical));
return true;
}
if (ref.data.size () && size == 2 && canonical == "tags")
{
value = Variant (ref.hasTag (elements[1]) ? elements[1] : "");
return true;
}
if (ref.data.size () && size == 2 && column && column->type () == "date")
{
ISO8601d date (ref.get_date (canonical));
if (elements[1] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
else if (elements[1] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
else if (elements[1] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
else if (elements[1] == "week") { value = Variant (static_cast<int> (date.week ())); return true; }
else if (elements[1] == "weekday") { value = Variant (static_cast<int> (date.dayOfWeek ())); return true; }
else if (elements[1] == "julian") { value = Variant (static_cast<int> (date.dayOfYear ())); return true; }
else if (elements[1] == "hour") { value = Variant (static_cast<int> (date.hour ())); return true; }
else if (elements[1] == "minute") { value = Variant (static_cast<int> (date.minute ())); return true; }
else if (elements[1] == "second") { value = Variant (static_cast<int> (date.second ())); return true; }
}
}
if (ref.data.size () && size == 3 && elements[0] == "annotations")
{
std::map <std::string, std::string> annos;
ref.getAnnotations (annos);
int a = strtol (elements[1].c_str (), NULL, 10);
int count = 0;
// Count off the 'a'th annotation.
for (auto& i : annos)
{
if (++count == a)
{
if (elements[2] == "entry")
{
// annotation_1234567890
// 0 ^11
value = Variant ((time_t) strtol (i.first.substr (11).c_str (), NULL, 10), Variant::type_date);
return true;
}
else if (elements[2] == "description")
{
value = Variant (i.second);
return true;
}
}
}
}
if (ref.data.size () && size == 4 && elements[0] == "annotations" && elements[2] == "entry")
{
std::map <std::string, std::string> annos;
ref.getAnnotations (annos);
int a = strtol (elements[1].c_str (), NULL, 10);
int count = 0;
// Count off the 'a'th annotation.
for (auto& i : annos)
{
if (++count == a)
{
// <annotations>.<N>.entry.year
// <annotations>.<N>.entry.month
// <annotations>.<N>.entry.day
// <annotations>.<N>.entry.week
// <annotations>.<N>.entry.weekday
// <annotations>.<N>.entry.julian
// <annotations>.<N>.entry.hour
// <annotations>.<N>.entry.minute
// <annotations>.<N>.entry.second
ISO8601d date (i.first.substr (11));
if (elements[3] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
else if (elements[3] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
else if (elements[3] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
else if (elements[3] == "week") { value = Variant (static_cast<int> (date.week ())); return true; }
else if (elements[3] == "weekday") { value = Variant (static_cast<int> (date.dayOfWeek ())); return true; }
else if (elements[3] == "julian") { value = Variant (static_cast<int> (date.dayOfYear ())); return true; }
else if (elements[3] == "hour") { value = Variant (static_cast<int> (date.hour ())); return true; }
else if (elements[3] == "minute") { value = Variant (static_cast<int> (date.minute ())); return true; }
else if (elements[3] == "second") { value = Variant (static_cast<int> (date.second ())); return true; }
}
}
}
// Delegate to the context-free version of DOM::get.
return this->get (name, value);
}
////////////////////////////////////////////////////////////////////////////////