//Iterate through every resource and build up a list of resources, and get options
using(Stream inputStream = File.OpenRead(inputFileName))
{
XPathDocument doc = new XPathDocument(inputStream);
// Get options
XPathNodeIterator headerIt = doc.CreateNavigator().Select("//resheader");
while(headerIt.MoveNext())
{
string opt = headerIt.Current.GetAttribute("name", string.Empty);
XPathNodeIterator valueIt = headerIt.Current.Select("value");
if (valueIt.MoveNext())
{
string arg = valueIt.Current.Value;
switch (opt.ToLower())
{
case "accessor_class_accessibility":
if (arg.ToLower() == "public")
{
options.PublicSRClass = true;
}
break;
case "culture_info":
options.CultureInfoFragment = arg;
break;
case "generate_class":
generateClass = bool.Parse(arg);
break;
case "resource_namespace":
options.ResourceFileNamespace = arg;
break;
}
}
}
XPathNodeIterator it = doc.CreateNavigator().Select("//data");
while(it.MoveNext())
{
if (it.Current.GetAttribute("type", String.Empty).Length != 0)
{
continue; // Only interested in string resources
}
string key = it.Current.GetAttribute("name", String.Empty);
string textValue = null;
string commentValue = null;
// Get the value and comment elements
XPathNodeIterator valueIt = it.Current.SelectChildren(XPathNodeType.Element);
while (valueIt.MoveNext())
{
if (valueIt.Current.Name == "value")
{
textValue = valueIt.Current.Value;
}
else if (valueIt.Current.Name == "comment")
{
commentValue = valueIt.Current.Value;
}
}
sList.Add(key, new ResourceHolder(key, textValue, commentValue) );
}
}
string className = Path.GetFileNameWithoutExtension(inputFileName);
CodeCompileUnit compileUnit = new CodeCompileUnit();
//Just for VB.NET
compileUnit.UserData.Add("AllowLateBound", false);
compileUnit.UserData.Add("RequireVariableDeclaration", true);
//Dummy namespace, so the Import statements would appear above the main namespace declaration
CodeNamespace dummyNamespace = new CodeNamespace("");
compileUnit.Namespaces.Add(dummyNamespace);
if (hasCustomToolNamespace
&& options.ResourceFileNamespace == null)
{
string errorMessage = "You must specify the option resource_namespace when using a Custom Tool Namespace, to provide the namespace used for the resources.";
//dummyNamespace.Comments.Add(new CodeCommentStatement(errorMessage));
//feedback.LineError(errorMessage, null, 0);
return new CodeSnippetCompileUnit("#error " + errorMessage);
}
if (!generateClass)
{
dummyNamespace.Comments.Add(new CodeCommentStatement("-----------------------------------------------------------------------------"));
dummyNamespace.Comments.Add(new CodeCommentStatement(" <autogeneratedinfo>"));
dummyNamespace.Comments.Add(new CodeCommentStatement(" This code was generated by:"));
dummyNamespace.Comments.Add(new CodeCommentStatement(" SR Resource Generator custom tool for VS.NET, by Martin Granell, Readify"));
dummyNamespace.Comments.Add(new CodeCommentStatement(""));
dummyNamespace.Comments.Add(new CodeCommentStatement(" NO CLASS WILL BE GENERATED "));
dummyNamespace.Comments.Add(new CodeCommentStatement(" as the resource file contains the option: "));
dummyNamespace.Comments.Add(new CodeCommentStatement(" generate_class = false. "));
dummyNamespace.Comments.Add(new CodeCommentStatement(""));
dummyNamespace.Comments.Add(new CodeCommentStatement(" The resource filename is:" + inputFileName));
dummyNamespace.Comments.Add(new CodeCommentStatement(""));
dummyNamespace.Comments.Add(new CodeCommentStatement(" Generated: " + DateTime.Now.ToString("f", new CultureInfo("en-US"))));
dummyNamespace.Comments.Add(new CodeCommentStatement(" </autogeneratedinfo>"));
dummyNamespace.Comments.Add(new CodeCommentStatement("-----------------------------------------------------------------------------"));
return compileUnit;
}
//Namespace Import
dummyNamespace.Imports.Add(new CodeNamespaceImport("System"));
dummyNamespace.Imports.Add(new CodeNamespaceImport("System.Resources"));
dummyNamespace.Imports.Add(new CodeNamespaceImport("System.Reflection"));
//Namespace
CodeNamespace nSpace = new CodeNamespace(FileNameSpace);
compileUnit.Namespaces.Add(nSpace);
//Namespace comments
#region [liubin 2006-05-16 17:42] 增加修改记录注释
nSpace.Comments.Add(new CodeCommentStatement("-----------------------------------------------------------------------------"));
nSpace.Comments.Add(new CodeCommentStatement(" <autogeneratedinfo>"));
nSpace.Comments.Add(new CodeCommentStatement(" This code was generated by:"));
nSpace.Comments.Add(new CodeCommentStatement(" SR Resource Generator custom tool for VS.NET, by Martin Granell, Readify"));
nSpace.Comments.Add(new CodeCommentStatement(""));
nSpace.Comments.Add(new CodeCommentStatement(" It contains classes defined from the contents of the resource file:"));
nSpace.Comments.Add(new CodeCommentStatement(" " + inputFileName));
nSpace.Comments.Add(new CodeCommentStatement(""));
nSpace.Comments.Add(new CodeCommentStatement(" Generated: " + DateTime.Now.ToString("f", new CultureInfo("en-US"))));
nSpace.Comments.Add(new CodeCommentStatement(" Modified by: liubin 2006-05-16 17:42"));
nSpace.Comments.Add(new CodeCommentStatement(" 修改记录:"));
nSpace.Comments.Add(new CodeCommentStatement(" 1.原所有构建static方法改写成构建Final方法;"));
nSpace.Comments.Add(new CodeCommentStatement(" 2.原所有常量字段属性被剔除;"));
nSpace.Comments.Add(new CodeCommentStatement(" 3.在SR Class中增加静态字段mENSR、mCNSR和非静态字段mKeys;"));
nSpace.Comments.Add(new CodeCommentStatement(" 4.在SR Class中增加静态方法GetENSR和GetCNSR"));
nSpace.Comments.Add(new CodeCommentStatement(" 5.在SR Class中增加构造函数SR"));
nSpace.Comments.Add(new CodeCommentStatement(" </autogeneratedinfo>"));
nSpace.Comments.Add(new CodeCommentStatement("-----------------------------------------------------------------------------"));
#endregion
// Define SR class
CodeTypeDeclaration cSR = new CodeTypeDeclaration(className);
// CodeDom Sucks. It doesn't allow support for specifying assembly visibility.
// So we have to do a search and replace on the resulting text after we have generated
// the file.
cSR.TypeAttributes = TypeAttributes.Public;
_convertPublicToInternal = !options.PublicSRClass;
#region [liubin 2005-06-16] 增加SR私有实体类字段
CodeConstructor cmSRCodeConstructor = new CodeConstructor();
cmSRCodeConstructor.Attributes = MemberAttributes.Private;
cmSRCodeConstructor.Name = "SR";
CodeObjectCreateExpression coCreateExp = new CodeObjectCreateExpression();
coCreateExp.CreateType = new CodeTypeReference("Keys");
cmSRCodeConstructor.Statements.Add(new CodeAssignStatement(
new CodeFieldReferenceExpression(
new CodeThisReferenceExpression(),"mKeys"),
coCreateExp));
cSR.Members.Add(cmSRCodeConstructor);
#endregion
#region [liubin 2005-06-16] 增加SR私有实体类字段
CodeMemberField cmCNSRClassField = new CodeMemberField();
cmCNSRClassField.Name = "mCNSR";
cmCNSRClassField.Attributes = MemberAttributes.Private | MemberAttributes.Static;
cmCNSRClassField.Type = new CodeTypeReference(className);
cSR.Members.Add(cmCNSRClassField);
CodeMemberField cmENSRClassField = new CodeMemberField();
cmENSRClassField.Name = "mENSR";
cmENSRClassField.Attributes = MemberAttributes.Private | MemberAttributes.Static;
cmENSRClassField.Type = new CodeTypeReference(className);
cSR.Members.Add(cmENSRClassField);
#endregion
#region [liubin 2005-06-16] 增加SR公共静态实体类方法
CodeMemberMethod mGetENSRInstance = new CodeMemberMethod();
mGetENSRInstance.Attributes = MemberAttributes.Public | MemberAttributes.Static;
mGetENSRInstance.ReturnType = new CodeTypeReference(className);
mGetENSRInstance.Name = "GetENSR";
mGetENSRInstance.Comments.Add(new CodeCommentStatement("获得英文SR"));
CodeObjectCreateExpression coCreaterENSRExp = new CodeObjectCreateExpression();
coCreaterENSRExp.CreateType = new CodeTypeReference(className);
// CodeExpression enExpression = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(
// new CodeTypeReference("LanguageCulture")),"GetCultureInfo",
// new CodePrimitiveExpression("en-US"));
CodeObjectCreateExpression enObjectExpression = new CodeObjectCreateExpression();
enObjectExpression.CreateType = new CodeTypeReference(typeof(CultureInfo));
enObjectExpression.Parameters.Add(new CodePrimitiveExpression("en-US"));
mGetENSRInstance.Statements.Add(new CodeConditionStatement(
new CodeSnippetExpression("mENSR == null"),
new CodeAssignStatement(new CodeFieldReferenceExpression(null, "mENSR"),
coCreaterENSRExp)));
mGetENSRInstance.Statements.Add(
new CodeAssignStatement(
new CodePropertyReferenceExpression(null, "mENSR.Culture"),enObjectExpression));
mGetENSRInstance.Statements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(null,"mENSR")));
cSR.Members.Add(mGetENSRInstance);
#endregion
#region [liubin 2005-06-16] 增加SR公共静态实体类方法
CodeMemberMethod mGetCNSRInstance = new CodeMemberMethod();
mGetCNSRInstance.Attributes = MemberAttributes.Public | MemberAttributes.Static;
mGetCNSRInstance.ReturnType = new CodeTypeReference(className);
mGetCNSRInstance.Name = "GetCNSR";
mGetCNSRInstance.Comments.Add(new CodeCommentStatement("获得中文SR"));
CodeObjectCreateExpression coCreaterCNSRExp = new CodeObjectCreateExpression();
coCreaterCNSRExp.CreateType = new CodeTypeReference(className);
mGetCNSRInstance.Statements.Add(new CodeConditionStatement(
new CodeSnippetExpression("mCNSR == null"),
new CodeAssignStatement(new CodeFieldReferenceExpression(null, "mCNSR"),
coCreaterCNSRExp)));
//
// CodeExpression cnExpression = new CodeMethodInvokeExpression(
// new CodeTypeReferenceExpression(
// new CodeTypeReference("LanguageCulture")),"GetCultureInfo",
// new CodePrimitiveExpression("zh-CN"));
CodeObjectCreateExpression cnObjectExpression = new CodeObjectCreateExpression();
cnObjectExpression.CreateType = new CodeTypeReference(typeof(CultureInfo));
cnObjectExpression.Parameters.Add(new CodePrimitiveExpression("zh-CN"));
mGetCNSRInstance.Statements.Add(
new CodeAssignStatement(
new CodePropertyReferenceExpression(null, "mCNSR.Culture"),cnObjectExpression));
/*
mGetSRInstance.Statements.Add(
new CodeConditionStatement(
new CodeBinaryOperatorExpression(
new CodeArgumentReferenceExpression("mLanguage"), CodeBinaryOperatorType.ValueEquality,
new CodeSnippetExpression("LanguageOption.CN")),
new CodeStatement[]{
new CodeAssignStatement(new CodePropertyReferenceExpression(null, "mSR.Culture"),
ceRight1)},
new CodeStatement[]{
new CodeAssignStatement(new CodePropertyReferenceExpression(null,"mSR.Culture"),
ceRight2)}));
*/
mGetCNSRInstance.Statements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(null,"mCNSR")));
cSR.Members.Add(mGetCNSRInstance);
#endregion
if (options.CultureInfoFragment == null)
{
// Create Static Culture property
CodeMemberProperty cmStaticCulture = new CodeMemberProperty();
cmStaticCulture.Name = "Culture";
// [liubin 2006-05-16 13:45] 不生成静态属性
// cmStaticCulture.Attributes = MemberAttributes.Public | MemberAttributes.Static;
cmStaticCulture.Attributes = MemberAttributes.Public | MemberAttributes.Final;
cmStaticCulture.Type = new CodeTypeReference(typeof(CultureInfo));
cmStaticCulture.GetStatements.Add(
new CodeMethodReturnStatement(
new CodePropertyReferenceExpression(
new CodeTypeReferenceExpression("mKeys"), "Culture")));
cmStaticCulture.SetStatements.Add(
new CodeAssignStatement(
new CodePropertyReferenceExpression(
new CodeTypeReferenceExpression("mKeys"), "Culture"), new CodeArgumentReferenceExpression("value")));
cSR.Members.Add(cmStaticCulture);
}
// Define Keys class
CodeTypeDeclaration cKeys = new CodeTypeDeclaration("Keys");
cKeys.TypeAttributes = TypeAttributes.Public;
// Create resource manager
// static ResourceManager resourceManager =
// new ResourceManager("Microsoft.Practices.EnterpriseLibrary.Data.SR", typeof(Microsoft.Practices.EnterpriseLibrary.Data.SR).Module.Assembly );
//
CodeMemberField fResourceManager = new CodeMemberField(typeof(ResourceManager), "resourceManager");
#region [liubin 2005-06-16] 增加mKeys私有实体类字段
CodeMemberField cmKeyClassField = new CodeMemberField();
cmKeyClassField.Name = "mKeys";
cmKeyClassField.Attributes = MemberAttributes.Private;
cmKeyClassField.Type = new CodeTypeReference("Keys");
cSR.Members.Add(cmKeyClassField);
#endregion
// [liubin 2006-05-16 13:52] 不用静态属性
// fResourceManager.Attributes = MemberAttributes.Static;
fResourceManager.Attributes = MemberAttributes.Private;
if (options.ResourceFileNamespace != null)
{
string resName = className;
if (options.ResourceFileNamespace.Length > 0)
{
resName = options.ResourceFileNamespace.Length + "." + resName;
}
fResourceManager.InitExpression = new CodeObjectCreateExpression(typeof(ResourceManager), new CodePrimitiveExpression(options.ResourceFileNamespace + "." + className), new CodePropertyReferenceExpression(new CodeTypeOfExpression(className), "Assembly"));
}
else
{
fResourceManager.InitExpression = new CodeObjectCreateExpression(typeof(ResourceManager), new CodeTypeOfExpression(className));
}
cKeys.Members.Add(fResourceManager);
CodeExpression cultureCodeExp;
if (options.CultureInfoFragment == null) // Add a property that is settable
{
// Culture field and property
CodeMemberField fCulture = new CodeMemberField(typeof(CultureInfo), "_culture");
// [liubin 2006-05-16 13:52] 不用静态属性
// fCulture.Attributes = MemberAttributes.Static;
fCulture.Attributes = MemberAttributes.Private | MemberAttributes.Final;
fCulture.InitExpression = new CodePrimitiveExpression(null);
cKeys.Members.Add(fCulture);
CodeMemberProperty pCulture = new CodeMemberProperty();
// [liubin 2006-05-16 13:52] 不用静态属性
// pCulture.Attributes = MemberAttributes.Static | MemberAttributes.Public;
pCulture.Attributes = MemberAttributes.Public | MemberAttributes.Final;
pCulture.Type = new CodeTypeReference( typeof(CultureInfo));
pCulture.Name = "Culture";
pCulture.GetStatements.Add( new CodeMethodReturnStatement( new CodeFieldReferenceExpression(null, "_culture") ));
pCulture.SetStatements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression(null, "_culture"), new CodeArgumentReferenceExpression("value")));
cKeys.Members.Add(pCulture);
cultureCodeExp = new CodeFieldReferenceExpression(null, "_culture");
}
else
{
cultureCodeExp = new CodeSnippetExpression(options.CultureInfoFragment);
}
// Get String methods
// No parameters, just return the result of the resourceManager.GetString method.
// return resourceManager.GetString( key, _culture );
CodeMemberMethod mGetStringNoParams = new CodeMemberMethod();
mGetStringNoParams.Name = "GetString";
// [liubin 2006-05-16 13:52] 不用静态属性
// mGetStringNoParams.Attributes = MemberAttributes.Public | MemberAttributes.Static;
mGetStringNoParams.Attributes = MemberAttributes.Public|MemberAttributes.Final;
mGetStringNoParams.ReturnType = new CodeTypeReference(typeof(string));
mGetStringNoParams.Parameters.Add( new CodeParameterDeclarationExpression(typeof(string), "key") );
mGetStringNoParams.Statements.Add( new CodeMethodReturnStatement(
new CodeMethodInvokeExpression(
new CodeFieldReferenceExpression(null, "resourceManager"),
"GetString",
new CodeArgumentReferenceExpression("key"),
cultureCodeExp)));
cKeys.Members.Add(mGetStringNoParams);
// With parameters, format the results using String.Format
// return msg;
CodeMemberMethod mGetStringWithParams = new CodeMemberMethod();
mGetStringWithParams.Name = "GetString";
// [liubin 2006-05-16 13:52] 不用静态属性
// mGetStringWithParams.Attributes = MemberAttributes.Public | MemberAttributes.Static;
mGetStringWithParams.Attributes = MemberAttributes.Public|MemberAttributes.Final;
mGetStringWithParams.ReturnType = new CodeTypeReference(typeof(string));
mGetStringWithParams.Parameters.Add(
new CodeParameterDeclarationExpression(typeof(string), "key"));
mGetStringWithParams.Parameters.Add(
new CodeParameterDeclarationExpression(typeof(object[]), "args"));
// string msg = resourceManager.GetString( key, _culture );
mGetStringWithParams.Statements.Add(
new CodeVariableDeclarationStatement(typeof(string), "msg",
new CodeMethodInvokeExpression(
new CodeFieldReferenceExpression(null, "resourceManager"),
"GetString",
new CodeArgumentReferenceExpression("key"),
cultureCodeExp)));
// msg = string.Format( msg, args );
mGetStringWithParams.Statements.Add(
new CodeAssignStatement( new CodeVariableReferenceExpression("msg"),
new CodeMethodInvokeExpression( new CodeTypeReferenceExpression(typeof(string)), "Format", new CodeVariableReferenceExpression("msg"), new CodeArgumentReferenceExpression("args"))));
// return msg
mGetStringWithParams.Statements.Add(
new CodeMethodReturnStatement(new CodeVariableReferenceExpression("msg")));
cKeys.Members.Add(mGetStringWithParams);
IDictionaryEnumerator resEnumerator = sList.GetEnumerator();
Regex safeRegex = new Regex("[^a-zA-Z0-9_]");
// Create a class definition for each string entry
while(resEnumerator.MoveNext())
{
ResourceHolder resource = (ResourceHolder)resEnumerator.Value;
// Create a safe identifier
string safeKey = XmlConvert.EncodeName(resource.Key);
// Deal with an initial numeral.
if (safeKey[0] >= '0' && safeKey[0] <= '9')
safeKey = "_" + safeKey;
// Make sure we don't conflict with a language identifier.
safeKey = generator.CreateValidIdentifier(safeKey);
// The VS Generator always lower cases the first letter, which is not
// wanted in this case.
safeKey = char.ToUpper(safeKey[0], CultureInfo.InvariantCulture) + safeKey.Substring(1);
/*
//It is necessary to know how many replaceable parameters the string has
string ParameterMatchExpression = @"(\{[^\}\{]+\})";
MatchCollection mc = Regex.Matches(resource.Text, ParameterMatchExpression);
//Don't include duplicates in count as String.Format argument can be specified
//more than once, ie: "First: {0}, Second: {1}, First again: {0}"
StringCollection parameters = new StringCollection();
foreach(Match match in mc)
{
if(!parameters.Contains(match.Value))
{
parameters.Add(match.Value);
}
}
*/
// Get parameter names from comma sep names in comment
string[] parameterNames = new string[0];
if (resource.Comment != null)
{
parameterNames = resource.Comment.Split(',');
}
if (parameterNames.Length > 0)
{
// Create as a method that takes the right number of parameters.
CodeMemberMethod mGetString = new CodeMemberMethod();
mGetString.Name = safeKey;
// [liubin 2006-05-16 13:52] 不用静态属性
mGetString.Attributes = MemberAttributes.Public|MemberAttributes.Final;
// mGetString.Attributes = MemberAttributes.Static | MemberAttributes.Public;
mGetString.ReturnType= new CodeTypeReference(typeof(string));
//Create the argument lists
CodeExpression[] args = new CodeExpression[parameterNames.Length];
for(int i = 0; i < parameterNames.Length; i++)
{
string parameterName = parameterNames[i];
parameterName = parameterName.Trim();
if (parameterName.IndexOf(' ') > -1)
{
// parameter name includes type.
string typeName = MapType(parameterName.Substring(0,parameterName.IndexOf(' ')).Trim());
parameterName = parameterName.Substring(parameterName.IndexOf(' ')+1).Trim();
mGetString.Parameters.Add(new CodeParameterDeclarationExpression(typeName, parameterName));
}
else
{
mGetString.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), parameterName));
}
args[i] = new CodeArgumentReferenceExpression(parameterName);
}
mGetString.Statements.Add(
new CodeMethodReturnStatement(
new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression("mKeys"),
"GetString",
new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("mKeys"), safeKey),
new CodeArrayCreateExpression(typeof(object), args))));
cSR.Members.Add(mGetString);
}
else
{
// Create as a property
CodeMemberProperty pGetString = new CodeMemberProperty();
pGetString.Name = safeKey;
// [liubin 2006-05-16 13:52] 不用静态属性
// pGetString.Attributes = MemberAttributes.Static | MemberAttributes.Public;
pGetString.Attributes = MemberAttributes.Public|MemberAttributes.Final;
pGetString.Type= new CodeTypeReference(typeof(string));
pGetString.GetStatements.Add(
new CodeMethodReturnStatement(
new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("mKeys"), "GetString", new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("mKeys"), safeKey))));
cSR.Members.Add(pGetString);
}
// Add a const field to the Keys class that contains the actual reference
CodeMemberField fKey = new CodeMemberField(typeof(string), safeKey);
fKey.InitExpression = new CodePrimitiveExpression(resource.Key);
// [liubin 2006-05-16 13:52] 不用静态属性
// fKey.Attributes = MemberAttributes.Const | MemberAttributes.Public;
fKey.Attributes = MemberAttributes.Public;
cKeys.Members.Add(fKey);
}
cSR.Members.Add(cKeys);
nSpace.Types.Add(cSR);
return compileUnit;
}