private static bool HasAttribute(XElement cp, string attributeName, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { bool found = cp.Attribute(attributeName) != null; if (!found) { foreach (var child in cp.Elements()) { string namespaceName = child.Name.NamespaceName; string[] typeAndProperty = child.Name.LocalName.Split('.'); if (typeAndProperty.Length == 2) { // First check if this is the right property. if (typeAndProperty[1].Trim() == attributeName) { // Then make sure this is not an attached property. bool isProperty = reflectionOnSeparateAppDomain.IsAssignableFrom( GeneratingCSharpCode.DefaultXamlNamespace.NamespaceName, "ContentPresenter", namespaceName, typeAndProperty[0]); if (isProperty) { found = true; break; } } } } } return(found); }
public static bool IsPropertyOrFieldACollection(XElement propertyElement, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain, bool isAttachedProperty) { if (isAttachedProperty) { string methodName = "Get" + propertyElement.Name.LocalName.Split('.')[1]; // In case of attached property, we check the return type of the method "GetPROPERTYNAME()". For example, in case of "Grid.Row", we check the return type of the method "Grid.GetRow()". XName elementName = propertyElement.Name.Namespace + propertyElement.Name.LocalName.Split('.')[0]; // eg. if the propertyElement is <VisualStateManager.VisualStateGroups>, this will be "DefaultNamespace+VisualStateManager" string namespaceName, localName, assemblyNameIfAny; GetClrNamespaceAndLocalName(elementName, out namespaceName, out localName, out assemblyNameIfAny); return(reflectionOnSeparateAppDomain.DoesMethodReturnACollection(methodName, namespaceName, localName, assemblyNameIfAny)); } else { var propertyOrFieldName = propertyElement.Name.LocalName.Split('.')[1]; //todo: keep the full local name (propertyElement.Name.LocalName) and pass it to the reflectionOnSeparateAppDomain method for the cases of binding on attached properties --> it will be used to get the type of the attached property and the actual name of the property. var parentElement = propertyElement.Parent; string parentNamespaceName, parentLocalName, parentAssemblyNameIfAny; GetClrNamespaceAndLocalName(parentElement.Name, out parentNamespaceName, out parentLocalName, out parentAssemblyNameIfAny); return(reflectionOnSeparateAppDomain.IsPropertyOrFieldACollection(propertyOrFieldName, parentNamespaceName, parentLocalName, parentAssemblyNameIfAny)); } }
internal static bool IsElementAMarkupExtension(XElement element, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { string elementLocalName, elementNameSpace, assemblyNameIfAny; GetClrNamespaceAndLocalName(element.Name, out elementNameSpace, out elementLocalName, out assemblyNameIfAny); return(reflectionOnSeparateAppDomain.IsElementAMarkupExtension(elementNameSpace, elementLocalName, assemblyNameIfAny)); }
static void TraverseNextElement(XElement currentElement, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { if (currentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "ColorAnimation") { //Storyboard.TargetProperty="Background.Color" XAttribute targetPropertyAttr = currentElement.Attribute("Storyboard.TargetProperty"); if (targetPropertyAttr != null) { string value = targetPropertyAttr.Value; if (!string.IsNullOrWhiteSpace(value) && !(value.TrimStart('(').TrimEnd(')') == "Color") && !value.TrimEnd(')').EndsWith(".Color")) { value += ".Color"; currentElement.Attribute("Storyboard.TargetProperty").SetValue(value); } } } // Recursion: foreach (var childElements in currentElement.Elements()) { TraverseNextElement(childElements, reflectionOnSeparateAppDomain); } }
public static string Convert( string xaml, string sourceFile, string fileNameWithPathRelativeToProjectRoot, string assemblyNameWithoutExtension, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain, bool isFirstPass, bool isSLMigration, string outputRootPath, string outputAppFilesPath, string outputLibrariesPath, string outputResourcesPath, ILogger logger) { // Process the "HtmlPresenter" nodes in order to "escape" its content, because the content is HTML and it could be badly formatted and not be parsable using XDocument.Parse. xaml = ProcessingHtmlPresenterNodes.Process(xaml); // Parse the XML: XDocument doc = XDocument.Parse(xaml, LoadOptions.SetLineInfo); // Process the "TextBlock" and "Span" nodes in order to surround direct text content with "<Run>" tags: ProcessingTextBlockNodes.Process(doc, reflectionOnSeparateAppDomain); // Insert implicit nodes in XAML: if (!isFirstPass) // Note: we skip this step during the 1st pass because some types are not known yet, so we cannot determine the default "ContentProperty". { InsertingImplicitNodes.InsertImplicitNodes(doc, reflectionOnSeparateAppDomain); FixingPropertiesOrder.FixPropertiesOrder(doc, reflectionOnSeparateAppDomain); // Process the "ContentPresenter" nodes in order to transform "<ContentPresenter />" into "<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" />" ProcessingContentPresenterNodes.Process(doc, reflectionOnSeparateAppDomain); // Process the "ColorAnimation" nodes in order to transform "Storyboard.TargetProperty="XXX"" into "Storyboard.TargetProperty="XXX.Color"" if XXX doesn't end on ".Color" or ".Color)" ProcessingColorAnimationNodes.Process(doc, reflectionOnSeparateAppDomain); // Convert markup extensions into XDocument nodes: InsertingMarkupNodesInXaml.InsertMarkupNodes(doc, reflectionOnSeparateAppDomain); // Fix names of visual states (for instance "PointerOver" in UWP becomes "MouseOver" in Silverlight and WPF). FixingVisualStatesName.Fix(doc, isSLMigration); } // Generate unique names for XAML elements: GeneratingUniqueNames.ProcessDocument(doc); // Prepare the code that will be put in the "InitializeComponent" of the Application class, which means that it will be executed when the application is launched: string codeToPutInTheInitializeComponentOfTheApplicationClass = string.Format(@" global::CSHTML5.Internal.StartupAssemblyInfo.OutputRootPath = @""{0}""; global::CSHTML5.Internal.StartupAssemblyInfo.OutputAppFilesPath = @""{1}""; global::CSHTML5.Internal.StartupAssemblyInfo.OutputLibrariesPath = @""{2}""; global::CSHTML5.Internal.StartupAssemblyInfo.OutputResourcesPath = @""{3}""; ", outputRootPath, outputAppFilesPath, outputLibrariesPath, outputResourcesPath); // Generate C# code from the tree: return(GeneratingCSharpCode.GenerateCSharpCode(doc, sourceFile, fileNameWithPathRelativeToProjectRoot, assemblyNameWithoutExtension, reflectionOnSeparateAppDomain, isFirstPass, isSLMigration, codeToPutInTheInitializeComponentOfTheApplicationClass, logger)); }
static void Main(string[] args) { #if NO_LONGER_USED // Create the logger: ILogger logger = new LoggerThatUsesConsoleWriteLine(); // Define main configuration: string csprojFilePath = @"..\..\..\InputProject\InputProject.csproj"; string outputPath = @"C:\DotNetForHtml5\TestOuptut2"; List <string> coreAssembliesForUseByXamlToCSharpConverter = new List <string>() { //@"C:\Program Files (x86)\Windows Kits\8.0\References\CommonConfiguration\Neutral\Windows.winmd" Path.GetFullPath(@"..\..\..\DotNetForHtml5.Core\bin\ForUseByXamlToCSharpConverter\CSharpXamlForHtml5.dll") }; List <string> coreAssembliesForUseByCSharpToExeOrDllCompiler = new List <string>() { Path.GetFullPath(@"..\..\..\DotNetForHtml5.Core\bin\Debug\CSharpXamlForHtml5.dll"), //Path.GetFullPath(@"..\..\..\DotNetForHtml5.Proxies\bin\Debug\DotNetForHtml5.Proxies.dll"), }; List <string> librariesFolders = new List <string>() { Path.GetFullPath(@"..\..\..\JSIL\Libraries"), Path.GetFullPath(@"..\..\..\OtherLibraries") }; using (var reflectionOnSeparateAppDomain = new ReflectionOnSeparateAppDomainHandler()) { reflectionOnSeparateAppDomain.LoadAssembly(Path.GetFullPath(@"..\..\..\DotNetForHtml5.Core\bin\ForUseByXamlToCSharpConverter\CSharpXamlForHtml5.dll")); //todo: call reflectionOnSeparateAppDomain.LoadAssembly to also load the assemblies that are referenced by the entry-point project // Start compilation: StandaloneCompilerTest.ProcessProject( Path.GetFullPath(csprojFilePath), outputPath, librariesFolders, reflectionOnSeparateAppDomain, coreAssembliesForUseByXamlToCSharpConverter, coreAssembliesForUseByCSharpToExeOrDllCompiler, logger); } // Finish: logger.WriteMessage(""); logger.WriteMessage("-------- COMPILATION FINISHED: Press any key to close --------"); Console.ReadLine(); #endif }
public static bool IsPropertyAttached(XElement propertyElement, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { string namespaceName, localName, assemblyNameIfAny; GetClrNamespaceAndLocalName(propertyElement.Name, out namespaceName, out localName, out assemblyNameIfAny); if (localName.Contains(".")) { var split = localName.Split('.'); var typeLocalName = split[0]; var propertyOrFieldName = split[1]; string parentNamespaceName, parentLocalTypeName, parentAssemblyIfAny; GetClrNamespaceAndLocalName(propertyElement.Parent.Name, out parentNamespaceName, out parentLocalTypeName, out parentAssemblyIfAny); return(reflectionOnSeparateAppDomain.IsPropertyAttached(propertyOrFieldName, namespaceName, typeLocalName, parentNamespaceName, parentLocalTypeName, assemblyNameIfAny)); } else { return(false); } }
public static void FixSelectorItemsSourceOrder(XElement currentElement, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { // Check if the current element is an object (rather than a property): bool isElementAnObjectRatherThanAProperty = !currentElement.Name.LocalName.Contains("."); if (isElementAnObjectRatherThanAProperty) { //check if the element has a attribute called ItemsSource: XAttribute itemsSourceAttribute = currentElement.Attribute(XName.Get("ItemsSource")); bool hasItemsSourceProperty = itemsSourceAttribute != null; //if the element has an attribute called ItemsSource, we check if it represents an object of a type that inherits from Selector: if (hasItemsSourceProperty) { // Get the namespace, local name, and optional assembly that correspond to the element: string namespaceName, localTypeName, assemblyNameIfAny; GettingInformationAboutXamlTypes.GetClrNamespaceAndLocalName(currentElement.Name, out namespaceName, out localTypeName, out assemblyNameIfAny); string selectorNamespaceName, selectorLocalTypeName, selectorAssemblyNameIfAny; GettingInformationAboutXamlTypes.GetClrNamespaceAndLocalName("Selector", out selectorNamespaceName, out selectorLocalTypeName, out selectorAssemblyNameIfAny); bool typeInheritsFromSelector = reflectionOnSeparateAppDomain.IsTypeAssignableFrom(namespaceName, localTypeName, assemblyNameIfAny, selectorNamespaceName, selectorLocalTypeName, selectorAssemblyNameIfAny); //Type elementType = reflectionOnSeparateAppDomain.GetCSharpEquivalentOfXamlType(namespaceName, localTypeName, assemblyNameIfAny); //if the type inherits from the element, we want to put the itemsSource attribute to the end: if (typeInheritsFromSelector) { itemsSourceAttribute.Remove(); currentElement.Add(itemsSourceAttribute); } //else we do nothing } //else we do nothing } //else we do nothing //we move on to the children of the element. if (currentElement.HasElements) { foreach (XElement child in currentElement.Elements()) { FixSelectorItemsSourceOrder(child, reflectionOnSeparateAppDomain); } } }
static void TraverseNextNode(XNode currentNode, int siblingNodesCount, XElement parentElement, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { if (currentNode is XText) { if ((parentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "TextBlock" && siblingNodesCount > 1) || // Note: if there is only one XNode inside a TextBlock, we do not surround with a <Run> for runtime performance optimization. parentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "Span" || parentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "Italic" || parentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "Underline" || parentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "Bold" || parentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "Hyperlink" || parentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "Paragraph") { // Surround with a <Run>: XElement contentWrapper = new XElement(GeneratingCSharpCode.DefaultXamlNamespace + "Run"); //todo: read the "ContentWrapperAttribute" of the collection (cf. InlineCollection.cs) instead of hard-coding this. XNode content = currentNode; currentNode.ReplaceWith(contentWrapper); contentWrapper.Add(content); } } // Recursion: if (currentNode is XElement) { List <XNode> childNodes = ((XElement)currentNode).Nodes().ToList(); // Node: we convert to list because the code inside the loop below is going to modify the collection, so a "foreach" is not appropriate. int childNodesCount = childNodes.Count; for (int i = 0; i < childNodesCount; i++) { TraverseNextNode(childNodes[i], childNodesCount, ((XElement)currentNode), reflectionOnSeparateAppDomain); } } }
//------------------------------------------------------------ // This class will process the "TextBlock" and "Span" nodes // in order to add the implicit "<Run>" elements. For example, // <TextBlock>This <Run>is</Run> a test</TextBlock> becomes: // <TextBlock><Run>This </Run><Run>is</Run><Run> a test</Run></TextBlock> // The goal is that any direct text becomes wrapped inside a <Run>. // SPECIAL CASE: if there is only one text inside the TextBlock, // we do not surround it with a <Run> for performance optimization. //------------------------------------------------------------ public static void Process(XDocument doc, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { TraverseNextNode(doc.Root, 1, null, reflectionOnSeparateAppDomain); }
static void TraverseNextElement(XElement currentElement, XNamespace lastDefaultNamespace, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { XNamespace currentDefaultNamespace = currentElement.GetDefaultNamespace(); if (currentDefaultNamespace == XNamespace.None) { currentDefaultNamespace = lastDefaultNamespace; } List <XAttribute> attributesToRemove = new List <XAttribute>(); var attributes = currentElement.Attributes().ToList <XAttribute>(); //we use a secondary list so that when we remove an element from the XAttributes, we don't skip the next element. foreach (XAttribute currentAttribute in attributes) { if (IsMarkupExtension(currentAttribute)) { string currentElementTypeName = currentElement.Name.LocalName.Split('.')[0]; string currentElementNamespaceName = currentElement.Name.NamespaceName; string currentAttributeTypeName; string currentAttributeName; string currentAttributeNamespaceName = currentAttribute.Name.NamespaceName; string currentAttributeValueEscaped = EscapeCommasInQuotes(currentAttribute.Value); // This will replace for example Fallback='3,3,3,3' with Fallback='3<COMMA>3<COMMA>3<COMMA>3' (to make later parsing easier) if (currentAttribute.Name.LocalName.Contains(".")) // case where the type of the currentAttribute is mentionned (ex : <Border Border.Background="..." />) { string[] attributeSplittedLocalName = currentAttribute.Name.LocalName.Split('.'); currentAttributeTypeName = attributeSplittedLocalName[0]; currentAttributeName = attributeSplittedLocalName[1]; } else // if the type is not mentionned, we assume the property is defined in the type of currentElement (ex : <Border Background="..." />) { currentAttributeNamespaceName = currentElementNamespaceName; currentAttributeTypeName = currentElementTypeName; currentAttributeName = currentAttribute.Name.LocalName; } if (string.IsNullOrEmpty(currentAttributeNamespaceName)) // if the namespace of the currentAttribute is still empty at this point, it means that currentAttribute is an attached property defined in the current default namespace. { currentAttributeNamespaceName = currentDefaultNamespace.NamespaceName; } if (currentElementNamespaceName == currentAttributeNamespaceName && currentElementTypeName == currentAttributeTypeName) // currentAttribute is a property defined in the type of currentElement (or one of his parents) { currentElement.Add(GenerateNodeForAttribute(currentElement.Name + ("." + currentAttributeName), currentAttributeValueEscaped, currentDefaultNamespace, reflectionOnSeparateAppDomain, currentElement.GetNamespaceOfPrefix)); } else // currentAttribute is an attached property { currentElement.Add(GenerateNodeForAttribute("{" + currentAttributeNamespaceName + "}" + currentAttributeTypeName + "." + currentAttributeName, currentAttributeValueEscaped, currentDefaultNamespace, reflectionOnSeparateAppDomain, currentElement.GetNamespaceOfPrefix)); } currentAttribute.Remove(); } } // Recursion: foreach (var childElements in currentElement.Elements()) { TraverseNextElement(childElements, currentDefaultNamespace, reflectionOnSeparateAppDomain); } }
//todo: support strings that contain commas, like in: {Binding Value, ConverterParameter = 'One, two, three, four, five, six', Mode = OneWay} internal static void InsertMarkupNodes(XDocument doc, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { TraverseNextElement(doc.Root, doc.Root.GetDefaultNamespace(), reflectionOnSeparateAppDomain); }
/// <summary> /// Generates a XElement with the name as given as a parameter and which contains the elements defined in the attributeValue parameter /// </summary> /// <param name="nodeName">the name Of the XElement to create (for example: Border.Background)</param> /// <param name="attributeValue">a string containing the whole attribute's definition (for example: "{Binding Toto, Mode = TwoWay, Converter = {StaticResource Myconverter}}")</param> /// <param name="reflectionOnSeparateAppDomain"></param> /// <returns></returns> private static XElement GenerateNodeForAttribute(XName nodeName, string attributeValue, XNamespace lastDefaultNamespace, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain, Func <string, XNamespace> getNamespaceOfPrefix) { //we get rid of the part that defines the type of the attribute (in the example: we get rid of "{Binding ") //if (attributeValue.StartsWith("{")) //{ // int indexOfFirstSpace = attributeValue.IndexOf(' '); // attributeValue = attributeValue.Remove(0, indexOfFirstSpace + 1); //+1 because we want to remove the space // //we also need to remove the '}' at the end: // attributeValue = attributeValue.Remove(attributeValue.Length - 1); //} Dictionary <string, string> listOfSubAttributes = GenerateListOfAttributesFromString(attributeValue); List <XElement> elementsToAdd = new List <XElement>(); List <XAttribute> attributesToAdd = new List <XAttribute>(); foreach (string keyString in listOfSubAttributes.Keys) { string currentAttribute = listOfSubAttributes[keyString]; bool isMarkupExtension = currentAttribute.StartsWith("{"); if (isMarkupExtension) { int indexOfNextClosingBracket = currentAttribute.IndexOf("}"); string contentBetweenBrackets = currentAttribute.Substring(1, indexOfNextClosingBracket - 1); isMarkupExtension = (!string.IsNullOrWhiteSpace(contentBetweenBrackets)); if (isMarkupExtension) { //We check whether the first character is a Number because of StringFormat (example: "{Binding ... StringFormat={0:N4}}" the "{0:N4}" part is not a MarkupExtension). string tempCurrentAttribute = contentBetweenBrackets.Trim(); char c = tempCurrentAttribute[0]; isMarkupExtension = !(c >= '0' && c <= '9'); } } if (isMarkupExtension) //example: {Binding Toto, Mode = TwoWay, Converter = {StaticResource Myconverter}} { //------------------------- // Markup Extension //------------------------- try { int indexOfCharacterAfterClassName = currentAttribute.IndexOf(' '); if (indexOfCharacterAfterClassName < 0) { indexOfCharacterAfterClassName = currentAttribute.IndexOf('}'); } string currentSubAttributeWithoutUselessPart = currentAttribute; string nextClassName = currentAttribute.Substring(0, indexOfCharacterAfterClassName); //nextClassName = "{Binding" nextClassName = nextClassName.Remove(0, 1); //we remove the '{' currentSubAttributeWithoutUselessPart = currentSubAttributeWithoutUselessPart.Remove(0, indexOfCharacterAfterClassName).Trim(); currentSubAttributeWithoutUselessPart = currentSubAttributeWithoutUselessPart.Remove(currentSubAttributeWithoutUselessPart.Length - 1, 1); //to remove the '}' at the end //we replace {TemplateBinding ...} with {Binding ..., RelativeSource={RelativeSource TemplatedParent}} if (nextClassName == "TemplateBinding") { nextClassName = "Binding"; currentSubAttributeWithoutUselessPart += ", RelativeSource={RelativeSource TemplatedParent}"; //we simply add it at the end because the order doesn't change anything. } // We add the suffix "Extension" to the markup extension name (unless it is a Binding or RelativeSource). For example, "StaticResource" becomes "StaticResourceExtension": if (!nextClassName.EndsWith("Binding") && !nextClassName.EndsWith("RelativeSource") && !nextClassName.EndsWith("Extension")) { // this is a trick, we need to check if : // - type named 'MyCurrentMarkupExtensionName' exist. // - if the previous does not exist, look for 'MyCurrentMarkupExtensionNameExtension'. // - if none exist throw an Exception // note: currently, this implementation can lead to a crash if a MarkupExtension is named "xxxxxExtensionExtension" and is used with "xxxxxExtension" in some xaml code. nextClassName += "Extension"; } // Determine the namespace and local name: XNamespace ns; string localName; if (!TryGetNamespaceFromNameThatMayHaveAPrefix(nextClassName, getNamespaceOfPrefix, out ns, out localName)) { ns = lastDefaultNamespace; localName = nextClassName; } // Generate the elements: XElement subXElement = GenerateNodeForAttribute(ns + localName, currentSubAttributeWithoutUselessPart, lastDefaultNamespace, reflectionOnSeparateAppDomain, getNamespaceOfPrefix); XElement subXElement1 = subXElement; if (!nodeName.LocalName.Contains('.')) { subXElement1 = new XElement(nodeName + "." + keyString, subXElement); } elementsToAdd.Add(subXElement1); } catch (Exception ex) { throw new wpf::System.Windows.Markup.XamlParseException("Error in the following markup extension: \"" + currentAttribute + "\". " + ex.Message); } } else //it can be directly set as an attribute because it is not a markupExtension: { //------------------------- // Not a markup extension //------------------------- string keyStringAfterPlaceHolderReplacement = keyString; if (keyString == "_placeHolderForDefaultValue") //this test is to replace the name of the attribute (which is in keyString) if it was a placeholder { string namespaceName, localName, assemblyNameIfAny; GettingInformationAboutXamlTypes.GetClrNamespaceAndLocalName(nodeName, out namespaceName, out localName, out assemblyNameIfAny); keyStringAfterPlaceHolderReplacement = reflectionOnSeparateAppDomain.GetContentPropertyName(namespaceName, localName, assemblyNameIfAny); } else if (keyStringAfterPlaceHolderReplacement.StartsWith("{")) //if we enter this if, it means that keyString is of the form "{Binding ElementName" so we want to remove "{Binding " { int indexOfFirstSpace = keyStringAfterPlaceHolderReplacement.IndexOf(' '); keyStringAfterPlaceHolderReplacement = keyStringAfterPlaceHolderReplacement.Remove(0, indexOfFirstSpace + 1); //+1 because we want to remove the space } if (currentAttribute.EndsWith("}")) { int i = 0; foreach (char c in currentAttribute) { if (c == '{') { ++i; } if (c == '}') { --i; } } if (i < 0) // We do not want to remove the last '}' if it comes from something like "StringFormat=This is {0}" (in Bindings here). { currentAttribute = currentAttribute.Remove(currentAttribute.Length - 1); } } currentAttribute = currentAttribute.Replace("<COMMA>", ","); // Unescape (cf. code where <COMMA> is added) XAttribute attribute = new XAttribute(keyStringAfterPlaceHolderReplacement, currentAttribute); attributesToAdd.Add(attribute); } } XNamespace xNamespace = @"http://schemas.microsoft.com/winfx/2006/xaml/presentation"; //todo: support markup extensions that use custom namespaces. string actualNodeName = nodeName.LocalName; string nodeNamespace = "{" + nodeName.NamespaceName + "}"; string[] splittedNodeName = actualNodeName.Split('.'); if (splittedNodeName.Length == 3) //case where we have an attached property (ex: <Border Canvas.Left={Binding...}) { actualNodeName = splittedNodeName[1] + "." + splittedNodeName[2]; } XElement xElement = new XElement(nodeNamespace + actualNodeName, attributesToAdd, elementsToAdd); return(xElement); }
static void TraverseNextElement(XElement currentElement, bool isInsideControlTemplate, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { if (currentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "ControlTemplate") { isInsideControlTemplate = true; } if (isInsideControlTemplate && currentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "ContentPresenter") { if (currentElement.Attribute("Content") == null) { if (!DoesContentPresenterContainDirectContent(currentElement)) { currentElement.Add(new XAttribute("Content", "{TemplateBinding Content}")); } } if (currentElement.Attribute("ContentTemplate") == null) //todo: also check if there is a <ContentPresenter.ContentTemplate> child node. { currentElement.Add(new XAttribute("ContentTemplate", "{TemplateBinding ContentTemplate}")); } } // Recursion: foreach (var childElements in currentElement.Elements()) { TraverseNextElement(childElements, isInsideControlTemplate, reflectionOnSeparateAppDomain); } }
public static bool Execute(bool isSecondPass, string flagsString, string referencePathsString, string sourceAssemblyForPass2, string nameOfAssembliesThatDoNotContainUserCode, bool isBridgeBasedVersion, bool isProcessingCSHTML5Itself, ILogger logger, string typeForwardingAssemblyPath) #endif { string passNumber = (isSecondPass ? "2" : "1"); string operationName = string.Format("C#/XAML for HTML5: BeforeXamlPreprocessor (pass {0})", passNumber); try { using (var executionTimeMeasuring = new ExecutionTimeMeasuring()) { //------- DISPLAY THE PROGRESS ------- logger.WriteMessage(operationName + " started."); //----------------------------------------------------- // Note: we create a static instance of the "ReflectionOnSeparateAppDomainHandler" to avoid reloading the assemblies for each XAML file. // We dispose the static instance in the "AfterXamlPreprocessor" task. //----------------------------------------------------- if (isSecondPass && string.IsNullOrEmpty(sourceAssemblyForPass2)) { throw new Exception(operationName + " failed because the SourceAssembly parameter was not specified during the second pass."); } // Create a new static instance of the "ReflectionOnSeparateAppDomainHandler": ReflectionOnSeparateAppDomainHandler.Current = new ReflectionOnSeparateAppDomainHandler(typeForwardingAssemblyPath); ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain = ReflectionOnSeparateAppDomainHandler.Current; #if BRIDGE //todo: if we are compiling CSHTML5 itself (or CSHTML5.Stubs), we need to process the XAML files in CSHTML5, // and for that we need to load the XAML types, so we need to load the previous version of CSHTML5 (from // the NuGet package). Note: this is not supposed to lead to a circular reference because it is only used // for the XamlPreprocessor to generate the .xaml.g.cs files from the .xaml files. // To do so, we need to stop skipping the processing of the CSHTML5 and CSHTML5.Stubs assemblies (c.f. // "Skip the assembly if it is not a user assembly" in "LoadAndProcessReferencedAssemblies"). #endif // we load the source assembly early in case we are processing the CSHTML5. if (isSecondPass && isProcessingCSHTML5Itself) { reflectionOnSeparateAppDomain.LoadAssembly(sourceAssemblyForPass2, loadReferencedAssembliesToo: true, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode); } #if CSHTML5BLAZOR // work-around: reference path string is not correctly setted so we set it manually string referencePathsString = OpenSilverHelper.ReferencePathsString(resolvedReferences); #endif // Retrieve paths of referenced .dlls and load them: HashSet <string> referencePaths = (referencePathsString != null) ? new HashSet <string>(referencePathsString.Split(';')) : new HashSet <string>(); referencePaths.RemoveWhere(s => !s.ToLower().EndsWith(".dll") || s.Contains("DotNetBrowser") || s.ToLower().EndsWith(@"\bridge.dll")); foreach (string referencedAssembly in AssembliesLoadHelper.EnsureCoreAssemblyIsFirstInList(referencePaths)) // Note: we ensure that the Core assembly is loaded first so that types such as "XmlnsDefinitionAttribute" are known when loading the other assemblies. { reflectionOnSeparateAppDomain.LoadAssembly(referencedAssembly, loadReferencedAssembliesToo: false, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode); } // Load "mscorlib.dll" too (this is useful for resolving Mscorlib types in XAML, such as <system:String x:Key="TestString" xmlns:system="clr-namespace:System;assembly=mscorlib">Test</system:String>) reflectionOnSeparateAppDomain.LoadAssemblyMscorlib(isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode); // Load for reflection the source assembly itself and the referenced assemblies if second path: if (isSecondPass && !isProcessingCSHTML5Itself) { reflectionOnSeparateAppDomain.LoadAssembly(sourceAssemblyForPass2, loadReferencedAssembliesToo: true, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode); } bool isSuccess = true; //------- DISPLAY THE PROGRESS ------- logger.WriteMessage(operationName + (isSuccess ? " completed in " + executionTimeMeasuring.StopAndGetTimeInSeconds() + " seconds." : " failed.") + "\". IsSecondPass: "******". Source assembly file: \"" + (sourceAssemblyForPass2 ?? "").ToString()); return(isSuccess); } } catch (Exception ex) { if (ReflectionOnSeparateAppDomainHandler.Current != null) { ReflectionOnSeparateAppDomainHandler.Current.Dispose(); } logger.WriteError(operationName + " failed: " + ex.ToString()); return(false); } }
public static bool Execute(string sourceFile, string outputFile, string fileNameWithPathRelativeToProjectRoot, string assemblyNameWithoutExtension, string coreAssemblyFiles, bool isSecondPass, bool isSLMigration, ILogger logger, string activationAppPath, string cSharpXamlForHtml5OutputType, bool overrideOutputOnlyIfSourceHasChanged, string outputRootPath, string outputAppFilesPath, string outputLibrariesPath, string outputResourcesPath, string flagsString, bool isBridgeBasedVersion, string nameOfAssembliesThatDoNotContainUserCode) { string passNumber = (isSecondPass ? "2" : "1"); string operationName = string.Format("C#/XAML for HTML5: XamlPreprocessor (pass {0})", passNumber); try { using (var executionTimeMeasuring = new ExecutionTimeMeasuring()) { // Validate input strings: if (string.IsNullOrEmpty(sourceFile)) { throw new Exception(operationName + " failed because the source file argument is invalid."); } if (string.IsNullOrEmpty(outputFile)) { throw new Exception(operationName + " failed because the output file argument is invalid."); } if (string.IsNullOrEmpty(fileNameWithPathRelativeToProjectRoot)) { throw new Exception(operationName + " failed because the FileNameWithPathRelativeToProjectRoot argument is invalid."); } if (string.IsNullOrEmpty(assemblyNameWithoutExtension)) { throw new Exception(operationName + " failed because the AssemblyNameWithoutExtension argument is invalid."); } if (string.IsNullOrEmpty(coreAssemblyFiles)) { throw new Exception(operationName + " failed because the core assembly file argument is invalid."); } HashSet <string> flags = (flagsString != null ? new HashSet <string>(flagsString.Split(';')) : new HashSet <string>()); #if REQUIRE_ACTIVATION_FOR_SILVERLIGHT_MIGRATION #if SILVERLIGHTCOMPATIBLEVERSION #if !BRIDGE //------- Check SL Migration license (unless we are compiling a class library, in which case we do not want to check so that it is more convenient for developing Extensions that work with any CSHTML5 edition) ------- if (cSharpXamlForHtml5OutputType == null || cSharpXamlForHtml5OutputType.ToLower() != "library") { if (!CheckSLMigrationLicense(logger, activationAppPath, flags)) { return(false); } } #endif #endif #endif //------- DISPLAY THE PROGRESS ------- logger.WriteMessage(operationName + " started for file \"" + sourceFile + "\". Output file: \"" + outputFile + "\". FileNameWithPathRelativeToProjectRoot: \"" + fileNameWithPathRelativeToProjectRoot + "\". AssemblyNameWithoutExtension: \"" + assemblyNameWithoutExtension + "\". Core assembly files: \"" + coreAssemblyFiles + "\". IsSecondPass: "******"\"."); //todo: do not display the output file location? // Read the XAML file: using (StreamReader sr = new StreamReader(sourceFile)) { String xaml = sr.ReadToEnd(); // Determine if the file should be processed or if there is no need to process it again (for example if the XAML has not changed and we are in design-time, we don't want to re-process the XAML): bool shouldTheFileBeProcessed = DetermineIfTheXamlFileNeedsToBeProcessed(xaml, outputFile, overrideOutputOnlyIfSourceHasChanged, isSecondPass); if (shouldTheFileBeProcessed) { // The "ReflectionOnSeparateAppDomainHandler" class lets us use a separate AppDomain to resolve the types so that the types can be unloaded when done (when disposed, it frees any hook on the user application DLL's): ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain = ReflectionOnSeparateAppDomainHandler.Current; // Note: this is not supposed to be null because it was instantiated in the "BeforeXamlPreprocessor" task. We use a static instance to avoid reloading the assemblies for each XAML file that is processed. // Make sure that the reference is not null: if (reflectionOnSeparateAppDomain == null) { throw new Exception("ReflectionOnSeparateAppDomainHandler.Current is null. It should not be null because it was supposed to be populated by the 'BeforeXamlPreprocessor' task. Please verify that the MSBuild Targets are up to date."); } // Convert XAML to CS: string generatedCode = ConvertingXamlToCSharp.Convert(xaml, sourceFile, fileNameWithPathRelativeToProjectRoot, assemblyNameWithoutExtension, reflectionOnSeparateAppDomain, isFirstPass: !isSecondPass, isSLMigration: isSLMigration, outputRootPath: outputRootPath, outputAppFilesPath: outputAppFilesPath, outputLibrariesPath: outputLibrariesPath, outputResourcesPath: outputResourcesPath, logger: logger); // Add the header that contains the file hash so as to avoid re-processing the file if not needed: generatedCode = CreateHeaderContainingHash(generatedCode, xaml, isSecondPass) + Environment.NewLine + Environment.NewLine + generatedCode; // Create output directory: Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); // Save output: using (StreamWriter outfile = new StreamWriter(outputFile)) { outfile.Write(generatedCode); } } } //------- DISPLAY THE PROGRESS ------- logger.WriteMessage(operationName + " completed in " + executionTimeMeasuring.StopAndGetTimeInSeconds() + " seconds."); return(true); } } catch (Exception ex) { //----------------------------------------------------- // Dispose the static instance of the "ReflectionOnSeparateAppDomainHandler": //----------------------------------------------------- /* * We dispose the static instance of the "ReflectionOnSeparateAppDomainHandler" * that was created in the "BeforeXamlPreprocessor" task, in order to free any * hooks on the user app DLL's. * Note: this is normally done in the task named "AfterXamlPreprocessor", but * since we are going to cancel the Build process, that task will never be * executed, resulting in potential hooks to the DLL not being freed (causing * issues when the user recompiles his application). So we free them now. */ ReflectionOnSeparateAppDomainHandler.Current.Dispose(); // Note: this is not supposed to be null because it was instantiated in the "BeforeXamlPreprocessor" task. //----------------------------------------------------- // Display the error and cancel the Build process: //----------------------------------------------------- string message = $"{operationName} failed: {ex.Message}\nNote: the XAML editor sometimes raises errors that are misleading. To see only real non-misleading errors, make sure to close all the XAML editor windows/tabs before compiling."; if (ex is wpf::System.Windows.Markup.XamlParseException) { int lineNumber = ((wpf::System.Windows.Markup.XamlParseException)ex).LineNumber; logger.WriteError(message, file: sourceFile, lineNumber: lineNumber); } else { logger.WriteError(message, file: sourceFile); } return(false); } }
private static void ProcessXamlFile(string sourceFileWithAbsolutePath, string destinationFileWithAbsolutePath, string csprojFilePath, string tempFolder, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain, ILogger logger) { // Read XAML file: using (StreamReader sr = new StreamReader(sourceFileWithAbsolutePath)) { String xaml = sr.ReadToEnd(); // Convert XAML to CS: string generatedCode = ConvertingXamlToCSharp.Convert(xaml, sourceFileWithAbsolutePath, "", "", reflectionOnSeparateAppDomain, isFirstPass: false, isSLMigration: false, outputRootPath: "", outputAppFilesPath: "", outputLibrariesPath: "", outputResourcesPath: "", logger: logger); //todo: fill the missing arguments //todo: support first and second pass? // Save output: using (StreamWriter outfile = new StreamWriter(destinationFileWithAbsolutePath + ".g.cs")) { outfile.Write(generatedCode); } } //File.Copy(sourceFileWithAbsolutePath, destinationFileWithAbsolutePath + ".g.cs", true); }
private static void ProcessFile(string filePathRelativeToProjectFolder, string csprojFilePath, string tempFolder, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain, ILogger logger) { if (filePathRelativeToProjectFolder.StartsWith(".") || filePathRelativeToProjectFolder.Contains(":")) { throw new Exception("The files included in the project must be located inside the project folder."); } // Calculte absolute source file with path: string sourceFileWithAbsolutePath = Path.Combine(Path.GetDirectoryName(csprojFilePath), filePathRelativeToProjectFolder); // Calculate absolute dest file with path: string destinationFileWithAbsolutePath = Path.Combine(tempFolder, filePathRelativeToProjectFolder); // Create destination directory: Directory.CreateDirectory(Path.GetDirectoryName(destinationFileWithAbsolutePath)); // Process file: if (filePathRelativeToProjectFolder.ToLower().EndsWith(".cs")) { ProcessCSharpFile(sourceFileWithAbsolutePath, destinationFileWithAbsolutePath, csprojFilePath, tempFolder, reflectionOnSeparateAppDomain); } else if (filePathRelativeToProjectFolder.ToLower().EndsWith(".xaml")) { ProcessXamlFile(sourceFileWithAbsolutePath, destinationFileWithAbsolutePath, csprojFilePath, tempFolder, reflectionOnSeparateAppDomain, logger); } }
static void IsReferenceAcceptable(string referenceName, string referenceHintPath, string allowedAssemblies, HashSet <string> flags, bool isBridgeBasedVersion, string nameOfAssembliesThatDoNotContainUserCode, string projectDir, string referencesPaths, string typeForwardingAssemblyPath, out ResultEnum result, out string minimumRequiredCompilerVersionFriendlyNameIfAny) { minimumRequiredCompilerVersionFriendlyNameIfAny = null; //-------------------------------------------------------------------------------- // This method will verify that the referenced DLL is itself of type C#/XAML for HTML5, and that the minimum version number is OK. //-------------------------------------------------------------------------------- #if !BRIDGE // Check if the reference is a file that belongs to "C#/XAML for HTML5": if (referenceName.ToLower().Contains(Constants.LOWERCASE_CORE_ASSEMBLY_NAME) || (!string.IsNullOrEmpty(referenceHintPath) && (Path.GetFileName(referenceHintPath)).ToLower().Contains(Constants.LOWERCASE_CORE_ASSEMBLY_NAME))) { result = ResultEnum.ReferenceIsOK; return; } #else // In the Bridge version, we use the "AllowedAssemblies" below to allow the CSHTML5 assemblies. #endif // Check if the reference is among the specially allowed ones: string[] allowedAssembliesArray = allowedAssemblies != null?allowedAssemblies.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries) : new string[] { }; foreach (var allowedAssembly in allowedAssembliesArray) { if (referenceName.ToLower() == allowedAssembly.ToLower()) { result = ResultEnum.ReferenceIsOK; return; } } // Otherwise, check if the reference was compiled with "C#/XAML for HTML5" // and if the user is running the Professional Edition // and if the version of the compiler is OK: if (!string.IsNullOrEmpty(referenceHintPath)) { // Perform reflection on a separate AppDomain so that the types loaded for reflection can be unloaded when done. using (var reflectionOnSeparateAppDomain = new ReflectionOnSeparateAppDomainHandler(typeForwardingAssemblyPath)) { #if BRIDGE // In the Bridge.NET version, we use "Assembly.LoadFile" instead of "Assembly.LoadFrom", so we need to convert a Relative path into an Absolute path: if (!Path.IsPathRooted(referenceHintPath)) { referenceHintPath = Path.Combine(projectDir, referenceHintPath); } // Verify that the file was found: if (!File.Exists(referenceHintPath)) { throw new CompilationExceptionWithOptions("File not found: " + referenceHintPath) { DisplayOnlyTheMessageInTheOutputNothingElse = true } } ; // In the Bridge.NET version, we need to preload the CSHTML5 assemblies so that it can find types such as "XmlnsDefinitionAttribute" or "CompilerVersionNumberAttribute": HashSet <string> referencesPathsHasSet = (referencesPaths != null) ? new HashSet <string>(referencesPaths.Split(';')) : new HashSet <string>(); referencesPathsHasSet.RemoveWhere(s => !s.ToLower().EndsWith(".dll") || s.Contains("DotNetBrowser") || s.ToLower().EndsWith(@"\bridge.dll")); foreach (string referencedAssembly in AssembliesLoadHelper.EnsureCoreAssemblyIsFirstInList(referencesPathsHasSet)) // Note: we ensure that the Core assembly is loaded first so that types such as "XmlnsDefinitionAttribute" are known when loading the other assemblies. { reflectionOnSeparateAppDomain.LoadAssembly(referencedAssembly, loadReferencedAssembliesToo: false, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode, skipReadingAttributesFromAssemblies: false); } #endif string assemblySimpleName = reflectionOnSeparateAppDomain.LoadAssembly(referenceHintPath, loadReferencedAssembliesToo: false, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode, skipReadingAttributesFromAssemblies: false); //----------------------------------------------------------------------------- // Check if the reference was compiled with "C#/XAML for HTML5" by reading the "C#/XAML for HTML5" compiler version attributes: //----------------------------------------------------------------------------- string compilerVersionNumber = reflectionOnSeparateAppDomain.GetCSharpXamlForHtml5CompilerVersionNumberOrNull(assemblySimpleName); string compilerVersionFriendlyName = reflectionOnSeparateAppDomain.GetCSharpXamlForHtml5CompilerVersionFriendlyNameOrNull(assemblySimpleName); // If at least one of those attributes exists, it means that the assembly was compiled with C#/XAML fot HTML5: bool wasCompiledWithCSharpXamlForHtml5 = (compilerVersionNumber != null || compilerVersionFriendlyName != null); if (wasCompiledWithCSharpXamlForHtml5) { // The reference is OK, it was compiled with "C#/XAML for HTML5". //---------------------------------------------------------------------- // Now check if the "minimum compiler version" (if any) required by the DLL is compatible with the current version: //---------------------------------------------------------------------- string minimumRequiredCompilerVersionNumberIfAny = reflectionOnSeparateAppDomain.GetCSharpXamlForHtml5MinimumRequiredCompilerVersionNumberOrNull(assemblySimpleName); minimumRequiredCompilerVersionFriendlyNameIfAny = reflectionOnSeparateAppDomain.GetCSharpXamlForHtml5MinimumRequiredCompilerVersionFriendlyNameOrNull(assemblySimpleName); if (minimumRequiredCompilerVersionNumberIfAny == null || compilerVersionNumber == null || (new Version(minimumRequiredCompilerVersionNumberIfAny) <= new Version(compilerVersionNumber))) { // The reference is OK, the version of the "C#/XAML for HTML5" compiler is compatible. #if REQUIRE_PRO_EDITION_FOR_REFERENCING_ASSEMBLIES //------------------------------------------------------------------------- // Now check if the user is running the Professional Edition, which is required for referencing non-default DLLs: //------------------------------------------------------------------------- if (ActivationHelpers.IsFeatureEnabled(Constants.ENTERPRISE_EDITION_FEATURE_ID, flags)) { // It's OK to proceed, the Enterprise Edition is being used. // Everything is OK: result = ResultEnum.ReferenceIsOK; return; } else if (ActivationHelpers.IsFeatureEnabled(Constants.SL_MIGRATION_EDITION_FEATURE_ID, flags)) { // It's OK to proceed, the SL Migration Edition is being used. //todo: once (if it happens) sl migration and enterprise editions will be different, add a test like for the professional edition. // Everything is OK: result = ResultEnum.ReferenceIsOK; return; } else if (ActivationHelpers.IsFeatureEnabled(Constants.PROFESSIONAL_EDITION_FEATURE_ID, flags)) { // It's OK to proceed, the Professional Edition is being used. //todo: actually test what the references tries to use to make sure it limits to the professional edition // Everything is OK: result = ResultEnum.ReferenceIsOK; return; } else { // Display the activation app and, if we are not in trial mode, stop the compilation and raise the compilation error: result = ResultEnum.ReferenceRequiresToUpgradeToTheProEdition; return; } #else // Everything is OK: result = ResultEnum.ReferenceIsOK; return; #endif } else { // A new version of the compiler is required: result = ResultEnum.ReferenceRequiresNewerVersionOfTheCompiler; if (minimumRequiredCompilerVersionFriendlyNameIfAny == null) { minimumRequiredCompilerVersionFriendlyNameIfAny = "Build " + minimumRequiredCompilerVersionNumberIfAny; } return; } } else { // Otherwise, fail: result = ResultEnum.ReferenceIsNotCompatible; return; } } } else { // Otherwise, fail: result = ResultEnum.ReferenceIsNotCompatible; return; } }
static void TraverseNextElement(XElement currentElement, bool isInsideControlTemplate, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { if (currentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "ControlTemplate") { isInsideControlTemplate = true; } if (isInsideControlTemplate && !currentElement.Name.LocalName.Contains(".")) { bool isContentPresenter = reflectionOnSeparateAppDomain.IsAssignableFrom( GeneratingCSharpCode.DefaultXamlNamespace.NamespaceName, "ContentPresenter", currentElement.Name.NamespaceName, currentElement.Name.LocalName); if (isContentPresenter) { if (!HasAttribute(currentElement, "Content", reflectionOnSeparateAppDomain)) { currentElement.Add(new XAttribute("Content", "{TemplateBinding Content}")); } if (!HasAttribute(currentElement, "ContentTemplate", reflectionOnSeparateAppDomain)) { currentElement.Add(new XAttribute("ContentTemplate", "{TemplateBinding ContentTemplate}")); } } } // Recursion: foreach (var childElements in currentElement.Elements()) { TraverseNextElement(childElements, isInsideControlTemplate, reflectionOnSeparateAppDomain); } }
internal static string GetKeyNameOfProperty(XElement element, string propertyName, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { string elementLocalName, elementNameSpace, assemblyNameIfAny; GetClrNamespaceAndLocalName(element.Name, out elementNameSpace, out elementLocalName, out assemblyNameIfAny); return(reflectionOnSeparateAppDomain.GetKeyNameOfProperty(elementNameSpace, elementLocalName, assemblyNameIfAny, propertyName)); }
//internal static bool IsElementAnUIElement(XElement element, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) //{ // string elementLocalName, elementNameSpace, assemblyNameIfAny; // GetClrNamespaceAndLocalName(element.Name, out elementNameSpace, out elementLocalName, out assemblyNameIfAny); // return reflectionOnSeparateAppDomain.IsElementAnUIElement(elementNameSpace, elementLocalName, assemblyNameIfAny); //} internal static bool IsTypeAssignableFrom(XName elementOfTypeToAssignFrom, XName elementOfTypeToAssignTo, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain, bool isAttached = false) { string nameOfTypeToAssignFrom, nameSpaceOfTypeToAssignFrom, assemblyNameOfTypeToAssignFrom; GetClrNamespaceAndLocalName(elementOfTypeToAssignFrom, out nameSpaceOfTypeToAssignFrom, out nameOfTypeToAssignFrom, out assemblyNameOfTypeToAssignFrom); string nameOfTypeToAssignTo, nameSpaceOfTypeToAssignTo, assemblyNameOfTypeToAssignTo; GetClrNamespaceAndLocalName(elementOfTypeToAssignTo, out nameSpaceOfTypeToAssignTo, out nameOfTypeToAssignTo, out assemblyNameOfTypeToAssignTo); return(reflectionOnSeparateAppDomain.IsTypeAssignableFrom(nameSpaceOfTypeToAssignFrom, nameOfTypeToAssignFrom, assemblyNameOfTypeToAssignFrom, nameSpaceOfTypeToAssignTo, nameOfTypeToAssignTo, assemblyNameOfTypeToAssignTo, isAttached)); }
private static void ProcessCSharpFile(string sourceFileWithAbsolutePath, string destinationFileWithAbsolutePath, string csprojFilePath, string tempFolder, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { File.Copy(sourceFileWithAbsolutePath, destinationFileWithAbsolutePath, true); }
static void TraverseNextElement(XElement currentElement, XNamespace lastDefaultNamespace, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { XNamespace currentDefaultNamespace = currentElement.GetDefaultNamespace(); if (currentDefaultNamespace == XNamespace.None) { currentDefaultNamespace = lastDefaultNamespace; } List <XAttribute> attributesToRemove = new List <XAttribute>(); // We use a secondary list so that when we remove an element from the XAttributes, we don't skip the next element. // We reverse the list because we want to preserve the order in which we set the object properties. Since XContainer // only allow to add a child at the start or the end, we need call XContainer.AddFirst(element) while iterating on the // reverse list of attributes to do so. var attributes = currentElement.Attributes().Reverse().ToList(); foreach (XAttribute currentAttribute in attributes) { if (IsMarkupExtension(currentAttribute)) { // Skip if the attribute has a namespace but no "dot", such as d:DataContext="{...}", so that it is in line with what we do in "GeneratingCSharpCode.cs". This is actually needed to be able to compile because something like d:DataContext="{d:ClassThatDoesNotExist ...}" where "d" is in the list of "mc:Ignorable". //todo: a better approach would be to remove all attributes that have a prefix in the list of "mc:Ignorable". if (string.IsNullOrEmpty(currentAttribute.Name.NamespaceName) || currentAttribute.Name.LocalName.Contains(".")) { string currentElementTypeName = currentElement.Name.LocalName.Split('.')[0]; string currentElementNamespaceName = currentElement.Name.NamespaceName; string currentAttributeTypeName; string currentAttributeName; string currentAttributeNamespaceName = currentAttribute.Name.NamespaceName; string currentAttributeValueEscaped = EscapeCommasInQuotes(currentAttribute.Value); // This will replace for example Fallback='3,3,3,3' with Fallback='3<COMMA>3<COMMA>3<COMMA>3' (to make later parsing easier) if (currentAttribute.Name.LocalName.Contains(".")) // case where the type of the currentAttribute is mentionned (ex : <Border Border.Background="..." />) { string[] attributeSplittedLocalName = currentAttribute.Name.LocalName.Split('.'); currentAttributeTypeName = attributeSplittedLocalName[0]; currentAttributeName = attributeSplittedLocalName[1]; } else // if the type is not mentionned, we assume the property is defined in the type of currentElement (ex : <Border Background="..." />) { currentAttributeNamespaceName = currentElementNamespaceName; currentAttributeTypeName = currentElementTypeName; currentAttributeName = currentAttribute.Name.LocalName; } if (string.IsNullOrEmpty(currentAttributeNamespaceName)) // if the namespace of the currentAttribute is still empty at this point, it means that currentAttribute is an attached property defined in the current default namespace. { currentAttributeNamespaceName = currentDefaultNamespace.NamespaceName; } if (currentElementNamespaceName == currentAttributeNamespaceName && currentElementTypeName == currentAttributeTypeName) // currentAttribute is a property defined in the type of currentElement (or one of his parents) { currentElement.AddFirst(GenerateNodeForAttribute(currentElement.Name + ("." + currentAttributeName), currentAttributeValueEscaped, currentDefaultNamespace, reflectionOnSeparateAppDomain, currentElement.GetNamespaceOfPrefix)); } else // currentAttribute is an attached property { currentElement.AddFirst(GenerateNodeForAttribute("{" + currentAttributeNamespaceName + "}" + currentAttributeTypeName + "." + currentAttributeName, currentAttributeValueEscaped, currentDefaultNamespace, reflectionOnSeparateAppDomain, currentElement.GetNamespaceOfPrefix)); } currentAttribute.Remove(); } } } // Recursion: foreach (var childElements in currentElement.Elements()) { TraverseNextElement(childElements, currentDefaultNamespace, reflectionOnSeparateAppDomain); } }
internal static void ReadAllFilesInSCProjAndCopyProcessThem( string csProjFilePath, string tempFolder, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain, List <string> coreAssembliesForUseByXamlToCSharpConverter, List <string> coreAssembliesForUseByCSharpToExeOrDllCompiler, ILogger logger) { // Load CSProj: Project project = new Project(new Engine()); project.Load(csProjFilePath); // Process files: //foreach (BuildItem buildItem in Enumerable.SelectMany<BuildItemGroup, BuildItem, BuildItem>(Enumerable.Cast<BuildItemGroup>((IEnumerable)project.ItemGroups), (Func<BuildItemGroup, IEnumerable<BuildItem>>)(grp => Enumerable.Cast<BuildItem>((IEnumerable)grp)), (Func<BuildItemGroup, BuildItem, BuildItem>)((grp, item) => item))) foreach (BuildItemGroup buildItemGroup in project.ItemGroups) { List <BuildItem> buildItemsToRemove = new List <BuildItem>(); foreach (BuildItem buildItem in buildItemGroup) { if ((buildItem.Name == "Compile" || buildItem.Name == "Content") && !string.IsNullOrEmpty(buildItem.Include) && (buildItem.Include.ToLower().EndsWith(".cs") || buildItem.Include.ToLower().EndsWith(".xaml"))) { ProcessFile(buildItem.Include, csProjFilePath, tempFolder, reflectionOnSeparateAppDomain, logger); // In the CSPROJ, rename ".xaml" files to ".xaml.g.cs", and change their BuildAction from "Content" to "Compile" if (buildItem.Include.ToLower().EndsWith(".xaml")) { buildItem.Include = buildItem.Include + ".g.cs"; buildItem.Name = "Compile"; } } else if (buildItem.Name == "ProjectReference") { foreach (string coreAssemblyFilePath in coreAssembliesForUseByCSharpToExeOrDllCompiler) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(coreAssemblyFilePath); if (buildItem.Include.EndsWith(fileNameWithoutExtension + ".csproj")) { buildItemsToRemove.Add(buildItem); } } } } // Remove build items marked for removal: foreach (BuildItem buildItemToRemove in buildItemsToRemove) { buildItemGroup.RemoveItem(buildItemToRemove); } } // Add reference to the core assemblies: if (coreAssembliesForUseByXamlToCSharpConverter.Count > 0) { BuildItemGroup newBuildItemGroup = project.AddNewItemGroup(); foreach (string coreAssemblyFilePath in coreAssembliesForUseByCSharpToExeOrDllCompiler) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(coreAssemblyFilePath); BuildItem newBuildItem = newBuildItemGroup.AddNewItem("Reference", fileNameWithoutExtension); newBuildItem.SetMetadata("HintPath", coreAssemblyFilePath); } } // Remove the build target, since we are handling the compilation manually: Import importToRemove = null; foreach (Import import in project.Imports) { if (import.ProjectPath.EndsWith("Build.targets")) { importToRemove = import; } } if (importToRemove != null) { project.Imports.RemoveImport(importToRemove); } //todo: copy other file types to temp folder (images, etc.) ? // Save the modified CSProj to the temp folder: project.Save(Path.Combine(tempFolder, Path.GetFileName(csProjFilePath))); }
public static void InsertImplicitNodes(XDocument doc, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { Stack <List <int> > indexesMapper = new Stack <List <int> >(); TraverseNextElement(doc.Root, 0, indexesMapper, reflectionOnSeparateAppDomain); }
public static bool Start(string assemblyPath, string outputPathAbsolute, string outputResourcesPath, HashSet <string> simpleNameOfAssembliesToIgnore, HashSet <string> supportedExtensionsLowercase, ILogger logger, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain, bool isBridgeBasedVersion, string nameOfAssembliesThatDoNotContainUserCode, out List <string> resXFilesCopied) { resXFilesCopied = new List <string>(); List <string> assemblySimpleNames; reflectionOnSeparateAppDomain.LoadAssemblyAndAllReferencedAssembliesRecursively( assemblyPath, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode, skipReadingAttributesFromAssemblies: true, assemblySimpleNames: out assemblySimpleNames); foreach (string assemblySimpleName in assemblySimpleNames) { if (!simpleNameOfAssembliesToIgnore.Contains(assemblySimpleName)) { //----------------------------------------------- // Process JavaScript, CSS, Image, Video, Audio files: //----------------------------------------------- #if !CSHTML5BLAZOR Dictionary <string, byte[]> jsAndCssFiles = reflectionOnSeparateAppDomain.GetResources(assemblySimpleName, supportedExtensionsLowercase); #else Dictionary <string, byte[]> jsAndCssFiles = reflectionOnSeparateAppDomain.GetManifestResources(assemblySimpleName, supportedExtensionsLowercase); #endif // Copy files: foreach (KeyValuePair <string, byte[]> file in jsAndCssFiles) { string fileName = file.Key; byte[] fileContent = file.Value; // Combine the root output path and the relative "resources" folder path, while also ensuring that there is no forward slash, and that the path ends with a backslash: string absoluteOutputResourcesPath = PathsHelper.CombinePathsWhileEnsuringEndingBackslashAndMore(outputPathAbsolute, outputResourcesPath); // Create the destination folders hierarchy if it does not already exist: string destinationFile = Path.Combine(absoluteOutputResourcesPath, assemblySimpleName + "\\", fileName); if (destinationFile.Length < 256) { string destinationDirectory = Path.GetDirectoryName(destinationFile); if (!Directory.Exists(destinationDirectory)) { Directory.CreateDirectory(destinationDirectory); } // Create the file: File.WriteAllBytes(destinationFile, fileContent); //put the file name in the list of files: if (fileName.EndsWith(".resx.js")) { resXFilesCopied.Add(destinationFile); } } else { logger.WriteWarning("Could not create the following output file because its path is too long: " + destinationFile); } } #region Commented because this operation is now done in the "JavaScriptGenerator" build task ("ConvertingResXFilesToJavaScript.cs"). ////----------------------------------------------- //// Process RESX files: ////----------------------------------------------- //Dictionary<string, byte[]> resXFiles = reflectionOnSeparateAppDomain.GetManifestResources(assemblySimpleName, // (fn) => fn.ToLower().EndsWith(".resources") && !fn.ToLower().EndsWith(".g.resources")); //foreach (KeyValuePair<string, byte[]> file in resXFiles) //{ // string fileName = file.Key; // byte[] fileContent = file.Value; // // Create destination folders hierarchy: // string destinationFile = Path.Combine(outputPath, ResourceCopier.NAME_OF_FOLDER_THAT_CONTAINS_RESOURCES + "\\", Path.GetFileNameWithoutExtension(fileName) + ".resj"); // string destinationDirectory = Path.GetDirectoryName(destinationFile); // Directory.CreateDirectory(destinationDirectory); // // Create JSON from RESX: // string json = ""; // using (Stream stream = new MemoryStream(fileContent)) // { // json = ConvertingResXFilesToJavaScript.ConvertResources(stream); // } // // Save file: // File.WriteAllText(destinationFile, json); //} #endregion } } return(true); }
static void TraverseNextElement(XElement currentElement, int currentElementIndex, /*Stack<Dictionary<int, int>> indexesMapper*/ Stack <List <int> > indexesMapper, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { bool skipTraversalOfChildren = false; // Copy the children into a new array so that if we remove items from the collection, it does not affect the traversal: XElement[] children = currentElement.Elements().ToArray(); // Check if the current element is an object (rather than a property): bool isElementAnObjectRatherThanAProperty = !currentElement.Name.LocalName.Contains("."); List <int> indexesMap = new List <int>(children.Length); if (isElementAnObjectRatherThanAProperty) { //---------------------------------- // CASE: OBJECT (e.g. <Button> or <TextBlock>) //---------------------------------- // Make a list of all the child nodes that are not part of a property of the current element (e.g. if the current element is a Border that contains a Button, we detect "<Button>" but we ignore "<Border.Child>" and "<ToolTipService.ToolTip>" because they are properties): List <XElement> nodesThatAreNotPropertiesOfTheObject = new List <XElement>(); XElement child; for (int i = 0; i < children.Length; i++) { child = children[i]; if (!child.Name.LocalName.Contains(".")) { nodesThatAreNotPropertiesOfTheObject.Add(child); indexesMap.Add(i); } } //------------------------------------------------------------- // Explicitly add the "ContentProperty" to the XAML. For example, <Border><TextBlock/></Border> becomes <Border><Border.Child><TextBlock/></Border.Child></Border> //------------------------------------------------------------- // If that list is not empty, put those child elements into a group, which name is the default children property (aka "ContentProperty") of the parent: if (nodesThatAreNotPropertiesOfTheObject.Count > 0) { // Find out the name of the default children property (aka "ContentProperty") of the current element: string namespaceName, localName, assemblyNameIfAny; GettingInformationAboutXamlTypes.GetClrNamespaceAndLocalName(currentElement.Name, out namespaceName, out localName, out assemblyNameIfAny); var contentPropertyName = reflectionOnSeparateAppDomain.GetContentPropertyName(namespaceName, localName, assemblyNameIfAny); XElement contentWrapper; if (contentPropertyName != null) { // Wrap the child elements: contentWrapper = new XElement(currentElement.Name + "." + contentPropertyName); } else { contentWrapper = currentElement; } foreach (var childElement in nodesThatAreNotPropertiesOfTheObject) { childElement.Remove(); } contentWrapper.Add(nodesThatAreNotPropertiesOfTheObject.ToArray <object>()); if (contentWrapper != currentElement) { currentElement.Add(contentWrapper); } } //------------------------------------------------------------- // If there is some direct text content (such as <Button>content</Button), convert the text into an attribute (such as <Button Content="content"></Button>) if the element has the "[ContentProperty]" attribute. // Note: if the type is a system type (such as <sys:Double>50</sys:Double>), we ignore it because later its value will be directly assigned. // Similarly, if the type has the "[SupportsDirectContentViaTypeFromStringConvertersAttribute]" attribute or is an Enum, we also ignore it because later it will be transformed into a call to the "TypeFromStringConverters" class. //------------------------------------------------------------- XText directTextContent; if (DoesElementContainDirectTextContent(currentElement, out directTextContent)) { // Read the content: string contentValue = directTextContent.Value; // Get information about the element namespace and assembly: string namespaceName, localName, assemblyNameIfAny; GettingInformationAboutXamlTypes.GetClrNamespaceAndLocalName(currentElement.Name, out namespaceName, out localName, out assemblyNameIfAny); // Distinguish system types (string, double, etc.) to other types: if (SystemTypesHelper.IsSupportedSystemType(namespaceName, localName, assemblyNameIfAny)) { // In this case we do nothing because system types are handled later in the process. Example: "<sys:Double>50</sys:Double>" becomes: "Double x = 50;" } else { // Ensure the content is not empty: if (!string.IsNullOrWhiteSpace(contentValue)) //todo: do we really need this? { List <int> siblings = indexesMapper.Peek(); //If it is the first child, we want to trim the start of the string. (Silverlight behavior) if (currentElementIndex == siblings[0]) //at least of size 1 (it contains currentElement) { contentValue = contentValue.TrimStart(); } //If it is the last child, we want to trim the end of the string. (Silverlight behavior) if (currentElementIndex == siblings[siblings.Count - 1]) { contentValue = contentValue.TrimEnd(); } // Replace multiple spaces (and line returns) with just one space (same behavior as in WPF): //cf. http://stackoverflow.com/questions/1279859/how-to-replace-multiple-white-spaces-with-one-white-space contentValue = Regex.Replace(contentValue, @"\s{2,}", " "); // Check if the type has the "[SupportsDirectContentViaTypeFromStringConvertersAttribute]" attribute (such as "<Color>Red</Color>") // or it is an Enum (such as "<Visibility>Collapsed</Visibility>"): if (reflectionOnSeparateAppDomain.DoesTypeContainAttributeToConvertDirectContent(namespaceName, localName, assemblyNameIfAny) || reflectionOnSeparateAppDomain.IsTypeAnEnum(namespaceName, localName, assemblyNameIfAny)) { // Add the attribute that will tell the compiler to later intialize the type by converting from the string using the "TypeFromStringConverters" class. currentElement.SetAttributeValue(AttributeNameForTypesToBeInitializedFromString, contentValue); // Remove the direct text content: directTextContent.Remove(); } else { // Find out the name of the default children property (aka "ContentProperty") of the current element: var contentPropertyName = reflectionOnSeparateAppDomain.GetContentPropertyName(namespaceName, localName, assemblyNameIfAny); // Verify that the default children property (aka "ContentProperty") was found: if (string.IsNullOrEmpty(contentPropertyName)) { throw new wpf::System.Windows.Markup.XamlParseException(string.Format("The element '{0}' does not support direct content.", currentElement.Name)); } // Verify that the attribute is not already set: if (currentElement.Attribute(contentPropertyName) != null) { throw new wpf::System.Windows.Markup.XamlParseException(string.Format("The property '{0}' is set more than once.", contentPropertyName)); } // SPECIAL CASE: If we are in a TextBlock, we want to set the property "TextBlock.Text" instead of "TextBlock.Inlines": if (currentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "TextBlock" || currentElement.Name == GeneratingCSharpCode.DefaultXamlNamespace + "Run") { contentPropertyName = "Text"; } // Add the Content attribute: XAttribute attribute = new XAttribute(contentPropertyName, contentValue); currentElement.Add(attribute); // Remove the direct text content: directTextContent.Remove(); } } } } } else { //---------------------------------- // CASE: PROPERTY (e.g. <Button.Visibility> or <TextBlock.Text> or <ToolTipService.ToolTip>) //---------------------------------- // If there is some direct text content (such as <Button.Visibility>Collapsed</Button.Visibility> or <ToolTipService.ToolTip>Test</ToolTipService.ToolTip>), we convert the text into an attribute (such as <Button Visibility="Collapsed"></Button> or <Button ToolTipService.ToolTip="Test></Button>): XText directTextContent; if (DoesElementContainDirectTextContent(currentElement, out directTextContent)) { // Check if we are on a direct object property (such as <Button.Visibility>) or an attached property (such as <ToolTipService.ToolTip>). For example, if the current element is <TextBlock.Text> and the parent is <TextBlock>, the result is true. Conversely, if the current element is <ToolTipService.ToolTip> and the parent is <Border>, the result is false. bool isAttachedProperty = currentElement.Parent.Name != (currentElement.Name.Namespace + currentElement.Name.LocalName.Substring(0, currentElement.Name.LocalName.IndexOf("."))); // Read the content: string contentValue = directTextContent.Value; // Get the property name: string contentPropertyName = isAttachedProperty ? currentElement.Name.LocalName : currentElement.Name.LocalName.Substring(currentElement.Name.LocalName.IndexOf(".") + 1); // Replace multiple spaces (and line returns) with just one space (same behavior as in WPF): //cf. http://stackoverflow.com/questions/1279859/how-to-replace-multiple-white-spaces-with-one-white-space contentValue = Regex.Replace(contentValue, @"\s{2,}", " ").Trim(); if (!string.IsNullOrEmpty(contentValue)) { contentValue = contentValue[0] == '{' ? "{}" + contentValue : contentValue; } // Verify that the attribute is not already set: if (currentElement.Attribute(contentPropertyName) != null) { throw new wpf::System.Windows.Markup.XamlParseException(string.Format("The property '{0}' is set more than once.", contentPropertyName)); } // Add the attribute: XAttribute attribute = new XAttribute(contentPropertyName, contentValue); currentElement.Parent.Add(attribute); // Remove the element: currentElement.Remove(); // It's useless to traverse the children because we have removed the element: skipTraversalOfChildren = true; } else { XElement child; for (int i = 0; i < children.Length; i++) { child = children[i]; if (!child.Name.LocalName.Contains(".")) { indexesMap.Add(i); } } } } // Recursion: if (!skipTraversalOfChildren) { indexesMapper.Push(indexesMap); int i = 0; foreach (var childElements in children) { TraverseNextElement(childElements, i, indexesMapper, reflectionOnSeparateAppDomain); ++i; } indexesMapper.Pop(); } }
internal static bool GenerateSgenCommandLineParameters(string intermediateOutputDirectory, string sourceAssembly, ILogger logger, bool isBridgeBasedVersion, out string sgenCommandLineParameters, out string sgenCommandLineParametersContinued, out bool sgenIsContinued) { //------------------------------------------------------------------------------- // Create or clear the obj/Debug/TempSGen folder //------------------------------------------------------------------------------- string tempFolderForSerializationAssemblies = GetTempFolderForSerializationAssemblies(intermediateOutputDirectory); if (Directory.Exists(tempFolderForSerializationAssemblies)) { foreach (string filePath in Directory.GetFiles(tempFolderForSerializationAssemblies)) { try { File.Delete(filePath); } catch (Exception ex) { logger.WriteError("Could not delete the following temporary file: " + filePath + " - Please delete the file manually. If the error persists, please contact [email protected] - " + ex.Message); sgenCommandLineParameters = null; sgenCommandLineParametersContinued = null; sgenIsContinued = false; return(false); } } } else { Directory.CreateDirectory(tempFolderForSerializationAssemblies); } //------------------------------------------------------------------------------- // Find all the types for which we need to create the serialization assemblies: //------------------------------------------------------------------------------- string[] typesThatAreSerializable; // Create the "TypesResolver" on a separate AppDomain so that the types loaded for reflection can be unloaded when done. using (var reflectionOnSeparateAppDomain = new ReflectionOnSeparateAppDomainHandler()) { string assemblySimpleName = reflectionOnSeparateAppDomain.LoadAssembly(sourceAssembly, loadReferencedAssembliesToo: false, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: ""); string commaSeparatedTypesThatAreSerializable = reflectionOnSeparateAppDomain.FindCommaSeparatedTypesThatAreSerializable(assemblySimpleName); typesThatAreSerializable = commaSeparatedTypesThatAreSerializable.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); } //------------------------------------------------------------------------------- // Generate the command line call to the "sgen.exe" tool, that will generate the C# source code for the serialization assemblies: //------------------------------------------------------------------------------- if (typesThatAreSerializable.Length > 0) { StringBuilder commandLineBuilder = new StringBuilder(); StringBuilder commandLineBuilderContinued = new StringBuilder(); string sourceAssemblyAbsolutePath = Path.Combine(Directory.GetCurrentDirectory(), sourceAssembly); string shortPathName = ShortPathHelper.GetShortPathName(sourceAssemblyAbsolutePath); // Note: we call "ShortPathHelper.GetShortPathName" so that we don't need to surround the path with double quotes (which don't work with the MSBuild Exec tasc): commandLineBuilder.Append(shortPathName); commandLineBuilderContinued.Append(shortPathName); commandLineBuilder.Append(" /keep"); // Note: "keep" will prevent sgen from deleting the ".cs" source file after generating the serialization assembly commandLineBuilderContinued.Append(" /keep"); commandLineBuilder.Append(" /v"); // Note: "v" will display verbose output for debugging, and will list types from the target assembly that cannot be serialized with the XmlSerializer. commandLineBuilderContinued.Append(" /v"); commandLineBuilder.Append(" /force"); // Note: "force" will force replace the generated assembly if it already exists. commandLineBuilderContinued.Append(" /force"); int charactersCount = 0; // We count the characters because command lines are limited to 32,768 characters. sgenIsContinued = false; Dictionary <string, string> typesLocalNameToFullName = new Dictionary <string, string>(); foreach (string serializableType in typesThatAreSerializable) { // Verify that there are no 2 classes with the same local name (SGEN.exe does not support processing two classes with the same local name, unless they have some XML attribute to specify the namespace, but the current version of the XmlSerializer does not support such XML namespace attributes: string serializableTypeLocalName = (serializableType.Contains('.') ? serializableType.Substring(serializableType.LastIndexOf('.') + 1) : serializableType); if (typesLocalNameToFullName.ContainsKey(serializableTypeLocalName)) { throw new Exception(@"The following serializable classes have the same name: """ + serializableType + @""" and """ + typesLocalNameToFullName[serializableTypeLocalName] + @""". The current version of C#/XAML for HTML5 does not allow serializing two classes that have the same name. This is due to the fact that the XmlSerializer does not support namespaces at the moment. To work around this limitation, please rename one of the two classes, or remove its [Serializable] or [DataContract] attribute."); } else { typesLocalNameToFullName.Add(serializableTypeLocalName, serializableType); } // Build the command line parameter related to the list of types that are serializable: string fragment = " /type:" + serializableType; // Note: the full type name (ie. namespace + name) is required. if (!sgenIsContinued) // This is due to the fact that command lines are limited to 32,768 characters, so we split into two calls if necessary. { commandLineBuilder.Append(fragment); } else { commandLineBuilderContinued.Append(fragment); } charactersCount += fragment.Length; if (charactersCount > 32000) { sgenIsContinued = true; } if (charactersCount > 64000) { throw new Exception("The maximum length of the SGEN command line has been exceeded (64,000 characters). Please reduce the number of serializable types and try again."); } } string outParam = @" /out:" + ShortPathHelper.GetShortPathName(tempFolderForSerializationAssemblies); // Note: we call "ShortPathHelper.GetShortPathName" so that we don't need to surround the path with double quotes (which don't work with the MSBuild Exec tasc): commandLineBuilder.Append(outParam); commandLineBuilderContinued.Append(outParam); sgenCommandLineParameters = commandLineBuilder.ToString(); if (sgenIsContinued) { sgenCommandLineParametersContinued = commandLineBuilderContinued.ToString(); } else { sgenCommandLineParametersContinued = string.Empty; } // Fix the 8192 characters length limitation (for more information, read the comments in the method "Fix8192charactersIssue"): sgenCommandLineParameters = Fix8192charactersIssue(sgenCommandLineParameters); sgenCommandLineParametersContinued = Fix8192charactersIssue(sgenCommandLineParametersContinued); } else { sgenCommandLineParameters = string.Empty; sgenCommandLineParametersContinued = string.Empty; sgenIsContinued = false; } logger.WriteMessage("SGEN command line parameters: " + sgenCommandLineParameters, Microsoft.Build.Framework.MessageImportance.Low); return(true); }
//------------------------------------------------------------ // This class will process the "ContentPresenter" nodes // in order to transform "<ContentPresenter />" into // "<ContentPresenter Content="{TemplateBinding Content}" // ContentTemplate="{TemplateBinding ContentTemplate}" />" //------------------------------------------------------------ public static void Process(XDocument doc, ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain) { TraverseNextElement(doc.Root, false, reflectionOnSeparateAppDomain); }