1
0
mirror of https://github.com/twiglet/cs2j.git synced 2025-01-18 13:15:17 +01:00

Upgrade DirectoryHT to support variants at the leaves

This commit is contained in:
Kevin Glynn 2011-09-06 10:04:41 +02:00
parent 1001d5151f
commit f2a3c580da

View File

@ -6,6 +6,26 @@ using System;
using System.Text; using System.Text;
using System.Collections.Generic; using System.Collections.Generic;
/*
DirectoryHT is a dictionary, designed to model C#'s class name space.
Keys are dot separated strings (e.g. System.IO.TextWriter). The value stored has
a parameterized type (in CS2J we are storing TypeRepTemplates).
Each Key can point to a list of possible values, indexed by variant.
Valid variants are stored in the Alts property. The Alts are ordered by priority,
lowest to highest. We return the value with highest priority variant.
There is a default variant (with variant specified by the Default property). This is
always valid and has lowest ptiority.
Default Default (ahem!) is the empty string.
*/
namespace Twiglet.CS2J.Translator.Utils namespace Twiglet.CS2J.Translator.Utils
{ {
@ -13,18 +33,28 @@ namespace Twiglet.CS2J.Translator.Utils
public class DirectoryHT<TValue> : IDictionary<string, TValue> public class DirectoryHT<TValue> : IDictionary<string, TValue>
{ {
private DirectoryHT<TValue> _parent = null; private DirectoryHT<TValue> parent = null;
private Dictionary<string, TValue> leaves = new Dictionary<string, TValue>(); // Leaves are a dictionary from Variant to Entry
private Dictionary<string, Dictionary<string, TValue>> leaves = new Dictionary<string, Dictionary<string, TValue>>();
// Children point to sub directories.
private Dictionary<string, DirectoryHT<TValue>> children = new Dictionary<string, DirectoryHT<TValue>>(); private Dictionary<string, DirectoryHT<TValue>> children = new Dictionary<string, DirectoryHT<TValue>>();
// The enabled Variants, in priority order.
private IList<string> alts = new List<string>(); private IList<string> alts = new List<string>();
// Default Variant (always enabled)
private string _default = String.Empty;
public DirectoryHT(DirectoryHT<TValue> p) public DirectoryHT(DirectoryHT<TValue> p)
{ {
_parent = p; parent = p;
alts.Add(""); if (p != null)
{
Default = p.Default;
Alts = p.Alts;
}
} }
public DirectoryHT() public DirectoryHT()
@ -32,7 +62,7 @@ namespace Twiglet.CS2J.Translator.Utils
{ } { }
public Dictionary<string, TValue> Leaves public Dictionary<string, Dictionary<string, TValue>> Leaves
{ {
get { return leaves; } get { return leaves; }
} }
@ -40,14 +70,36 @@ namespace Twiglet.CS2J.Translator.Utils
// p is key to a sub directory // p is key to a sub directory
public DirectoryHT<TValue> Parent public DirectoryHT<TValue> Parent
{ {
get { return _parent; } get { return parent; }
} }
// When searching for A.B.C.D.E we will first search for each A.B.C.D.<alt>.E where <alt> comes from Alts // When looking for A.B.C.D.E We get a dictionary of variants. We will first look for each
// variant in Alts before falling back on the default (variant = Default)
// This allows to override the default translations where necessary // This allows to override the default translations where necessary
// Alts appearing later in the list have priority.
public IList<string> Alts public IList<string> Alts
{ {
get { return alts; } get { return alts; }
set
{
alts = value;
foreach (KeyValuePair<string, DirectoryHT<TValue>> child in children)
child.Value.Alts = value;
}
}
public string Default
{
get
{
return _default;
}
set
{
_default = value;
foreach (KeyValuePair<string,DirectoryHT<TValue>> child in children)
child.Value.Default = value;
}
} }
// p is key to a sub directory // p is key to a sub directory
@ -65,14 +117,49 @@ namespace Twiglet.CS2J.Translator.Utils
} }
} }
private bool hasLeaf(Dictionary<string,TValue> variants)
{
if (variants == null)
return false;
if (variants.ContainsKey(Default))
return true;
foreach (string alt in Alts)
{
if (variants.ContainsKey(alt))
return true;
}
return false;
}
private TValue getLeaf(Dictionary<string,TValue> variants)
{
return getLeaf(variants, default(TValue));
}
private TValue getLeaf(Dictionary<string,TValue> variants, TValue def)
{
if (variants == null)
return def;
for (int i = Alts.Count - 1; i >= 0; i--)
{
if (variants.ContainsKey(Alts[i]))
return variants[Alts[i]];
}
if (variants.ContainsKey(Default))
return variants[Default];
return def;
}
#region IDictionary Members #region IDictionary Members
public bool ContainsKey(string key) public bool ContainsKey(string key)
{ {
string[] components = key.Split(new char[] { '.' }, 2); string[] components = key.Split(new char[] { '.' }, 2);
if (components.Length == 1) if (components.Length == 1)
{ {
return leaves.ContainsKey(components[0]); return leaves.ContainsKey(components[0]) ? hasLeaf(leaves[components[0]]) : false;
} }
else else
{ {
@ -111,7 +198,10 @@ namespace Twiglet.CS2J.Translator.Utils
{ {
List<string> keys = new List<string>(); List<string> keys = new List<string>();
foreach (string k in leaves.Keys) foreach (string k in leaves.Keys)
keys.Add(k); {
if (hasLeaf(leaves[k]))
keys.Add(k);
}
foreach (KeyValuePair<string, DirectoryHT<TValue>> de in children) foreach (KeyValuePair<string, DirectoryHT<TValue>> de in children)
foreach (string k in de.Value.Keys) foreach (string k in de.Value.Keys)
keys.Add(de.Key + "." + k); keys.Add(de.Key + "." + k);
@ -128,7 +218,14 @@ namespace Twiglet.CS2J.Translator.Utils
} }
else else
{ {
return children[components[0]].Remove(components[1]); if (children.ContainsKey(components[0]))
{
return children[components[0]].Remove(components[1]);
}
else
{
return false;
}
} }
} }
@ -138,8 +235,13 @@ namespace Twiglet.CS2J.Translator.Utils
get get
{ {
List<TValue> vals = new List<TValue>(); List<TValue> vals = new List<TValue>();
foreach (TValue v in leaves.Values) foreach (Dictionary<string,TValue> variants in leaves.Values)
vals.Add(v); {
if (hasLeaf(variants))
{
vals.Add(getLeaf(variants));
}
}
foreach (KeyValuePair<string, DirectoryHT<TValue>> de in children) foreach (KeyValuePair<string, DirectoryHT<TValue>> de in children)
foreach (TValue v in de.Value.Values) foreach (TValue v in de.Value.Values)
vals.Add(v); vals.Add(v);
@ -155,9 +257,14 @@ namespace Twiglet.CS2J.Translator.Utils
string[] components = key.Split(new char[] { '.' }, 2); string[] components = key.Split(new char[] { '.' }, 2);
if (components.Length == 1) if (components.Length == 1)
{ {
TValue val = leaves[key]; if (leaves.ContainsKey(key) && hasLeaf(leaves[key]))
// keving: this isn't typesafe!: return (val != null ? val : children[components[0]]); {
return val; return getLeaf(leaves[key]);
}
else
{
throw new KeyNotFoundException(key);
}
} }
else else
{ {
@ -170,25 +277,27 @@ namespace Twiglet.CS2J.Translator.Utils
Add(key, value); Add(key, value);
} }
} }
public bool TryGetValue(string key, out TValue value) public bool TryGetValue(string key, out TValue value)
{ {
string[] components = key.Split(new char[] { '.' }, 2); string[] components = key.Split(new char[] { '.' }, 2);
if (components.Length == 1) if (components.Length == 1)
{ {
return leaves.TryGetValue(key, out value); if (leaves.ContainsKey(components[0]) && hasLeaf(leaves[components[0]]))
{
value = getLeaf(leaves[components[0]]);
return true;
}
} }
else else
{ {
if (children.ContainsKey(components[0])) if (children.ContainsKey(components[0]))
{ {
return children[components[0]].TryGetValue(components[1], out value); return children[components[0]].TryGetValue(components[1], out value);
}
else
{
value = default(TValue);
return false;
} }
} }
value = default(TValue);
return false;
} }
// search for name, given searchPath // search for name, given searchPath
@ -198,54 +307,28 @@ namespace Twiglet.CS2J.Translator.Utils
// This allows to override the default translations where necessary // This allows to override the default translations where necessary
public TValue Search(IList<string> searchPath, string name, TValue def) { public TValue Search(IList<string> searchPath, string name, TValue def) {
bool doneGlobal = false;
// First check against each element of the search path // First check against each element of the search path
if (searchPath != null) if (searchPath != null)
{ {
// check against each alt override in turn
foreach (string altIterator in Alts) {
string alt = altIterator.EndsWith(".") ? altIterator : altIterator + ".";
for (int i = searchPath.Count-1; i >= 0; i--) {
String ns = searchPath[i];
String fullName = (ns ?? "") + (String.IsNullOrEmpty(ns) ? "" : ".") + name;
// insert alt after last period
int lastPeriod = fullName.LastIndexOf('.')+1;
fullName = fullName.Substring(0,lastPeriod) + alt + fullName.Substring(lastPeriod);
// Console.WriteLine(fullName);
if (this.ContainsKey(fullName)) {
return this[fullName];
}
}
}
// Not in alts, check kosher
for (int i = searchPath.Count-1; i >= 0; i--) { for (int i = searchPath.Count-1; i >= 0; i--) {
String ns = searchPath[i]; String ns = searchPath[i];
if (String.IsNullOrEmpty(ns))
doneGlobal = true;
String fullName = (ns ?? "") + (String.IsNullOrEmpty(ns) ? "" : ".") + name; String fullName = (ns ?? "") + (String.IsNullOrEmpty(ns) ? "" : ".") + name;
// Console.WriteLine(fullName);
if (this.ContainsKey(fullName)) { if (this.ContainsKey(fullName)) {
return this[fullName]; return this[fullName];
} }
} }
} }
// Not in search path, check for fully qualified name
// Check if name is fully qualified if (!doneGlobal)
foreach (string altIterator in Alts) { {
string alt = altIterator.EndsWith(".") ? altIterator : altIterator + "."; if (this.ContainsKey(name)) {
// insert alt after last period return this[name];
int lastPeriod = name.LastIndexOf('.')+1;
string fullName = name.Substring(0,lastPeriod) + alt + name.Substring(lastPeriod);
// Console.WriteLine(fullName);
if (this.ContainsKey(fullName)) {
return this[fullName];
} }
} }
// Not found *anywhere*!
// Not in alt, check kosher
// Console.WriteLine(name);
if (this.ContainsKey(name)) {
return this[name];
}
return def; return def;
} }
@ -263,11 +346,6 @@ namespace Twiglet.CS2J.Translator.Utils
return Search(new List<string>(), name, def); return Search(new List<string>(), name, def);
} }
public void Add(KeyValuePair<string, TValue> item)
{
this.Add(item.Key, item.Value);
}
public bool Contains(KeyValuePair<string, TValue> item) public bool Contains(KeyValuePair<string, TValue> item)
{ {
TValue value; TValue value;
@ -290,18 +368,32 @@ namespace Twiglet.CS2J.Translator.Utils
Copy(this, array, arrayIndex); Copy(this, array, arrayIndex);
} }
public void Add(KeyValuePair<string, TValue> item)
{
Add(item.Key, item.Value);
}
public void Add(string key, TValue value) public void Add(string key, TValue value)
{
Add(key, value, Default);
}
public void Add(string key, TValue value, string variant)
{ {
string[] components = key.Split(new char[] { '.' }, 2); string[] components = key.Split(new char[] { '.' }, 2);
if (components.Length == 1) if (components.Length == 1)
{ {
leaves[components[0]] = value; if (!leaves.ContainsKey(components[0]))
{
leaves[components[0]] = new Dictionary<string, TValue>();
}
leaves[components[0]][variant] = value;
} }
else else
{ {
if (!children.ContainsKey(components[0])) if (!children.ContainsKey(components[0]))
children[components[0]] = new DirectoryHT<TValue>(this); children[components[0]] = new DirectoryHT<TValue>(this);
children[components[0]].Add(components[1], value); children[components[0]].Add(components[1], value, variant);
} }
} }
@ -314,9 +406,12 @@ namespace Twiglet.CS2J.Translator.Utils
yield return new KeyValuePair<string, TValue>(de.Key + "." + cur.Key, cur.Value); yield return new KeyValuePair<string, TValue>(de.Key + "." + cur.Key, cur.Value);
} }
} }
foreach (KeyValuePair<string, TValue> de in leaves) foreach (KeyValuePair<string, Dictionary<string, TValue>> de in leaves)
{ {
yield return new KeyValuePair<string, TValue>(de.Key, de.Value); if (hasLeaf(de.Value))
{
yield return new KeyValuePair<string, TValue>(de.Key, getLeaf(de.Value));
}
} }
} }