I am creating an inf file parser (windows drivers, in theory similar to infs, in reality quite different).
I have a parent InfFile class which contains the structured but unparsed data in the form of Sections and Keys (I call it unparsed because it's not been converted into anything more useful than just Sections and Keys, as oppose to the rest of the data that comes after), but also gives access to some parsed data, such as SupportedOperatingSystems, Folders, Folder.Files etc.
The problem I'm facing is that in inf file can reference 4 types of Folders: default, X86, Ia64 and Amd64. So, it felt logical to me to try to shift all of the folder related code to a InfFolders class, with properties Default, X86, Ia64 and Amd64 (I can't simply merge them).
However, in order to figure out which folders are referenced in it, I need access to both the Sections and Keys properties of the InfFile class. That means either:
my InfFolders class contains a reference to its parent (which I think is probably not the best solution),
InfFile parses the data first and then passes it to the InfFolders as part of the constructor, but that means all of the Folder logic now lives in the InfFile class again,
I pass the needed Sections and Keys to InfFolders as part of the constructor, and have the logic to sort them into usable objects in there.
What is the best way to do this and what is best practice in situations like this?
An idea of what the InfFolders class looks like is this:
public class InfFolders
{
public IReadOnlyCollection<InfSourceFolder> Default()
{
return GetFolders("SourceDiskNames").ToList().AsReadOnly();
}
public IReadOnlyCollection<InfSourceFolder> X86()
{
return GetFolders("SourceDiskNames.X86").ToList().AsReadOnly();
}
public IReadOnlyCollection<InfSourceFolder> Ia64()
{
return GetFolders("SourceDisksNames.Ia64").ToList().AsReadOnly();
}
public IReadOnlyCollection<InfSourceFolder> Amd64()
{
return GetFolders("SourceDisksNames.Amd64").ToList().AsReadOnly();
}
private IEnumerable<InfSourceFolder> GetFolders(string sectionName)
{
return <InfFile reference>[sectionName]?.Keys?.Select(
x =>
new InfSourceFolder(Convert.ToInt32(x.Name),
new FileInfo(<InfFile reference>.FilePath).DirectoryName + x.Values.ElementAtOrDefault(3)));
}
}
As you can see in the method GetFolders() there are 2 places where a reference to the parent is needed.
I could shift all of this code to InfClass and remove the InfFolders class altogether, but don't think that makes for a very good model either. The way I see it is there 4 properties all relate to the same thing, so they should be grouped together.
Related
I have made a simple localization of messages. All messages are stored in the static class Lng
public static partial class Lng
{
public static readonly string AppName = "My application";
public static class Category1
{
public static readonly string ConfirmDelete = "Are you sure want to delete?";
}
}
In code usage is as simple as referencing fields
MessageBox.Show(Lng.Category1.ConfirmDelete, ...
Then there is a manager, which does following:
language selection
load corresponding translation
updating fields via reflection
export currently selected language on application exit for an update (in case if default language is selected - to create first translation for any other language)
It's irrelevant of how language files looks likes, but here is a reflection part
TranslateLng("Lng.", typeof(Lng));
...
private static void TranslateLng(string parent, Type type)
{
foreach (Type nested in type.GetNestedTypes())
{
string child = string.Format("{0}{1}.", parent, nested.Name);
TranslateLng(child, nested);
foreach (var field in nested.GetFields())
{
string key = child + field.Name;
DefaultAdd(key, (string)field.GetValue(null)); // store value in default language dictionary (if not created yet)
field.SetValue(null, GetValue(key)); // get value for currently selected language
}
}
This system has one problem: all messages are defined in one class, which required manual management (deleting and updating messages when updating code which uses them).
And I was thinking to change manager to register strings dynamically and simplify usage to something like
MessageBox.Show(Lng.Text("Are you sure want to delete?"), ...
So that text is defined right where it used, duplicated text can be handled by manager and so on.
There are however 2 problems:
I will need a complete list of all messages at the end of application run to export complete list of messages (for currently selected language). What if some of Lng.Text() are never called at that run? Is there a way to register them as they are used in code (compile time?)? So that all calls will be registered somehow, even if peace of code is never used.
How to generate key. I could use CallerMemberName, but right key are more useful, as they are telling exact purpose. To example, Lng.Configuration.Appearance.CaptionText. I could call Lng.Text(key, message), but then I have to manage keys, ensure in their uniqueness, which doesn't appeals me.
I recently worked on a project with internationaliztion and we used Resources in con junction with the Sisulizer program with great success. Having the resources solves your key problem as you manually enter the key when you extract the resources. You also get great support from Resharper which makes the whole process a breeze.
Sisulizer is then used to extract resources as well as strings hard-coded in our Win Forms and WPF classes. It can export a CSV which you can give your translators and it also supports pseudo translation, which makes testing such apps very easy as well.
Is it possible to retain the logical layout of the project file?
If I have 2 embedded resources laid out like the following
When I call Assembly.GetManifestResourceNames(), the two of them get named:
MyLibrary._MyItems.SubItems.SubItem1.xml
MyLibrary._MyItems.SubItems.SubItem2.xml
However, this gives me no real insight into how they're logically ordered in my project. For all I know, they could both be in the same directory.
I worry that this may not be possible, because if I name the files like so, my app will not even compile:
I want to be able to distinguish between
MyLibrary._MyItems\SubItems\SubItem1.xml
and
MyLibrary._MyItems\SubItems.SubItem2.xml
Similar question, but less detail than I am looking for
Exact duplicate, no traction
One simple solution would be to come up with a convention to indicate where a file name starts. You could then "safely" assume the rest of the name indicates the folder structure.
However, since these are embedded resources you could create a T4 script (or some similar piece of code) that runs prior to compilation that examines the folder structure and builds a class detailing that structure.
Here is an example of what the generated class could look like.
public static class MyResources
{
private readonly Dictionary<String, String> ResourceNameToDirPathMappingSetSource = new Dictionary<String, String>();
static MyResources()
{
ResourceNameToPathMappingSetSource.Add("MyLibrary._MyItems.SubItems.SubItem1.xml", #"MyLibrary._MyItems\SubItems");
ResourceNameToPathMappingSetSource.Add("MyLibrary._MyItems.SubItems.SubItem2.xml", #"MyLibrary._MyItems");
}
public static IReadOnlyDictionary<String, String> ResourceNameToDirPathMappingSet
{
get
{
return ResourceNameToDirPathMappingSetSource;
}
}
}
My assumption being that you want to take the resource name and determine the folder path it was originally in. The dictionary of course could contain whatever values (read: custom class) you need to map to.
Our system complexity has risen to the point that we need to make permission names tied to the client from the database more specific. In the client, permissions are referenced from a static class since a lot of client functionality is dependent on the permissions each user has and the roles have a ton of variety. I've referenced this post as an example, but I'm looking for a more specific use case. Take for instance this reference, where PermissionAlpha would be a const string:
return HasPermission(PermissionNames.PermissionAlpha);
Which is great, except now that things are growing more complex the classes are being structured like this:
public static class PermissionNames
{
public static class PermissionAlpha
{
public const string SubPermission = "PermissionAlpha.SubPermission";
}
}
I'm trying to find an easy way to reference PermissionAlpha in this new setup that will act similar to the first declaration above. Would the only way to do this be to resort to pulling the value of the class name like in the example below? I'm trying to keep all the names in one place that can be reference anywhere in the application.
public static class PermissionAlpha
{
public static string Name { get { return typeof(PermissionAlpha).Name; } }
}
** Edit ** - Added missing permission name.
Maybe this would be too big of a change for you with the size of your project, but we have all of our business objects split into partial classes. One is for manual changes and one gets generated. During code-generation, we write the permission keys into the generated side of the partial classes from our "single source of truth". We're using a set of classes as our source of truth and CodeDom to generate, but you could also use a database as your source and use T4, CodeSmith, or others to generate.
Why not create reflectable attribute(s) on the classes in question? That way one can add all the extra information required. I provide a way of divining attributes on my blog article entitled:
C# Using Extended Attribute Information on Objects
HTH
I need to model in memory a collection web files, but that relationships between them. That is file A (e.g. html) may have a link to file B (e.g. css) and file C (e.g. javascript). Also file D may also require file B. If I wanted to delete file A I would need to make sure any files it uses (e.g. file B) is not also being used by another file (e.g. file D). Perhaps something like:
List<WebFile> list_of_webfiles
public class WebFile
- string url
- bool parentFile
public class FileRelationship
- private WebFile parentWebFile;
- private WebFile childWebFile;
QUESTION - What would be the best way to model this in C#? (e.g. which collection type & how to model)
Note - it has to be modeled in memory (no database), and I need to be able to serialize to XML too to save. An example of what I mean would be something that looked like this...
XmlSerializer serializer = new XmlSerializer(typeof(List<WebFile>));
TextWriter textWriter = new StreamWriter(CONFIG_FILE_PATH);
serializer.Serialize(textWriter, list_of_webfiles);
textWriter.Close();
Thanks
This seems to imply a hierarchical 'tree' relationsihp where you may have
Class WebFile:
- URL : string
- Parent : WebFile
- Children : WebFile[] (could be a list depending on the need)
Then somewhere you have a
List<WebFile> webFiles;
This approach makes it easy to traverse the tree of webfiles and find the related ones, but harder to list all the files themselves.
Alternatively, you could store the list of files and relationships seperately
Class WebFile
- URL : string
Class WebFileRelationship
- Parent : WebFile
- Child : WebFile
And you have 2 containers
List<WebFile> webFiles;
List<WebFileRelationship> relationships;
This approach makes it easy to list all the relationships or all the files, but hard to determine the individual relationships.
It all depends on your application, do you need more information about the individual files or the relationships?
The fact that you have duplicates (in terms of multiple files requiring B) means that it would be a pain to use the most obvious "requires" structure as a tree, since that would involve nesting B multiple times (from different parents). A few options:
keep the object-references in the object model, but only list the name (or some other reference) in the file; relatively simple to do, but requires fixups after deserialization
only list the name (or some other reference) in the relationship, and mirror this in the object model - i.e. "file.Parent" is a key, not another object
have the full object model, and use a graph serializer, such as DataContractSerializer with preserve-object-references enabled
I would probably choose between the last two; the last has "not very pretty" xml, but is relatively simple to implement. But I'd be tempted to just use the middle option, and have just the key references in the object model, i.e.
[XmlType("file"), XmlRoot("file")]
public class File {
[XmlAttribute("name")]
public string Name {get;set;}
[XmlElement("ref")]
public List<string> References {get;set;}
public File() {References = new List<string>();}
}
maybe not pure OO, but simple to do. Also - avoid the need to duplicate data; if you store it just like the above, you can always scan to see "what uses this file" (with some indexing if you need). But trying to maintain relationships in both directions (i.e. "UsedBy") is a nightmare.
So if I have a method of parsing a text file and returning a list of a list of key value pairs, and want to create objects from the kvps returned (each list of kvps represents a different object), what would be the best method?
The first method that pops into mind is pretty simple, just keep a list of keywords:
private const string NAME = "name";
private const string PREFIX = "prefix";
and check against the keys I get for the constants I want, defined above. This is a fairly core piece of the project I'm working on though, so I want to do it well; does anyone have any more robust suggestions (not saying there's anything inherently un-robust about the above method - I'm just asking around)?
Edit:
More details have been asked for. I'm working on a little game in my spare time, and I am building up the game world with configuration files. There are four - one defines all creatures, another defines all areas (and their locations in a map), another all objects, and a final one defines various configuration options and things that don't fit else where. With the first three configuration files, I will be creating objects based on the content of the files - it will be quite text-heavy, so there will be a lot of strings, things like names, plurals, prefixes - that sort of thing. The configuration values are all like so:
-
key: value
key: value
-
key: value
key: value
-
Where the '-' line denotes a new section/object.
Take a deep look at the XmlSerializer. Even if you are constrained to not use XML on-disk, you might want to copy some of its features. This could then look like this:
public class DataObject {
[Column("name")]
public string Name { get; set; }
[Column("prefix")]
public string Prefix { get; set; }
}
Be careful though to include some kind of format version in your files, or you will be in hell's kitchen come the next format change.
Making a lot of unwarranted assumptions, I think that the best approach would be to create a Factory that will receive the list of key value pairs and return the proper object or throw an exception if it's invalid (or create a dummy object, or whatever is better in the particular case).
private class Factory {
public static IConfigurationObject Factory(List<string> keyValuePair) {
switch (keyValuePair[0]) {
case "x":
return new x(keyValuePair[1]);
break;
/* etc. */
default:
throw new ArgumentException("Wrong parameter in the file");
}
}
}
The strongest assumption here is that all your objects can be treated partly like the same (ie, they implement the same interface (IConfigurationObject in the example) or belong to the same inheritance tree).
If they don't, then it depends on your program flow and what are you doing with them. But nonetheless, they should :)
EDIT: Given your explanation, you could have one Factory per file type, the switch in it would be the authoritative source on the allowed types per file type and they probably share something in common. Reflection is possible, but it's riskier because it's less obvious and self documenting than this one.
What do you need object for? The way you describe it, you'll use them as some kind (of key-wise) restricted map anyway. If you do not need some kind of inheritance, I'd simply wrap a map-like structure into a object like this:
[java-inspired pseudo-code:]
class RestrictedKVDataStore {
const ALLOWED_KEYS = new Collection('name', 'prefix');
Map data = new Map();
void put(String key, Object value) {
if (ALLOWED_KEYS.contains(key))
data.put(key, value)
}
Object get(String key) {
return data.get(key);
}
}
You could create an interface that matched the column names, and then use the Reflection.Emit API to create a type at runtime that gave access to the data in the fields.
EDIT:
Scratch that, this still applies, but I think what your doing is reading a configuration file and parsing it into this:
List<List<KeyValuePair<String,String>>> itemConfig =
new List<List<KeyValuePair<String,String>>>();
In this case, we can still use a reflection factory to instantiate the objects, I'd just pass in the nested inner list to it, instead of passing each individual key/value pair.
OLD POST:
Here is a clever little way to do this using reflection:
The basic idea:
Use a common base class for each Object class.
Put all of these classes in their own assembly.
Put this factory in that assembly too.
Pass in the KeyValuePair that you read from your config, and in return it finds the class that matches KV.Key and instantiates it with KV.Value
public class KeyValueToObjectFactory
{
private Dictionary _kvTypes = new Dictionary();
public KeyValueToObjectFactory()
{
// Preload the Types into a dictionary so we can look them up later
// Obviously, you want to reuse the factory to minimize overhead, so don't
// do something stupid like instantiate a new factory in a loop.
foreach (Type type in typeof(KeyValueToObjectFactory).Assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(KVObjectBase)))
{
_kvTypes[type.Name.ToLower()] = type;
}
}
}
public KVObjectBase CreateObjectFromKV(KeyValuePair kv)
{
if (kv != null)
{
string kvName = kv.Key;
// If the Type information is in our Dictionary, instantiate a new instance of that class.
Type kvType;
if (_kvTypes.TryGetValue(kvName, out kvType))
{
return (KVObjectBase)Activator.CreateInstance(kvType, kv.Value);
}
else
{
throw new ArgumentException("Unrecognized KV Pair");
}
}
else
{
return null;
}
}
}
#David:
I already have the parser (and most of these will be hand written, so I decided against XML). But that looks like I really nice way of doing it; I'll have to check it out. Excellent point about versioning too.
#Argelbargel:
That looks good too. :')
...This is a fairly core piece of the
project I'm working on though...
Is it really?
It's tempting to just abstract it and provide a basic implementation with the intention of refactoring later on.
Then you can get on with what matters: the game.
Just a thought
<bb />
Is it really?
Yes; I have thought this out. Far be it from me to do more work than neccessary. :')