// "Therefore those skilled at the unorthodox
// are infinite as heaven and earth,
// inexhaustible as the great rivers.
// When they come to an end,
// they begin again,
// like the days and months;
// they die and are reborn,
// like the four seasons."
//
// - Sun Tsu,
// "The Art of War"
using System.Collections.Generic;
using TheArtOfDev.HtmlRenderer.Core.Utils;
namespace TheArtOfDev.HtmlRenderer.Core.Entities
{
///
/// Represents a block of CSS property values.
/// Contains collection of key-value pairs that are CSS properties for specific css class.
/// Css class can be either custom or html tag name.
///
///
/// To learn more about CSS blocks visit CSS spec: http://www.w3.org/TR/CSS21/syndata.html#block
///
public sealed class CssBlock
{
#region Fields and Consts
///
/// the name of the css class of the block
///
private readonly string _class;
///
/// the CSS block properties and values
///
private readonly Dictionary _properties;
///
/// additional selectors to used in hierarchy (p className1 > className2)
///
private readonly List _selectors;
///
/// is the css block has :hover pseudo-class
///
private readonly bool _hover;
#endregion
///
/// Creates a new block from the block's source
///
/// the name of the css class of the block
/// the CSS block properties and values
/// optional: additional selectors to used in hierarchy
/// optional: is the css block has :hover pseudo-class
public CssBlock(string @class, Dictionary properties, List selectors = null, bool hover = false)
{
ArgChecker.AssertArgNotNullOrEmpty(@class, "@class");
ArgChecker.AssertArgNotNull(properties, "properties");
_class = @class;
_selectors = selectors;
_properties = properties;
_hover = hover;
}
///
/// the name of the css class of the block
///
public string Class
{
get { return _class; }
}
///
/// additional selectors to used in hierarchy (p className1 > className2)
///
public List Selectors
{
get { return _selectors; }
}
///
/// Gets the CSS block properties and its values
///
public IDictionary Properties
{
get { return _properties; }
}
///
/// is the css block has :hover pseudo-class
///
public bool Hover
{
get { return _hover; }
}
///
/// Merge the other block properties into this css block.
/// Other block properties can overwrite this block properties.
///
/// the css block to merge with
public void Merge(CssBlock other)
{
ArgChecker.AssertArgNotNull(other, "other");
foreach (var prop in other._properties.Keys)
{
_properties[prop] = other._properties[prop];
}
}
///
/// Create deep copy of the CssBlock.
///
/// new CssBlock with same data
public CssBlock Clone()
{
return new CssBlock(_class, new Dictionary(_properties), _selectors != null ? new List(_selectors) : null);
}
///
/// Check if the two css blocks are the same (same class, selectors and properties).
///
/// the other block to compare to
/// true - the two blocks are the same, false - otherwise
public bool Equals(CssBlock other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
if (!Equals(other._class, _class))
return false;
if (!Equals(other._properties.Count, _properties.Count))
return false;
foreach (var property in _properties)
{
if (!other._properties.ContainsKey(property.Key))
return false;
if (!Equals(other._properties[property.Key], property.Value))
return false;
}
if (!EqualsSelector(other))
return false;
return true;
}
///
/// Check if the selectors of the css blocks is the same.
///
/// the other block to compare to
/// true - the selectors on blocks are the same, false - otherwise
public bool EqualsSelector(CssBlock other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
if (other.Hover != Hover)
return false;
if (other._selectors == null && _selectors != null)
return false;
if (other._selectors != null && _selectors == null)
return false;
if (other._selectors != null && _selectors != null)
{
if (!Equals(other._selectors.Count, _selectors.Count))
return false;
for (int i = 0; i < _selectors.Count; i++)
{
if (!Equals(other._selectors[i].Class, _selectors[i].Class))
return false;
if (!Equals(other._selectors[i].DirectParent, _selectors[i].DirectParent))
return false;
}
}
return true;
}
///
/// Check if the two css blocks are the same (same class, selectors and properties).
///
/// the other block to compare to
/// true - the two blocks are the same, false - otherwise
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != typeof(CssBlock))
return false;
return Equals((CssBlock)obj);
}
///
/// Serves as a hash function for a particular type.
///
/// A hash code for the current .
public override int GetHashCode()
{
unchecked
{
return ((_class != null ? _class.GetHashCode() : 0) * 397) ^ (_properties != null ? _properties.GetHashCode() : 0);
}
}
///
/// Returns a that represents the current .
///
public override string ToString()
{
var str = _class + " { ";
foreach (var property in _properties)
{
str += string.Format("{0}={1}; ", property.Key, property.Value);
}
return str + " }";
}
}
}