1
mirror of https://github.com/carlospolop/PEASS-ng synced 2025-03-14 15:14:28 +01:00

- implemented YAML checks

- updated sensitive_files.yaml
This commit is contained in:
makikvues 2021-07-06 13:57:25 +02:00
parent d8cc0f31d3
commit 2a1d2cfb52
31 changed files with 12415 additions and 1926 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
--- 2009-10-04 Osamu TAKEUCHI <osamu@big.jp>
Alpha release of YamlSerializer as 0.9.0.2
* All "_"s in integer and floating point values are neglected
to accommodate the !!int and !!float encoding.
* YamlConfig.DontUseVerbatimTag is added but the default value is set false.
Note that !<!System.Int32[,]> is much human friendly than !System.Int32%5B%2C%5D.
* Equality of YamlNode with an unknown tag is evaluated by identity,
while that of !!map and !!seq node is still evaluated by YAML's standard.
Note that equality of !!map and !!seq are different from that of object[]
and Dictionary<object, object>.
* YamlConfig.OmitTagForRootNode was added. Fixed issue #2850.
* Serialize Dictionary<object,object> to !!map. Fixed #2891.
* Modified [126-130] ns-plain-???, [147] c-ns-flow-map-separate-value(n,c)
to accommodate revision 2009-10-01
* Omit !< > if Tag contains only ns-tag-char, Fixed issue #2813
--- 2009-09-23 Osamu TAKEUCHI <osamu@big.jp>
Alpha release of YamlSerializer as 0.9.0.1
* Removed TODO's for reporting bugs in YAML spec that are done.
* Fixed assembly copyright.
* !!merge is supported. Fixed issue#2605.
* Read-only class-type member with no child members are omitted when
serializing. Fixed issue#2599.
* Culture for TypeConverter is set to be CultureInfo.InvariantCulture.
Fixed issue #2629.
* To fix Issue#2631
* Field names and property names are always presented as simple texts.
* When deserializing, we can not avoid the parser parses some spacial
names to !!bool and !!null. Such non-text nodes are converted to
texts at construction stage.
* To fix issue#2663
* Hash code stored in a mapping node is now updated when the a key node's
content is changed.
* Hash code and equality became independent on the order of keys in a
mapping node.
* A mapping node checks for duplicated keys every time the node content
is changed.
* Test results are changed because some of them are dependent on the hash
key order.
* The current equality evaluation is too strict, probably needs some adjustment.
* NativeObject property was added to YamlScalar.
* YamlScalar's equality is evaluated by comparing NativeObject.
--- 2009-09-11 Osamu TAKEUCHI <osamu@big.jp>
First release of YamlSerializer as 0.9.0.0

View File

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Globalization;
namespace System.Yaml.Serialization
{
/// <summary>
/// Converts various types to / from string.<br/>
/// I don't remember why this class was needed....
/// </summary>
/// <example>
/// <code>
/// object obj = GetObjectToConvert();
///
/// // Check if the type has [TypeConverter] attribute.
/// if( EasyTypeConverter.IsTypeConverterSpecified(type) ) {
///
/// // Convert the object to string.
/// string s = EasyTypeConverter.ConvertToString(obj);
///
/// // Convert the string to an object of the spific type.
/// object restored = EasyTypeConverter.ConvertFromString(s, type);
///
/// Assert.AreEqual(obj, restored);
///
/// }
/// </code>
/// </example>
internal class EasyTypeConverter
{
internal CultureInfo Culture;
public EasyTypeConverter()
{
Culture = System.Globalization.CultureInfo.InvariantCulture;
}
private static Dictionary<Type, TypeConverter> TypeConverters = new Dictionary<Type, TypeConverter>();
private static Dictionary<Type, bool> TypeConverterSpecified = new Dictionary<Type, bool>();
public static bool IsTypeConverterSpecified(Type type)
{
if ( !TypeConverterSpecified.ContainsKey(type) )
RegisterTypeConverterFor(type);
return TypeConverterSpecified[type];
}
private static TypeConverter FindConverter(Type type)
{
if ( !TypeConverters.ContainsKey(type) ) {
return RegisterTypeConverterFor(type);
} else {
return TypeConverters[type];
}
}
private static TypeConverter RegisterTypeConverterFor(Type type)
{
var converter_attr = type.GetAttribute<TypeConverterAttribute>();
if ( converter_attr != null ) {
// What is the difference between these two conditions?
TypeConverterSpecified[type] = true;
var converterType = TypeUtils.GetType(converter_attr.ConverterTypeName);
return TypeConverters[type] = Activator.CreateInstance(converterType) as TypeConverter;
} else {
// What is the difference between these two conditions?
TypeConverterSpecified[type] = false;
return TypeConverters[type] = TypeDescriptor.GetConverter(type);
}
}
public string ConvertToString(object obj)
{
if ( obj == null )
return "null";
var converter = FindConverter(obj.GetType());
if ( converter != null ) {
return converter.ConvertToString(null, Culture, obj);
} else {
return obj.ToString();
}
}
public object ConvertFromString(string s, Type type)
{
return FindConverter(type).ConvertFromString(null, Culture, s);
}
}
}

View File

@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace YamlSerializerNamespace
{
public static class ObjectExtensions
{
public static T ToObject<T>(this IDictionary<string, object> source)
where T : class, new()
{
var someObject = new T();
var someObjectType = someObject.GetType();
foreach (var item in source)
{
someObjectType
.GetProperty((item.Key))
.SetValue(someObject, item.Value, null);
}
return someObject;
}
public static string PascalCase(this string word)
{
return string.Join("", word.Split('_')
.Select(w => w.Trim())
.Where(w => w.Length > 0)
.Select(w => w.Substring(0, 1).ToUpper() + w.Substring(1).ToLower()));
}
public static IDictionary<string, object> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
{
return source.GetType().GetProperties(bindingAttr).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source, null)
);
}
}
}

View File

@ -0,0 +1,224 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace System.Yaml.Serialization
{
/// <summary>
///
/// object に代入されたクラスや構造体のメンバーに、リフレクションを
/// 解して簡単にアクセスできるようにしたクラス
///
/// アクセス方法をキャッシュするので、繰り返し使用する場合に高速化が
/// 期待できる
/// </summary>
internal class ObjectMemberAccessor
{
private readonly static object[] EmptyObjectArray = new object[0];
/// <summary>
/// Caches ObjectMemberAccessor instances for reuse.
/// </summary>
static Dictionary<Type, ObjectMemberAccessor> MemberAccessors = new Dictionary<Type, ObjectMemberAccessor>();
/// <summary>
///
/// 指定した型へのアクセス方法を表すインスタンスを返す
/// キャッシュに存在すればそれを返す
/// キャッシュに存在しなければ新しく作って返す
/// 作った物はキャッシュされる
/// </summary>
/// <param name="type">クラスまたは構造体を表す型情報</param>
/// <returns></returns>
public static ObjectMemberAccessor FindFor(Type type)
{
if ( !MemberAccessors.ContainsKey(type) )
MemberAccessors[type] = new ObjectMemberAccessor(type);
return MemberAccessors[type];
}
private ObjectMemberAccessor(Type type)
{
/*
if ( !TypeUtils.IsPublic(type) )
throw new ArgumentException(
"Can not serialize non-public type {0}.".DoFormat(type.FullName));
*/
// public properties
foreach ( var p in type.GetProperties(
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.GetProperty) ) {
var prop = p; // create closures with this local variable
// not readable or parameters required to access the property
if ( !prop.CanRead || prop.GetGetMethod(false) == null || prop.GetIndexParameters().Count() != 0 )
continue;
Func<object, object> get = obj => prop.GetValue(obj, EmptyObjectArray);
Action<object, object> set = null;
if ( prop.CanWrite && prop.GetSetMethod(false) != null )
set = (obj, value) => prop.SetValue(obj, value, EmptyObjectArray);
RegisterMember(type, prop, prop.PropertyType, get, set);
}
// public fields
foreach ( var f in type.GetFields(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.GetField) ) {
var field = f;
if ( !field.IsPublic )
continue;
Func<object, object> get = obj => field.GetValue(obj);
Action<object, object> set = (obj, value) => field.SetValue(obj, value);
RegisterMember(type, field, field.FieldType, get, set);
}
Type itype;
// implements IDictionary
if ( type.GetInterface("System.Collections.IDictionary") != null ) {
IsDictionary = true;
IsReadOnly = obj => ( (System.Collections.IDictionary)obj ).IsReadOnly;
// extract Key, Value types from IDictionary<??, ??>
itype = type.GetInterface("System.Collections.Generic.IDictionary`2");
if ( itype != null ) {
KeyType = itype.GetGenericArguments()[0];
ValueType = itype.GetGenericArguments()[1];
}
} else
// implements ICollection<T>
if ( ( itype = type.GetInterface("System.Collections.Generic.ICollection`1") ) != null ) {
ValueType = itype.GetGenericArguments()[0];
var add = itype.GetMethod("Add", new Type[] { ValueType });
CollectionAdd = (obj, value) => add.Invoke(obj, new object[] { value });
var clear = itype.GetMethod("Clear", new Type[0]);
CollectionClear = obj => clear.Invoke(obj, new object[0]);
var isReadOnly = itype.GetProperty("IsReadOnly", new Type[0]).GetGetMethod();
IsReadOnly = obj => (bool)isReadOnly.Invoke(obj, new object[0]);
} else
// implements IList
if ( ( itype = type.GetInterface("System.Collections.IList") ) != null ) {
var add = itype.GetMethod("Add", new Type[] { typeof(object) });
CollectionAdd = (obj, value) => add.Invoke(obj, new object[] { value });
var clear = itype.GetMethod("Clear", new Type[0]);
CollectionClear = obj => clear.Invoke(obj, new object[0]);
/* IList<T> implements ICollection<T>
// Extract Value Type from IList<T>
itype = type.GetInterface("System.Collections.Generic.IList`1");
if ( itype != null )
ValueType = itype.GetGenericArguments()[0];
*/
IsReadOnly = obj => ((System.Collections.IList)obj).IsReadOnly;
}
}
private void RegisterMember(Type type, System.Reflection.MemberInfo m, Type mType, Func<object, object> get, Action<object, object> set)
{
// struct that holds access method for property/field
MemberInfo accessor = new MemberInfo();
accessor.Type = mType;
accessor.Get = get;
accessor.Set = set;
if(set!=null){ // writeable ?
accessor.SerializeMethod = YamlSerializeMethod.Assign;
} else {
accessor.SerializeMethod = YamlSerializeMethod.Never;
if ( mType.IsClass )
accessor.SerializeMethod = YamlSerializeMethod.Content;
}
var attr1 = m.GetAttribute<YamlSerializeAttribute>();
if ( attr1 != null ) { // specified
if ( set == null ) { // read only member
if ( attr1.SerializeMethod == YamlSerializeMethod.Assign ||
( mType.IsValueType && accessor.SerializeMethod == YamlSerializeMethod.Content ) )
throw new ArgumentException("{0} {1} is not writeable by {2}."
.DoFormat(mType.FullName, m.Name, attr1.SerializeMethod.ToString()));
}
accessor.SerializeMethod = attr1.SerializeMethod;
}
if ( accessor.SerializeMethod == YamlSerializeMethod.Never )
return; // no need to register
if ( accessor.SerializeMethod == YamlSerializeMethod.Binary ) {
if ( !mType.IsArray )
throw new InvalidOperationException("{0} {1} of {2} is not an array. Can not be serialized as binary."
.DoFormat(mType.FullName, m.Name, type.FullName));
if ( !TypeUtils.IsPureValueType(mType.GetElementType()) )
throw new InvalidOperationException(
"{0} is not a pure ValueType. {1} {2} of {3} can not serialize as binary."
.DoFormat(mType.GetElementType(), mType.FullName, m.Name, type.FullName));
}
// ShouldSerialize
// YamlSerializeAttribute(Never) => false
// ShouldSerializeSomeProperty => call it
// DefaultValueAttribute(default) => compare to it
// otherwise => true
var shouldSerialize = type.GetMethod("ShouldSerialize" + m.Name,
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,
null, Type.EmptyTypes, new System.Reflection.ParameterModifier[0]);
if ( shouldSerialize != null && shouldSerialize.ReturnType == typeof(bool) && accessor.ShouldSeriealize == null )
accessor.ShouldSeriealize = obj => (bool)shouldSerialize.Invoke(obj, EmptyObjectArray);
var attr2 = m.GetAttribute<DefaultValueAttribute>();
if ( attr2 != null && accessor.ShouldSeriealize == null ) {
var defaultValue = attr2.Value;
if ( TypeUtils.IsNumeric(defaultValue) && defaultValue.GetType() != mType )
defaultValue = TypeUtils.CastToNumericType(defaultValue, mType);
accessor.ShouldSeriealize = obj => !TypeUtils.AreEqual(defaultValue, accessor.Get(obj));
}
if ( accessor.ShouldSeriealize == null )
accessor.ShouldSeriealize = obj => true;
Accessors.Add(m.Name, accessor);
}
public bool IsDictionary = false;
public Action<object, object> CollectionAdd = null;
public Action<object> CollectionClear = null;
public Type KeyType = null;
public Type ValueType = null;
public Func<object,bool> IsReadOnly;
public struct MemberInfo
{
public YamlSerializeMethod SerializeMethod;
public Func<object, object> Get;
public Action<object, object> Set;
public Func<object, bool> ShouldSeriealize;
public Type Type;
}
Dictionary<string, MemberInfo> Accessors = new Dictionary<string, MemberInfo>();
public MemberInfo this[string name]
{
get { return Accessors[name]; }
}
public bool ContainsKey(string name)
{
return Accessors.ContainsKey(name);
}
/// <summary>
/// メンバへの読み書きを行うことができる
/// </summary>
/// <param name="obj">オブジェクト</param>
/// <param name="name">メンバの名前</param>
/// <returns></returns>
public object this[object obj, string name]
{
get { return Accessors[name].Get(obj); }
set { Accessors[name].Set(obj, value); }
}
/// <summary>
/// メンバ名と Accessor のペアを巡回する
/// </summary>
/// <returns></returns>
public Dictionary<string, MemberInfo>.Enumerator GetEnumerator()
{
return Accessors.GetEnumerator();
}
}
}

View File

