/* * Copyright (C) 2011 Collabora Ltd. * Copyright (C) 2011 Philip Withnall * * 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 * Raul Gutierrez Segales * Philip Withnall */ using GLib; using Gee; /** * Object representing a phone number that can have some parameters associated * with it. * * See {@link Folks.AbstractFieldDetails} for details on common parameter names * and values. * * @since 0.6.0 */ public class Folks.PhoneFieldDetails : AbstractFieldDetails { private const string[] _extension_chars = { "p", "P", "w", "W", "x", "X" }; private const string[] _common_delimiters = { ",", ".", "(", ")", "-", " ", "\t", "/" }; private const string[] _valid_digits = { "#", "*", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; private string _id; /** * {@inheritDoc} */ public override string id { get { return this._id; } set { this._id = (value != null ? value : ""); } } /** * Create a new PhoneFieldDetails. * * @param value the value of the field, which should be a non-empty phone * number (no particular format is mandated) * @param parameters initial parameters. See * {@link AbstractFieldDetails.parameters}. A `null` value is equivalent to a * empty map of parameters. * * @return a new PhoneFieldDetails * * @since 0.6.0 */ public PhoneFieldDetails (string value, MultiMap? parameters = null) { if (value == "") { warning ("Empty phone number passed to PhoneFieldDetails."); } Object (value: value, parameters: parameters); } /** * {@inheritDoc} * * @since 0.6.0 */ public override bool equal (AbstractFieldDetails that) { return base.equal (that); } /** * {@inheritDoc} */ public override bool values_equal (AbstractFieldDetails that) { var _that_fd = that as PhoneFieldDetails; if (_that_fd == null) return false; PhoneFieldDetails that_fd = (!) _that_fd; var n1 = this._drop_extension (this.get_normalised ()); var n2 = this._drop_extension (that_fd.get_normalised ()); /* Based on http://blog.barisione.org/2010-06/handling-phone-numbers/ */ if (n1.length >= 7 && n2.length >= 7) { var n1_reduced = n1.slice (-7, n1.length); var n2_reduced = n2.slice (-7, n2.length); debug ("[PhoneDetails.equal] Comparing %s with %s", n1_reduced, n2_reduced); return n1_reduced == n2_reduced; } return n1 == n2; } /** * {@inheritDoc} * * @since 0.6.0 */ public override uint hash () { return base.hash (); } /** * Return this object's normalised phone number. * * Typical normalisations: * * - `1-800-123-4567` → `18001234567` * - `+1-800-123-4567` → `18001234567` * - `+1-800-123-4567P123` → `18001234567P123` * * @return the normalised form of `number` * * @since 0.6.0 */ public string get_normalised () { string normalised_number = ""; for (int i = 0; i < this.value.length; i++) { var digit = this.value.slice (i, i + 1); if (i == 0 && digit == "+") { /* we drop the initial + */ continue; } else if (digit in this._extension_chars || digit in this._valid_digits) { /* lets keep valid digits */ normalised_number += digit; } else if (digit in this._common_delimiters) { continue; } else { debug ("[PhoneDetails.get_normalised] unknown digit: %s", digit); } } return normalised_number.up (); } /** * Returns the given number without its extension (if any). * * @param number the phone number to process * @return the number without its extension; if the number didn't have an * extension in the first place, the number is returned unmodified * * @since 0.6.0 */ internal static string _drop_extension (string number) { for (var i = 0; i < PhoneFieldDetails._extension_chars.length; i++) { if (number.index_of (PhoneFieldDetails._extension_chars[i]) >= 0) { return number.split (PhoneFieldDetails._extension_chars[i])[0]; } } return number; } } /** * Interface for classes that can provide a phone number, such as * {@link Persona} and {@link Individual}. * * @since 0.3.5 */ public interface Folks.PhoneDetails : Object { /** * The phone numbers of the contact. * * A list of phone numbers associated to the contact. * * @since 0.6.0 */ public abstract Set phone_numbers { get; set; } /** * Change the contact's phone numbers. * * It's preferred to call this rather than setting * {@link PhoneDetails.phone_numbers} directly, as this method gives error * notification and will only return once the phone numbers have been written * to the relevant backing store (or the operation's failed). * * @param phone_numbers the set of phone numbers * @throws PropertyError if setting the phone numbers failed * @since 0.6.2 */ public virtual async void change_phone_numbers ( Set phone_numbers) throws PropertyError { /* Default implementation. */ throw new PropertyError.NOT_WRITEABLE ( _("Phone numbers are not writeable on this contact.")); } }