/* LineSpanArticulator Copyright (C) 2009 Raymond Glover 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ using System; using System.Reflection; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cynosura { [Flags] public enum TemporalGroupType { [TimeSpan("year", Days = 365)] year = 1, [TimeSpan("month", Days = 30, Hours = 12)] month = 2, [TimeSpan("week", Days = 7)] week = 4, [TimeSpan("day", Days = 1)] day = 8, [TimeSpan("hour", Hours = 1)] hour = 16, [TimeSpan("minute", Minutes = 1)] minute = 32, [TimeSpan("second", Seconds = 1)] second = 64 } internal class TimeSpanAttribute : Attribute { public int Days { get; set; } public int Hours { get; set; } public int Minutes { get; set; } public int Seconds { get; set; } public string Name { get; private set; } public TimeSpanAttribute(string name) { this.Name = name; } /// /// Calculates and returns the Timespan for this attributes state /// public TimeSpan GetTimeSpan() { return new TimeSpan( this.Days, this.Hours, this.Minutes, this.Seconds); } /// /// Uses reflection to retrieve an instance of this attribute /// on a given enum /// public static TimeSpanAttribute RetrieveAttribute(Enum target) { object[] attributes = target.GetType().GetField(target.ToString()).GetCustomAttributes( typeof(TimeSpanAttribute), true); if (attributes != null && attributes.Length > 0) return (TimeSpanAttribute)attributes[0]; else return null; } } public static class TimeSpanArticulator { private static readonly string Seperator = ","; private static readonly string Plural = "s"; private static readonly string And = "and"; private static readonly string Space = " "; private const TemporalGroupType defaultAccuracy = TemporalGroupType.hour | TemporalGroupType.day | TemporalGroupType.week | TemporalGroupType.month | TemporalGroupType.year; internal class TemporalGrouping { /// /// The type of the temporal grouping /// e.g. 'hour' or 'day' /// internal TemporalGroupType Type { get; private set; } /// /// The size of the temporal grouping. /// e.g. '1' hour, or '3' hours /// internal int Magnitude { get; private set; } internal TemporalGrouping( TemporalGroupType type, int magnitude) { this.Type = type; this.Magnitude = magnitude; } public override string ToString() { string result = this.Magnitude.ToString(); result += " " + TimeSpanAttribute.RetrieveAttribute(this.Type).Name; if (this.Magnitude > 1) { result += Plural; } return result; } } // a cache of all the TemporalGroupTypes private static List groupTypes; // static contructor static TimeSpanArticulator() { groupTypes = new List(Enum.GetValues( typeof(TemporalGroupType)) as IEnumerable); } /// /// Articulates a given TimeSpan using the default accuracy /// /// The TimeSpan to articulate public static string Articulate(TimeSpan span) { return Articulate(span, defaultAccuracy); } /// The TimeSpan to articulate /// Accuracy Flags public static string Articulate(TimeSpan span, TemporalGroupType accuracy) { // populate a list with temporalgroupings. Each temporal grouping // represents a particular element of the articulation, ordered // accoring to the temporal duration of each element. List groupings = new List(4); // foreach possible temporal type (day/hour/minute etc.) foreach (TemporalGroupType type in groupTypes) { // if the temporal type isn't specified in the accuracy, skip. if ((accuracy & type) != type) continue; // get the timespan for this temporal type TimeSpan ts = TimeSpanAttribute.RetrieveAttribute(type).GetTimeSpan(); if (span.Ticks >= ts.Ticks) { // divide the current timespan with the temporal group span int magnitude = (int)(span.Ticks / ts.Ticks); groupings.Add(new TemporalGrouping(type, magnitude)); span = new TimeSpan(span.Ticks % ts.Ticks); } } return Textify(groupings); } /// /// converts a list of groupings into text /// private static string Textify(IList groupings) { string result = String.Empty; for (int i = 0; i < groupings.Count; i++) { string groupingStr = groupings[i].ToString(); if (i > 0) { if (i == groupings.Count - 1) { // this is the last element. Add an "and" // between this and the last. result += Space + And + Space; } else { // add comma between this and the next element result += Seperator + Space; } } result += groupingStr; } return result; } } }