/*
* Copyright (C) 2011 Collabora Ltd.
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*
* Authors:
* Marco Barisione
* Travis Reitter
*/
using GLib;
using Gee;
/**
* Object representing any type of value that can have some vCard-like
* parameters associated with it.
*
* Some contact details, like phone numbers or URLs, can have some
* extra details associated with them.
* For instance, a phone number expressed in vcard notation as
* `tel;type=work,voice:(111) 555-1234` would be represented as
* a AbstractFieldDetails with value "(111) 555-1234" and with parameters
* `['type': ('work', 'voice')]`.
*
* The parameter name "type" with values "work", "home", or "other" are common
* amongst most vCard attributes (and thus most AbstractFieldDetails-derived
* classes). A "type" of "pref" may be used to indicate a preferred
* {@link AbstractFieldDetails.value} amongst many. See specific classes for
* information on additional parameters and values specific to that class.
*
* See [[http://www.ietf.org/rfc/rfc2426.txt|RFC2426]] for more details on
* pre-defined parameter names and values.
*
* @since 0.6.0
*/
public abstract class Folks.AbstractFieldDetails : Object
{
/**
* Parameter name for classifying the type of value this field contains.
*
* For example, the value could be relevant to the contact's home life, or to
* their work life; values of {@link AbstractFieldDetails.PARAM_TYPE_HOME}
* and {@link AbstractFieldDetails.PARAM_TYPE_WORK} would be used for the
* {@link AbstractFieldDetails.PARAM_TYPE} parameter, respectively, in those
* cases.
*
* @since 0.6.3
*/
public static const string PARAM_TYPE = "type";
/**
* Parameter value for home-related field values.
*
* Value for a parameter with name {@link AbstractFieldDetails.PARAM_TYPE}.
*
* @since 0.6.3
*/
public static const string PARAM_TYPE_HOME = "home";
/**
* Parameter value for work-related field values.
*
* Value for a parameter with name {@link AbstractFieldDetails.PARAM_TYPE}.
*
* @since 0.6.3
*/
public static const string PARAM_TYPE_WORK = "work";
/**
* Parameter value for miscellaneous field values.
*
* Value for a parameter with name {@link AbstractFieldDetails.PARAM_TYPE}.
*
* @since 0.6.3
*/
public static const string PARAM_TYPE_OTHER = "other";
private T _value;
/**
* The value of the field.
*
* The value of the field, the exact type and content of which depends on what
* the structure is used for.
*
* @since 0.6.0
*/
public virtual T @value
{
get { return this._value; }
construct set { this._value = value; }
}
/**
* The {@link GLib.Type} of the {@link AbstractFieldDetails.value}.
*
* This is particularly useful for treating collections of different types of
* {@link AbstractFieldDetails} in a uniform way without needing to name them
* explicitly.
*
* @since 0.6.5
*/
public Type value_type
{
get { return typeof (T); }
}
private string _id;
/**
* A unique ID (if any) for this specific detail.
*
* This is primarily intended for {@link PersonaStore}s which need to track
* specific instances of details (because their backing store is wacky).
*
* In most cases, this will be an empty string.
*
* The content of this is opaque to all but the package which set it.
*
* @since 0.6.5
*/
public virtual string id
{
get { return this._id; }
set { this._id = (value != null ? value : ""); }
}
private MultiMap _parameters =
new HashMultiMap ();
/**
* The parameters associated with the value.
*
* A multi-map of the parameters associated with
* {@link Folks.AbstractFieldDetails.value}. The keys are the names of
* the parameters, while the values are a list of strings.
*
* @since 0.6.0
*/
public virtual MultiMap parameters
{
get { return this._parameters; }
construct set
{
if (value == null)
this._parameters.clear ();
else
this._parameters = value;
}
}
/**
* Get the values for a parameter
*
* @param parameter_name the parameter name
* @return a collection of values for `parameter_name` or `null` (i.e. no
* collection) if there are no such parameters.
*
* @since 0.6.0
*/
public Collection? get_parameter_values (string parameter_name)
{
if (this.parameters.contains (parameter_name) == false)
{
return null;
}
return this.parameters.get (parameter_name).read_only_view;
}
/**
* Add a new value for a parameter.
*
* If there is already a parameter called `parameter_name` then
* `parameter_value` is added to the existing ones.
*
* @param parameter_name the name of the parameter
* @param parameter_value the value to add
*
* @since 0.6.0
*/
public void add_parameter (string parameter_name, string parameter_value)
{
this.parameters.set (parameter_name, parameter_value);
}
/**
* Set the value of a parameter.
*
* Sets the parameter called `parameter_name` to be `parameter_value`.
* If there were already parameters with the same name they are replaced.
*
* @param parameter_name the name of the parameter
* @param parameter_value the value to add
*
* @since 0.6.0
*/
public void set_parameter (string parameter_name, string parameter_value)
{
this.parameters.remove_all (parameter_name);
this.parameters.set (parameter_name, parameter_value);
}
/**
* Extend the existing parameters.
*
* Merge the parameters from `additional` into the existing ones.
*
* @param additional the parameters to add
*
* @since 0.6.0
*/
public void extend_parameters (MultiMap additional)
{
foreach (var name in additional.get_keys ())
{
var values = additional.get (name);
foreach (var val in values)
{
this.add_parameter (name, val);
}
}
}
/**
* Remove all instances of a parameter.
*
* @param parameter_name the name of the parameter
*
* @since 0.6.0
*/
public void remove_parameter_all (string parameter_name)
{
this.parameters.remove_all (parameter_name);
}
/**
* A fairly-strict equality function for {@link AbstractFieldDetails}.
*
* This function compares:
*
* * {@link AbstractFieldDetails.value}s
* * {@link AbstractFieldDetails.parameters}
*
* And does not compare:
*
* * {@link AbstractFieldDetails.id}s
*
* See the description of {@link AbstractFieldDetails.values_equal} for
* details on the value comparison.
*
* To check equality not including the parameters, see
* {@link AbstractFieldDetails.values_equal}.
*
* @param that another {@link AbstractFieldDetails}
*
* @return whether the elements are equal
*
* @see AbstractFieldDetails.parameters_equal
* @see AbstractFieldDetails.values_equal
* @since 0.6.0
*/
public virtual bool equal (AbstractFieldDetails that)
{
return (this.get_type () == that.get_type ()) &&
this.values_equal (that) &&
this.parameters_equal (that);
}
/**
* An equality function which only considers parameters.
*
* This function compares:
*
* * {@link AbstractFieldDetails.parameters}
*
* And does not compare:
*
* * {@link AbstractFieldDetails.value}s
* * {@link AbstractFieldDetails.id}s
*
* @param that another {@link AbstractFieldDetails}
*
* @return whether the elements' {@link AbstractFieldDetails.value}s are
* equal.
*
* @see AbstractFieldDetails.equal
* @see AbstractFieldDetails.values_equal
* @since 0.6.5
*/
public virtual bool parameters_equal (AbstractFieldDetails that)
{
/* Check that the parameter names and their values match exactly in both
* AbstractFieldDetails objects. */
if (this.parameters.size != that.parameters.size)
return false;
foreach (var param in this.parameters.get_keys ())
{
/* Since these parameters are meant to model vCard parameters, we
* should compare on a case-insensitive basis. However, this leads to
* ambiguity in the case:
*
* this.parameters = {"foo": {"bar"}}
* that.parameters = {"foo": {"bar"}, "FOO": {"qux"}}
*
* So parameter names should normalised elsewhere (either in the
* insertion functions (with a big warning for the clients) or in the
* clients themselves).
*
* Note that parameter values can't be normalised in general, since
* they can be user-set labels.
*/
if (!that.parameters.contains (param))
return false;
var this_param_values = this.parameters.get_values ();
var that_param_values = that.parameters.get_values ();
if (this_param_values.size != that_param_values.size)
return false;
foreach (var param_val in this.parameters.get_values ())
{
if (!that_param_values.contains (param_val))
return false;
}
}
return true;
}
/**
* An equality function which does not consider parameters.
*
* Specific classes may override this function to provide "smart" value
* comparisons (eg, considering the phone number values "+1 555 123 4567" and
* "123-4567" equal). If you wish to do strict comparisons, simply compare the
* {@link AbstractFieldDetails.value}s directly.
*
* This function compares:
*
* * {@link AbstractFieldDetails.value}s
*
* And does not compare:
*
* * {@link AbstractFieldDetails.parameters}
* * {@link AbstractFieldDetails.id}s
*
* This defaults to string comparison of the
* {@link AbstractFieldDetails.value}s if the generic type is string;
* otherwise, direct pointer comparison of the
* {@link AbstractFieldDetails.value}s.
*
* @param that another {@link AbstractFieldDetails}
*
* @return whether the elements' {@link AbstractFieldDetails.value}s are
* equal.
*
* @see AbstractFieldDetails.equal
* @see AbstractFieldDetails.parameters_equal
* @since 0.6.5
*/
public virtual bool values_equal (AbstractFieldDetails that)
{
EqualFunc equal_func = direct_equal;
if (typeof (T) == typeof (string))
equal_func = str_equal;
if ((this.get_type () != that.get_type ()) ||
!equal_func (this.value, that.value))
{
return false;
}
return true;
}
/**
* A hash function for the {@link AbstractFieldDetails}.
*
* This defaults to a string hash of the
* {@link AbstractFieldDetails.value} if the generic type is string;
* otherwise, direct hash of the {@link AbstractFieldDetails.value}.
*
* @return the hash value
*
* @since 0.6.0
*/
public virtual uint hash ()
{
HashFunc hash_func = direct_hash;
if (typeof (T) == typeof (string))
hash_func = str_hash;
return hash_func (this.value);
}
}