@ -0,0 +1,942 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace System.Yaml
{
/// <summary>
/// <para>When <see cref="Parser&lt;State&gt;"/> reports syntax error by exception, this class is thrown.</para>
///
/// <para>Sytax errors can also be reported by simply returing false with giving some warnings.</para>
/// </summary>
internal class ParseErrorException: Exception
{
/// <summary>
/// Initialize an instance of <see cref="ParseErrorException"/>
/// </summary>
/// <param name="message">Error message.</param>
public ParseErrorException(string message) : base(message) { }
}
/// <summary>
/// <para>Base class to implement a parser class.</para>
///
/// <para>It allows not very efficient but easy implementation of a text parser along
/// with a parameterized BNF productions.</para>
/// </summary>
/// <typeparam name="State">Parser specific state structure.</typeparam>
internal abstract class Parser<State>
where State: struct
{
/// <summary>
/// Parse the <paramref name="text"/> using the <paramref name="start_rule"/>
/// as the starting rule.
/// </summary>
/// <param name="start_rule">Starting rule.</param>
/// <param name="text">Text to be parsed.</param>
/// <returns></returns>
protected bool Parse(Func<bool> start_rule, string text)
{
this.text = text;
InitializeParser();
return start_rule();
}
void InitializeParser()
{
InitializeLines();
p = 0;
stringValue.Length = 0;
state = new State();
}
#region Fields and Properties
/// <summary>
/// <para>Gets / sets source text to be parsed.</para>
/// <para>While parsing, this variable will not be changed.</para>
/// <para>The current position to be read by parser is represented by the field <see cref="p"/>.</para>
/// <para>Namely, the next character to be read is <c>text[p]</c>.</para>
/// </summary>
protected string text;
/// <summary>
/// <para>The current reading position.</para>
///
/// <para>The next character to be read by the parser is <c>text[p]</c>.</para>
///
/// <para>Increase <see cref="p"/> to reduce some part of source text <see cref="text"/>.</para>
///
/// <para>The current position <see cref="p"/> is automatically reverted at rewinding.</para>
/// </summary>
/// <example>
/// Example to show how to reduce BNF reduction rule of ( "t" "e" "x" "t" ).
/// <code>
/// return RewindUnless(()=>
/// text[p++] == 't' &amp;&amp;
/// text[p++] == 'e' &amp;&amp;
/// text[p++] == 'x' &amp;&amp;
/// text[p++] == 't'
/// );
/// </code>
/// </example>
protected int p;
/// <summary>
/// <para>Use this variable to build some string data from source text.</para>
///
/// <para>It will be automatically reverted at rewinding.</para>
/// </summary>
protected StringBuilder stringValue = new StringBuilder();
/// <summary>
/// <para>Individual-parser-specific state object.</para>
///
/// <para>It will be automatically reverted at rewinding.</para>
///
/// <para>If some action, in addition to simply restore the value of the state object,
/// is needed to recover the previous state, override <see cref="Rewind"/>
/// method.</para>
/// </summary>
protected State state;
/// <summary>
/// Get current position represented by raw and column.
/// </summary>
public Position CurrentPosition
{
get
{
Position pos = new Position();
pos.Raw = Lines.BinarySearch(p);
if ( pos.Raw < 0 ) {
pos.Raw = ~pos.Raw;
pos.Column = p - Lines[pos.Raw - 1] + 1;
} else {
pos.Raw++; // 1 base
pos.Column = 1;
}
return pos;
}
}
/// <summary>
/// Initialize <see cref="Lines"/>, which represents line number to
/// start position of each line list.
/// </summary>
private void InitializeLines()
{
Lines = new List<int>();
Lines.Add(0);
for ( var p = 0; p < text.Length; p++ ) {
if ( text[p] == '\r' ) {
if ( p + 1 < text.Length - 1 && text[p + 1] == '\n' )
p++;
Lines.Add(p + 1);
} else
if ( text[p] == '\n' )
Lines.Add(p + 1);
}
}
/// <summary>
/// Line number to start position list.
/// </summary>
List<int> Lines = new List<int>();
/// <summary>
/// Represents a position in a multiline text.
/// </summary>
public struct Position {
/// <summary>
/// Raw in a text.
/// </summary>
public int Raw;
/// <summary>
/// Column in a text.
/// </summary>
public int Column;
}
#endregion
#region Error / Warning
/// <summary>
/// Reporting syntax error by throwing <see cref="ParseErrorException"/>.
/// </summary>
/// <param name="message"><see cref="string.Format(string,object[])"/> template for the error message.</param>
/// <param name="args"><see cref="string.Format(string,object[])"/> parameters if required</param>
/// <returns>Because it throw exception, nothing will be returned in reality.</returns>
public bool Error(string message, params object[] args)
{
throw new ParseErrorException(
string.Format("Syntax error at line {0} column {1}\r\n", CurrentPosition.Raw, CurrentPosition.Column) +
string.Format(message, args));
}
/// <summary>
/// <para>Give warning if <paramref name="condition"/> is true.</para>
///
/// <para>By default, the warning will not be shown / stored to anywhere.
/// To show or log the warning, override <see cref="StoreWarning"/>.</para>
/// </summary>
/// <example>
/// <code>
/// return
/// SomeObsoleteReductionRule() &amp;&amp;
/// WarningIf(
/// context != Context.IndeedObsolete,
/// "Obsolete");
/// </code>
/// </example>
/// <param name="condition">If true, warning is given; otherwize do nothing.</param>
/// <param name="message"><see cref="string.Format(string,object[])"/> template for the warning message.</param>
/// <param name="args"><see cref="string.Format(string,object[])"/> parameters if required</param>
/// <returns>Always true.</returns>
protected bool WarningIf(bool condition, string message, params object[] args)
{
if ( condition )
Warning(message, args);
return true;
}
/// <summary>
/// <para>Give warning if <paramref name="condition"/> is false.</para>
///
/// <para>By default, the warning will not be shown / stored to anywhere.
/// To show or log the warning, override <see cref="StoreWarning"/>.</para>
/// </summary>
/// <example>
/// <code>
/// return
/// SomeObsoleteReductionRule() &amp;&amp;
/// WarningUnless(
/// context != Context.NotObsolete,
/// "Obsolete");
/// </code>
/// </example>
/// <param name="condition">If false, warning is given; otherwize do nothing.</param>
/// <param name="message"><see cref="string.Format(string,object[])"/> template for the warning message.</param>
/// <param name="args"><see cref="string.Format(string,object[])"/> parameters if required</param>
/// <returns>Always true.</returns>
protected bool WarningUnless(bool condition, string message, params object[] args)
{
if ( !condition )
Warning(message, args);
return true;
}
/// <summary>
/// <para>Give warning.</para>
///
/// <para>By default, the warning will not be shown / stored to anywhere.
/// To show or log the warning, override <see cref="StoreWarning"/>.</para>
/// </summary>
/// <example>
/// <code>
/// return
/// SomeObsoleteReductionRule() &amp;&amp;
/// Warning("Obsolete");
/// </code>
/// </example>
/// <param name="message"><see cref="string.Format(string,object[])"/> template for the warning message.</param>
/// <param name="args"><see cref="string.Format(string,object[])"/> parameters if required</param>
/// <returns>Always true.</returns>
protected bool Warning(string message, params object[] args)
{
message = string.Format(
"Warning: {0} at line {1} column {2}.",
string.Format(message, args),
CurrentPosition.Raw,
CurrentPosition.Column
);
StoreWarning(message);
return true;
}
/// <summary>
/// <para>Invoked when warning was given while parsing.</para>
///
/// <para>Override this method to display / store the warning.</para>
/// </summary>
/// <param name="message">Warning message.</param>
protected virtual void StoreWarning(string message) { }
#endregion
#region EBNF operators
/// <summary>
/// <para>Represents EBNF operator of "join", i.e. serial appearence of several rules.</para>
/// </summary>
/// <remarks>
/// <para>This recoveres <see cref="p"/>, <see cref="stringValue"/>, <see cref="state"/>
/// when <paramref name="condition"/> does not return <code>true</code>.</para>
///
/// <para>If any specific operation is needed for rewinding, in addition to simply
/// recover the value of <see cref="state"/>, override <see cref="Rewind()"/>.</para>
/// </remarks>
/// <param name="rule">If false is returned, the parser status is rewound.</param>
/// <returns>true if <paramref name="rule"/> returned true; otherwise false.</returns>
/// <example>
/// name ::= first-name middle-name? last-name
/// <code>
/// bool Name()
/// {
/// return RewindUnless(()=>
/// FirstName() &amp;&amp;
/// Optional(MiddleName) &amp;&amp;
/// LastName()
/// );
/// }
/// </code>
/// </example>
protected bool RewindUnless(Func<bool> rule) // (join)
{
var savedp = p;
var stringValueLength = stringValue.Length;
var savedStatus = state;
if ( rule() )
return true;
state = savedStatus;
stringValue.Length = stringValueLength;
p = savedp;
Rewind();
return false;
}
/// <summary>
/// This method is called just after <see cref="RewindUnless"/> recovers <see cref="state"/>.
/// Override it to do any additional operation for rewinding.
/// </summary>
protected virtual void Rewind() {}
/// <summary>
/// Represents EBNF operator of "*".
/// </summary>
/// <param name="rule">Reduction rule to be repeated.</param>
/// <returns>Always true.</returns>
/// <example>
/// lines-or-empty ::= line*
/// <code>
/// bool LinesOrEmpty()
/// {
/// return
/// Repeat(Line);
/// }
/// </code>
///
/// <para>lines-or-empty ::= (text line-break)*</para>
/// <para>Note: Do not forget <see cref="RewindUnless"/> if several
/// rules are sequentially appears in <see cref="Repeat(Func&lt;bool&gt;)"/> operator.</para>
/// <code>
/// bool LinesOrEmpty()
/// {
/// return
/// Repeat(()=>
/// RewindUnless(()=>
/// Text() &amp;&amp;
/// LineBreak()
/// )
/// );
/// }
/// </code>
/// </example>
protected bool Repeat(Func<bool> rule) // *
{
// repeat while condition() returns true and
// it reduces any part of text.
int start;
do {
start = p;
} while ( rule() && start != p );
return true;
}
/// <summary>
/// Represents EBNF operator of "+".
/// </summary>
/// <param name="rule">Reduction rule to be repeated.</param>
/// <returns>true if the rule matches; otherwise false.</returns>
/// <example>
/// lines ::= line+
/// <code>
/// bool Lines()
/// {
/// return
/// Repeat(Line);
/// }
/// </code>
/// </example>
/// <example>
/// lines ::= (text line-break)+
///
/// Note: Do not forget RewindUnless in Repeat operator.
/// <code>
/// bool Lines()
/// {
/// return
/// Repeat(()=>
/// RewindUnless(()=>
/// Text() &amp;&amp;
/// LineBreak()
/// )
/// );
/// }
/// </code>
/// </example>
protected bool OneAndRepeat(Func<bool> rule) // +
{
return rule() && Repeat(rule);
}
/// <summary>
/// Represents <code>n</code> times repeatition.
/// </summary>
/// <example>
/// <para>four-lines ::= (text line-break){4}</para>
///
/// <para>Note: Do not forget <see cref="RewindUnless"/> if several
/// rules are sequentially appears in <see cref="Repeat(int,Func&lt;bool&gt;)"/> operator.</para>
/// <code>
/// bool FourLines()
/// {
/// return
/// Repeat(4, ()=>
/// RewindUnless(()=>
/// Text() &amp;&amp;
/// LineBreak()
/// )
/// );
/// }
/// </code>
/// </example>
/// <param name="n">Repetition count.</param>
/// <param name="rule">Reduction rule to be repeated.</param>
/// <returns>true if the rule matches; otherwise false.</returns>
protected bool Repeat(int n, Func<bool> rule)
{
return RewindUnless(() => {
for ( int i = 0; i < n; i++ )
if ( !rule() )
return false;
return true;
});
}
/// <summary>
/// Represents at least <paramref name="min"/>, at most <paramref name="max"/> times repeatition.
/// </summary>
/// <example>
/// <para>google ::= "g" "o"{2,100} "g" "l" "e"</para>
/// <para>Note: Do not forget <see cref="RewindUnless"/> if several
/// rules are sequentially appears in <see cref="Repeat(int,int,Func&lt;bool&gt;)"/> operator.</para>
/// <code>
/// bool Google()
/// {
/// return
/// RewindUnless(()=>
/// text[p++] == 'g' &amp;&amp;
/// Repeat(2, 100,
/// RewindUnless(()=>
/// text[p++] == 'o'
/// )
/// )
/// text[p++] == 'g' &amp;&amp;
/// text[p++] == 'l' &amp;&amp;
/// text[p++] == 'e'
/// );
/// }
/// </code>
/// </example>
/// <param name="min">Minimum repetition count. Negative value is treated as 0.</param>
/// <param name="max">Maximum repetition count. Negative value is treated as positive infinity.</param>
/// <param name="rule">Reduction rule to be repeated.</param>
/// <returns>true if the rule matches; otherwise false.</returns>
protected bool Repeat(int min, int max, Func<bool> rule)
{
return RewindUnless(() => {
for ( int i = 0; i < min; i++ )
if ( !rule() )
return false;
for ( int i = 0; i < max || max < 0; i++ )
if ( !rule() )
return true;
return true;
});
}
/// <summary>
/// Represents BNF operator "?".
/// </summary>
/// <example>
/// <para>file ::= header? body footer?</para>
///
/// <para>Note: Do not forget <see cref="RewindUnless"/> if several
/// rules are sequentially appears in <see cref="Optional(bool)"/> operator.</para>
/// <code>
/// bool File()
/// {
/// return
/// Optional(Header()) &amp;&amp;
/// Body() &amp;&amp;
/// Optional(Footer());
/// }
/// </code>
/// </example>
/// <param name="rule">Reduction rule that is optional.</param>
/// <returns>Always true.</returns>
protected bool Optional(bool rule) // ?
{
return rule || true;
}
/// <summary>
/// Represents BNF operator "?" (WITH rewinding wrap).
/// </summary>
/// <example>
/// file = header? body footer?
///
/// <para>Note: Do not forget <see cref="RewindUnless"/> if several
/// rules are sequentially appears in <see cref="Optional(Func&lt;bool&gt;)"/> operator.</para>
/// <code>
/// bool File()
/// {
/// return
/// Optional(Header) &amp;&amp;
/// Body() &amp;&amp;
/// Optional(Footer);
/// }
/// </code>
/// </example>
/// <param name="rule">Reduction rule that is optional.</param>
/// <returns>Always true.</returns>
protected bool Optional(Func<bool> rule) // ?
{
return
RewindUnless(()=> rule()) || true;
}
#endregion
#region Chars
/// <summary>
/// Reduce one character if it is a member of the specified character set.
/// </summary>
/// <param name="charset">Acceptable character set.</param>
/// <returns>true if the rule matches; otherwise false.</returns>
/// <example>
/// alpha ::= [A-Z][a-z]<br/>
/// num ::= [0-9]<br/>
/// alpha-num :: alpha | num<br/>
/// word ::= alpha ( alpha-num )*<br/>
/// <code>
/// Func&lt;char,bool&gt; Alpha = Charset( c =>
/// ( 'A' &lt;= c &amp;&amp; c &lt;= 'Z' ) ||
/// ( 'a' &lt;= c &amp;&amp; c &lt;= 'z' )
/// );
/// Func&lt;char,bool&gt; Num = Charset( c =>
/// '0' &lt;= c &amp;&amp; c &lt;= '9'
/// );
/// Func&lt;char,bool&gt; AlphaNum = Charset( c =>
/// Alpha(c) || Num(c)
/// );
/// bool Word()
/// {
/// return
/// Accept(Alpha) &amp;&amp;
/// Repeat(AlphaNum);
/// // No need for RewindUnless
/// }
/// </code>
/// </example>
protected bool Accept(Func<char, bool> charset)
{
if ( p < text.Length && charset(text[p]) ) {
p++;
return true;
}
return false;
}
/// <summary>
/// <para>Accepts a character 'c'.</para>
///
/// <para>It can be also represented by <c>text[p++] == c</c> wrapped by <see cref="RewindUnless"/>.</para>
/// </summary>
/// <param name="c">The character to be accepted.</param>
/// <returns>true if the rule matches; otherwise false.</returns>
/// <example>
/// YMCA ::= "Y" "M" "C" "A"
/// <code>
/// bool YMCA()
/// {
/// return
/// RewindUnless(()=>
/// Accept('Y') &amp;&amp;
/// Accept('M') &amp;&amp;
/// Accept('C') &amp;&amp;
/// Accept('A')
/// );
/// }
/// </code>
/// -or-
/// <code>
/// bool YMCA()
/// {
/// return
/// RewindUnless(()=>
/// text[p++] == 'Y' &amp;&amp;
/// text[p++] == 'M' &amp;&amp;
/// text[p++] == 'C' &amp;&amp;
/// text[p++] == 'A'
/// );
/// }
/// </code>
/// </example>
protected bool Accept(char c)
{
if ( p < text.Length && text[p] == c ) {
p++;
return true;
}
return false;
}
/// <summary>
/// Accepts a sequence of characters.
/// </summary>
/// <param name="s">Sequence of characters to be accepted.</param>
/// <returns>true if the rule matches; otherwise false.</returns>
/// <example>
/// YMCA ::= "Y" "M" "C" "A"
/// <code>
/// bool YMCA()
/// {
/// return
/// Accept("YMCA");
/// }
/// </code>
/// </example>
protected bool Accept(string s)
{
if ( p + s.Length >= text.Length )
return false;
for ( int i = 0; i < s.Length; i++ )
if ( s[i] != text[p + i] )
return false;
p += s.Length;
return true;
}
/// <summary>
/// Represents sequence of characters.
/// </summary>
/// <param name="r">Sequence of characters to be accepted.</param>
/// <returns>true if the rule matches; otherwise false.</returns>
protected bool Accept(Regex r)
{
var m = r.Match(text, p);
if ( !m.Success )
return false;
p += m.Length;
return true;
}
/// <summary>
/// Represents BNF operator of "*".
/// </summary>
/// <param name="charset">Character set to be accepted.</param>
/// <returns>Always true.</returns>
protected bool Repeat(Func<char, bool> charset)
{
while ( charset(text[p]) )
p++;
return true;
}
/// <summary>
/// Represents BNF operator of "+".
/// </summary>
/// <param name="charset">Character set to be accepted.</param>
/// <returns>true if the rule matches; otherwise false.</returns>
protected bool OneAndRepeat(Func<char, bool> charset)
{
if ( !charset(text[p]) )
return false;
while ( charset(text[++p]) )
;
return true;
}
/// <summary>
/// Represents <code>n</code> times repetition of characters.
/// </summary>
/// <param name="charset">Character set to be accepted.</param>
/// <param name="n">Repetition count.</param>
/// <returns>true if the rule matches; otherwise false.</returns>
protected bool Repeat(Func<char, bool> charset, int n)
{
for ( int i = 0; i < n; i++ )
if ( !charset(text[p + i]) )
return false;
p += n;
return true;
}
/// <summary>
/// Represents at least <code>min</code> times, at most <code>max</code> times
/// repetition of characters.
/// </summary>
/// <param name="charset">Character set to be accepted.</param>
/// <param name="min">Minimum repetition count. Negative value is treated as 0.</param>
/// <param name="max">Maximum repetition count. Negative value is treated as positive infinity.</param>
/// <returns>true if the rule matches; otherwise false.</returns>
protected bool Repeat(Func<char, bool> charset, int min, int max)
{
for ( int i = 0; i < min; i++ )
if ( !charset(text[p + i]) )
return false;
for ( int i = 0; i < max; i++ )
if ( !charset(text[p + min + i]) ) {
p += min + i;
return true;
}
p += min + max;
return true;
}
/// <summary>
/// Represents BNF operator "?".
/// </summary>
/// <param name="charset">Character set to be accepted.</param>
/// <returns>Always true.</returns>
protected bool Optional(Func<char, bool> charset) // ?
{
if ( !charset(text[p]) )
return true;
p++;
return true;
}
#endregion
#region Charset
/// <summary>
/// <para>Builds a performance-optimized table-based character set definition from a simple
/// but slow comparison-based definition.</para>
///
/// <para>By default, the character table size is 0x100, namely only the characters of [\0-\xff] are
/// judged by using a character table and others are by the as-given slow comparisn-based definitions.</para>
///
/// <para>To have maximized performance, locate the comparison for non-table based judgement first
/// in the definition as the example below.</para>
///
/// <para>Use <see cref="Charset(System.Int32, System.Func&lt;char, bool&gt;)"/> form to explicitly
/// specify the table size.</para>
/// </summary>
/// <example>This sample shows how to build a character set delegate.
/// <code>
/// static class YamlCharsets: Charsets
/// {
/// Func&lt;char, bool&gt; cPrintable;
/// Func&lt;char, bool&gt; sWhite;
///
/// static YamlCharsets()
/// {
/// cPrintable = CacheResult(c =&gt;
/// /* ( 0x10000 &lt; c &amp;&amp; c &lt; 0x110000 ) || */
/// ( 0xe000 &lt;= c &amp;&amp; c &lt;= 0xfffd ) ||
/// ( 0xa0 &lt;= c &amp;&amp; c &lt;= 0xd7ff ) ||
/// ( c &lt; 0x100 &amp;&amp; ( // to improve performance
/// c == 0x85 ||
/// ( 0x20 &lt;= c &amp;&amp; c &lt;= 0x7e ) ||
/// c == 0x0d ||
/// c == 0x0a ||
/// c == 0x09
/// ) )
/// );
/// sWhite = CacheResult(c =&gt;
/// c &lt; 0x100 &amp;&amp; ( // to improve performance
/// c == '\t' ||
/// c == ' '
/// )
/// );
/// }
/// }
/// </code></example>
/// <param name="definition">A simple but slow comparison-based definition of the charsert.</param>
/// <returns>A performance-optimized table-based delegate built from the given <paramref name="definition"/>.</returns>
protected static Func<char, bool> Charset(Func<char, bool> definition)
{
return Charset(0x100, definition);
}
/// <summary>
/// <para>Builds a performance-optimized table-based character set definition from a simple
/// but slow comparison-based definition.</para>
///
/// <para>Characters out of the table are judged by the as-given slow comparisn-based
/// definitions.</para>
///
/// <para>So, to have maximized performance, locate the comparison for non-table based
/// judgement first in the definition as the example below.</para>
/// </summary>
/// <example>This sample shows how to build a character set delegate.
/// <code>
/// static class YamlCharsets: Charsets
/// {
/// Func&lt;char, bool&gt; cPrintable;
/// Func&lt;char, bool&gt; sWhite;
///
/// static YamlCharsets()
/// {
/// cPrintable = CacheResult(c =&gt;
/// /* ( 0x10000 &lt; c &amp;&amp; c &lt; 0x110000 ) || */
/// ( 0xe000 &lt;= c &amp;&amp; c &lt;= 0xfffd ) ||
/// ( 0xa0 &lt;= c &amp;&amp; c &lt;= 0xd7ff ) ||
/// ( c &lt; 0x100 &amp;&amp; ( // to improve performance
/// c == 0x85 ||
/// ( 0x20 &lt;= c &amp;&amp; c &lt;= 0x7e ) ||
/// c == 0x0d ||
/// c == 0x0a ||
/// c == 0x09
/// ) )
/// );
/// sWhite = CacheResult(c =&gt;
/// c &lt; 0x100 &amp;&amp; ( // to improve performance
/// c == '\t' ||
/// c == ' '
/// )
/// );
/// }
/// }
/// </code></example>
/// <param name="table_size">Character table size.</param>
/// <param name="definition">A simple but slow comparison-based definition of the charsert.</param>
/// <returns>A performance-optimized table-based delegate built from the given <paramref name="definition"/>.</returns>
protected static Func<char, bool> Charset(
int table_size, Func<char, bool> definition)
{
var table = new bool[table_size];
for ( char c = '\0'; c < table_size; c++ )
table[c] = definition(c);
return c => c < table_size ? table[c] : definition(c);
}
#endregion
#region Actions
/// <summary>
/// <para>Saves a part of the source text that is reduced in the <paramref name="rule"/>.</para>
/// <para>If the rule does not match, nothing happends.</para>
/// </summary>
/// <param name="rule">Reduction rule to match.</param>
/// <param name="value">If the <paramref name="rule"/> matches,
/// the part of the source text reduced in the <paramref name="rule"/> is set;
/// otherwise String.Empty is set.</param>
/// <returns>true if <paramref name="rule"/> matches; otherwise false.</returns>
protected bool Save(Func<bool> rule, ref string value)
{
var value_ = "";
var result = Save(rule, s => value_ = s);
if ( result )
value = value_;
return result;
}
/// <summary>
/// <para>Saves a part of the source text that is reduced in the <paramref name="rule"/>
/// and append it to <see cref="stringValue"/>.</para>
/// <para>If the rule does not match, nothing happends.</para>
/// </summary>
/// <param name="rule">Reduction rule to match.</param>
/// <returns>true if <paramref name="rule"/> matches; otherwise false.</returns>
protected bool Save(Func<bool> rule)
{
return
Save(rule, s => stringValue.Append(s));
}
/// <summary>
/// <para>Saves a part of the source text that is reduced in the <paramref name="rule"/>.</para>
/// <para>If the rule does not match, nothing happends.</para>
/// </summary>
/// <param name="rule">Reduction rule to match.</param>
/// <param name="save">If <paramref name="rule"/> matches, this delegate is invoked
/// with the part of the source text that is reduced in the <paramref name="rule"/>
/// as the parameter. Do any action in the delegate.</param>
/// <returns>true if <paramref name="rule"/> matches; otherwise false.</returns>
/// <example>
/// <code>
/// bool SomeRule()
/// {
/// return
/// Save(()=> SubRule(), s => MessageBox.Show(s));
/// }
/// </code></example>
protected bool Save(Func<bool> rule, Action<string> save)
{
int start = p;
var result = rule();
if ( result )
save(text.Substring(start, p - start));
return result;
}
/// <summary>
/// Execute some action.
/// </summary>
/// <param name="action">Action to be done.</param>
/// <returns>Always true.</returns>
/// <example>
/// <code>
/// bool SomeRule()
/// {
/// return
/// SubRule() &amp;&amp;
/// Action(()=> do_some_action());
/// }
/// </code></example>
protected bool Action(Action action)
{
action();
return true;
}
/// <summary>
/// Report error by throwing <see cref="ParseErrorException"/> when the <paramref name="rule"/> does not match.
/// </summary>
/// <param name="rule">Some reduction rule that must match.</param>
/// <param name="message">Error message as <see cref="string.Format(string,object[])"/> template</param>
/// <param name="args">Parameters for <see cref="string.Format(string,object[])"/> template</param>
/// <returns>Always true; otherwise an exception thrown.</returns>
protected bool ErrorUnless(bool rule, string message, params object[] args)
{
if ( !rule )
Error(message, args);
return true;
}
/// <summary>
/// Report error by throwing <see cref="ParseErrorException"/> when the <paramref name="rule"/> does not match.
/// </summary>
/// <param name="rule">Some reduction rule that must match.</param>
/// <param name="message">Error message as <see cref="string.Format(string,object[])"/> template</param>
/// <param name="args">Parameters for <see cref="string.Format(string,object[])"/> template</param>
/// <returns>Always true; otherwise an exception is thrown.</returns>
protected bool ErrorUnless(Func<bool> rule, string message, params object[] args)
{
return ErrorUnless(rule(), message);
}
/// <summary>
/// Report error by throwing <see cref="ParseErrorException"/> when the <paramref name="rule"/> does not match
/// and an additional condition <paramref name="to_be_error"/> is true.
/// </summary>
/// <param name="rule">Some reduction rule that must match.</param>
/// <param name="to_be_error">Additional condition: if this parameter is false,
/// rewinding occurs, instead of throwing exception.</param>
/// <param name="message">Error message as <see cref="string.Format(string,object[])"/> template</param>
/// <param name="args">Parameters for <see cref="string.Format(string,object[])"/> template</param>
/// <returns>true if the reduction rule matches; otherwise false.</returns>
protected bool ErrorUnlessWithAdditionalCondition(Func<bool> rule, bool to_be_error, string message, params object[] args)
{
if ( to_be_error ) {
if ( !rule() )
Error(message, args);
return true;
} else {
return RewindUnless(rule);
}
}
/// <summary>
/// Report error by throwing <see cref="ParseErrorException"/> when <paramref name="condition"/> is true.
/// </summary>
/// <param name="condition">True to throw exception.</param>
/// <param name="message">Error message as <see cref="string.Format(string,object[])"/> template</param>
/// <param name="args">Parameters for <see cref="string.Format(string,object[])"/> template</param>
/// <returns>Always true.</returns>
protected bool ErrorIf(bool condition, string message, params object[] args)
{
if ( condition )
Error(message, args);
return true;
}
/// <summary>
/// Assign <c>var = value</c> and return true;
/// </summary>
/// <typeparam name="T">Type of the variable and value.</typeparam>
/// <param name="var">Variable to be assigned.</param>
/// <param name="value">Value to be assigned.</param>
/// <returns>Always true.</returns>
protected bool Assign<T>(out T var, T value)
{
var = value;
return true;
}
#endregion
}
}

View File

@ -0,0 +1,39 @@
YamlSerializer 0.9.0.2 (2009-10-04) Osamu TAKEUCHI <osamu@big.jp>
Description:
A library that serialize / deserialize C# native objects into YAML1.2 text.
Development environment:
Visual C# 2008 Express Edition
Sandcastle (2008-05-29)
SandcastleBuilder 1.8.0.2
HTML Help workshop 4.74.8702
NUnit 2.5.0.9122
TestDriven.NET 2.0
Support web page:
http://yamlserializer.codeplex.com/
License:
YamlSerializer is distributed under the MIT license as following:
---
The MIT License (MIT)
Copyright (c) 2009 Osamu TAKEUCHI <osamu@big.jp>
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.

View File

