internal class AppResourcesCompiler
{
const string cachePrefix = "@@LocalResourcesAssemblies";
+ public const string DefaultCultureKey = ".:!DefaultCulture!:.";
bool isGlobal;
HttpContext context;
AppResourceFilesCollection files;
string tempDirectory;
+ string virtualPath;
+ Dictionary <string, List <string>> cultureFiles;
string TempDirectory {
get {
return (tempDirectory = AppDomain.CurrentDomain.SetupInformation.DynamicBase);
}
}
+
+ public Dictionary <string, List <string>> CultureFiles {
+ get { return cultureFiles; }
+ }
- public AppResourcesCompiler (HttpContext context, bool isGlobal)
+ public AppResourcesCompiler (HttpContext context)
{
this.context = context;
- this.isGlobal = isGlobal;
- this.files = new AppResourceFilesCollection (context, isGlobal);
+ this.isGlobal = true;
+ this.files = new AppResourceFilesCollection (context);
+ this.cultureFiles = new Dictionary <string, List <string>> ();
}
+ public AppResourcesCompiler (string virtualPath)
+ {
+
+ this.virtualPath = virtualPath;
+ this.isGlobal = false;
+ this.files = new AppResourceFilesCollection (HttpContext.Current.Request.MapPath (virtualPath));
+ this.cultureFiles = new Dictionary <string, List <string>> ();
+ }
- public void Compile ()
+ public Assembly Compile ()
{
files.Collect ();
if (!files.HasFiles)
- return;
+ return null;
if (isGlobal)
- CompileGlobal ();
+ return CompileGlobal ();
else
- CompileLocal ();
+ return CompileLocal ();
}
- void CompileGlobal ()
+ Assembly CompileGlobal ()
{
string assemblyPath = FileUtils.CreateTemporaryFile (TempDirectory,
"App_GlobalResources",
"dll",
OnCreateRandomFile) as string;
+
if (assemblyPath == null)
throw new ApplicationException ("Failed to create global resources assembly");
- CompilationSection config = WebConfigurationManager.GetSection ("system.web/compilation") as CompilationSection;
- if (config == null || !CodeDomProvider.IsDefinedLanguage (config.DefaultLanguage))
- throw new ApplicationException ("Could not get the default compiler.");
- CompilerInfo ci = CodeDomProvider.GetCompilerInfo (config.DefaultLanguage);
- if (ci == null || !ci.IsCodeDomProviderTypeValid)
- throw new ApplicationException ("Failed to obtain the default compiler information.");
-
- CompilerParameters cp = ci.CreateDefaultCompilerParameters ();
- cp.OutputAssembly = assemblyPath;
- cp.GenerateExecutable = false;
- cp.TreatWarningsAsErrors = true;
- cp.IncludeDebugInformation = config.Debug;
-
- List <string>[] fileGroups = GroupGlobalFiles (cp);
+ List <string>[] fileGroups = GroupGlobalFiles ();
if (fileGroups == null || fileGroups.Length == 0)
- return;
-
+ return null;
+
CodeCompileUnit unit = new CodeCompileUnit ();
CodeNamespace ns = new CodeNamespace (null);
ns.Imports.Add (new CodeNamespaceImport ("System"));
ns.Imports.Add (new CodeNamespaceImport ("System.Resources"));
unit.Namespaces.Add (ns);
- CodeDomProvider provider;
- provider = ci.CreateProvider ();
- if (provider == null)
- throw new ApplicationException ("Failed to instantiate the default compiler.");
+ AppResourcesAssemblyBuilder builder = new AppResourcesAssemblyBuilder ("App_GlobalResources", assemblyPath,
+ this);
+ CodeDomProvider provider = builder.Provider;
Dictionary <string,bool> assemblies = new Dictionary<string,bool> ();
foreach (List<string> ls in fileGroups)
DomFromResource (ls [0], unit, assemblies, provider);
+
foreach (KeyValuePair<string,bool> de in assemblies)
unit.ReferencedAssemblies.Add (de.Key);
- AssemblyBuilder abuilder = new AssemblyBuilder (provider);
- abuilder.AddCodeCompileUnit (unit);
-
- CompilerResults results = abuilder.BuildAssembly (cp);
- if (results.Errors.Count == 0) {
- BuildManager.TopLevelAssemblies.Add (results.CompiledAssembly);
- HttpContext.AppGlobalResourcesAssembly = results.CompiledAssembly;
- } else {
- if (context.IsCustomErrorEnabled)
- throw new ApplicationException ("An error occurred while compiling global resources.");
- throw new CompilationException (null, results.Errors, null);
- }
+ builder.Build (unit);
+ HttpContext.AppGlobalResourcesAssembly = builder.MainAssembly;
+
+ return builder.MainAssembly;
}
- void CompileLocal ()
+ Assembly CompileLocal ()
{
- string path = Path.GetDirectoryName (VirtualPathUtility.ToAbsolute (context.Request.CurrentExecutionFilePath));
+ if (String.IsNullOrEmpty (virtualPath))
+ return null;
- if (String.IsNullOrEmpty (path))
- throw new ApplicationException ("Unable to determine the request virtual path.");
-
- Assembly cached = GetCachedLocalResourcesAssembly (path);
+ Assembly cached = GetCachedLocalResourcesAssembly (virtualPath);
if (cached != null)
- return;
+ return cached;
string prefix;
- if (path == "/")
+ if (virtualPath == "/")
prefix = "App_LocalResources.root";
else
- prefix = "App_LocalResources" + path.Replace ('/', '.');
+ prefix = "App_LocalResources" + virtualPath.Replace ('/', '.');
string assemblyPath = FileUtils.CreateTemporaryFile (TempDirectory,
prefix,
"dll",
OnCreateRandomFile) as string;
if (assemblyPath == null)
- throw new ApplicationException ("Failed to create global resources assembly");
-
- CompilationSection config = WebConfigurationManager.GetSection ("system.web/compilation") as CompilationSection;
- if (config == null || !CodeDomProvider.IsDefinedLanguage (config.DefaultLanguage))
- throw new ApplicationException ("Could not get the default compiler.");
- CompilerInfo ci = CodeDomProvider.GetCompilerInfo (config.DefaultLanguage);
- if (ci == null || !ci.IsCodeDomProviderTypeValid)
- throw new ApplicationException ("Failed to obtain the default compiler information.");
-
- CompilerParameters cp = ci.CreateDefaultCompilerParameters ();
- cp.OutputAssembly = assemblyPath;
- cp.GenerateExecutable = false;
- cp.TreatWarningsAsErrors = true;
- cp.IncludeDebugInformation = config.Debug;
+ throw new ApplicationException ("Failed to create local resources assembly");
List<AppResourceFileInfo> files = this.files.Files;
foreach (AppResourceFileInfo arfi in files)
- GetResourceFile (arfi, cp);
+ GetResourceFile (arfi, true);
- CodeDomProvider provider;
- provider = ci.CreateProvider ();
- if (provider == null)
- throw new ApplicationException ("Failed to instantiate the default compiler.");
+ AppResourcesAssemblyBuilder builder = new AppResourcesAssemblyBuilder ("App_LocalResources", assemblyPath,
+ this);
+ builder.Build ();
+ Assembly ret = builder.MainAssembly;
- AssemblyBuilder abuilder = new AssemblyBuilder (provider);
- CompilerResults results = abuilder.BuildAssembly (cp);
- if (results.Errors.Count == 0) {
- AddAssemblyToCache (path, results.CompiledAssembly);
- } else {
- if (context.IsCustomErrorEnabled)
- throw new ApplicationException ("An error occurred while compiling global resources.");
- throw new CompilationException (null, results.Errors, null);
- }
- }
+ if (ret != null)
+ AddAssemblyToCache (virtualPath, ret);
+ return ret;
+ }
+
internal static Assembly GetCachedLocalResourcesAssembly (string path)
{
Dictionary <string, Assembly> cache;
- cache = HttpRuntime.Cache[cachePrefix] as Dictionary <string, Assembly>;
+ cache = HttpRuntime.InternalCache[cachePrefix] as Dictionary <string, Assembly>;
if (cache == null || !cache.ContainsKey (path))
return null;
return cache [path];
}
-
+
void AddAssemblyToCache (string path, Assembly asm)
{
- Cache runtimeCache = HttpRuntime.Cache;
+ Cache runtimeCache = HttpRuntime.InternalCache;
Dictionary <string, Assembly> cache;
cache = runtimeCache[cachePrefix] as Dictionary <string, Assembly>;
return ret;
}
- bool IsFileCultureValid (string fileName)
+ string IsFileCultureValid (string fileName)
{
string tmp = Path.GetFileNameWithoutExtension (fileName);
tmp = Path.GetExtension (tmp);
tmp = tmp.Substring (1);
try {
CultureInfo.GetCultureInfo (tmp);
- return true;
+ return tmp;
} catch {
- return false;
+ return null;
}
}
- return false;
+ return null;
}
-
- string GetResourceFile (AppResourceFileInfo arfi, CompilerParameters cp)
+
+ string GetResourceFile (AppResourceFileInfo arfi, bool local)
{
string resfile;
if (arfi.Kind == AppResourceFileKind.ResX)
- resfile = CompileResource (arfi);
+ resfile = CompileResource (arfi, local);
else
resfile = arfi.Info.FullName;
- if (!String.IsNullOrEmpty (resfile))
- cp.EmbeddedResources.Add (resfile);
+ if (!String.IsNullOrEmpty (resfile)) {
+ string culture = IsFileCultureValid (resfile);
+ if (culture == null)
+ culture = DefaultCultureKey;
+
+ List <string> cfiles;
+ if (cultureFiles.ContainsKey (culture))
+ cfiles = cultureFiles [culture];
+ else {
+ cfiles = new List <string> (1);
+ cultureFiles [culture] = cfiles;
+ }
+ cfiles.Add (resfile);
+ }
+
return resfile;
}
- List <string>[] GroupGlobalFiles (CompilerParameters cp)
+ List <string>[] GroupGlobalFiles ()
{
List<AppResourceFileInfo> files = this.files.Files;
List<List<string>> groups = new List<List<string>> ();
filedots = CountChars ('.', tmp);
if (filedots == basedots + 1 && tmp.StartsWith (basename)) {
- if (IsFileCultureValid (s2)) {
+ if (IsFileCultureValid (s2) != null) {
// A valid translated file for this name
defaultFile = arfi;
break;
}
if (defaultFile != null) {
List<string> al = new List<string> ();
- al.Add (GetResourceFile (arfi, cp));
+ al.Add (GetResourceFile (arfi, false));
arfi.Seen = true;
groups.Add (al);
foreach (List<string> al in groups) {
s = al [0];
tmp = Path.GetFileNameWithoutExtension (s);
+ if (tmp.StartsWith ("Resources."))
+ tmp = tmp.Substring (10);
foreach (AppResourceFileInfo arfi in files) {
if (arfi.Seen)
continue;
-
s = arfi.Info.FullName;
if (s == null)
continue;
tmp2 = arfi.Info.Name;
if (tmp2.StartsWith (tmp)) {
- al.Add (GetResourceFile (arfi, cp));
+ al.Add (GetResourceFile (arfi, false));
arfi.Seen = true;
}
}
if (arfi.Seen)
continue;
- if (IsFileCultureValid (arfi.Info.FullName))
+ if (IsFileCultureValid (arfi.Info.FullName) != null)
continue; // Culture found, we reject the file
// A single default file, create a group
List<string> al = new List<string> ();
- al.Add (GetResourceFile (arfi, cp));
+ al.Add (GetResourceFile (arfi, false));
groups.Add (al);
}
groups.Sort (lcList);
void DomFromResource (string resfile, CodeCompileUnit unit, Dictionary <string,bool> assemblies,
CodeDomProvider provider)
{
+ if (String.IsNullOrEmpty (resfile))
+ return;
+
string fname, nsname, classname;
fname = Path.GetFileNameWithoutExtension (resfile);
classname = nsname;
nsname = "Resources";
} else {
- nsname = String.Format ("Resources.{0}", nsname);
+ if (!nsname.StartsWith ("Resources", StringComparison.InvariantCulture))
+ nsname = String.Concat ("Resources.", nsname);
classname = classname.Substring(1);
}
+ if (!String.IsNullOrEmpty (classname))
+ classname = classname.Replace ('.', '_');
+ if (!String.IsNullOrEmpty (nsname))
+ nsname = nsname.Replace ('.', '_');
+
if (!provider.IsValidIdentifier (nsname) || !provider.IsValidIdentifier (classname))
throw new ApplicationException ("Invalid resource file name.");
+
+ ResourceReader res;
+ try {
+ res = new ResourceReader (resfile);
+ } catch (ArgumentException) {
+ // invalid stream, probably empty - ignore silently and abort
+ return;
+ }
CodeNamespace ns = new CodeNamespace (nsname);
CodeTypeDeclaration cls = new CodeTypeDeclaration (classname);
cls.IsClass = true;
cls.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed;
- CodeMemberField cmf = new CodeMemberField (typeof(CultureInfo), "culture");
+ CodeMemberField cmf = new CodeMemberField (typeof(CultureInfo), "_culture");
cmf.InitExpression = new CodePrimitiveExpression (null);
cmf.Attributes = MemberAttributes.Private | MemberAttributes.Final | MemberAttributes.Static;
cls.Members.Add (cmf);
- cmf = new CodeMemberField (typeof(ResourceManager), "resourceManager");
+ cmf = new CodeMemberField (typeof(ResourceManager), "_resourceManager");
cmf.InitExpression = new CodePrimitiveExpression (null);
cmf.Attributes = MemberAttributes.Private | MemberAttributes.Final | MemberAttributes.Static;
cls.Members.Add (cmf);
cmp.HasGet = true;
cmp.HasSet = true;
cmp.Type = new CodeTypeReference (typeof(CultureInfo));
- CodePropertyGenericGet (cmp.GetStatements, "culture", classname);
- CodePropertyGenericSet (cmp.SetStatements, "culture", classname);
+ CodePropertyGenericGet (cmp.GetStatements, "_culture", classname);
+ CodePropertyGenericSet (cmp.SetStatements, "_culture", classname);
cls.Members.Add (cmp);
// Add the resource properties
Dictionary<string,bool> imports = new Dictionary<string,bool> ();
try {
- ResourceReader res = new ResourceReader (resfile);
foreach (DictionaryEntry de in res) {
Type type = de.Value.GetType ();
cls.Members.Add (cmp);
}
} catch (Exception ex) {
- throw new ApplicationException ("Failed to comipile global resources.", ex);
+ throw new ApplicationException ("Failed to compile global resources.", ex);
}
foreach (KeyValuePair<string,bool> de in imports)
ns.Imports.Add (new CodeNamespaceImport(de.Key));
CodeStatement st;
CodeExpression exp;
- exp = new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typename), "resourceManager");
+ exp = new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typename), "_resourceManager");
st = new CodeConditionStatement (
new CodeBinaryOperatorExpression (
exp,
gotstr ? "GetString" : "GetObject",
new CodeExpression [] { new CodePrimitiveExpression (resname),
new CodeFieldReferenceExpression (
- new CodeTypeReferenceExpression (typename), "culture") });
+ new CodeTypeReferenceExpression (typename), "_culture") });
st = new CodeVariableDeclarationStatement (
restype,
"obj",
new CodeVariableReferenceExpression ("value")));
}
- string CompileResource (AppResourceFileInfo arfi)
+ string CompileResource (AppResourceFileInfo arfi, bool local)
{
string path = arfi.Info.FullName;
- string resource = Path.Combine (TempDirectory,
- Path.GetFileNameWithoutExtension (path) + ".resources");
+ string rname = Path.GetFileNameWithoutExtension (path) + ".resources";
+ if (!local)
+ rname = "Resources." + rname;
+
+ string resource = Path.Combine (TempDirectory, rname);
FileStream source = null, destination = null;
IResourceReader reader = null;
ResourceWriter writer = null;