private static void EmitBasicClassMembers(CodeTypeDeclaration srClass, String nameSpace, String baseName, String resourcesNamespace, bool internalClass, bool useStatic) { const String tmpVarName = "temp"; String resMgrCtorParam; if (resourcesNamespace != null) { if (resourcesNamespace.Length > 0) { resMgrCtorParam = resourcesNamespace + '.' + baseName; } else { resMgrCtorParam = baseName; } } else if (!string.IsNullOrEmpty(nameSpace)) { resMgrCtorParam = nameSpace + '.' + baseName; } else { resMgrCtorParam = baseName; } var suppressMessageAttrib = new CodeAttributeDeclaration(new CodeTypeReference(typeof(SuppressMessageAttribute))); suppressMessageAttrib.AttributeType.Options = CodeTypeReferenceOptions.GlobalReference; suppressMessageAttrib.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression("Microsoft.Performance"))); suppressMessageAttrib.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression("CA1811:AvoidUncalledPrivateCode"))); // Emit a constructor - make it protected even if it is a "static" class to allow subclassing CodeConstructor ctor = new CodeConstructor(); ctor.CustomAttributes.Add(suppressMessageAttrib); if (useStatic || internalClass) { ctor.Attributes = MemberAttributes.FamilyAndAssembly; } else { ctor.Attributes = MemberAttributes.Public; } srClass.Members.Add(ctor); // Emit _resMgr field. var ResMgrCodeTypeReference = new CodeTypeReference(typeof(ResourceManager), CodeTypeReferenceOptions.GlobalReference); var field = new CodeMemberField(ResMgrCodeTypeReference, ResMgrFieldName) { Attributes = MemberAttributes.Private }; if (useStatic) { field.Attributes |= MemberAttributes.Static; } srClass.Members.Add(field); // Emit _resCulture field, and leave it set to null. var CultureTypeReference = new CodeTypeReference(typeof(CultureInfo), CodeTypeReferenceOptions.GlobalReference); field = new CodeMemberField(CultureTypeReference, CultureInfoFieldName); field.Attributes = MemberAttributes.Private; if (useStatic) { field.Attributes |= MemberAttributes.Static; } srClass.Members.Add(field); // Emit ResMgr property CodeMemberProperty resMgr = new CodeMemberProperty(); srClass.Members.Add(resMgr); resMgr.Name = ResMgrPropertyName; resMgr.HasGet = true; resMgr.HasSet = false; resMgr.Type = ResMgrCodeTypeReference; if (internalClass) { resMgr.Attributes = MemberAttributes.Assembly; } else { resMgr.Attributes = MemberAttributes.Public; } if (useStatic) { resMgr.Attributes |= MemberAttributes.Static; } // Mark the ResMgr property as advanced var editorBrowsableStateTypeRef = new CodeTypeReference(typeof(System.ComponentModel.EditorBrowsableState)) { Options = CodeTypeReferenceOptions.GlobalReference }; var editorBrowsableStateAdvanced = new CodeAttributeArgument(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(editorBrowsableStateTypeRef), "Advanced")); var editorBrowsableAdvancedAttribute = new CodeAttributeDeclaration("System.ComponentModel.EditorBrowsableAttribute", editorBrowsableStateAdvanced); editorBrowsableAdvancedAttribute.AttributeType.Options = CodeTypeReferenceOptions.GlobalReference; resMgr.CustomAttributes.Add(editorBrowsableAdvancedAttribute); // Emit the Culture property (read/write) var culture = new CodeMemberProperty(); srClass.Members.Add(culture); culture.Name = CultureInfoPropertyName; culture.HasGet = true; culture.HasSet = true; culture.Type = CultureTypeReference; if (internalClass) { culture.Attributes = MemberAttributes.Assembly; } else { culture.Attributes = MemberAttributes.Public; } if (useStatic) { culture.Attributes |= MemberAttributes.Static; } // Mark the Culture property as advanced culture.CustomAttributes.Add(editorBrowsableAdvancedAttribute); /* * // Here's what I'm trying to emit. Since not all languages support * // try/finally, we'll avoid our double lock pattern here. * // This will only hurt perf when we get two threads racing through * // this method the first time. Unfortunate, but not a big deal. * // Also, the .NET Compact Framework doesn't support * // Thread.MemoryBarrier (they only run on processors w/ a strong * // memory model, and who knows about IA64...) * // Once we have Interlocked.CompareExchange<T>, we should use it here. * if (_resMgr == null) { * ResourceManager tmp = new ResourceManager("<resources-name-with-namespace>", typeof("<class-name>").Assembly); * _resMgr = tmp; * } * return _resMgr; */ var field_resMgr = new CodeFieldReferenceExpression(null, ResMgrFieldName); var object_equalsMethod = new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(Object)), "ReferenceEquals"); var isResMgrNull = new CodeMethodInvokeExpression(object_equalsMethod, field_resMgr, new CodePrimitiveExpression(null)); // typeof(<class-name>).Assembly var getAssembly = new CodePropertyReferenceExpression(new CodeTypeOfExpression(new CodeTypeReference(srClass.Name)), "Assembly"); // new ResourceManager(resMgrCtorParam, typeof(<class-name>).Assembly); var newResMgr = new CodeObjectCreateExpression(ResMgrCodeTypeReference, new CodePrimitiveExpression(resMgrCtorParam), getAssembly); var init = new CodeStatement[2]; init[0] = new CodeVariableDeclarationStatement(ResMgrCodeTypeReference, tmpVarName, newResMgr); init[1] = new CodeAssignStatement(field_resMgr, new CodeVariableReferenceExpression(tmpVarName)); resMgr.GetStatements.Add(new CodeConditionStatement(isResMgrNull, init)); resMgr.GetStatements.Add(new CodeMethodReturnStatement(field_resMgr)); // Add a doc comment to the ResourceManager property resMgr.Comments.Add(new CodeCommentStatement(DocCommentSummaryStart, true)); resMgr.Comments.Add(new CodeCommentStatement(SR.GetString(SR.ResMgrPropertyComment), true)); resMgr.Comments.Add(new CodeCommentStatement(DocCommentSummaryEnd, true)); // Emit code for Culture property var field_resCulture = new CodeFieldReferenceExpression(null, CultureInfoFieldName); culture.GetStatements.Add(new CodeMethodReturnStatement(field_resCulture)); var newCulture = new CodePropertySetValueReferenceExpression(); culture.SetStatements.Add(new CodeAssignStatement(field_resCulture, newCulture)); // Add a doc comment to Culture property culture.Comments.Add(new CodeCommentStatement(DocCommentSummaryStart, true)); culture.Comments.Add(new CodeCommentStatement(SR.GetString(SR.CulturePropertyComment1), true)); culture.Comments.Add(new CodeCommentStatement(SR.GetString(SR.CulturePropertyComment2), true)); culture.Comments.Add(new CodeCommentStatement(DocCommentSummaryEnd, true)); }
private static bool DefineResourceFetchingProperty(String propertyName, String resourceName, ResourceData data, CodeTypeDeclaration srClass, bool internalClass, bool useStatic) { var prop = new CodeMemberProperty { Name = propertyName, HasGet = true, HasSet = false }; Type type = data.Type; if (type == null) { return(false); } if (type == typeof(MemoryStream)) { type = typeof(UnmanagedMemoryStream); } // Ensure type is internalally visible. This is necessary to ensure // users can access classes via a base type. Imagine a class like // Image or Stream as a internalally available base class, then an // internal type like MyBitmap or __UnmanagedMemoryStream as an // internal implementation for that base class. For internalally // available strongly typed resource classes, we must return the // internal type. For simplicity, we'll do that for internal strongly // typed resource classes as well. Ideally we'd also like to check // for interfaces like IList, but I don't know how to do that without // special casing collection interfaces & ignoring serialization // interfaces or IDisposable. while (!type.IsPublic) { type = type.BaseType; } var valueType = new CodeTypeReference(type); prop.Type = valueType; if (internalClass) { prop.Attributes = MemberAttributes.Assembly; } else { prop.Attributes = MemberAttributes.Public; } if (useStatic) { prop.Attributes |= MemberAttributes.Static; } // For Strings, emit this: // return ResourceManager.GetString("name", _resCulture); // For Streams, emit this: // return ResourceManager.GetStream("name", _resCulture); // For Objects, emit this: // Object obj = ResourceManager.GetObject("name", _resCulture); // return (MyValueType) obj; var resMgr = new CodePropertyReferenceExpression(null, "ResourceManager"); var resCultureField = new CodeFieldReferenceExpression((useStatic) ? null : new CodeThisReferenceExpression(), CultureInfoFieldName); bool isString = type == typeof(String); bool isStream = type == typeof(UnmanagedMemoryStream) || type == typeof(MemoryStream); String getMethodName; String text; String valueAsString = TruncateAndFormatCommentStringForOutput(data.ValueAsString); String typeName = String.Empty; if (!isString) // Stream or Object { typeName = TruncateAndFormatCommentStringForOutput(type.ToString()); } if (isString) { getMethodName = "GetString"; } else if (isStream) { getMethodName = "GetStream"; } else { getMethodName = "GetObject"; } if (isString) { text = SR.GetString(SR.StringPropertyComment, valueAsString); } else { // Stream or Object if (valueAsString == null || String.Equals(typeName, valueAsString)) // If the type did not override ToString, ToString just returns the type name. { text = SR.GetString(SR.NonStringPropertyComment, typeName); } else { text = SR.GetString(SR.NonStringPropertyDetailedComment, typeName, valueAsString); } } prop.Comments.Add(new CodeCommentStatement(DocCommentSummaryStart, true)); prop.Comments.Add(new CodeCommentStatement(text, true)); prop.Comments.Add(new CodeCommentStatement(DocCommentSummaryEnd, true)); var getValue = new CodeMethodInvokeExpression(resMgr, getMethodName, new CodePrimitiveExpression(resourceName), resCultureField); CodeMethodReturnStatement ret; if (isString || isStream) { ret = new CodeMethodReturnStatement(getValue); } else { var returnObj = new CodeVariableDeclarationStatement(typeof(Object), "obj", getValue); prop.GetStatements.Add(returnObj); ret = new CodeMethodReturnStatement(new CodeCastExpression(valueType, new CodeVariableReferenceExpression("obj"))); } prop.GetStatements.Add(ret); srClass.Members.Add(prop); return(true); }
private static CodeCompileUnit InternalCreate(Dictionary <String, ResourceData> resourceList, String baseName, String generatedCodeNamespace, String resourcesNamespace, CodeDomProvider codeProvider, bool internalClass, out String[] unmatchable) { if (baseName == null) { throw new ArgumentNullException(nameof(baseName)); } if (codeProvider == null) { throw new ArgumentNullException(nameof(codeProvider)); } // Keep a list of errors describing known strings that couldn't be // fixed up (like "4"), as well as listing all duplicate resources that // were fixed up to the same name (like "A B" and "A-B" both going to // "A_B"). var errors = new List <string>(); // Verify the resource names are valid property names, and they don't // conflict. This includes checking for language-specific keywords, // translating spaces to underscores, etc. SortedList <string, ResourceData> cleanedResourceList = VerifyResourceNames(resourceList, codeProvider, errors, out Dictionary <string, string> reverseFixupTable); // Verify the class name is legal. String className = baseName; // Attempt to fix up class name, and throw an exception if it fails. if (!codeProvider.IsValidIdentifier(className)) { String fixedClassName = VerifyResourceName(className, codeProvider); if (fixedClassName != null) { className = fixedClassName; } } if (!codeProvider.IsValidIdentifier(className)) { throw new ArgumentException(SR.GetString(SR.InvalidIdentifier, className)); } // If we have a namespace, verify the namespace is legal, // attempting to fix it up if needed. if (!String.IsNullOrEmpty(generatedCodeNamespace)) { if (!codeProvider.IsValidIdentifier(generatedCodeNamespace)) { String fixedNamespace = VerifyResourceName(generatedCodeNamespace, codeProvider, true); if (fixedNamespace != null) { generatedCodeNamespace = fixedNamespace; } } // Note we cannot really ensure that the generated code namespace // is a valid identifier, as namespaces can have '.' and '::', but // identifiers cannot. } var ccu = new CodeCompileUnit(); ccu.ReferencedAssemblies.Add("System.dll"); ccu.UserData.Add("AllowLateBound", false); ccu.UserData.Add("RequireVariableDeclaration", true); var ns = new CodeNamespace(generatedCodeNamespace); ns.Imports.Add(new CodeNamespaceImport("System")); ccu.Namespaces.Add(ns); // Generate class var srClass = new CodeTypeDeclaration(className); ns.Types.Add(srClass); AddGeneratedCodeAttributeforMember(srClass); TypeAttributes ta = internalClass ? TypeAttributes.NotPublic : TypeAttributes.Public; //ta |= TypeAttributes.Sealed; srClass.TypeAttributes = ta; srClass.Comments.Add(new CodeCommentStatement(DocCommentSummaryStart, true)); srClass.Comments.Add(new CodeCommentStatement(SR.GetString(SR.ClassDocComment), true)); var comment = new CodeCommentStatement(SR.GetString(SR.ClassComments1), true); srClass.Comments.Add(comment); comment = new CodeCommentStatement(SR.GetString(SR.ClassComments3), true); srClass.Comments.Add(comment); srClass.Comments.Add(new CodeCommentStatement(DocCommentSummaryEnd, true)); var debuggerAttrib = new CodeTypeReference(typeof(System.Diagnostics.DebuggerNonUserCodeAttribute)) { Options = CodeTypeReferenceOptions.GlobalReference }; srClass.CustomAttributes.Add(new CodeAttributeDeclaration(debuggerAttrib)); var compilerGenedAttrib = new CodeTypeReference(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute)) { Options = CodeTypeReferenceOptions.GlobalReference }; srClass.CustomAttributes.Add(new CodeAttributeDeclaration(compilerGenedAttrib)); // Figure out some basic restrictions to the code generation bool useStatic = internalClass || codeProvider.Supports(GeneratorSupport.PublicStaticMembers); EmitBasicClassMembers(srClass, generatedCodeNamespace, baseName, resourcesNamespace, internalClass, useStatic); // Now for each resource, add a property foreach (KeyValuePair <string, ResourceData> entry in cleanedResourceList) { String propertyName = entry.Key; // The resourceName will be the original value, before fixups, if any. if (reverseFixupTable.TryGetValue(propertyName, out string resourceName)) { resourceName = propertyName; } bool r = DefineResourceFetchingProperty(propertyName, resourceName, entry.Value, srClass, internalClass, useStatic); if (!r) { errors.Add(propertyName); } } unmatchable = errors.ToArray(); // Validate the generated class now CodeGenerator.ValidateIdentifiers(ccu); return(ccu); }