@ -0,0 +1,621 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace System.Yaml
{
interface IRehashableKey
{
event EventHandler Changed;
}
/// <summary>
/// <para>Dictionary that automatically rehash when the content of a key is changed.
/// Keys of this dictionary must implement <see cref="IRehashableKey"/>.</para>
/// <para>It also call back item addition and removal by <see cref="Added"/> and
/// <see cref="Removed"/> events.</para>
/// </summary>
/// <typeparam name="K">Type of key. Must implements <see cref="IRehashableKey"/>.</typeparam>
/// <typeparam name="V">Type of value.</typeparam>
class RehashableDictionary<K, V>: IDisposable, IDictionary<K, V>
where K: class, IRehashableKey
{
class KeyValue
{
public K key;
public V value;
public KeyValue(K key, V value)
{
this.key = key;
this.value = value;
}
}
/// <summary>
/// <para>A dictionary that returns <see cref="KeyValue"/> or <see cref="List&lt;KeyValue&gt;"/>
/// from hash code. This is the main repository that stores the <see cref="KeyValue"/> pairs.</para>
/// <para>If there are several entries that have same hash code for thir keys,
/// a <see cref="List&lt;KeyValue&gt;"/> is stored to hold all those entries.
/// Otherwise, a <see cref="KeyValue"/> is stored.</para>
/// </summary>
SortedDictionary<int, object> items = new SortedDictionary<int, object>();
/// <summary>
/// <para>A dictionary that returns hash code from the key reference.
/// The key must be the instance that <see cref="object.ReferenceEquals"/>
/// to one exsisting in the dictionary.</para>
/// </summary>
/// <remarks>
/// <para>We store the hashes correspoinding to each key. So that when rehash,
/// we can find old hash code to quickly find the entry for the key.</para>
/// <para>It is also used to remember the number of keys exists in the dictionary.</para>
/// </remarks>
Dictionary<K, int> hashes = new Dictionary<K, int>(
TypeUtils.EqualityComparerByRef<K>.Default);
/// <summary>
/// Recalc hash key of the <paramref name="key"/>.
/// </summary>
/// <param name="key">The key to be rehash. The key must be the instance that
/// <see cref="object.ReferenceEquals"/> to one exsisting in the dictionary.</param>
void Rehash(K key)
{
// Note that key is compared by reference in this function!
// update hash
var oldHash = hashes[key];
var newHash = key.GetHashCode();
hashes[key] = newHash;
// remove old entry
var item = items[oldHash];
KeyValue kv = null;
if ( item is KeyValue ) {
// only one item was found whose hash code equals to oldHash.
kv = (KeyValue)item;
// must be found
Debug.Assert(kv.key == key);
items.Remove(oldHash);
} else {
// several items were found whose hash codes equal to oldHash.
var list = (List<KeyValue>)item;
for ( int i = 0; i < list.Count; i++ ) {
kv = list[i];
if ( kv.key == key ) {
list.RemoveAt(i);
break;
}
// must be found
Debug.Assert(i + 1 < list.Count);
}
// only one item is left, whose hash code equals to oldHash.
if ( list.Count == 1 )
items[oldHash] = list.First();
}
// add new entry
if ( items.TryGetValue(newHash, out item) ) {
if ( item is KeyValue ) {
// must not exist already
Debug.Assert(!( (KeyValue)item ).key.Equals(key));
var list = new List<KeyValue>();
list.Add((KeyValue)item);
list.Add(kv);
items[newHash] = list;
} else {
// must not exist already
Debug.Assert(!( item as List<KeyValue> ).Any(li => li.key.Equals(key)));
( item as List<KeyValue> ).Add(kv);
}
} else {
items[newHash] = kv;
}
}
public class DictionaryEventArgs: EventArgs
{
public K Key;
public V Value;
public DictionaryEventArgs(K key, V value)
{
Key = key;
Value = value;
}
}
protected virtual void OnAdded(K key, V value)
{
// set observer
key.Changed += new EventHandler(KeyChanged);
if ( Added != null )
Added(this, new DictionaryEventArgs(key, value));
}
public event EventHandler<DictionaryEventArgs> Added;
void KeyChanged(object sender, EventArgs e)
{
Rehash((K)sender);
}
protected virtual void OnRemoved(K key, V value)
{
// remove observer
key.Changed -= new EventHandler(KeyChanged);
if ( Removed != null )
Removed(this, new DictionaryEventArgs(key, value));
}
public event EventHandler<DictionaryEventArgs> Removed;
public void Dispose()
{
// remove observers
Clear();
}
void AddCore(K key, V value, bool exclusive)
{
var newkv = new KeyValue(key, value);
FindItem(key, false, default(V),
(hash) => { // not found hash
items.Add(hash, newkv);
hashes.Add(key, hash);
},
(hash, oldkv) => { // hash hit one entry but key not found
var list = new List<KeyValue>();
list.Add(oldkv);
items[hash] = list;
list.Add(newkv);
hashes.Add(key, hash);
},
(hash, oldkv) => { // hash hit one entry and key found
ReplaceKeyValue(oldkv, newkv, exclusive);
},
(hash, list) => { // hash hit several entries but key not found
list.Add(newkv);
hashes.Add(key, hash);
},
(hash, oldkv, list, i) => { // hash hit several entries and key found
ReplaceKeyValue(oldkv, newkv, exclusive);
}
);
OnAdded(key, value);
}
void ReplaceKeyValue(KeyValue oldkv, KeyValue newkv, bool exclusive)
{
if ( exclusive )
throw new InvalidOperationException("Same key already exists.");
var oldkv_saved = new KeyValue(oldkv.key, oldkv.value);
oldkv.key = newkv.key;
oldkv.value = newkv.value;
hashes.Remove(oldkv_saved.key);
OnRemoved(oldkv_saved.key, oldkv_saved.value);
}
bool RemoveCore(K key, bool compareValue, V value)
{
bool result = true;
FindItem(key, compareValue, value,
(hash) => { result = false; }, // key not found
(hash, kv) => { // hash hit one entry and key found
items.Remove(hash);
hashes.Remove(kv.key);
OnRemoved(kv.key, kv.value);
},
(hash, kv, list, i) => { // hash hit several entries and key found
list.RemoveAt(i);
// only one entry left
if ( list.Count == 1 )
items[hash] = list.First();
hashes.Remove(kv.key);
OnRemoved(kv.key, kv.value);
}
);
return result;
}
bool TryGetValueCore(K key, out V value)
{
bool result = true;
V v = default(V);
FindItem(key, false, default(V),
(hash) => { result = false; }, // key not found
(hash, kv) => { v = kv.value; }, // hash hit one entry and key found
(hash, kv, list, i) => { v = kv.value; } // hash hit several entries and key found
);
value = v;
return result;
}
public ICollection<KeyValuePair<K, V>> ItemsFromHash(int key_hash)
{
object entry;
if ( items.TryGetValue(key_hash, out entry) ) {
if ( entry is KeyValue ) {
return new ItemsCollection(this, (KeyValue)entry);
} else {
return new ItemsCollection(this, (List<KeyValue>)entry);
}
} else {
return new ItemsCollection(this);
}
}
class ItemsCollection: KeysValuesBase<KeyValuePair<K, V>>
{
List<KeyValue> list;
public ItemsCollection(RehashableDictionary<K, V> dictionary, List<KeyValue> list)
: base(dictionary)
{
this.list = list;
}
public ItemsCollection(RehashableDictionary<K, V> dictionary, KeyValue entry)
: base(dictionary)
{
this.list = new List<KeyValue>();
list.Add(entry);
}
public ItemsCollection(RehashableDictionary<K, V> dictionary)
: base(dictionary)
{
this.list = new List<KeyValue>();
}
public override int Count
{
get { return list.Count; }
}
public override bool Contains(KeyValuePair<K, V> item)
{
return list.Any(entry => entry.key.Equals(item.Key) && entry.value.Equals(item.Value));
}
public override void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
{
foreach ( var entry in list )
array[arrayIndex++] = new KeyValuePair<K, V>(entry.key, entry.value);
}
public override IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
foreach ( var item in list )
yield return new KeyValuePair<K, V>(item.key, item.value);
}
}
/// <summary>
/// Try to find entry for key (and value).
/// </summary>
/// <param name="key">key to find</param>
/// <param name="compareValue">if true, value matters</param>
/// <param name="value">value to find</param>
/// <param name="NotFound">key not found</param>
/// <param name="FoundOne">hash hit one entry and key found</param>
/// <param name="FoundList">hash hit several entries and key found</param>
void FindItem(K key, bool compareValue, V value,
Action<int> NotFound, // key not found
Action<int, KeyValue> FoundOne, // hash hit one entry and key found
Action<int, KeyValue, List<KeyValue>, int> FoundList) // hash hit several entries and key found
{
FindItem(key, compareValue, value,
(hash) => NotFound(hash),
(hash, kv) => NotFound(hash),
(hash, kv) => FoundOne(hash, kv),
(hash, list) => NotFound(hash),
(hash, kv, list, i) => FoundList(hash, kv, list, i)
);
}
/// <summary>
/// Try to find entry for key (and value).
/// </summary>
/// <param name="key">key to find</param>
/// <param name="compareValue">if true, value matters</param>
/// <param name="value">value to find</param>
/// <param name="NotFoundHash">hash not found</param>
/// <param name="NotFoundKeyOne">hash hit one entry but key not found</param>
/// <param name="FoundOne">hash hit one entry and key found</param>
/// <param name="NotFoundKeyList">hash hit several entries but key not found</param>
/// <param name="FoundList">hash hit several entries and key found</param>
void FindItem(K key, bool compareValue, V value,
Action<int> NotFoundHash, // hash not found
Action<int, KeyValue> NotFoundKeyOne, // hash hit one entry but key not found
Action<int, KeyValue> FoundOne, // hash hit one entry and key found
Action<int, List<KeyValue>> NotFoundKeyList, // hash hit several entries but key not found
Action<int, KeyValue, List<KeyValue>, int> FoundList) // hash hit several entries and key found
{
var hash = key.GetHashCode();
object item;
if ( !items.TryGetValue(hash, out item) ) {
NotFoundHash(hash); // hash not found
} else {
KeyValue kv;
if ( item is KeyValue ) {
kv = (KeyValue)item;
if ( !kv.key.Equals(key) || ( compareValue && !kv.value.Equals(value) ) ) {
NotFoundKeyOne(hash, kv); // hash hit one entry but key not found
} else {
FoundOne(hash, kv); // hash hit one entry and key found
}
} else {
var list = (List<KeyValue>)item;
var i = list.FindIndex(i2 => i2.key.Equals(key));
if ( i < 0 ) {
NotFoundKeyList(hash, list); // hash hit several entries but key not found
} else {
kv = list[i];
if ( compareValue && !kv.value.Equals(value) ) {
NotFoundKeyList(hash, list); // hash hit several entries but key not found
} else {
FoundList(hash, kv, list, i); // hash hit several entries and key found
}
}
}
}
}
IEnumerator<KeyValue> GetEnumeratorCore(IDictionary<int, object> items)
{
foreach ( var item in items )
if ( item.Value is KeyValue ) {
var kv = (KeyValue)item.Value;
yield return kv;
} else {
var list = (List<KeyValue>)item.Value;
foreach ( var kv in list )
yield return kv;
}
}
#region IDictionary<K,V>
public void Add(K key, V value)
{
AddCore(key, value, true);
}
public bool ContainsKey(K key)
{
V value;
return TryGetValueCore(key, out value);
}
public ICollection<K> Keys
{
get { return new KeyCollection(this); }
}
/// <summary>
/// Collection that is readonly and invalidated when an item is
/// added to or removed from the dictionary.
/// </summary>
abstract class KeysValuesBase<T>: ICollection<T>, IDisposable
{
protected bool Invalid = false;
protected RehashableDictionary<K, V> Dictionary;
public KeysValuesBase(RehashableDictionary<K, V> dictionary)
{
Dictionary = dictionary;
Dictionary.Added += DictionaryChanged;
Dictionary.Removed += DictionaryChanged;
}
public void Dispose()
{
Dictionary.Added -= DictionaryChanged;
Dictionary.Removed -= DictionaryChanged;
}
void DictionaryChanged(object sender, RehashableDictionary<K, V>.DictionaryEventArgs e)
{
Invalid = true;
}
protected void CheckValid()
{
if ( Invalid )
throw new InvalidOperationException(
"Dictionary was modified after this collection was created.");
}
static void ThrowReadOnlyError()
{
throw new InvalidOperationException("Collection is readonly.");
}
#region ICollection<K>
public void Add(T item)
{ ThrowReadOnlyError(); }
public void Clear()
{ ThrowReadOnlyError(); }
public abstract bool Contains(T item);
public abstract void CopyTo(T[] array, int arrayIndex);
public virtual int Count
{ get { return Dictionary.Count; } }
public bool IsReadOnly
{ get { return true; } }
public bool Remove(T item)
{ ThrowReadOnlyError(); return false; }
#endregion
#region IEnumerable<T>
public abstract IEnumerator<T> GetEnumerator();
#endregion
#region IEnumerable
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
class KeyCollection: KeysValuesBase<K>
{
public KeyCollection(RehashableDictionary<K, V> dictionary)
: base(dictionary)
{ }
public override bool Contains(K item)
{
return Dictionary.ContainsKey(item);
}
public override void CopyTo(K[] array, int arrayIndex)
{
foreach ( var item in Dictionary ) {
CheckValid();
array[arrayIndex++] = item.Key;
}
}
public override IEnumerator<K> GetEnumerator()
{
foreach ( var item in Dictionary ) {
CheckValid();
yield return item.Key;
}
}
}
public bool Remove(K key)
{
return RemoveCore(key, false, default(V));
}
public bool TryGetValue(K key, out V value)
{
return TryGetValueCore(key, out value);
}
public ICollection<V> Values
{
get { return new ValueCollection(this); }
}
class ValueCollection: KeysValuesBase<V>
{
public ValueCollection(RehashableDictionary<K, V> dictionary)
: base(dictionary)
{ }
public override bool Contains(V item)
{
return Dictionary.Any(entry=>entry.Value.Equals(item));
}
public override void CopyTo(V[] array, int arrayIndex)
{
foreach ( var item in Dictionary ) {
CheckValid();
array[arrayIndex++] = item.Value;
}
}
public override IEnumerator<V> GetEnumerator()
{
foreach ( var item in Dictionary ) {
CheckValid();
yield return item.Value;
}
}
}
public V this[K key]
{
get
{
V value;
if ( TryGetValueCore(key, out value) )
return value;
throw new ArgumentException("Key not exist.");
}
set
{
AddCore(key, value, false);
}
}
#endregion
#region ICollection<KeyValuePair<K,V>>
public void Add(KeyValuePair<K, V> item)
{
AddCore(item.Key, item.Value, true);
}
public void Clear()
{
var oldItems = items;
items = new SortedDictionary<int, object>();
hashes.Clear();
var iter= GetEnumeratorCore(oldItems);
while(iter.MoveNext())
OnRemoved(iter.Current.key, iter.Current.value);
}
public bool Contains(KeyValuePair<K, V> item)
{
V value;
if ( !TryGetValueCore(item.Key, out value) )
return false;
return value.Equals(item.Value);
}
public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
{
foreach ( var item in this )
array[arrayIndex++] = item;
}
public int Count
{
get { return hashes.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(KeyValuePair<K, V> item)
{
return RemoveCore(item.Key, true, item.Value);
}
#endregion
#region IEnumerable<KeyValuePair<K,V>>
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
var iter = GetEnumeratorCore(items);
while ( iter.MoveNext() )
yield return new KeyValuePair<K, V>(iter.Current.key, iter.Current.value);
}
#endregion
#region IEnumerable
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}

View File

@ -0,0 +1,418 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
namespace System.Yaml
{
/// <summary>
/// Type 関連のユーティリティメソッド
/// </summary>
/// <example>
/// <code>
/// Type type;
/// AttributeType attr = type.GetAttribute&lt;AttributeType&gt;();
///
/// PropertyInfo propInfo;
/// AttributeType attr = propInfo.GetAttribute&lt;AttributeType&gt;();
///
/// string name;
/// Type type = TypeUtils.GetType(name); // search from all assembly loaded
///
///
/// </code>
/// </example>
internal static class TypeUtils
{
/// <summary>
/// Type や PropertyInfo, FieldInfo から指定された型の属性を取り出して返す
/// 複数存在した場合には最後の値を返す
/// 存在しなければ null を返す
/// </summary>
/// <typeparam name="AttributeType">取り出したい属性の型</typeparam>
/// <returns>取り出した属性値</returns>
public static AttributeType GetAttribute<AttributeType>(this System.Reflection.MemberInfo info)
where AttributeType: Attribute
{
var attrs = info.GetCustomAttributes(typeof(AttributeType), true);
if ( attrs.Length > 0 ) {
return attrs.Last() as AttributeType;
} else {
return null;
}
}
/// <summary>
/// 現在ロードされているすべてのアセンブリから name という名の型を探して返す
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static Type GetType(string name)
{
if ( AvailableTypes.ContainsKey(name) )
return AvailableTypes[name];
Type type = Type.GetType(name);
if ( type == null ) // ロードされているすべてのアセンブリから探す
type = System.AppDomain.CurrentDomain.GetAssemblies().Select(
asm => asm.GetType(name)).FirstOrDefault(t => t != null);
return AvailableTypes[name] = type;
}
static Dictionary<string, Type> AvailableTypes = new Dictionary<string, Type>();
/// <summary>
/// Check if the type is a ValueType and does not contain any non ValueType members.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsPureValueType(Type type)
{
if ( type == typeof(IntPtr) )
return false;
if ( type.IsPrimitive )
return true;
if ( type.IsEnum )
return true;
if ( !type.IsValueType )
return false;
// struct
foreach ( var f in type.GetFields(
BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) )
if ( !IsPureValueType(f.FieldType) )
return false;
return true;
}
/// <summary>
/// Returnes true if the specified <paramref name="type"/> is a struct type.
/// </summary>
/// <param name="type"><see cref="Type"/> to be analyzed.</param>
/// <returns>true if the specified <paramref name="type"/> is a struct type; otehrwise false.</returns>
public static bool IsStruct(Type type)
{
return type.IsValueType && !type.IsPrimitive;
}
/// <summary>
/// Compare two objects to see if they are equal or not. Null is acceptable.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
public static bool AreEqual(object a, object b)
{
if ( a == null )
return b == null;
if ( b == null )
return false;
return a.Equals(b) || b.Equals(a);
}
/// <summary>
/// Return if an object is a numeric value.
/// </summary>
/// <param name="obj">Any object to be tested.</param>
/// <returns>True if object is a numeric value.</returns>
public static bool IsNumeric(object obj)
{
if ( obj == null )
return false;
Type type = obj.GetType();
return type == typeof(sbyte) || type == typeof(short) || type == typeof(int) || type == typeof(long) ||
type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(ulong) ||
type == typeof(float) || type == typeof(double) || type == typeof(decimal);
}
/// <summary>
/// Cast an object to a specified numeric type.
/// </summary>
/// <param name="obj">Any object</param>
/// <param name="type">Numric type</param>
/// <returns>Numeric value or null if the object is not a numeric value.</returns>
public static object CastToNumericType(object obj, Type type)
{
var doubleValue = CastToDouble(obj);
if ( double.IsNaN(doubleValue) )
return null;
if ( obj is decimal && type == typeof(decimal) )
return obj; // do not convert into double
object result = null;
if ( type == typeof(sbyte) )
result = (sbyte)doubleValue;
if ( type == typeof(byte) )
result = (byte)doubleValue;
if ( type == typeof(short) )
result = (short)doubleValue;
if ( type == typeof(ushort) )
result = (ushort)doubleValue;
if ( type == typeof(int) )
result = (int)doubleValue;
if ( type == typeof(uint) )
result = (uint)doubleValue;
if ( type == typeof(long) )
result = (long)doubleValue;
if ( type == typeof(ulong) )
result = (ulong)doubleValue;
if ( type == typeof(float) )
result = (float)doubleValue;
if ( type == typeof(double) )
result = doubleValue;
if ( type == typeof(decimal) )
result = (decimal)doubleValue;
return result;
}
/// <summary>
/// Cast boxed numeric value to double
/// </summary>
/// <param name="obj">boxed numeric value</param>
/// <returns>Numeric value in double. Double.Nan if obj is not a numeric value.</returns>
public static double CastToDouble(object obj)
{
var result = double.NaN;
var type = obj != null ? obj.GetType() : null;
if ( type == typeof(sbyte) )
result = (double)(sbyte)obj;
if ( type == typeof(byte) )
result = (double)(byte)obj;
if ( type == typeof(short) )
result = (double)(short)obj;
if ( type == typeof(ushort) )
result = (double)(ushort)obj;
if ( type == typeof(int) )
result = (double)(int)obj;
if ( type == typeof(uint) )
result = (double)(uint)obj;
if ( type == typeof(long) )
result = (double)(long)obj;
if ( type == typeof(ulong) )
result = (double)(ulong)obj;
if ( type == typeof(float) )
result = (double)(float)obj;
if ( type == typeof(double) )
result = (double)obj;
if ( type == typeof(decimal) )
result = (double)(decimal)obj;
return result;
}
/// <summary>
/// Check if type is fully public or not.
/// Nested class is checked if all declared types are public.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsPublic(Type type)
{
return type.IsPublic ||
( type.IsNestedPublic && type.IsNested && IsPublic(type.DeclaringType) );
}
/// <summary>
/// Equality comparer that uses Object.ReferenceEquals(x, y) to compare class values.
/// </summary>
/// <typeparam name="T"></typeparam>
public class EqualityComparerByRef<T>: EqualityComparer<T>
where T: class
{
/// <summary>
/// Determines whether two objects of type T are equal by calling Object.ReferenceEquals(x, y).
/// </summary>
/// <param name="x">The first object to compare.</param>
/// <param name="y">The second object to compare.</param>
/// <returns>true if the specified objects are equal; otherwise, false.</returns>
public override bool Equals(T x, T y)
{
return Object.ReferenceEquals(x, y);
}
/// <summary>
/// Serves as a hash function for the specified object for hashing algorithms and
/// data structures, such as a hash table.
/// </summary>
/// <param name="obj">The object for which to get a hash code.</param>
/// <returns>A hash code for the specified object.</returns>
/// <exception cref="System.ArgumentNullException"><paramref name="obj"/> is null.</exception>
public override int GetHashCode(T obj)
{
return HashCodeByRef<T>.GetHashCode(obj);
}
/// <summary>
/// Returns a default equality comparer for the type specified by the generic argument.
/// </summary>
/// <value>The default instance of the System.Collections.Generic.EqualityComparer&lt;T&gt;
/// class for type T.</value>
new public static EqualityComparerByRef<T> Default { get { return _default; } }
static EqualityComparerByRef<T> _default = new EqualityComparerByRef<T>();
}
/// <summary>
/// Calculate hash code by reference.
/// </summary>
/// <typeparam name="T"></typeparam>
public class HashCodeByRef<T> where T: class
{
/// <summary>
/// Calculate hash code by reference.
/// </summary>
new static public Func<T, int> GetHashCode { get; private set; }
/// <summary>
/// Initializes a new instance of the HashCodeByRef&lt;T&gt; class.
/// </summary>
static HashCodeByRef()
{
var dm = new DynamicMethod(
"GetHashCodeByRef", // name of the dynamic method
typeof(int), // type of return value
new Type[] {
typeof(T) // type of "this"
},
typeof(EqualityComparerByRef<T>)); // owner
var ilg = dm.GetILGenerator();
ilg.Emit(OpCodes.Ldarg_0); // push "this" on the stack
ilg.Emit(OpCodes.Call,
typeof(object).GetMethod("GetHashCode")); // returned value is on the stack
ilg.Emit(OpCodes.Ret); // return
GetHashCode = (Func<T, int>)dm.CreateDelegate(typeof(Func<T, int>));
}
}
class RehashableDictionary<K, V>: IDictionary<K, V> where K: class
{
Dictionary<int, object> items = new Dictionary<int, object>();
Dictionary<K, int> hashes =
new Dictionary<K, int>(EqualityComparerByRef<K>.Default);
class KeyValue
{
public int hash;
public K key;
public V value;
public KeyValue(K key, V value)
{
this.key = key;
this.value = value;
this.hash = key.GetHashCode();
}
}
#region IDictionary<K,V>
public void Add(K key, V value)
{
if ( hashes.ContainsKey(key) )
throw new ArgumentException("Same key already exists.");
var entry = new KeyValue(key, value);
object item;
if ( items.TryGetValue(entry.hash, out item) ) {
} else {
}
}
public bool ContainsKey(K key)
{
throw new NotImplementedException();
}
public ICollection<K> Keys
{
get { throw new NotImplementedException(); }
}
public bool Remove(K key)
{
throw new NotImplementedException();
}
public bool TryGetValue(K key, out V value)
{
throw new NotImplementedException();
}
public ICollection<V> Values
{
get { throw new NotImplementedException(); }
}
public V this[K key]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
#endregion
#region ICollection<KeyValuePair<K,V>>
public void Add(KeyValuePair<K, V> item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(KeyValuePair<K, V> item)
{
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public int Count
{
get { throw new NotImplementedException(); }
}
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
public bool Remove(KeyValuePair<K, V> item)
{
throw new NotImplementedException();
}
#endregion
#region IEnumerable<KeyValuePair<K,V>>
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
#region IEnumerable
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
}
}

View File

@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace System.Yaml
{
/// <summary>
/// Add string class two methods: .UriEscape(), .UriUnescape()
///
/// Charset that is not escaped is represented NonUriChar member.
///
/// NonUriChar = new Regex(@"[^0-9A-Za-z\-_.!~*'()\\;/?:@&amp;=$,\[\]]");
/// </summary>
internal static class StringUriEncodingExtention
{
/// <summary>
/// Escape the string in URI encoding format.
/// </summary>
/// <param name="s">String to be escaped.</param>
/// <returns>Escaped string.</returns>
public static string UriEscape(this string s)
{
return UriEncoding.Escape(s);
}
/// <summary>
/// Escape the string in URI encoding format.
/// </summary>
/// <param name="s">String to be escaped.</param>
/// <returns>Escaped string.</returns>
public static string UriEscapeForTag(this string s)
{
return UriEncoding.EscapeForTag(s);
}
/// <summary>
/// Unescape the string escaped in URI encoding format.
/// </summary>
/// <param name="s">String to be unescape.</param>
/// <returns>Unescaped string.</returns>
public static string UriUnescape(this string s)
{
return UriEncoding.Unescape(s);
}
}
/// <summary>
/// Escape / Unescape string in URI encoding format
///
/// Charset that is not escaped is represented NonUriChar member.
///
/// NonUriChar = new Regex(@"[^0-9A-Za-z\-_.!~*'()\\;/?:@&amp;=$,\[\]]");
/// </summary>
internal class UriEncoding
{
public static string Escape(string s)
{
return NonUriChar.Replace(s, m => {
var c = m.Value[0];
return ( c == ' ' ) ? "+" :
( c < 0x80 ) ? IntToHex(c) :
( c < 0x0800 ) ? IntToHex(( ( c >> 6 ) & 0x1f ) + 0xc0, ( c & 0x3f ) + 0x80) :
IntToHex(( ( c >> 12 ) & 0x0f ) + 0xe0, ( ( c >> 6 ) & 0x3f ) + 0x80, ( c & 0x3f ) + 0x80);
}
);
}
static Regex NonUriChar = new Regex(@"[^0-9A-Za-z\-_.!~*'()\\;/?:@&=$,\[\]]");
public static string EscapeForTag(string s)
{
return NonTagChar.Replace(s, m => {
var c = m.Value[0];
return ( c == ' ' ) ? "+" :
( c < 0x80 ) ? IntToHex(c) :
( c < 0x0800 ) ? IntToHex(( ( c >> 6 ) & 0x1f ) + 0xc0, ( c & 0x3f ) + 0x80) :
IntToHex(( ( c >> 12 ) & 0x0f ) + 0xe0, ( ( c >> 6 ) & 0x3f ) + 0x80, ( c & 0x3f ) + 0x80);
}
);
}
static Regex NonTagChar = new Regex(@"[^0-9A-Za-z\-_.!~*'()\\;/?:@&=$]");
static char[] intToHex = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
static string IntToHex(int c)
{
return new string(new char[] {
'%', intToHex[c>>4], intToHex[c&0x0f],
});
}
static string IntToHex(int c1, int c2)
{
return new string(new char[] {
'%', intToHex[c1>>4], intToHex[c1&0x0f],
'%', intToHex[c2>>4], intToHex[c2&0x0f],
});
}
static string IntToHex(int c1, int c2, int c3)
{
return new string(new char[] {
'%', intToHex[c1>>4], intToHex[c1&0x0f],
'%', intToHex[c2>>4], intToHex[c2&0x0f],
'%', intToHex[c3>>4], intToHex[c3&0x0f],
});
}
public static string Unescape(string s)
{
s = s.Replace('+', ' ');
var result = new StringBuilder();
var p = 0;
int pp;
while ( ( pp = s.IndexOf('%', p) ) >= 0 ) {
result.Append(s.Substring(p, pp - p));
p = pp;
var c0 = ( HexToInt(s[p + 1]) << 4 ) + HexToInt(s[p + 2]);
if ( c0 < 0x80 ) {
p += 3;
result.Append((char)c0);
continue;
}
var c1 = ( HexToInt(s[p + 4]) << 4 ) + HexToInt(s[p + 5]);
if ( c0 < 0xe0 ) {
p += 6;
var c = (char)( ( ( c0 & 0x1f ) << 6 ) + ( c1 & 0x7f ) );
result.Append(c);
continue;
}
var c2 = ( HexToInt(s[p + 7]) << 4 ) + HexToInt(s[p + 8]);
if ( c0 < 0xf1 ) {
p += 9;
var c = (char)( ( ( c0 & 0x0f ) << 12 ) + ( ( c1 & 0x7f ) << 6 ) + ( c2 & 0x7f ) );
result.Append(c);
continue;
}
throw new FormatException("Charcorde over 0xffff is not supported");
}
return result.Append(s.Substring(p)).ToString();
}
static int HexToInt(char c)
{
return c <= '9' ? c - '0' : c < 'Z' ? c - 'A' + 10 : c - 'a' + 10;
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace System.Yaml
{
internal class AnchorDictionary
{
Dictionary<string, YamlNode> Items = new Dictionary<string, YamlNode>();
struct RewindInfo
{
public RewindInfo(string anchor_name, YamlNode old_value)
{
this.anchor_name = anchor_name;
this.old_value = old_value;
}
public string anchor_name;
public YamlNode old_value;
}
Stack<RewindInfo> ItemsToRewind = new Stack<RewindInfo>();
Func<string, object[], bool> error;
public AnchorDictionary(Func<string, object[], bool> error)
{
this.error = error;
}
bool Error(string format, params object[] args)
{
return error(format, args);
}
public YamlNode this[string anchor_name]
{
get
{
if ( !Items.ContainsKey(anchor_name) )
Error("Anchor {0} has not been registered.", anchor_name);
return Items[anchor_name];
}
}
public void Add(string anchor_name, YamlNode node)
{
if ( Items.ContainsKey(anchor_name) ) {
// override an existing anchor
ItemsToRewind.Push(new RewindInfo(anchor_name, this[anchor_name]));
Items[anchor_name] = node;
} else {
ItemsToRewind.Push(new RewindInfo(anchor_name, null));
Items.Add(anchor_name, node);
}
}
public int RewindDeapth
{
get { return ItemsToRewind.Count; }
set
{
if ( RewindDeapth < value )
throw new ArgumentOutOfRangeException();
while ( value < RewindDeapth ) {
var rewind_item = ItemsToRewind.Pop();
if ( rewind_item.old_value == null ) {
Items.Remove(rewind_item.anchor_name);
} else {
Items[rewind_item.anchor_name] = rewind_item.old_value;
}
}
}
}
}
}

View File

@ -0,0 +1,298 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
namespace System.Yaml.Serialization
{
internal class ObjectActivator
{
Dictionary<Type, Func<object>> activators =
new Dictionary<Type, Func<object>>();
public void Add<T>(Func<object> activator)
where T: class
{
activators.Add(typeof(T), activator);
}
public T Activate<T>() where T: class
{
return (T)Activate(typeof(T));
}
public object Activate(Type type)
{
if ( !activators.ContainsKey(type) )
return Activator.CreateInstance(type);
return activators[type].Invoke();
}
}
/// <summary>
/// Construct YAML node tree that represents a given C# object.
/// </summary>
internal class YamlConstructor
{
/// <summary>
/// Construct YAML node tree that represents a given C# object.
/// </summary>
/// <param name="node"><see cref="YamlNode"/> to be converted to C# object.</param>
/// <param name="config"><see cref="YamlConfig"/> to customize serialization.</param>
/// <returns></returns>
public object NodeToObject(YamlNode node, YamlConfig config)
{
return NodeToObject(node, null, config);
}
/// <summary>
/// Construct YAML node tree that represents a given C# object.
/// </summary>
/// <param name="node"><see cref="YamlNode"/> to be converted to C# object.</param>
/// <param name="expected">Expected type for the root object.</param>
/// <param name="config"><see cref="YamlConfig"/> to customize serialization.</param>
/// <returns></returns>
public object NodeToObject(YamlNode node, Type expected, YamlConfig config)
{
this.config = config;
var appeared =
new Dictionary<YamlNode, object>(TypeUtils.EqualityComparerByRef<YamlNode>.Default);
return NodeToObjectInternal(node, expected, appeared);
}
YamlConfig config;
static public YamlTagResolver TagResolver = new YamlTagResolver();
private static Type TypeFromTag(string tag)
{
if ( tag.StartsWith(YamlNode.DefaultTagPrefix) ) {
switch ( tag.Substring(YamlNode.DefaultTagPrefix.Length) ) {
case "str":
return typeof(string);
case "int":
return typeof(Int32);
case "null":
return typeof(object);
case "bool":
return typeof(bool);
case "float":
return typeof(double);
case "seq":
case "map":
return null;
default:
throw new NotImplementedException();
}
} else {
return TypeUtils.GetType(tag.Substring(1));
}
}
object NodeToObjectInternal(YamlNode node, Type expected, Dictionary<YamlNode, object> appeared)
{
if ( appeared.ContainsKey(node) )
return appeared[node];
object obj = null;
// Type resolution
Type type = expected == typeof(object) ? null : expected;
Type fromTag = TagResolver.TypeFromTag(node.Tag);
if ( fromTag == null )
fromTag = TypeFromTag(node.Tag);
if ( fromTag != null && type != fromTag && fromTag.IsClass && fromTag != typeof(string) )
type = fromTag;
if ( type == null )
type = fromTag;
// try TagResolver
if ( type == fromTag && fromTag != null )
if ( node is YamlScalar && TagResolver.Decode((YamlScalar)node, out obj) )
return obj;
if ( node.Tag == YamlNode.DefaultTagPrefix + "null" ) {
obj = null;
} else
if ( node is YamlScalar ) {
obj = ScalarToObject((YamlScalar)node, type);
} else
if ( node is YamlMapping ) {
obj = MappingToObject((YamlMapping)node, type, null, appeared);
} else
if ( node is YamlSequence ) {
obj = SequenceToObject((YamlSequence)node, type, appeared);
} else
throw new NotImplementedException();
if ( !appeared.ContainsKey(node) )
if(obj != null && obj.GetType().IsClass && ( !(obj is string) || ((string)obj).Length >= 1000 ) )
appeared.Add(node, obj);
return obj;
}
object ScalarToObject(YamlScalar node, Type type)
{
if ( type == null )
throw new FormatException("Could not find a type '{0}'.".DoFormat(node.Tag));
// To accommodate the !!int and !!float encoding, all "_"s in integer and floating point values
// are simply neglected.
if ( type == typeof(byte) || type == typeof(sbyte) || type == typeof(short) || type == typeof(ushort) ||
type == typeof(int) || type == typeof(uint) || type == typeof(long) || type == typeof(ulong)
|| type == typeof(float) || type == typeof(decimal) )
return config.TypeConverter.ConvertFromString(node.Value.Replace("_", ""), type);
if ( type.IsEnum || type.IsPrimitive || type == typeof(char) || type == typeof(bool) ||
type == typeof(string) || EasyTypeConverter.IsTypeConverterSpecified(type) )
return config.TypeConverter.ConvertFromString(node.Value, type);
if ( type.IsArray ) {
// Split dimension from base64 strings
var s = node.Value;
var regex = new Regex(@" *\[([0-9 ,]+)\][\r\n]+((.+|[\r\n])+)");
int[] dimension;
byte[] binary;
var elementSize = Marshal.SizeOf(type.GetElementType());
if ( type.GetArrayRank() == 1 ) {
binary = System.Convert.FromBase64CharArray(s.ToCharArray(), 0, s.Length);
var arrayLength = binary.Length / elementSize;
dimension = new int[] { arrayLength };
} else {
var m = regex.Match(s);
if ( !m.Success )
throw new FormatException("Irregal binary array");
// Create array from dimension
dimension = m.Groups[1].Value.Split(',').Select(n => Convert.ToInt32(n)).ToArray();
if ( type.GetArrayRank() != dimension.Length )
throw new FormatException("Irregal binary array");
// Fill values
s = m.Groups[2].Value;
binary = System.Convert.FromBase64CharArray(s.ToCharArray(), 0, s.Length);
}
var paramType = dimension.Select(n => typeof(int) /* n.GetType() */).ToArray();
var array = (Array)type.GetConstructor(paramType).Invoke(dimension.Cast<object>().ToArray());
if ( binary.Length != array.Length * elementSize )
throw new FormatException("Irregal binary: data size does not match to array dimension");
int j = 0;
for ( int i = 0; i < array.Length; i++ ) {
var p = Marshal.UnsafeAddrOfPinnedArrayElement(array, i);
Marshal.Copy(binary, j, p, elementSize);
j += elementSize;
}
return array;
}
if ( node.Value == "" ) {
return config.Activator.Activate(type);
} else {
return TypeDescriptor.GetConverter(type).ConvertFromString(node.Value);
}
}
object SequenceToObject(YamlSequence seq, Type type, Dictionary<YamlNode, object> appeared)
{
if ( type == null )
type = typeof(object[]);
if ( type.IsArray ) {
var lengthes= new int[type.GetArrayRank()];
GetLengthes(seq, 0, lengthes);
var array = (Array)type.GetConstructor(lengthes.Select(l => typeof(int) /* l.GetType() */).ToArray())
.Invoke(lengthes.Cast<object>().ToArray());
appeared.Add(seq, array);
var indices = new int[type.GetArrayRank()];
SetArrayElements(array, seq, 0, indices, type.GetElementType(), appeared);
return array;
} else {
throw new NotImplementedException();
}
}
void SetArrayElements(Array array, YamlSequence seq, int i, int[] indices, Type elementType, Dictionary<YamlNode, object> appeared)
{
if ( i < indices.Length - 1 ) {
for ( indices[i] = 0; indices[i] < seq.Count; indices[i]++ )
SetArrayElements(array, (YamlSequence)seq[indices[i]], i + 1, indices, elementType, appeared);
} else {
for ( indices[i] = 0; indices[i] < seq.Count; indices[i]++ )
array.SetValue(NodeToObjectInternal(seq[indices[i]], elementType, appeared), indices);
}
}
private static void GetLengthes(YamlSequence seq, int i, int[] lengthes)
{
lengthes[i] = Math.Max(lengthes[i], seq.Count);
if ( i < lengthes.Length - 1 )
for ( int j = 0; j < seq.Count; j++ )
GetLengthes((YamlSequence)seq[j], i + 1, lengthes);
}
object MappingToObject(YamlMapping map, Type type, object obj, Dictionary<YamlNode, object> appeared)
{
// Naked !!map is constructed as Dictionary<object, object>.
if ( ( ( map.ShorthandTag() == "!!map" && type == null ) || type == typeof(Dictionary<object,object>) ) && obj == null ) {
var dict = new Dictionary<object, object>();
appeared.Add(map, dict);
foreach ( var entry in map )
dict.Add(NodeToObjectInternal(entry.Key, null, appeared), NodeToObjectInternal(entry.Value, null, appeared));
return dict;
}
if ( obj == null ) {
obj = config.Activator.Activate(type);
appeared.Add(map, obj);
} else {
if ( appeared.ContainsKey(map) )
throw new InvalidOperationException("This member is not writeable: {0}".DoFormat(obj.ToString()));
}
var access = ObjectMemberAccessor.FindFor(type);
foreach(var entry in map){
if ( obj == null )
throw new InvalidOperationException("Object is not initialized");
var name = (string)NodeToObjectInternal(entry.Key, typeof(string), appeared);
switch ( name ) {
case "ICollection.Items":
if ( access.CollectionAdd == null )
throw new FormatException("{0} is not a collection type.".DoFormat(type.FullName));
access.CollectionClear(obj);
foreach(var item in (YamlSequence)entry.Value)
access.CollectionAdd(obj, NodeToObjectInternal(item, access.ValueType, appeared));
break;
case "IDictionary.Entries":
if ( !access.IsDictionary )
throw new FormatException("{0} is not a dictionary type.".DoFormat(type.FullName));
var dict = obj as IDictionary;
dict.Clear();
foreach ( var child in (YamlMapping)entry.Value )
dict.Add(NodeToObjectInternal(child.Key, access.KeyType, appeared), NodeToObjectInternal(child.Value, access.ValueType, appeared));
break;
default:
if (!access.ContainsKey(name))
// ignoring non existing properties
//throw new FormatException("{0} does not have a member {1}.".DoFormat(type.FullName, name));
continue;
switch ( access[name].SerializeMethod ) {
case YamlSerializeMethod.Assign:
access[obj, name] = NodeToObjectInternal(entry.Value, access[name].Type, appeared);
break;
case YamlSerializeMethod.Content:
MappingToObject((YamlMapping)entry.Value, access[name].Type, access[obj, name], appeared);
break;
case YamlSerializeMethod.Binary:
access[obj, name] = ScalarToObject((YamlScalar)entry.Value, access[name].Type);
break;
default:
throw new InvalidOperationException(
"Member {0} of {1} is not serializable.".DoFormat(name, type.FullName));
}
break;
}
}
return obj;
}
}
}

View File

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace System.Yaml
{
/// <summary>
/// Extend string object to have .DoubleQuoteEscape() / .DoubleQuoteUnescape().
/// </summary>
internal static class StringYamlDoubleQuoteEscapeExtention
{
/// <summary>
/// Escape control codes with YAML double quoted string format.
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string YamlDoubleQuoteEscape(this string s)
{
return YamlDoubleQuoteEscaping.Escape(s);
}
/// <summary>
/// Unescape control codes escaped with YAML double quoted string format.
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string YamlDoubleQuoteUnescape(this string s)
{
return YamlDoubleQuoteEscaping.Unescape(s);
}
}
/// <summary>
/// YAML style double quoted string escape / unescape.
/// </summary>
internal static class YamlDoubleQuoteEscaping
{
const int controlCodeMax = 0x1f;
static string[] controlCodes = new string[controlCodeMax + 1] {
@"\0", @"\x01", @"\x02", @"\x03", @"\x04", @"\x05", @"\x06", @"\a",
@"\b", @"\t", @"\n", @"\v", @"\f", @"\r", @"\x0e", @"\x0f",
@"\x10", @"\x11", @"\x12", @"\x13", @"\x14", @"\x15", @"\x16", @"\x17",
@"\x18", @"\x19", @"\x1a", @"\e", @"\x1c", @"\x1d", @"\x1e", @"\x1f",
};
static Dictionary<char, string> escapeTable = new Dictionary<char, string>();
static Regex escapeRegexp = null;
static Regex escapeNonprintable;
static Dictionary<string, string> unescapeTable = new Dictionary<string, string>();
static Regex unescapeRegexp = null;
/// <summary>
/// Initialize tables
/// </summary>
static YamlDoubleQuoteEscaping()
{
// Create (additional) escaping table
escapeTable['\\'] = @"\\";
escapeTable['"'] = "\\\"";
escapeTable['/'] = @"\/";
escapeTable['\x85'] = @"\N";
escapeTable['\xa0'] = @"\_";
escapeTable['\u2028'] = @"\L";
escapeTable['\u2029'] = @"\P";
// Create escaping regexp
escapeRegexp = new Regex(@"[\x00-\x1f\/\x85\xa0\u2028\u2029" + "\"]");
escapeNonprintable = new Regex(@"[\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]");
// Create unescaping table
for ( char c = '\0'; c < controlCodes.Length; c++ )
unescapeTable[controlCodes[c]] = c.ToString();
foreach ( var c in escapeTable.Keys )
unescapeTable[escapeTable[c]] = c.ToString();
// Create unescaping regex
var pattern = "";
unescapeTable.Keys.ToList().ForEach(esc => {
if ( pattern != "" )
pattern += "|";
pattern += Regex.Escape(esc);
});
unescapeRegexp = new Regex(pattern +
@"|\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}");
}
/// <summary>
/// Escape control codes, double quotations, backslashes in the YAML double quoted string format
/// </summary>
public static string Escape(string s)
{
s = s.Replace(@"\", @"\\");
s = escapeRegexp.Replace(s, escapeChar);
return escapeNonprintable.Replace(s, m => {
var c = m.Value[0];
return c < 0x100 ? string.Format(@"\x{0:x2}", (int)c) : string.Format(@"\u{0:x4}", (int)c);
});
}
static string escapeChar(Match m)
{
var c = m.Value[0];
return c < controlCodes.Length ? controlCodes[c] : escapeTable[c];
}
/// <summary>
/// Unescape control codes, double quotations, backslashes escape in the YAML double quoted string format
/// </summary>
public static string Unescape(string s)
{
return unescapeRegexp.Replace(s, unescapeChar);
}
static string unescapeChar(Match m)
{
string s;
switch ( m.Value[1] ) {
case 'x':
case 'u':
case 'U':
s = ( (char)Convert.ToInt32("0x" + m.Value.Substring(2)) ).ToString();
break;
default:
s = unescapeTable[m.Value];
break;
}
return s;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,505 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.IO;
namespace System.Yaml
{
/// <summary>
/// Converts YamlNode tree into yaml text.
/// </summary>
/// <example>
/// <code>
/// YamlNode node;
/// YamlPresenter.ToYaml(node);
///
/// YamlNode node1;
/// YamlNode node2;
/// YamlNode node3;
/// YamlPresenter.ToYaml(node1, node2, node3);
/// </code>
/// </example>
internal class YamlPresenter
{
TextWriter yaml;
int column, raw;
YamlConfig config;
public string ToYaml(YamlNode node)
{
return ToYaml(node, YamlNode.DefaultConfig);
}
public string ToYaml(YamlNode node, YamlConfig config)
{
yaml = new StringWriter();
ToYaml(yaml, node, config);
return yaml.ToString();
}
public void ToYaml(Stream s, YamlNode node, YamlConfig config)
{
using ( var yaml = new StreamWriter(s) )
ToYaml(yaml, node, config);
}
public void ToYaml(TextWriter yaml, YamlNode node, YamlConfig config)
{
this.config = config;
this.yaml = yaml;
MarkMultiTimeAppearingChildNodesToBeAnchored(node);
yaml.NewLine = config.LineBreakForOutput;
column = 1;
raw = 1;
WriteLine("%YAML 1.2");
WriteLine("---");
NodeToYaml(node, "", Context.Normal);
WriteLine("...");
}
static void MarkMultiTimeAppearingChildNodesToBeAnchored(YamlNode node)
{
var AlreadyAppeared = new Dictionary<YamlNode, bool>(
TypeUtils.EqualityComparerByRef<YamlNode>.Default);
var anchor = "";
Action<YamlNode> analyze = null;
analyze = n => {
if ( !AlreadyAppeared.ContainsKey(n) ) {
n.Properties.Remove("ToBeAnchored");
n.Properties.Remove("Anchor");
AlreadyAppeared[n] = true;
} else {
if ( !n.Properties.ContainsKey("Anchor") ) {
anchor = NextAnchor(anchor);
n.Properties["ToBeAnchored"] = "true";
n.Properties["Anchor"] = anchor;
}
return;
}
if ( n is YamlSequence ) {
var seq = (YamlSequence)n;
foreach ( var child in seq )
analyze(child);
}
if ( n is YamlMapping ) {
var map = (YamlMapping)n;
foreach ( var child in map ) {
analyze(child.Key);
analyze(child.Value);
}
}
};
analyze(node);
}
internal static string NextAnchor(string anchor) // this is "protected" for test use
{
if ( anchor == "" ) {
return "A";
} else
if ( anchor[anchor.Length - 1] != 'Z' ) {
return anchor.Substring(0, anchor.Length - 1) + ((char)( anchor[anchor.Length - 1] + 1 )).ToString();
} else {
return NextAnchor(anchor.Substring(0, anchor.Length - 1)) + "A";
}
}
internal enum Context
{
Normal,
List,
Map,
NoBreak
}
void Write(string s)
{
int start = 0;
for ( int p = 0; p < s.Length; ) {
if ( s[p] != '\r' && s[p] != '\n' ) {
// proceed until finding a line break
p++;
} else {
int pp = p;
if ( p + 1 < s.Length && s[p] == '\r' && s[p + 1] == '\n' )
p++;
p++;
if ( config.NormalizeLineBreaks ) {
// output with normalized line break
yaml.WriteLine(s.Substring(start, pp - start));
} else {
// output with native line break
yaml.Write(s.Substring(start, p - start));
}
raw++;
column = 1;
start = p;
}
}
// rest of the string
s = s.Substring(start, s.Length - start);
column += s.Length;
yaml.Write(s);
}
void WriteLine(string s)
{
Write(s);
WriteLine();
}
void WriteLine()
{
yaml.WriteLine();
raw++;
column = 1;
}
private void NodeToYaml(YamlNode node, string pres, Context c)
{
if ( node.Properties.ContainsKey("ToBeAnchored") ) {
node.Raw = raw;
node.Column = column;
Write("&" + node.Properties["Anchor"] + " ");
node.Properties.Remove("ToBeAnchored");
c = Context.Map;
} else {
if ( node.Properties.ContainsKey("Anchor") ) {
Write("*" + node.Properties["Anchor"]);
if ( c != Context.NoBreak ) {
WriteLine();
}
return;
}
node.Raw = raw;
node.Column = column;
}
if ( node is YamlSequence ) {
SequenceToYaml((YamlSequence)node, pres, c);
} else
if ( node is YamlMapping ) {
MappingToYaml((YamlMapping)node, pres, c);
} else {
ScalarToYaml((YamlScalar)node, pres, c);
}
}
private static string GetPropertyOrNull(YamlNode node, string name)
{
string result;
if ( node.Properties.TryGetValue(name, out result) )
return result;
return null;
}
private void ScalarToYaml(YamlScalar node, string pres, Context c)
{
var s = node.Value;
// If tag can be resolved from the content, or tag is !!str,
// no need to explicitly specify it.
var auto_tag = YamlNode.ShorthandTag(AutoTagResolver.Resolve(s));
var tag = TagToYaml(node, auto_tag);
if ( tag != "" && tag != "!!str" )
Write(tag + " ");
if ( IsValidPlainText(s, c) && !( node.ShorthandTag() == "!!str" && auto_tag != null && !node.Properties.ContainsKey("plainText")) ) {
// one line plain style
Write(s);
if ( c != Context.NoBreak )
WriteLine();
} else {
if ( ForbiddenChars.IsMatch(s) || OneLine.IsMatch(s) ||
( config.ExplicitlyPreserveLineBreaks &&
GetPropertyOrNull(node, "Don'tCareLineBreaks") == null ) ) {
// double quoted
Write(DoubleQuotedString.Quote(s, pres, c));
if ( c != Context.NoBreak )
WriteLine();
} else {
// Literal style
if ( s[s.Length - 1] == '\n' || s[s.Length - 1] == '\r' ) {
WriteLine("|+2");
} else {
WriteLine("|-2");
s += "\r\n"; // guard
}
var press = pres + " ";
for ( int p = 0; p < s.Length; ) {
var m = UntilBreak.Match(s, p); // does not fail because of the guard
Write(pres + s.Substring(p, m.Length));
p += m.Length;
}
}
}
}
private bool IsValidPlainText(string s, Context c)
{
if ( s == "" )
return true;
switch ( c ) {
case Context.Normal: // Block Key
case Context.Map: // BlockValue
case Context.List: // ListItem
case Context.NoBreak: // Flow Key
return ( s == "" || PlainChecker.IsValidPlainText(s, config) );
default:
throw new NotImplementedException();
}
}
private static DoubleQuote DoubleQuotedString = new DoubleQuote();
private static Regex ForbiddenChars = new Regex(@"[\x00-\x08\x0B\x0C\x0E-\x1F]");
private static Regex OneLine = new Regex(@"^([^\n\r]|\n)*(\r?\n|\r)?$");
private static Regex UntilBreak = new Regex(@"[^\r\n]*(\r?\n|\r)");
public class DoubleQuote: Parser<DoubleQuote.State>
{
internal struct State { }
Func<char, bool> nbDoubleSafeCharset = Charset(c =>
( 0x100 <= c && c != '\u2028' && c != '\u2029' ) ||
c == 0x09 ||
( 0x20 <= c && c < 0x100 && c != '\\' && c != '"' && c != 0x85 && c != 0xA0 )
);
public DoubleQuote()
{
CharEscaping.Add('\x00', @"\0");
CharEscaping.Add('\x07', @"\a");
CharEscaping.Add('\x08', @"\b");
CharEscaping.Add('\x0B', @"\v");
CharEscaping.Add('\x0C', @"\f");
CharEscaping.Add('\x1B', @"\e");
CharEscaping.Add('\x22', @"\""");
CharEscaping.Add('\x5C', @"\\");
CharEscaping.Add('\x85', @"\N");
CharEscaping.Add('\xA0', @"\_");
CharEscaping.Add('\u2028', @"\L");
CharEscaping.Add('\u2029', @"\P");
}
bool nbDoubleSafeChar()
{
if ( nbDoubleSafeCharset(text[p]) ) {
stringValue.Append(text[p++]);
return true;
}
return false;
}
public string Quote(string s, string pres, Context c)
{
base.Parse(() => DoubleQuoteString(pres, c), s);
return "\"" + stringValue.ToString() + "\"";
}
bool DoubleQuoteString(string pres, Context c)
{
return Repeat(() => cDoubleQuoteChar(pres, c));
}
bool cDoubleQuoteChar(string pres, Context c)
{
return
!EndOfString() && (
nbDoubleSafeChar() ||
bBreak(pres, c) ||
nsEscapedChar()
);
}
Dictionary<char, string> CharEscaping = new Dictionary<char, string>();
private bool nsEscapedChar()
{
var c= text[p];
string escaped;
if ( CharEscaping.TryGetValue(c, out escaped) ) {
stringValue.Append(escaped);
} else {
if ( c < 0x100 ) {
stringValue.Append(string.Format(@"\x{0:x2}", (int)c));
} else {
stringValue.Append(string.Format(@"\u{0:x4}", (int)c));
}
}
p++;
return true;
}
private bool bBreak(string pres, Context c)
{
if ( text[p] == '\r' ) {
stringValue.Append(@"\r");
p++;
if ( !EndOfString() && text[p] == '\n' ) {
stringValue.Append(@"\n");
p++;
}
} else
if ( text[p] == '\n' ) {
stringValue.Append(@"\n");
p++;
} else {
return false;
}
if ( EndOfString() || c == Context.NoBreak )
return true;
// fold the string with escaping line break
stringValue.AppendLine(@"\");
stringValue.Append(pres);
// if the following line starts from space char, escape it.
if ( text[p] == ' ' )
stringValue.Append(@"\");
return true;
}
private bool EndOfString()
{
return p == text.Length;
}
}
private static YamlTagResolver AutoTagResolver = new YamlTagResolver();
private static YamlParser PlainChecker = new YamlParser();
private void SequenceToYaml(YamlSequence node, string pres, Context c)
{
if ( node.Count == 0 || GetPropertyOrNull(node, "Compact") != null ) {
FlowSequenceToYaml(node, pres, c);
} else {
BlockSequenceToYaml(node, pres, c);
}
}
private void BlockSequenceToYaml(YamlSequence node, string pres, Context c)
{
var tag = TagToYaml(node, "!!seq");
if ( tag != "" || c == Context.Map ) {
WriteLine(tag);
c = Context.Normal;
}
string press = pres + " ";
foreach ( var item in node ) {
if ( c == Context.Normal )
Write(pres);
Write("- ");
NodeToYaml(item, press, Context.List);
c = Context.Normal;
}
}
private void FlowSequenceToYaml(YamlSequence node, string pres, Context c)
{
var tag = TagToYaml(node, "!!seq");
if ( column > 80 ) {
WriteLine();
Write(pres);
}
if ( tag != "" && tag != "!!seq" )
Write(tag + " ");
Write("[");
foreach ( var item in node ) {
if ( item != node.First() )
Write(", ");
if ( column > 100 ) {
WriteLine();
Write(pres);
}
NodeToYaml(item, pres, Context.NoBreak);
}
Write("]");
if ( c != Context.NoBreak )
WriteLine();
}
private void MappingToYaml(YamlMapping node, string pres, Context c)
{
var tag = TagToYaml(node, "!!map");
if ( node.Count > 0 ) {
if ( tag != "" || c == Context.Map ) {
WriteLine(tag);
c = Context.Normal;
}
string press = pres + " ";
foreach ( var item in node ) {
if ( c != Context.List )
Write(pres);
c = Context.Normal;
if ( WriteImplicitKeyIfPossible(item.Key, press, Context.NoBreak) ) {
Write(": ");
NodeToYaml(item.Value, press, Context.Map);
} else {
// explicit key
Write("? ");
NodeToYaml(item.Key, press, Context.List);
Write(pres);
Write(": ");
NodeToYaml(item.Value, press, Context.List);
}
}
} else {
if ( tag != "" && tag != "!!map" )
Write(tag + " ");
Write("{}");
if ( c != Context.NoBreak )
WriteLine();
}
}
bool WriteImplicitKeyIfPossible(YamlNode node, string pres, Context c)
{
if ( !( node is YamlScalar ) )
return false;
int raw_saved = raw;
int col_saved = column;
var yaml_saved = yaml;
var result = "";
using ( yaml = new StringWriter() ) {
NodeToYaml(node, pres, c);
result = yaml.ToString();
}
if ( result.Length < 80 && result.IndexOf('\n') < 0 ) {
yaml = yaml_saved;
yaml.Write(result);
return true;
} else {
yaml = yaml_saved;
raw = raw_saved;
column = col_saved;
return false;
};
}
private string TagToYaml(YamlNode node, string defaultTag)
{
var tag = node.ShorthandTag();
if ( tag == YamlNode.ShorthandTag(defaultTag) )
return "";
if ( tag == YamlNode.ShorthandTag(GetPropertyOrNull(node, "expectedTag")) )
return "";
if ( config.DontUseVerbatimTag ) {
if ( tag.StartsWith("!") ) {
tag = tag.UriEscapeForTag();
} else {
tag = "!<" + tag.UriEscape() + ">";
}
} else {
tag = tag.UriEscape();
if ( !CanBeShorthand.IsMatch(tag) )
tag = "!<" + tag + ">";
}
return tag;
}
// has a tag handle and the body contains only ns-tag-char.
static Regex CanBeShorthand = new Regex(@"^!([-0-9a-zA-Z]*!)?[-0-9a-zA-Z%#;/?:@&=+$_.^*'\(\)]*$");
}
}

View File

@ -0,0 +1,305 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Runtime.InteropServices;
namespace System.Yaml.Serialization
{
/// <summary>
/// Converts C# object to YamlNode
/// </summary>
/// <example>
/// <code>
/// object obj;
/// YamlNode node = YamlRepresenter.ObjectToNode(obj);
/// </code>
/// </example>
internal class YamlRepresenter: YamlNodeManipulator
{
private static string TypeNameToYamlTag(Type type)
{
/*
if ( TypeUtils.GetType(type.FullName) == null ) {
throw new ArgumentException(
"Can not serialize (non public?) type '{0}'.".DoFormat(type.FullName));
}
*/
if ( type == typeof(int) )
return YamlNode.ExpandTag("!!int");
if ( type == typeof(string) )
return YamlNode.ExpandTag("!!str");
if ( type == typeof(Double) )
return YamlNode.ExpandTag("!!float");
if ( type == typeof(bool) )
return YamlNode.ExpandTag("!!bool");
if ( type == typeof(object[]) )
return YamlNode.ExpandTag("!!seq");
return "!" + type.FullName;
}
public YamlNode ObjectToNode(object obj)
{
return ObjectToNode(obj, YamlNode.DefaultConfig);
}
YamlConfig config;
public YamlNode ObjectToNode(object obj, YamlConfig config)
{
this.config = config;
appeared.Clear();
if ( config.OmitTagForRootNode ) {
return ObjectToNode(obj, obj.GetType());
} else {
return ObjectToNode(obj, (Type)null);
}
}
YamlNode ObjectToNode(object obj, Type expect)
{
if ( obj != null && obj.GetType().IsClass && ( !(obj is string) || ((string)obj).Length >= 1000 ) )
if ( appeared.ContainsKey(obj) )
return appeared[obj];
var node = ObjectToNodeSub(obj, expect);
if ( expect != null && expect != typeof(Object) )
node.Properties["expectedTag"] = TypeNameToYamlTag(expect);
AppendToAppeared(obj, node);
return node;
}
private void AppendToAppeared(object obj, YamlNode node)
{
if ( obj != null && obj.GetType().IsClass && ( !( obj is string ) || ( (string)obj ).Length >= 1000 ) )
if ( !appeared.ContainsKey(obj) )
appeared.Add(obj, node);
}
Dictionary<object, YamlNode> appeared =
new Dictionary<object, YamlNode>(TypeUtils.EqualityComparerByRef<object>.Default);
YamlNode ObjectToNodeSub(object obj, Type expect)
{
// !!null
if ( obj == null )
return str("!!null", "null");
YamlScalar node;
if ( config.TagResolver.Encode(obj, out node) )
return node;
var type = obj.GetType();
if ( obj is IntPtr || type.IsPointer )
throw new ArgumentException("Pointer object '{0}' can not be serialized.".DoFormat(obj.ToString()));
if ( obj is char ) {
// config.TypeConverter.ConvertToString("\0") does not show "\0"
var n = str(TypeNameToYamlTag(type), obj.ToString() );
return n;
}
// bool, byte, sbyte, decimal, double, float, int ,uint, long, ulong, short, ushort, string, enum
if ( type.IsPrimitive || type.IsEnum || type == typeof(decimal) || type == typeof(string) ) {
var n = str(TypeNameToYamlTag(type), config.TypeConverter.ConvertToString(obj) );
return n;
}
// TypeConverterAttribute
if ( EasyTypeConverter.IsTypeConverterSpecified(type) )
return str(TypeNameToYamlTag(type), config.TypeConverter.ConvertToString(obj));
// array
if ( type.IsArray )
return CreateArrayNode((Array)obj);
if ( type == typeof(Dictionary<object, object>) )
return DictionaryToMap(obj);
// class / struct
if ( type.IsClass || type.IsValueType )
return CreateMapping(TypeNameToYamlTag(type), obj);
throw new NotImplementedException(
"Type '{0}' could not be written".DoFormat(type.FullName)
);
}
private YamlNode CreateArrayNode(Array array)
{
Type type = array.GetType();
return CreateArrayNodeSub(array, 0, new long[type.GetArrayRank()]);
}
private YamlNode CreateArrayNodeSub(Array array, int i, long[] indices)
{
var type= array.GetType();
var element = type.GetElementType();
var sequence = seq();
if ( i == 0 ) {
sequence.Tag = TypeNameToYamlTag(type);
AppendToAppeared(array, sequence);
}
if ( element.IsPrimitive || element.IsEnum || element == typeof(decimal) )
if ( array.Rank == 1 || ArrayLength(array, i+1) < 20 )
sequence.Properties["Compact"] = "true";
for ( indices[i] = 0; indices[i] < array.GetLength(i); indices[i]++ )
if ( i == array.Rank - 1 ) {
var n = ObjectToNode(array.GetValue(indices), type.GetElementType());
sequence.Add(n);
} else {
var s = CreateArrayNodeSub(array, i + 1, indices);
sequence.Add(s);
}
return sequence;
}
static long ArrayLength(Array array, int i)
{
long n = 1;
for ( ; i < array.Rank; i++ )
n *= array.GetLength(i);
return n;
}
private YamlNode CreateBinaryArrayNode(Array array)
{
var type = array.GetType();
var element = type.GetElementType();
if ( !TypeUtils.IsPureValueType(element) )
throw new InvalidOperationException(
"Can not serialize {0} as binary because it contains non-value-type(s).".DoFormat(type.FullName));
var elementSize = Marshal.SizeOf(element);
var binary = new byte[array.LongLength * elementSize];
int j = 0;
for ( int i = 0; i < array.Length; i++ ) {
IntPtr p = Marshal.UnsafeAddrOfPinnedArrayElement(array, i);
Marshal.Copy(p, binary, j, elementSize);
j += elementSize;
}
var dimension = "";
if ( array.Rank > 1 ) {
for ( int i = 0; i < array.Rank; i++ ) {
if ( dimension != "" )
dimension += ", ";
dimension += array.GetLength(i);
}
dimension = "[" + dimension + "]\r\n";
}
var result= str(TypeNameToYamlTag(type), dimension + Base64Encode(type, binary));
result.Properties["Don'tCareLineBreaks"] = "true";
return result;
}
private static string Base64Encode(Type type, byte[] binary)
{
var s = System.Convert.ToBase64String(binary);
var sb = new StringBuilder();
for ( int i = 0; i < s.Length; i += 80 ) {
if ( i + 80 < s.Length ) {
sb.AppendLine(s.Substring(i, 80));
} else {
sb.AppendLine(s.Substring(i));
}
}
return sb.ToString();
}
private YamlScalar MapKey(string key)
{
var node = (YamlScalar)key;
node.Properties["expectedTag"] = YamlNode.ExpandTag("!!str");
node.Properties["plainText"] = "true";
return node;
}
private YamlMapping CreateMapping(string tag, object obj /*, bool by_content */ )
{
var type = obj.GetType();
/*
if ( type.IsClass && !by_content && type.GetConstructor(Type.EmptyTypes) == null )
throw new ArgumentException("Type {0} has no default constructor.".DoFormat(type.FullName));
*/
var mapping = map();
mapping.Tag = tag;
AppendToAppeared(obj, mapping);
// iterate props / fields
var accessor = ObjectMemberAccessor.FindFor(type);
foreach ( var entry in accessor ) {
var name = entry.Key;
var access = entry.Value;
if ( !access.ShouldSeriealize(obj) )
continue;
if ( access.SerializeMethod == YamlSerializeMethod.Binary ) {
var array = CreateBinaryArrayNode((Array)access.Get(obj));
AppendToAppeared(access.Get(obj), array);
array.Properties["expectedTag"] = TypeNameToYamlTag(access.Type);
mapping.Add(MapKey(entry.Key), array);
} else {
try {
var value = ObjectToNode(access.Get(obj), access.Type);
if( (access.SerializeMethod != YamlSerializeMethod.Content) ||
!(value is YamlMapping) || ((YamlMapping)value).Count>0 )
mapping.Add(MapKey(entry.Key), value);
} catch {
}
}
}
// if the object is IDictionary or IDictionary<,>
if ( accessor.IsDictionary && !accessor.IsReadOnly(obj) ) {
var dictionary = DictionaryToMap(obj);
if ( dictionary.Count > 0 )
mapping.Add(MapKey("IDictionary.Entries"), dictionary);
} else {
// if the object is ICollection<> or IList
if ( accessor.CollectionAdd != null && !accessor.IsReadOnly(obj)) {
var iter = ( (IEnumerable)obj ).GetEnumerator();
if ( iter.MoveNext() ) { // Count > 0
iter.Reset();
mapping.Add(MapKey("ICollection.Items"), CreateSequence("!!seq", iter, accessor.ValueType));
}
}
}
return mapping;
}
private YamlMapping DictionaryToMap(object obj)
{
var accessor = ObjectMemberAccessor.FindFor(obj.GetType());
var iter = ( (IEnumerable)obj ).GetEnumerator();
var dictionary = map();
Func<object, object> key = null, value = null;
while ( iter.MoveNext() ) {
if ( key == null ) {
var keyvalue = iter.Current.GetType();
var keyprop = keyvalue.GetProperty("Key");
var valueprop = keyvalue.GetProperty("Value");
key = o => keyprop.GetValue(o, new object[0]);
value = o => valueprop.GetValue(o, new object[0]);
}
dictionary.Add(
ObjectToNode(key(iter.Current), accessor.KeyType),
ObjectToNode(value(iter.Current), accessor.ValueType)
);
}
return dictionary;
}
public YamlSequence CreateSequence(string tag, IEnumerator iter, Type expect)
{
var sequence = seq();
sequence.Tag = tag;
if ( expect != null && ( expect.IsPrimitive || expect.IsEnum || expect == typeof(decimal) ) )
sequence.Properties["Compact"] = "true";
while ( iter.MoveNext() )
sequence.Add(ObjectToNode(iter.Current, expect));
return sequence;
}
}
}

View File

@ -0,0 +1,869 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.IO;
namespace System.Yaml.Serialization
{
/// <summary>
/// <para><see cref="YamlSerializer"/> class has instance methods <see cref="Serialize(object)"/> and <see cref="Deserialize(string,Type[])"/>,
/// with which C# native objects can be converted into / from YAML text without any preparations.</para>
/// <code>
/// var serializer = new YamlSerializer();
/// object obj = GetObjectToSerialize();
/// string yaml = serializer.Serialize(obj);
/// object restored = serializer.Deserialize(yaml);
/// Assert.AreEqual(obj, restored);
/// </code>
/// </summary>
///
/// <remarks>
/// <h3>What kind of objects can be serialized?</h3>
///
/// <para><see cref="YamlSerializer"/> can serialize / deserialize native C# objects of primitive types
/// (bool, char, int,...), enums, built-in non-primitive types (string, decimal), structures,
/// classes and arrays of these types. </para>
///
/// <para>
/// On the other hand, it does not deal with IntPtr (which is a primitive type, though) and
/// pointer types (void*, int*, ...) because these types are, by their nature, not persistent.
/// </para>
///
/// <para>
/// Classes without a default constructor can be deserialized only when the way of activating an instance
/// is explicitly specified by <see cref="YamlConfig.AddActivator"/>.
/// </para>
///
/// <para><code>
/// object obj = new object[]{
/// null,
/// "abc",
/// true,
/// 1,
/// (Byte)1,
/// 1.0,
/// "1",
/// new double[]{ 1.1, 2, -3 },
/// new string[]{ "def", "ghi", "1" },
/// new System.Drawing.Point(1,3),
/// new System.Drawing.SolidBrush(Color.Blue)
/// };
///
/// var serializer = new YamlSerializer();
/// string yaml = serializer.Serialize(obj);
/// // %YAML 1.2
/// // ---
/// // - null
/// // - abc
/// // - True
/// // - 1
/// // - !System.Byte 1
/// // - !!float 1
/// // - "1"
/// // - !&lt;!System.Double[]%gt; [1.1, 2, -3]
/// // - !&lt;!System.String[]%gt;
/// // - def
/// // - ghi
/// // - !System.Drawing.Point 1, 3
/// // - !System.Drawing.SolidBrush
/// // Color: Blue
/// // ...
///
/// object restored;
/// try {
/// restored = YamlSerializer.Deserialize(yaml)[0];
/// } catch(MissingMethodException) {
/// // default constructor is missing for SolidBrush
/// }
///
/// // Let the library know how to activate an instance of SolidBrush.
/// YamlNode.DefaultConfig.AddActivator&lt;System.Drawing.SolidBrush&gt;(
/// () => new System.Drawing.SolidBrush(Color.Black /* dummy */));
///
/// // Then, all the objects can be restored correctly.
/// restored = serializer.Deserialize(yaml)[0];
/// </code></para>
///
/// <para>A YAML document generated by <see cref="YamlSerializer"/> always have a %YAML directive and
/// explicit document start (<c>"---"</c>) and end (<c>"..."</c>) marks.
/// This allows several documents to be written in a single YAML stream.</para>
///
/// <code>
/// var yaml = "";
/// var serializer = new YamlSerializer();
/// yaml += serializer.Serialize("a");
/// yaml += serializer.Serialize(1);
/// yaml += serializer.Serialize(1.1);
/// // %YAML 1.2
/// // ---
/// // a
/// // ...
/// // %YAML 1.2
/// // ---
/// // 1
/// // ...
/// // %YAML 1.2
/// // ---
/// // 1.1
/// // ...
///
/// object[] objects = serializer.Deserialize(yaml);
/// // objects[0] == "a"
/// // objects[1] == 1
/// // objects[2] == 1.1
/// </code>
///
/// <para>Since a YAML stream can consist of multiple YAML documents as above,
/// <see cref="Deserialize(string, Type[])"/> returns an array of <see cref="object"/>.
/// </para>
///
/// <h3>Serializing structures and classes</h3>
///
/// <para>For structures and classes, by default, all public fields and public properties are
/// serialized. Note that protected / private members are always ignored.</para>
///
/// <h4>Serialization methods</h4>
///
/// <para>Readonly value-type members are also ignored because there is no way to
/// assign a new value to them on deserialization, while readonly class-type members
/// are serialized. When deserializing, instead of creating a new object and assigning it
/// to the member, the child members of such class instance are restored independently.
/// Such a deserializing method is refered to <see cref="YamlSerializeMethod.Content">
/// YamlSerializeMethod.Content</see>.
/// </para>
/// <para>
/// On the other hand, when writeable fields/properties are deserialized, new objects are
/// created by using the parameters in the YAML description and assiend to the fields/properties.
/// Such a deserializing method is refered to <see cref="YamlSerializeMethod.Assign">
/// YamlSerializeMethod.Assign</see>. Writeable properties can be explicitly specified to use
/// <see cref="YamlSerializeMethod.Content"> YamlSerializeMethod.Content</see> method for
/// deserialization, by adding <see cref="YamlSerializeAttribute"/> to its definition.
/// </para>
///
/// <para>Another type of serializing method is <see cref="YamlSerializeMethod.Binary">
/// YamlSerializeMethod.Binary</see>.
/// This method is only applicable to an array-type field / property that contains
/// only value-type members.</para>
///
/// <para>If serializing method <see cref="YamlSerializeMethod.Never"/> is specified,
/// the member is never serialized nor deserialized.</para>
///
/// <code>
/// public class Test1
/// {
/// public int PublicProp { get; set; } // processed (by assign)
/// protected int ProtectedProp { get; set; } // Ignored
/// private int PrivateProp { get; set; } // Ignored
/// internal int InternalProp { get; set; } // Ignored
///
/// public int PublicField; // processed (by assign)
/// protected int ProtectedField; // Ignored
/// private int PrivateField; // Ignored
/// internal int InternalField; // Ignored
///
/// public List&lt;string&gt; ClassPropByAssign // processed (by assign)
/// { get; set; }
///
/// public int ReadOnlyValueProp { get; private set; } // Ignored
/// public List&lt;string&gt; ReadOnlyClassProp // processed (by content)
/// { get; private set; }
///
/// [YamlSerialize(YamlSerializeMethod.Content)]
/// public List&lt;string&gt; ClassPropByContent// processed (by content)
/// { get; set; }
///
/// public int[] IntArrayField = // processed (by assign)
/// new int[10];
///
/// [YamlSerialize(YamlSerializeMethod.Binary)]
/// public int[] IntArrayFieldBinary = // processed (as binary)
/// new int[100];
///
/// [YamlSerialize(YamlSerializeMethod.Never)]
/// public int PublicPropHidden; // Ignored
///
/// public Test1()
/// {
/// ClassPropByAssign = new List&lt;string&gt;();
/// ReadOnlyClassProp = new List&lt;string&gt;();
/// ClassPropByContent = new List&lt;string&gt;();
/// }
/// }
///
/// public void TestPropertiesAndFields1()
/// {
/// var test1 = new Test1();
/// test1.ClassPropByAssign.Add("abc");
/// test1.ReadOnlyClassProp.Add("def");
/// test1.ClassPropByContent.Add("ghi");
/// var rand = new Random(0);
/// for ( int i = 0; i &lt; test1.IntArrayFieldBinary.Length; i++ )
/// test1.IntArrayFieldBinary[i] = rand.Next();
///
/// var serializer = new YamlSerializer();
/// string yaml = serializer.Serialize(test1);
/// // %YAML 1.2
/// // ---
/// // !YamlSerializerTest.Test1
/// // PublicProp: 0
/// // ClassPropByAssign:
/// // Capacity: 4
/// // ICollection.Items:
/// // - abc
/// // ReadOnlyClassProp:
/// // Capacity: 4
/// // ICollection.Items:
/// // - def
/// // ClassPropByContent:
/// // Capacity: 4
/// // ICollection.Items:
/// // - ghi
/// // PublicField: 0
/// // IntArrayField: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
/// // IntArrayFieldBinary: |+2
/// // Gor1XAwenmhGkU5ib9NxR11LXxp1iYlH5LH4c9hImTitWSB9Z78II2UvXSXV99A79fj6UBn3GDzbIbd9
/// // yBDjAyslYm58iGd/NN+tVjuLRCg3cJBo+PWMbIWm9n4AEC0E7LKXWV5HXUNk7I13APEDWFMM/kWTz2EK
/// // s7LzFw2gBjpKugkmQJqIfinpQ1J1yqhhz/XjA3TBxDBsEuwrD+SNevQSqEC+/KRbwgE6D011ACMeyRt0
/// // BOG6ZesRKCtL0YU6tSnLEpgKVBz+R300qD3/W0aZVk+1vHU+auzyGCGUaHCGd6dpRoEhXoIg2m3+AwJX
/// // EJ37T+TA9BuEPJtyGoq+crQMFQtXj1Zriz3HFbReclLvDdVpZlcOHPga/3+3Y509EHZ7UyT7H1xGeJxn
/// // eXPrDDb0Ul04MfZb4UYREOfR3HNzNTUYGRsIPUvHOEW7AaoplIfkVQp19DvGBrBqlP2TZ9atlWUHVdth
/// // 7lIBeIh0wiXxoOpCbQ7qVP9GkioQUrMkOcAJaad3exyZaOsXxznFCA==
/// // ...
/// }
/// </code>
///
/// <h4>Default values of fields and properties</h4>
///
/// <para><see cref="YamlSerializer"/> is aware of <see cref="System.ComponentModel.DefaultValueAttribute">
/// System.ComponentModel.DefaultValueAttribute</see>.
/// So, when a member of a structure / class instance has a value that equals to the default value,
/// the member will not be written in the YAML text.</para>
///
/// <para>It also checkes for the result of ShouldSerializeXXX method. For instance, just before serializing <c>Font</c>
/// property of some type, <c>bool ShouldSerializeFont()</c> method is called if exists. If the method returns false,
/// <c>Font</c> property will not be written in the YAML text. ShouldSerializeXXX method can be non-public.</para>
///
/// <code>
/// using System.ComponentModel;
///
/// public class Test2
/// {
/// [DefaultValue(0)]
/// public int Default0 = 0;
///
/// [DefaultValue("a")]
/// public string Defaulta = "a";
///
/// public int DynamicDefault = 0;
///
/// bool ShouldSerializeDynamicDefault()
/// {
/// return Default0 != DynamicDefault;
/// }
/// }
///
/// public void TestDefaultValue()
/// {
/// var test2 = new Test2();
/// var serializer = new YamlSerializer();
///
/// // All properties have defalut values.
/// var yaml = serializer.Serialize(test2);
/// // %YAML 1.2
/// // ---
/// // !YamlSerializerTest.Test2 {}
/// // ...
///
/// test2.Defaulta = "b";
/// yaml = serializer.Serialize(test2);
/// // %YAML 1.2
/// // ---
/// // !YamlSerializerTest.Test2
/// // Defaulta: b
/// // ...
///
/// test2.Defaulta = "a";
/// var yaml = serializer.Serialize(test2);
/// // %YAML 1.2
/// // ---
/// // !YamlSerializerTest.Test2 {}
/// // ...
///
/// test2.DynamicDefault = 1;
/// yaml = serializer.Serialize(test2);
/// // %YAML 1.2
/// // ---
/// // !YamlSerializerTest.Test2
/// // DynamicDefault: 1
/// // ...
///
/// test2.Default0 = 1;
/// yaml = serializer.Serialize(test2);
/// // %YAML 1.2
/// // ---
/// // !YamlSerializerTest.Test2
/// // Default0: 1
/// // ...
/// }
/// </code>
///
/// <h4>Collection classes</h4>
///
/// <para>If an object implements <see cref="ICollection&lt;T&gt;"/>, <see cref="IList"/> or <see cref="IDictionary"/>
/// the child objects are serialized as well its other public members.
/// Pseudproperty <c>ICollection.Items</c> or <c>IDictionary.Entries</c> appears to hold the child objects.</para>
///
/// <h3>Multitime appearance of a same object</h3>
///
/// <para><see cref="YamlSerializer"/> preserve C# objects' graph structure. Namely, when a same objects are refered to
/// from several points in the object graph, the structure is correctly described in YAML text and restored objects
/// preserve the structure. <see cref="YamlSerializer"/> can safely manipulate directly / indirectly self refering
/// objects, too.</para>
///
/// <code>
/// public class TestClass
/// {
/// public List&lt;TestClass&gt; list =
/// new List&lt;TestClass&gt;();
/// }
///
/// public class ChildClass: TestClass
/// {
/// }
///
/// void RecursiveObjectsTest()
/// {
/// var a = new TestClass();
/// var b = new ChildClass();
/// a.list.Add(a);
/// a.list.Add(a);
/// a.list.Add(b);
/// a.list.Add(a);
/// a.list.Add(b);
/// b.list.Add(a);
/// var serializer = new YamlSerializer();
/// string yaml = serializer.Serialize(a);
/// // %YAML 1.2
/// // ---
/// // &amp;A !TestClass
/// // list:
/// // Capacity: 8
/// // ICollection.Items:
/// // - *A
/// // - *A
/// // - &amp;B !ChildClass
/// // list:
/// // Capacity: 4
/// // ICollection.Items:
/// // - *A
/// // - *A
/// // - *B
/// // ...
///
/// var restored = (TestClass)serializer.Deserialize(yaml)[0];
/// Assert.IsTrue(restored == restored.list[0]);
/// Assert.IsTrue(restored == restored.list[1]);
/// Assert.IsTrue(restored == restored.list[3]);
/// Assert.IsTrue(restored == restored.list[5]);
/// Assert.IsTrue(restored.list[2] == restored.list[4]);
/// }
/// </code>
///
/// <para>This is not the case if the object is <see cref="string"/>. Same instances of
/// <see cref="string"/> are repeatedly written in a YAML text and restored as different
/// instance of <see cref="string"/> when deserialized, unless the content of the string
/// is extremely long (longer than 999 chars).</para>
///
/// <code>
/// // 1000 chars
/// string long_str =
/// "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
/// "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
/// "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
/// "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
/// "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
/// "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
/// "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
/// "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
/// "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
/// "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
/// string short_str = "12345";
/// object obj = new object[] { long_str, long_str, short_str, short_str };
/// var serializer = new YamlSerializer();
/// string yaml = serializer.Serialize(obj);
/// // %YAML 1.2
/// // ---
/// // - &amp;A 01234567890123456789012345678901234567890123456789 ... (snip) ... 789
/// // - *A
/// // - "12345"
/// // - "12345"
/// // ...
/// </code>
///
/// <h3>YAML text written / read by <see cref="YamlSerializer"/></h3>
///
/// <para>When serializing, <see cref="YamlSerializer"/> intelligently uses various YAML 1.2 styles,
/// namely the block style, flow style, explicit mapping and implicit mapping, to maximize readability
/// of the YAML stream.</para>
///
/// <code>
/// [Flags]
/// enum TestEnum: uint
/// {
/// abc = 1,
/// あいう = 2
/// }
///
/// public void TestVariousFormats()
/// {
/// var dict = new Dictionary&lt;object, object&gt;();
/// dict.Add(new object[] { 1, "a" }, new object());
/// object obj = new object[]{
/// dict,
/// null,
/// "abc",
/// "1",
/// "a ",
/// "- a",
/// "abc\n",
/// "abc\ndef\n",
/// "abc\ndef\nghi",
/// new double[]{ 1.1, 2, -3, 3.12, 13.2 },
/// new int[,] { { 1, 3}, {4, 5}, {10, 1} },
/// new string[]{ "jkl", "mno\npqr" },
/// new System.Drawing.Point(1,3),
/// TestEnum.abc,
/// TestEnum.abc | TestEnum.あいう,
/// };
/// var config = new YamlConfig();
/// config.ExplicitlyPreserveLineBreaks = false;
/// var serializer = new YamlSerializer(config);
/// string yaml = serializer.Serialize(obj);
///
/// // %YAML 1.2
/// // ---
/// // - !&lt;!System.Collections.Generic.Dictionary%602[[System.Object,...],[System.Object,...]]&gt;
/// // Keys: {}
/// // Values: {}
/// // IDictionary.Entries:
/// // ? - 1
/// // - a
/// // : !System.Object {}
/// // - null
/// // - abc
/// // - "1"
/// // - "a "
/// // - "- a"
/// // - "abc\n"
/// // - |+2
/// // abc
/// // def
/// // - |-2
/// // abc
/// // def
/// // ghi
/// // - !&lt;!System.Double[]&gt; [1.1, 2, -3, 3.12, 13.2]
/// // - !&lt;!System.Int32[,]&gt; [[1, 3], [4, 5], [10, 1]]
/// // - !&lt;!System.String[]&gt;
/// // - jkl
/// // - |-2
/// // mno
/// // pqr
/// // - !System.Drawing.Point 1, 3
/// // - !TestEnum abc
/// // - !TestEnum abc, あいう
/// // ...
/// }
/// </code>
///
/// <para>When deserializing, <see cref="YamlSerializer"/> accepts any valid YAML 1.2 documents.
/// TAG directives, comments, flow / block styles, implicit / explicit mappings can be freely used
/// to express valid C# objects. Namely, the members of the array can be given eighter in a flow style
/// or in a block style.
/// </para>
///
/// <para>By default, <see cref="YamlSerializer"/> outputs a YAML stream with line break of "\r\n".
/// This can be customized either by setting <c>YamlNode.DefaultConfig.LineBreakForOutput</c> or
/// by giving an instance of <see cref="YamlConfig"/> to the <see cref="YamlSerializer(YamlConfig)">
/// constructor</see>.
/// </para>
///
/// <code>
/// var serializer = new YamlSerializer();
/// var yaml = serializer.Serialize("abc");
/// // %YAML 1.2\r\n // line breaks are explicitly shown in this example
/// // ---\r\n
/// // abc\r\n
/// // ...\r\n
///
/// var config = new YamlConfig();
/// config.LineBreakForOutput = "\n";
/// serializer = new YamlSerializer(config);
/// var yaml = serializer.Serialize("abc");
/// // %YAML 1.2\n
/// // ---\n
/// // abc\n
/// // ...\n
///
/// YamlNode.DefaultConfig.LineBreakForOutput = "\n";
///
/// var serializer = new YamlSerializer();
/// serializer = new YamlSerializer();
/// var yaml = serializer.Serialize("abc");
/// // %YAML 1.2\n
/// // ---\n
/// // abc\n
/// // ...\n
/// </code>
///
/// <h4>Line breaks in YAML text</h4>
///
/// <para>By default, line breaks in multi line values are explicitly presented as escaped style.
/// Although, this makes the resulting YAML stream hard to read, it is necessary to preserve
/// the exact content of the string because the YAML specification requires that a YAML parser
/// must normalize evely line break that is not escaped in a YAML document to be a single line
/// feed "\n" when deserializing.</para>
///
/// <para>In order to have the YAML documents easy to be read, set
/// <see cref="YamlConfig.ExplicitlyPreserveLineBreaks">YamlConfig.ExplicitlyPreserveLineBreaks
/// </see> false. Then, the multiline values of will be written in literal style.</para>
///
/// <para>Of course, it makes all the line breaks to be normalized into a single line feeds "\n".</para>
///
/// <code>
/// var serializer = new YamlSerializer();
/// var text = "abc\r\n def\r\nghi\r\n";
/// // abc
/// // def
/// // ghi
///
/// // By default, line breaks explicitly appears in escaped form.
/// var yaml = serializer.Serialize(text);
/// // %YAML 1.2
/// // ---
/// // "abc\r\n\
/// // \ def\r\n\
/// // ghi\r\n"
/// // ...
///
/// // Original line breaks are preserved
/// var restored = (string)serializer.Deserialize(yaml)[0];
/// // "abc\r\n def\r\nghi\r\n"
///
///
/// YamlNode.DefaultConfig.ExplicitlyPreserveLineBreaks = false;
///
/// // Literal style is easier to be read.
/// var yaml = serializer.Serialize(text);
/// // %YAML 1.2
/// // ---
/// // |+2
/// // abc
/// // def
/// // ghi
/// // ...
///
/// // Original line breaks are lost.
/// var restored = (string)serializer.Deserialize(yaml)[0];
/// // "abc\n def\nghi\n"
/// </code>
///
/// <para>This library offers two work arounds for this problem, although both of which
/// violates the official behavior of a YAML parser defined in the YAML specification.</para>
///
/// <para>One is to set <see cref="YamlConfig.LineBreakForInput">YamlConfig.LineBreakForInput</see>
/// to be "\r\n". Then, the YAML parser normalizes all line breaks into "\r\n" instead of "\n".</para>
///
/// <para>The other is to set <see cref="YamlConfig.NormalizeLineBreaks">YamlConfig.NormalizeLineBreaks</see>
/// false. It disables the line break normalization both at output and at input. Namely, the line breaks are
/// written and read as-is when serialized / deserialized.</para>
///
/// <code>
/// var serializer = new YamlSerializer();
///
/// // text with mixed line breaks
/// var text = "abc\r def\nghi\r\n";
/// // abc\r // line breaks are explicitly shown in this example
/// // def\n
/// // ghi\r\n
///
/// YamlNode.DefaultConfig.ExplicitlyPreserveLineBreaks = false;
///
/// // By default, all line breaks are normalized to "\r\n" when serialized.
/// var yaml = serializer.Serialize(text);
/// // %YAML 1.2\r\n
/// // ---\r\n
/// // |+2\r\n
/// // abc\r\n
/// // def\r\n
/// // ghi\r\n
/// // ...\r\n
///
/// // When deserialized, line breaks are normalized into "\n".
/// var restored = (string)serializer.Deserialize(yaml)[0];
/// // "abc\n def\nghi\n"
///
/// // Line breaks are normalized into "\r\n" instead of "\n".
/// YamlNode.DefaultConfig.LineBreakForInput = "\r\n";
/// restored = (string)serializer.Deserialize(yaml)[0];
/// // "abc\r\n def\r\nghi\r\n"
///
/// // Line breaks are written as is,
/// YamlNode.DefaultConfig.NormalizeLineBreaks = false;
/// var yaml = serializer.Serialize(text);
/// // %YAML 1.2\r\n
/// // ---\r\n
/// // |+2\r\n
/// // abc\r
/// // def\n
/// // ghi\r\n
/// // ...\r\n
///
/// // and are read as is.
/// restored = (string)serializer.Deserialize(yaml)[0];
/// // "abc\r def\nghi\r\n"
///
/// // Note that when the line breaks of YAML stream is changed
/// // between serialization and deserialization, the original
/// // line breaks are lost.
/// yaml = yaml.Replace("\r\n", "\n").Replace("\r", "\n");
/// restored = (string)serializer.Deserialize(yaml)[0];
/// // "abc\n def\nghi\n"
/// </code>
///
/// <para>It is repeatedly stated that although these two options are useful in many situation,
/// they makes the YAML parser violate the YAML specification. </para>
///
/// </remarks>
public class YamlSerializer
{
static YamlRepresenter representer = new YamlRepresenter();
static YamlConstructor constructor = new YamlConstructor();
YamlConfig config = null;
/// <summary>
/// Initialize an instance of <see cref="YamlSerializer"/> that obeys
/// <see cref="YamlNode.DefaultConfig"/>.
/// </summary>
public YamlSerializer()
{ }
/// <summary>
/// Initialize an instance of <see cref="YamlSerializer"/> with custom <paramref name="config"/>.
/// </summary>
/// <param name="config">Custom <see cref="YamlConfig"/> to customize serialization.</param>
public YamlSerializer(YamlConfig config)
{
this.config = config;
}
/// <summary>
/// Serialize C# object <paramref name="obj"/> into YAML text.
/// </summary>
/// <param name="obj">Object to be serialized.</param>
/// <returns>YAML text.</returns>
public string Serialize(object obj)
{
var c = config != null ? config : YamlNode.DefaultConfig;
var node = representer.ObjectToNode(obj, c);
return node.ToYaml(c);
}
/// <summary>
/// Serialize C# object <paramref name="obj"/> into YAML text and write it into a <see cref="Stream"/> <paramref name="s"/>.
/// </summary>
/// <param name="s">A <see cref="Stream"/> to which the YAML text is written.</param>
/// <param name="obj">Object to be serialized.</param>
public void Serialize(Stream s, object obj)
{
var c = config != null ? config : YamlNode.DefaultConfig;
var node = representer.ObjectToNode(obj, c);
node.ToYaml(s, c);
}
/// <summary>
/// Serialize C# object <paramref name="obj"/> into YAML text and write it into a <see cref="TextWriter"/> <paramref name="tw"/>.
/// </summary>
/// <param name="tw">A <see cref="TextWriter"/> to which the YAML text is written.</param>
/// <param name="obj">Object to be serialized.</param>
public void Serialize(TextWriter tw, object obj)
{
var c = config != null ? config : YamlNode.DefaultConfig;
var node = representer.ObjectToNode(obj, c);
node.ToYaml(tw, c);
}
/// <summary>
/// Serialize C# object <paramref name="obj"/> into YAML text and save it into a file named as <paramref name="YamlFileName"/>.
/// </summary>
/// <param name="YamlFileName">A file name to which the YAML text is written.</param>
/// <param name="obj">Object to be serialized.</param>
public void SerializeToFile(string YamlFileName, object obj)
{
using ( var s = new FileStream(YamlFileName, FileMode.Create, FileAccess.Write) )
Serialize(s, obj);
}
/// <summary>
/// Deserialize C# object(s) from a YAML text. Since a YAML text can contain multiple YAML documents, each of which
/// represents a C# object, the result is returned as an array of <see cref="object"/>.
/// </summary>
/// <param name="yaml">A YAML text from which C# objects are deserialized.</param>
/// <param name="types">Expected type(s) of the root object(s) in the YAML stream.</param>
/// <returns>C# object(s) deserialized from YAML text.</returns>
public object[] Deserialize(string yaml, params Type[] types)
{
var c = config != null ? config : YamlNode.DefaultConfig;
var parser = new YamlParser();
var nodes = parser.Parse(yaml, c);
var objects = new List<object>();
for ( int i = 0; i < nodes.Count; i++ ) {
var node = nodes[i];
if ( i < types.Length ) {
objects.Add(constructor.NodeToObject(node, types[i], c));
} else {
objects.Add(constructor.NodeToObject(node, null, c));
}
}
return objects.ToArray();
}
/// <summary>
/// Deserialize C# object(s) from a YAML text in a <see cref="Stream"/> <paramref name="s"/>.
/// Since a YAML text can contain multiple YAML documents, each of which
/// represents a C# object, the result is returned as an array of <see cref="object"/>.
/// </summary>
/// <param name="s">A <see cref="Stream"/> that contains YAML text from which C# objects are deserialized.</param>
/// <param name="types">Expected type(s) of the root object(s) in the YAML stream.</param>
/// <returns>C# object(s) deserialized from YAML text.</returns>
public object[] Deserialize(Stream s, params Type[] types)
{
return Deserialize(new StreamReader(s), types);
}
/// <summary>
/// Deserialize C# object(s) from a YAML text in a <see cref="TextReader"/> <paramref name="tr"/>.
/// Since a YAML text can contain multiple YAML documents, each of which
/// represents a C# object, the result is returned as an array of <see cref="object"/>.
/// </summary>
/// <param name="tr">A <see cref="TextReader"/> that contains YAML text from which C# objects are deserialized.</param>
/// <param name="types">Expected type(s) of the root object(s) in the YAML stream.</param>
/// <returns>C# object(s) deserialized from YAML text.</returns>
public object[] Deserialize(TextReader tr, params Type[] types)
{
return Deserialize(tr.ReadToEnd(), types);
}
/// <summary>
/// Deserialize C# object(s) from a YAML text in a file named as <paramref name="YamlFileName"/>.
/// Since a YAML text can contain multiple YAML documents, each of which
/// represents a C# object, the result is returned as an array of <see cref="object"/>.
/// </summary>
/// <param name="YamlFileName">The name of a file that contains YAML text from which C# objects are deserialized.</param>
/// <param name="types">Expected type(s) of the root object(s) in the YAML stream.</param>
/// <returns>C# object(s) deserialized from YAML text.</returns>
public object[] DeserializeFromFile(string YamlFileName, params Type[] types)
{
using ( var s = new FileStream(YamlFileName, FileMode.Open, FileAccess.Read) )
return Deserialize(s, types);
}
}
/// <summary>
/// Add .DoFunction method to string
/// </summary>
internal static class StringExtention
{
/// <summary>
/// Short expression of string.Format(XXX, arg1, arg2, ...)
/// </summary>
/// <param name="format"></param>
/// <param name="args"></param>
/// <returns></returns>
public static string DoFormat(this string format, params object[] args)
{
return string.Format(format, args);
}
}
/// <summary>
/// <para>Specify the way to store a property or field of some class or structure.</para>
/// <para>See <see cref="YamlSerializer"/> for detail.</para>
/// </summary>
/// <seealso cref="YamlSerializeAttribute"/>
/// <seealso cref="YamlSerializer"/>
public enum YamlSerializeMethod
{
/// <summary>
/// The property / field will not be stored.
/// </summary>
Never,
/// <summary>
/// When restored, new object is created by using the parameters in
/// the YAML data and assigned to the property / field. When the
/// property / filed is writeable, this is the default.
/// </summary>
Assign,
/// <summary>
/// Only valid for a property / field that has a class or struct type.
/// When restored, instead of recreating the whole class or struct,
/// the members are independently restored. When the property / field
/// is not writeable this is the default.
/// </summary>
Content,
/// <summary>
/// Only valid for a property / field that has an array type of a
/// some value type. The content of the array is stored in a binary
/// format encoded in base64 style.
/// </summary>
Binary
}
/// <summary>
/// Specify the way to store a property or field of some class or structure.
///
/// See <see cref="YamlSerializer"/> for detail.
/// </summary>
/// <seealso cref="YamlSerializeAttribute"/>
/// <seealso cref="YamlSerializer"/>
public sealed class YamlSerializeAttribute: Attribute
{
internal YamlSerializeMethod SerializeMethod;
/// <summary>
/// Specify the way to store a property or field of some class or structure.
///
/// See <see cref="YamlSerializer"/> for detail.
/// </summary>
/// <seealso cref="YamlSerializeAttribute"/>
/// <seealso cref="YamlSerializer"/>
/// <param name="SerializeMethod">
/// <para>
/// - Never: The property / field will not be stored.</para>
///
/// <para>
/// - Assign: When restored, new object is created by using the parameters in
/// the YAML data and assigned to the property / field. When the
/// property / filed is writeable, this is the default.</para>
///
/// <para>
/// - Content: Only valid for a property / field that has a class or struct type.
/// When restored, instead of recreating the whole class or struct,
/// the members are independently restored. When the property / field
/// is not writeable this is the default.</para>
///
/// <para>
/// - Binary: Only valid for a property / field that has an array type of a
/// some value type. The content of the array is stored in a binary
/// format encoded in base64 style.</para>
///
/// </param>
public YamlSerializeAttribute(YamlSerializeMethod SerializeMethod)
{
this.SerializeMethod = SerializeMethod;
}
}
}

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{AD9F3A60-C492-4823-8F24-6F4854E7CBF5}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>YamlSerializer</RootNamespace>
<AssemblyName>YamlSerializer</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>
</DocumentationFile>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\YamlSerializer.XML</DocumentationFile>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject>YamlSerializerProgram.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ObjectExtensions.cs" />
<Compile Include="Program.cs" />
<Compile Include="RehashableDictionary.cs" />
<Compile Include="YamlDoubleQuoteEscaping.cs" />
<Compile Include="EasyTypeConverter.cs" />
<Compile Include="ObjectMemberAccessor.cs" />
<Compile Include="Parser.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TypeUtils.cs" />
<Compile Include="UriEncoding.cs" />
<Compile Include="YamlAnchorDictionary.cs" />
<Compile Include="YamlConstructor.cs" />
<Compile Include="YamlParser.cs" />
<Compile Include="YamlNode.cs" />
<Compile Include="YamlPresenter.cs" />
<Compile Include="YamlRepresenter.cs" />
<Compile Include="YamlSerializer.cs" />
<Compile Include="YamlTagPrefixes.cs" />
<Compile Include="YamlTagResolutionScheme.cs" />
<Compile Include="YamlTagValidator.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="ChangeLog.txt" />
<Content Include="Readme.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace System.Yaml
{
/// <summary>
/// Reset();
/// SetupDefaultTagPrefixes();
/// Add(tag_handle, tag_prefix);
/// verbatim_tag = Resolve(tag_handle, tag_name);
/// </summary>
internal class YamlTagPrefixes
{
Dictionary<string, string> TagPrefixes = new Dictionary<string, string>();
Func<string, object[], bool> error;
#region Debug.Assert
#if DEBUG
/// <summary>
/// Since System.Diagnostics.Debug.Assert is too anoying while development,
/// this class temporarily override Debug.Assert action.
/// </summary>
private class Debug
{
public static void Assert(bool condition)
{
Assert(condition, "");
}
public static void Assert(bool condition, string message)
{
if ( !condition )
throw new Exception("assertion failed: " + message);
}
}
#endif
#endregion
public YamlTagPrefixes(Func<string, object[], bool> error)
{
this.error = error;
}
void Error(string format, params object[] args)
{
error(format, args);
}
public bool Reset()
{
TagPrefixes.Clear();
return true;
}
public void SetupDefaultTagPrefixes()
{
if ( !TagPrefixes.ContainsKey("!") )
TagPrefixes.Add("!", "!");
if ( !TagPrefixes.ContainsKey("!!") )
TagPrefixes.Add("!!", YamlNode.DefaultTagPrefix);
}
public void Add(string tag_handle, string tag_prefix)
{
if ( TagPrefixes.ContainsKey(tag_handle) ) {
switch ( tag_handle ) {
case "!":
Error("Primary tag prefix is already defined as '{0}'.", TagPrefixes["!"]);
break;
case "!!":
Error("Secondary tag prefix is already defined as '{0}'.", TagPrefixes["!!"]);
break;
default:
Error("Tag prefix for the handle {0} is already defined as '{1}'.", tag_handle, TagPrefixes[tag_handle]);
break;
}
}
TagPrefixes.Add(tag_handle, tag_prefix);
}
public string Resolve(string tag_handle, string tag_name)
{
if ( !TagPrefixes.ContainsKey(tag_handle) )
Error("Tag handle {0} is not registered.", tag_handle);
var tag = TagPrefixes[tag_handle] + tag_name;
return tag;
}
}
}

View File

@ -0,0 +1,289 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace System.Yaml
{
/// <summary>
/// Represents the way to automatically resolve Tag from the Value of a YamlScalar.
/// </summary>
internal class YamlTagResolver
{
/// <summary>
/// Create TagResolver with default resolution rules.
/// </summary>
public YamlTagResolver()
{
AddDefaultRules();
}
/// <summary>
/// Add default tag resolution rules to the rule list.
/// </summary>
void AddDefaultRules()
{
BeginUpdate();
AddRule<int>("!!int", @"([-+]?(0|[1-9][0-9_]*))",
m => Convert.ToInt32(m.Value.Replace("_", "")), null);
AddRule<int>("!!int", @"([-+]?)0b([01_]+)", m => {
var v = Convert.ToInt32(m.Groups[2].Value.Replace("_", ""), 2);
return m.Groups[1].Value == "-" ? -v : v;
}, null);
AddRule<int>("!!int", @"([-+]?)0o?([0-7_]+)", m => {
var v = Convert.ToInt32(m.Groups[2].Value.Replace("_", ""), 8);
return m.Groups[1].Value == "-" ? -v : v;
}, null);
AddRule<int>("!!int", @"([-+]?)0x([0-9a-fA-F_]+)", m => {
var v = Convert.ToInt32(m.Groups[2].Value.Replace("_", ""), 16);
return m.Groups[1].Value == "-" ? -v : v;
}, null);
// Todo: http://yaml.org/type/float.html is wrong => [0-9.] should be [0-9_]
AddRule<double>("!!float", @"[-+]?(0|[1-9][0-9_]*)\.[0-9_]*([eE][-+]?[0-9]+)?",
m => Convert.ToDouble(m.Value.Replace("_", "")), null);
AddRule<double>("!!float", @"[-+]?\._*[0-9][0-9_]*([eE][-+]?[0-9]+)?",
m => Convert.ToDouble(m.Value.Replace("_", "")), null);
AddRule<double>("!!float", @"[-+]?(0|[1-9][0-9_]*)([eE][-+]?[0-9]+)",
m => Convert.ToDouble(m.Value.Replace("_", "")), null);
AddRule<double>("!!float", @"\+?(\.inf|\.Inf|\.INF)", m => double.PositiveInfinity, null);
AddRule<double>("!!float", @"-(\.inf|\.Inf|\.INF)", m => double.NegativeInfinity, null);
AddRule<double>("!!float", @"\.nan|\.NaN|\.NAN", m => double.NaN, null);
AddRule<bool>("!!bool", @"y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON", m => true, null);
AddRule<bool>("!!bool", @"n|N|no|No|NO|false|False|FALSE|off|Off|OFF", m => false, null);
AddRule<object>("!!null", @"null|Null|NULL|\~|", m => null, null);
AddRule<string>("!!merge", @"<<", m => "<<", null);
AddRule<DateTime>("!!timestamp", // Todo: spec is wrong (([ \t]*)Z|[-+][0-9][0-9]?(:[0-9][0-9])?)? should be (([ \t]*)(Z|[-+][0-9][0-9]?(:[0-9][0-9])?))? to accept "2001-12-14 21:59:43.10 -5"
@"([0-9]{4})-([0-9]{2})-([0-9]{2})" +
@"(" +
@"([Tt]|[\t ]+)" +
@"([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})(\.([0-9]*))?" +
@"(" +
@"([ \t]*)" +
@"(Z|([-+])([0-9]{1,2})(:([0-9][0-9]))?)" +
@")?" +
@")?",
match => DateTime.Parse(match.Value),
datetime => {
var z = datetime.ToString("%K");
if ( z != "Z" && z != "" )
z = " " + z;
if ( datetime.Millisecond == 0 ) {
if ( datetime.Hour == 0 && datetime.Minute == 0 && datetime.Second == 0 ) {
return datetime.ToString("yyyy-MM-dd" + z);
} else {
return datetime.ToString("yyyy-MM-dd HH:mm:ss" + z);
}
} else {
return datetime.ToString("yyyy-MM-dd HH:mm:ss.fff" + z);
}
});
EndUpdate();
}
public Type TypeFromTag(string tag)
{
tag= YamlNode.ExpandTag(tag);
if ( types.ContainsKey(tag) )
return types[tag][0].GetTypeOfValue();
return null;
}
/// <summary>
/// List of tag resolution rules.
/// </summary>
List<YamlTagResolutionRule> Rules = new List<YamlTagResolutionRule>();
/// <summary>
/// Add a tag resolution rule that is invoked when <paramref name="regex"/> matches
/// the <see cref="YamlScalar.Value">Value of</see> a <see cref="YamlScalar"/> node.
///
/// The tag is resolved to <paramref name="tag"/> and <paramref name="decode"/> is
/// invoked when actual value of type <typeparamref name="T"/> is extracted from
/// the node text.
/// </summary>
/// <remarks>
/// Surround sequential calls of this function by <see cref="BeginUpdate"/> / <see cref="EndUpdate"/>
/// pair to avoid invoking slow internal calculation method many times.
/// </remarks>
/// <example>
/// <code>
/// BeginUpdate(); // to avoid invoking slow internal calculation method many times.
/// Add( ... );
/// Add( ... );
/// Add( ... );
/// Add( ... );
/// EndUpdate(); // automaticall invoke internal calculation method
/// </code></example>
/// <param name="tag"></param>
/// <param name="regex"></param>
/// <param name="decode"></param>
/// <param name="encode"></param>
public void AddRule<T>(string tag, string regex, Func<Match, T> decode, Func<T, string> encode)
{
Rules.Add(new YamlTagResolutionRule<T>(tag, regex, decode, encode));
if ( UpdateCounter == 0 )
Update();
}
public void AddRule<T>(string regex, Func<Match, T> decode, Func<T, string> encode)
{
Rules.Add(new YamlTagResolutionRule<T>("!"+typeof(T).FullName, regex, decode, encode));
if ( UpdateCounter == 0 )
Update();
}
int UpdateCounter = 0;
/// <summary>
/// Supress invoking slow internal calculation method when
/// <see cref="AddRule&lt;T&gt;(string,string,Func&lt;Match,T&gt;,Func&lt;T,string&gt;)"/> called.
///
/// BeginUpdate / <see cref="EndUpdate"/> can be called nestedly.
/// </summary>
public void BeginUpdate()
{
UpdateCounter++;
}
/// <summary>
/// Quit to supress invoking slow internal calculation method when
/// <see cref="AddRule&lt;T&gt;(string,string,Func&lt;Match,T&gt;,Func&lt;T,string&gt;)"/> called.
/// </summary>
public void EndUpdate()
{
if ( UpdateCounter == 0 )
throw new InvalidOperationException();
UpdateCounter--;
if ( UpdateCounter == 0 )
Update();
}
Dictionary<string, Regex> algorithms;
void Update()
{
// Tag to joined regexp source
var sources = new Dictionary<string, string>();
foreach ( var rule in Rules ) {
if ( !sources.ContainsKey(rule.Tag) ) {
sources.Add(rule.Tag, rule.PatternSource);
} else {
sources[rule.Tag] += "|" + rule.PatternSource;
}
}
// Tag to joined regexp
algorithms = new Dictionary<string, Regex>();
foreach ( var entry in sources ) {
algorithms.Add(
entry.Key,
new Regex("^(" + entry.Value + ")$")
);
}
// Tag to decoding methods
types = new Dictionary<string, List<YamlTagResolutionRule>>();
foreach ( var rule in Rules ) {
if ( !types.ContainsKey(rule.Tag) )
types[rule.Tag] = new List<YamlTagResolutionRule>();
types[rule.Tag].Add(rule);
}
TypeToRule = new Dictionary<Type, YamlTagResolutionRule>();
foreach ( var rule in Rules )
if(rule.HasEncoder())
TypeToRule[rule.GetTypeOfValue()] = rule;
}
Dictionary<string, List<YamlTagResolutionRule>> types;
Dictionary<Type, YamlTagResolutionRule> TypeToRule;
/// <summary>
/// Execute tag resolution and returns automatically determined tag value from <paramref name="text"/>.
/// </summary>
/// <param name="text">Node text with which automatic tag resolution is done.</param>
/// <returns>Automatically determined tag value .</returns>
public string Resolve(string text)
{
foreach ( var entry in algorithms )
if ( entry.Value.IsMatch(text) )
return entry.Key;
return null;
}
/// <summary>
/// Decode <paramref name="text"/> and returns actual value in C# object.
/// </summary>
/// <param name="node">Node to be decoded.</param>
/// <param name="obj">Decoded value.</param>
/// <returns>True if decoded successfully.</returns>
public bool Decode(YamlScalar node, out object obj)
{
obj = null;
if ( node.Tag == null || node.Value == null )
return false;
var tag= YamlNode.ExpandTag(node.Tag);
if ( !types.ContainsKey(tag) )
return false;
foreach ( var rule in types[tag] ) {
var m = rule.Pattern.Match(node.Value);
if ( m.Success ) {
obj = rule.Decode(m);
return true;
}
}
return false;
}
public bool Encode(object obj, out YamlScalar node)
{
node = null;
YamlTagResolutionRule rule;
if ( !TypeToRule.TryGetValue(obj.GetType(), out rule) )
return false;
node = new YamlScalar(rule.Tag, rule.Encode(obj));
return true;
}
}
internal abstract class YamlTagResolutionRule
{
public string Tag { get; protected set; }
public Regex Pattern { get; protected set; }
public string PatternSource { get; protected set; }
public abstract object Decode(Match m);
public abstract string Encode(object obj);
public abstract Type GetTypeOfValue();
public abstract bool HasEncoder();
public bool IsMatch(string value) { return Pattern.IsMatch(value); }
}
internal class YamlTagResolutionRule<T>: YamlTagResolutionRule
{
public YamlTagResolutionRule(string tag, string regex, Func<Match, T> decoder, Func<T, string> encoder)
{
Tag = YamlNode.ExpandTag(tag);
PatternSource = regex;
Pattern = new Regex("^(?:" + regex + ")$");
Decoder = decoder;
Encoder = encoder;
}
private Func<Match, T> Decoder;
private Func<T, string> Encoder;
public override object Decode(Match m)
{
return Decoder(m);
}
public override string Encode(object obj)
{
return Encoder((T)obj);
}
public override Type GetTypeOfValue()
{
return typeof(T);
}
public override bool HasEncoder()
{
return Encoder != null;
}
}
}

View File

@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace System.Yaml
{
/// <summary>
/// Validates a text as a global tag in YAML.
///
/// <a href="http://www.faqs.org/rfcs/rfc4151.html">RFC4151 - The 'tag' URI Scheme</a>>
/// </summary>
internal class YamlTagValidator: Parser<YamlTagValidator.Status>
{
/// <summary>
/// Not used in this parser
/// </summary>
public struct Status { }
public static YamlTagValidator Default
{
get { return default_; }
}
static YamlTagValidator default_ = new YamlTagValidator();
/// <summary>
/// Validates a text as a global tag in YAML.
/// </summary>
/// <param name="tag">A candidate for a global tag in YAML.</param>
/// <returns>True if <paramref name="tag"/> is a valid global tag.</returns>
public bool IsValid(string tag)
{
text = tag;
p = 0;
return TagUri();
}
private bool TagUri()
{
return
Accept("tag:") &&
taggingEntity() &&
Accept(':') &&
specific() &&
Optional(
Accept('#') &&
fragment()
) &&
EndOfString();
}
private bool taggingEntity()
{
return
RewindUnless(() =>
authorityName() &&
Accept(',') &&
date()
);
}
private bool authorityName()
{
return
emailAddress() ||
DNSname();
}
private bool DNSname()
{
return
DNScomp() &&
Repeat(() =>
Accept('.') &&
DNScomp()
);
}
private bool DNScomp()
{
return RewindUnless(() =>
OneAndRepeat(alphaNum) &&
Repeat(() => RewindUnless(() =>
Accept('-') &&
OneAndRepeat(alphaNum)
))
);
}
private bool alphaNum()
{
return
Accept(alphaNumCharset);
}
Func<char, bool> alphaNumCharset = Charset(c =>
c < 0x100 && (
( '0' <= c && c <= '9' ) ||
( 'A' <= c && c <= 'Z' ) ||
( 'a' <= c && c <= 'z' )
)
);
private bool emailAddress()
{
return RewindUnless(() =>
OneAndRepeat(() => alphaNum() || Accept('-') || Accept('.') || Accept('_')) &&
Accept('@') &&
DNSname()
);
}
private bool date()
{
return
Accept(dateRegex);
}
Regex dateRegex = new Regex(@"(19[89][0-9]|20[0-4][0-9])(-(0[1-9]|1[0-2])(-(0[1-9]|[12][0-9]|3[01]))?)?");
private bool num()
{
return
Accept(numCharset);
}
Func<char, bool> numCharset = Charset(c =>
c < 0x100 &&
( '0' <= c && c <= '9' )
);
private bool specific()
{
return
Repeat(() => pchar() || Accept('/') || Accept('?'));
}
private bool fragment()
{
return
Repeat(() => pchar() || Accept('/') || Accept('?'));
}
private bool EndOfString()
{
return text.Length == p;
}
bool pchar()
{
return
Accept(pcharCharsetSub) ||
RewindUnless(() =>
Accept('%') &&
hexDig() &&
hexDig()
);
}
Func<char, bool> pcharCharsetSub = Charset(c =>
c < 0x100 && (
( '0' <= c && c <= '9' ) ||
( 'A' <= c && c <= 'Z' ) ||
( 'a' <= c && c <= 'z' ) ||
"-._~!$&'()*+,;=:@".Contains(c)
)
);
bool hexDig()
{
return Accept(hexDigCharset);
}
Func<char, bool> hexDigCharset = Charset(c =>
c < 0x100 && (
( '0' <= c && c <= '9' ) ||
( 'A' <= c && c <= 'F' ) ||
( 'a' <= c && c <= 'f' )
)
);
}
}

View File

@ -8,6 +8,7 @@ using winPEAS.Helpers;
using winPEAS.Helpers.AppLocker;
using winPEAS.Helpers.Registry;
using winPEAS.Helpers.Search;
using winPEAS.Helpers.YamlConfig;
using winPEAS.Info.UserInfo;
namespace winPEAS.Checks
@ -35,6 +36,7 @@ namespace winPEAS.Checks
public static string PaintDisabledUsersNoAdministrator = "";
//static string paint_lockoutUsers = "";
public static string PaintAdminUsers = "";
public static YamlConfig YamlConfig;
private static List<SystemCheck> _systemChecks;
private static readonly HashSet<string> _systemCheckSelectedKeysHashSet = new HashSet<string>();
@ -78,6 +80,7 @@ namespace winPEAS.Checks
new SystemCheck("windowscreds", new WindowsCreds()),
new SystemCheck("browserinfo", new BrowserInfo()),
new SystemCheck("filesinfo", new FilesInfo()),
new SystemCheck("fileAnalysis", new FileAnalysis())
};
var systemCheckAllKeys = new HashSet<string>(_systemChecks.Select(i => i.Key));
@ -188,6 +191,8 @@ namespace winPEAS.Checks
RunChecks(isAllChecks, wait);
SearchHelper.CleanLists();
Beaprint.PrintMarketingBanner();
}, IsDebug, "Total time");
@ -225,9 +230,20 @@ namespace winPEAS.Checks
private static void CreateDynamicLists()
{
Beaprint.GrayPrint(" Creating Dynamic lists, this could take a while, please wait...");
try
{
Beaprint.GrayPrint(" - Loading YAML definitions file...");
YamlConfig = YamlConfigHelper.GetWindowsSearchConfig();
}
catch (Exception ex)
{
Beaprint.GrayPrint("Error while getting AD info: " + ex);
}
try
{
Beaprint.GrayPrint(" Creating Dynamic lists, this could take a while, please wait...");
Beaprint.GrayPrint(" - Checking if domain...");
CurrentAdDomainName = DomainHelper.IsDomainJoined();
IsPartOfDomain = !string.IsNullOrEmpty(CurrentAdDomainName);

View File

@ -0,0 +1,296 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using winPEAS.Helpers;
using winPEAS.Helpers.Search;
using static winPEAS.Helpers.YamlConfig.YamlConfig.SearchParameters;
namespace winPEAS.Checks
{
internal class FileAnalysis : ISystemCheck
{
private const int ListFileLimit = 70;
public void PrintInfo(bool isDebug)
{
Beaprint.GreatPrint("File Analysis");
new List<Action>
{
PrintYAMLSearchFiles
}.ForEach(action => CheckRunner.Run(action, isDebug));
}
private static List<CustomFileInfo> InitializeFileSearch()
{
var files = new List<CustomFileInfo>();
var systemDrive = $"{SearchHelper.SystemDrive}\\";
List<string> directories = new List<string>()
{
@$"{systemDrive}inetpub",
@$"{systemDrive}usr\etc\snmp",
@$"{systemDrive}windows\temp",
@$"{systemDrive}xampp",
};
List<string> wildcardDirectories = new List<string>()
{
"apache*",
"tomcat*",
};
foreach (var wildcardDirectory in wildcardDirectories)
{
directories.AddRange(Directory.GetDirectories(systemDrive, wildcardDirectory, SearchOption.TopDirectoryOnly));
}
foreach (var directory in directories)
{
files.AddRange(SearchHelper.GetFilesFast(directory, "*", isFoldersIncluded: true));
}
files.AddRange(SearchHelper.RootDirUsers);
// files.AddRange(SearchHelper.RootDirCurrentUser); // not needed, it's contained within RootDirUsers
files.AddRange(SearchHelper.DocumentsAndSettings);
files.AddRange(SearchHelper.GroupPolicyHistory); // TODO maybe not needed here
files.AddRange(SearchHelper.ProgramFiles);
files.AddRange(SearchHelper.ProgramFilesX86);
return files;
}
private static bool Search(List<CustomFileInfo> files, string fileName, FileSettings fileSettings, ref int resultsCount)
{
bool isRegexSearch = fileName.Contains("*");
string pattern = string.Empty;
if (isRegexSearch)
{
pattern = GetRegexpFromString(fileName);
}
foreach (var file in files)
{
bool isFileFound;
if (isRegexSearch)
{
isFileFound = Regex.IsMatch(file.Filename, pattern, RegexOptions.IgnoreCase);
}
else
{
isFileFound = file.Filename.ToLower() == fileName;
}
if (isFileFound)
{
// there are no inner sections
if (fileSettings.files == null)
{
var isProcessed = ProcessResult(file, fileSettings, ref resultsCount);
if (!isProcessed)
{
return true;
}
}
// there are inner sections
else
{
foreach (var innerFileToSearch in fileSettings.files)
{
// search for inner files/folders by inner file/folder name
var innerFiles = SearchHelper.GetFilesFast(file.FullPath, innerFileToSearch.name, isFoldersIncluded: true);
foreach (var innerFile in innerFiles)
{
// process inner file/folder
var isProcessed = ProcessResult(innerFile, innerFileToSearch.value, ref resultsCount);
if (!isProcessed)
{
return true;
}
}
}
}
}
}
return false;
}
private static void PrintYAMLSearchFiles()
{
try
{
var files = InitializeFileSearch();
var folders = files.Where(f => f.IsDirectory).ToList();
var config = Checks.YamlConfig;
var defaults = config.defaults;
var searchItems = config.search.Where(i => i.value.config.auto_check &&
(i.value.disable == null || !i.value.disable.Contains("winpeas")));
foreach (var searchItem in searchItems)
{
var searchName = searchItem.name;
var value = searchItem.value;
var searchConfig = value.config;
CheckRunner.Run(() =>
{
Beaprint.MainPrint($"Analyzing {searchName} Files (limit {ListFileLimit})");
int resultsCount = 0;
bool isSearchFinished = false;
foreach (var file in value.files)
{
var fileName = file.name.ToLower();
var fileSettings = file.value;
var itemsToSearch = fileSettings.type == "f" ? files : folders;
isSearchFinished = Search(itemsToSearch, fileName, fileSettings, ref resultsCount);
if (isSearchFinished)
{
break;
}
}
}, Checks.IsDebug);
}
}
catch (Exception e)
{
}
}
private static string GetRegexpFromString(string str)
{
// we need to update the regexp to work here
// . -> \.
// * -> .*
// add $ at the end to avoid false positives
var pattern = str.Replace(".", @"\.")
.Replace("*", @".*");
pattern = $"{pattern}$";
return pattern;
}
private static bool ProcessResult(
CustomFileInfo fileInfo,
Helpers.YamlConfig.YamlConfig.SearchParameters.FileSettings fileSettings,
ref int resultsCount)
{
// print depending on the options here
resultsCount++;
if (resultsCount > ListFileLimit) return false;
if (fileSettings.type == "f")
{
if ((bool)fileSettings.just_list_file)
{
Beaprint.BadPrint($" {fileInfo.FullPath}");
}
else
{
GrepResult(fileInfo, fileSettings);
}
}
else if (fileSettings.type == "d")
{
// just list the directory
if ((bool)fileSettings.just_list_file)
{
string[] files = Directory.GetFiles(fileInfo.FullPath, "*", SearchOption.TopDirectoryOnly);
foreach (var file in files)
{
Beaprint.BadPrint($" {file}");
}
}
else
{
// should not happen
}
}
return true;
}
private static void GrepResult(CustomFileInfo fileInfo, FileSettings fileSettings)
{
Beaprint.NoColorPrint($" '{fileInfo.FullPath}' - content:");
var fileContent = File.ReadLines(fileInfo.FullPath);
var colors = new Dictionary<string, string>();
if ((bool)fileSettings.only_bad_lines)
{
colors.Add(fileSettings.bad_regex, Beaprint.ansi_color_bad);
fileContent = fileContent.Where(l => Regex.IsMatch(l, fileSettings.bad_regex, RegexOptions.IgnoreCase));
}
else
{
string lineGrep = null;
if ((bool)fileSettings.remove_empty_lines)
{
fileContent = fileContent.Where(l => !string.IsNullOrWhiteSpace(l));
}
if (!string.IsNullOrWhiteSpace(fileSettings.remove_regex))
{
var pattern = GetRegexpFromString(fileSettings.remove_regex);
fileContent = fileContent.Where(l => !Regex.IsMatch(l, pattern, RegexOptions.IgnoreCase));
}
if (!string.IsNullOrWhiteSpace(fileSettings.good_regex))
{
colors.Add(fileSettings.good_regex, Beaprint.ansi_color_good);
}
if (!string.IsNullOrWhiteSpace(fileSettings.bad_regex))
{
colors.Add(fileSettings.bad_regex, Beaprint.ansi_color_bad);
}
if (!string.IsNullOrWhiteSpace(fileSettings.line_grep))
{
lineGrep = SanitizeLineGrep(fileSettings.line_grep);
}
fileContent = fileContent.Where(line => (!string.IsNullOrWhiteSpace(fileSettings.good_regex) && Regex.IsMatch(line, fileSettings.good_regex, RegexOptions.IgnoreCase)) ||
(!string.IsNullOrWhiteSpace(fileSettings.bad_regex) && Regex.IsMatch(line, fileSettings.bad_regex, RegexOptions.IgnoreCase)) ||
(!string.IsNullOrWhiteSpace(lineGrep) && Regex.IsMatch(line, lineGrep, RegexOptions.IgnoreCase)));
}
var content = string.Join(Environment.NewLine, fileContent);
Beaprint.AnsiPrint(content, colors);
Console.WriteLine();
}
private static string SanitizeLineGrep(string lineGrep)
{
// sanitize the string, e.g.
// '-i -a -o "description.*" | sort | uniq'
// - remove everything except from "description.*"
Regex regex = new Regex("\"([^\"]+)\"");
Match match = regex.Match(lineGrep);
if (match.Success)
{
var group = match.Groups[1];
return group.Value;
}
return null;
}
}
}

View File

@ -142,10 +142,8 @@ namespace winPEAS.Checks
PrintHiddenFilesAndFolders,
PrintOtherUsersInterestingFiles,
PrintExecutablesInNonDefaultFoldersWithWritePermissions,
PrintWSLDistributions,
PrintWSLDistributions
}.ForEach(action => CheckRunner.Run(action, isDebug));
SearchHelper.CleanLists();
}
void PrintCloudCreds()

View File

@ -2,16 +2,17 @@
{
internal class CustomFileInfo
{
public string Filename { get; set; }
public string Extension { get; set; }
public string FullPath { get; set; }
public bool IsDirectory { get { return string.IsNullOrEmpty(Filename); } }
public string Filename { get; }
public string Extension { get; }
public string FullPath { get; }
public bool IsDirectory { get; }
public CustomFileInfo(string filename, string extension, string fullPath)
public CustomFileInfo(string filename, string extension, string fullPath, bool isDirectory)
{
Filename = filename;
Extension = extension;
FullPath = fullPath;
IsDirectory = isDirectory;
}
}
}

View File

@ -9,94 +9,18 @@ namespace winPEAS.Helpers.Search
".cer",
".csr",
".der",
".ftpconfig",
".gpg",
".kdbx",
".ovpn",
".p12",
".pgp",
".rdg",
".git-credentials",
".gitconfig",
".htpasswd",
};
public static readonly HashSet<string> WhiteListExactfilenamesWithExtensions = new HashSet<string>()
{
"id_dsa",
"id_rsa",
"access.log",
"access_tokens.db",
"accesstokens.json",
"appcmd.exe",
"appevent.evt",
"azureprofile.json",
"bash.exe",
"datasources.xml",
"default.sav",
"docker-compose.yml",
"dockerfile",
"drives.xml",
"error.log",
"ffftp.ini",
"filezilla.xml",
"freesshdservice.ini",
"groups.xml",
"httpd.conf",
"https-xampp.conf",
"https.conf",
"iis6.log",
"index.dat",
"keepass.config",
"keepass.config.xml",
"my.cnf",
"my.ini",
"netsetup.log",
"ntds.dit",
"ntuser.dat",
"pagefile.sys",
"php.ini",
"printers.xml",
"protecteduserkey.bin",
"rdcman.settings",
"recentservers.xml",
"sam",
"scclient.exe",
"scheduledtasks.xml",
"secevent.evt",
"security",
"security.sav",
"server.xml",
"services.xml",
"setupinfo",
"setupinfo.bak",
"sitemanager.xml",
"sites.ini",
"software",
"software.sav",
"sysprep.inf",
"sysprep.xml",
"system",
"system.sav",
"tomcat-users.xml",
"unattend.txt",
"unattend.xml",
"unattended.xml",
"wcx_ftp.ini",
"winscp.ini",
"ws_ftp.ini",
"wsl.exe",
"known_hosts",
};
public static readonly IList<string> WhiteListRegexp = new List<string>()
{
".*_history\\.*",
"config.*\\.php$",
"vnc\\.*",
"elasticsearch\\.y*ml$",
"kibana\\.y*ml$",
"web.*\\.config$",
};
}
}

View File

@ -10,20 +10,20 @@ namespace winPEAS.Helpers.Search
{
static class SearchHelper
{
public static List<CustomFileInfo> RootDirUsers;
private static List<CustomFileInfo> RootDirCurrentUser;
public static List<CustomFileInfo> ProgramFiles;
public static List<CustomFileInfo> ProgramFilesX86;
public static List<CustomFileInfo> DocumentsAndSettings;
private static List<CustomFileInfo> GroupPolicyHistory;
public static List<CustomFileInfo> RootDirUsers = new List<CustomFileInfo>();
public static List<CustomFileInfo> RootDirCurrentUser = new List<CustomFileInfo>();
public static List<CustomFileInfo> ProgramFiles = new List<CustomFileInfo>();
public static List<CustomFileInfo> ProgramFilesX86 = new List<CustomFileInfo>();
public static List<CustomFileInfo> DocumentsAndSettings = new List<CustomFileInfo>();
public static List<CustomFileInfo> GroupPolicyHistory = new List<CustomFileInfo>();
private static string SystemDrive = Environment.GetEnvironmentVariable("SystemDrive");
public static string SystemDrive = Environment.GetEnvironmentVariable("SystemDrive");
private static string GlobalPattern = "*";
public static List<CustomFileInfo> GetFilesFast(string folder, string pattern = "*", HashSet<string> excludedDirs = null, bool isFoldersIncluded = false)
{
ConcurrentBag<CustomFileInfo> files = new ConcurrentBag<CustomFileInfo>();
IEnumerable<DirectoryInfo> startDirs = GetStartDirectories(folder, files, pattern);
IEnumerable<DirectoryInfo> startDirs = GetStartDirectories(folder, files, pattern, isFoldersIncluded);
IList<DirectoryInfo> startDirsExcluded = new List<DirectoryInfo>();
if (excludedDirs != null)
@ -52,7 +52,7 @@ namespace winPEAS.Helpers.Search
{
GetFiles(dir.FullName, pattern).ForEach(
(f) =>
files.Add(new CustomFileInfo(f.Name, f.Extension, f.FullName))
files.Add(new CustomFileInfo(f.Name, f.Extension, f.FullName, false))
);
});
});
@ -132,13 +132,13 @@ namespace winPEAS.Helpers.Search
{
foreach (var directory in directories)
{
files.Add(new CustomFileInfo(null, null, directory.FullName));
files.Add(new CustomFileInfo(directory.Name, null, directory.FullName, true));
}
}
foreach (var f in dirInfo.GetFiles(pattern))
{
files.Add(new CustomFileInfo(f.Name, f.Extension, f.FullName));
files.Add(new CustomFileInfo(f.Name, f.Extension, f.FullName, false));
}
if (directories.Length > 1) return new List<DirectoryInfo>(directories);

View File

@ -0,0 +1,82 @@
using static winPEAS.Helpers.YamlConfig.YamlConfig.SearchParameters;
namespace winPEAS.Helpers.YamlConfig
{
public class YamlConfig
{
public class FileParam
{
public string name { get; set; }
public FileSettings value { get; set; }
}
public class SearchParameters
{
public class FileSettings
{
public string bad_regex { get; set; }
// public string check_extra_path { get; set; } // not used in Winpeas
public string good_regex { get; set; }
public bool? just_list_file { get; set; }
public string line_grep { get; set; }
public bool? only_bad_lines { get; set; }
public bool? remove_empty_lines { get; set; }
// public string remove_path { get; set; } // not used in Winpeas
public string remove_regex { get; set; }
// public string[] search_in { get; set; } // not used in Winpeas
public string type { get; set; }
public FileParam[] files { get; set; }
}
public class FileParameters
{
public string file { get; set; }
public FileSettings options { get; set; }
}
public class Config
{
public bool auto_check { get; set; }
}
public Config config { get; set; }
public string[] disable { get; set; } // disabled scripts - linpeas/winpeas
public FileParam[] files { get; set; }
}
public class SearchParams
{
public string name { get; set; }
public SearchParameters value { get; set; }
}
public class Defaults
{
public bool auto_check { get; set; }
public string bad_regex { get; set; }
//public string check_extra_path { get; set; } not used in winpeas
public string good_regex { get; set; }
public bool just_list_file { get; set; }
public string line_grep { get; set; }
public bool only_bad_lines { get; set; }
public bool remove_empty_lines { get; set; }
public string remove_path { get; set; }
public string remove_regex { get; set; }
public string[] search_in { get; set; }
public string type { get; set; }
}
public class Variable
{
public string name { get; set; }
public string value { get; set; }
}
public SearchParams[] search { get; set; }
public Defaults defaults { get; set; }
public Variable[] variables { get; set; }
}
}

View File

@ -0,0 +1,110 @@
using System.Collections.Generic;
using System.Yaml.Serialization;
using System.IO;
using System.Reflection;
using System.Linq;
using static winPEAS.Helpers.YamlConfig.YamlConfig;
namespace winPEAS.Helpers.YamlConfig
{
internal class YamlConfigHelper
{
const string SENSITIVE_FILES = "sensitive_files.yaml";
public static YamlConfig GetWindowsSearchConfig()
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = assembly.GetManifestResourceNames().Where(i => i.EndsWith(SENSITIVE_FILES)).FirstOrDefault();
try
{
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
string configFileContent = reader.ReadToEnd();
YamlSerializer yamlSerializer = new YamlSerializer();
YamlConfig yamlConfig = (YamlConfig)yamlSerializer.Deserialize(configFileContent, typeof(YamlConfig))[0];
// update variables
foreach (var variable in yamlConfig.variables)
{
configFileContent = configFileContent.Replace($"${variable.name}", variable.value);
}
// deserialize again
yamlConfig = (YamlConfig)yamlSerializer.Deserialize(configFileContent, typeof(YamlConfig))[0];
// check
if (yamlConfig.defaults == null || yamlConfig.search == null || yamlConfig.search.Length == 0)
{
throw new System.Exception("No configuration was read");
}
// apply the defaults e.g. for filesearch
foreach (var searchItem in yamlConfig.search)
{
SetDefaultOptions(searchItem, yamlConfig.defaults);
}
return yamlConfig;
}
}
catch (System.Exception e)
{
Beaprint.PrintException($"An exception occured while parsing YAML configuration file: {e.Message}");
throw;
}
}
private static void SetDefaultOptions(SearchParams searchItem, Defaults defaults)
{
searchItem.value.config.auto_check = GetValueOrDefault(searchItem.value.config.auto_check, defaults.auto_check);
SetFileOptions(searchItem.value.files, defaults);
}
private static void SetFileOptions(FileParam[] fileParams, Defaults defaults)
{
foreach (var fileParam in fileParams)
{
var value = fileParam.value;
value.bad_regex = GetValueOrDefault(value.bad_regex, defaults.bad_regex);
value.good_regex = GetValueOrDefault(value.good_regex, defaults.good_regex);
value.just_list_file = GetValueOrDefault(value.just_list_file, defaults.just_list_file);
value.line_grep = GetValueOrDefault(value.line_grep, defaults.line_grep);
value.only_bad_lines = GetValueOrDefault(value.only_bad_lines, defaults.only_bad_lines);
value.remove_empty_lines = GetValueOrDefault(value.remove_empty_lines, defaults.remove_empty_lines);
value.remove_regex = GetValueOrDefault(value.remove_regex, defaults.remove_regex);
value.type = GetValueOrDefault(value.type, defaults.type).ToLower();
if (value.files != null)
{
SetFileOptions(value.files, defaults);
}
}
}
//private static WildcardOptions GetWildCardOptions(string str)
//{
// if (!str.Contains("*")) return WildcardOptions.None;
// if (str.StartsWith("*")) return WildcardOptions.StartsWith;
// if (str.EndsWith("*")) return WildcardOptions.EndsWith;
// return WildcardOptions.Middle;
//}
private static T GetValueOrDefault<T>(T val, T defaultValue)
{
return val == null ? defaultValue : val;
}
private static T GetValueOrDefault<T>(Dictionary<object, object> dict, string key, T defaultValue)
{
return dict.ContainsKey(key) ? (T)dict[key] : defaultValue;
}
}
}

View File

@ -371,8 +371,27 @@
<Compile Include="3rdParty\SQLite\src\walker_c.cs" />
<Compile Include="3rdParty\SQLite\src\where_c.cs" />
<Compile Include="3rdParty\SQLite\src\_Custom.cs" />
<Compile Include="3rdParty\YamlSerializer\EasyTypeConverter.cs" />
<Compile Include="3rdParty\YamlSerializer\ObjectExtensions.cs" />
<Compile Include="3rdParty\YamlSerializer\ObjectMemberAccessor.cs" />
<Compile Include="3rdParty\YamlSerializer\Parser.cs" />
<Compile Include="3rdParty\YamlSerializer\RehashableDictionary.cs" />
<Compile Include="3rdParty\YamlSerializer\TypeUtils.cs" />
<Compile Include="3rdParty\YamlSerializer\UriEncoding.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlAnchorDictionary.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlConstructor.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlDoubleQuoteEscaping.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlNode.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlParser.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlPresenter.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlRepresenter.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlSerializer.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlTagPrefixes.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlTagResolutionScheme.cs" />
<Compile Include="3rdParty\YamlSerializer\YamlTagValidator.cs" />
<Compile Include="Checks\ApplicationsInfo.cs" />
<Compile Include="Checks\BrowserInfo.cs" />
<Compile Include="Checks\FileAnalysis.cs" />
<Compile Include="Checks\FilesInfo.cs" />
<Compile Include="Checks\Globals.cs" />
<Compile Include="Checks\ISystemCheck.cs" />
@ -399,6 +418,8 @@
<Compile Include="Helpers\PermissionsHelper.cs" />
<Compile Include="Helpers\Search\LOLBAS.cs" />
<Compile Include="Helpers\Search\Patterns.cs" />
<Compile Include="Helpers\YamlConfig\YamlConfig.cs" />
<Compile Include="Helpers\YamlConfig\YamlConfigHelper.cs" />
<Compile Include="Info\ApplicationInfo\ApplicationInfoHelper.cs" />
<Compile Include="Info\ApplicationInfo\AutoRuns.cs" />
<Compile Include="Info\ApplicationInfo\DeviceDrivers.cs" />
@ -658,6 +679,7 @@
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\..\..\build_lists\sensitive_files.yaml" />
<EmbeddedResource Include="Properties\Resources.de.resx" />
<EmbeddedResource Include="Properties\Resources.es.resx" />
<EmbeddedResource Include="Properties\Resources.fr.resx" />
@ -670,6 +692,5 @@
<EmbeddedResource Include="Properties\Resources.ru.resx" />
<EmbeddedResource Include="Properties\Resources.zh-CN.resx" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>