// todo -- would be good to have this be an instance property because we need to clear dynamics every time we compile public static void AddDynamicElementType(ProcessedType processedType) { processedType.id = NextTypeId; typeMap[processedType.rawType] = processedType; dynamicTypes.Add(processedType); // templateTypes.Add(processedType); // todo -- maybe add to namespace map? }
public TemplateRootNode GetParsedTemplate(ProcessedType processedType) { TemplateAttribute templateAttr = processedType.templateAttr; templateAttr.filePath = ResolveTemplateFilePath(processedType); if (templateAttr.fullPathId == null) { templateAttr.fullPathId = templateAttr.templateId == null ? templateAttr.filePath : templateAttr.filePath + "#" + templateAttr.templateId; } Debug.Assert(templateAttr.fullPathId != null, "templateAttr.fullPathId != null"); if (templateMap.TryGetValue(templateAttr.fullPathId, out LightList <TemplateRootNode> list)) { for (int i = 0; i < list.size; i++) { if (list.array[i].processedType.rawType == processedType.rawType) { return(list.array[i]); } } TemplateRootNode retn = list[0].Clone(processedType); list.Add(retn); return(retn); } list = new LightList <TemplateRootNode>(2); templateMap[templateAttr.fullPathId] = list; TemplateDefinition templateDefinition = GetTemplateDefinition(processedType); templateAttr.source = templateDefinition.contents; TemplateShell shell = xmlTemplateParser.GetOuterTemplateShell(templateAttr); TemplateRootNode templateRootNode = new TemplateRootNode(templateAttr.templateId, shell, processedType, null, default) { tagName = processedType.tagName }; list.Add(templateRootNode); xmlTemplateParser.Parse(templateRootNode, processedType); return(templateRootNode); }
private TemplateDefinition GetTemplateDefinition(ProcessedType processedType) { TemplateAttribute templateAttr = processedType.templateAttr; string templatePath = ResolveTemplateFilePath(templateAttr.templateType, templateAttr.filePath); switch (templateAttr.templateType) { case TemplateType.Internal: { string file = settings.TryReadFile(templatePath); if (file == null) { throw new TemplateParseException(settings.templateResolutionBasePath, $"Cannot find template in (internal) path {templatePath}."); } return(new TemplateDefinition() { contents = file, filePath = templateAttr.templateType == TemplateType.File ? processedType.rawType.AssemblyQualifiedName : templateAttr.filePath, language = TemplateLanguage.XML }); } case TemplateType.DefaultFile: case TemplateType.File: { string file = settings.TryReadFile(templatePath); if (file == null) { throw new TemplateParseException(settings.templateResolutionBasePath, $"Cannot find template in path {templatePath}."); } return(new TemplateDefinition() { contents = file, filePath = templateAttr.filePath, language = TemplateLanguage.XML }); } default: return(new TemplateDefinition() { contents = templateAttr.source, filePath = templatePath, language = TemplateLanguage.XML }); } }
public static ProcessedType AddResolvedGenericElementType(Type newType, TemplateAttribute templateAttr, string tagName) { ProcessedType retn = null; if (!typeMap.TryGetValue(newType, out retn)) { retn = new ProcessedType(newType, templateAttr, tagName); retn.id = NextTypeId; typeMap.Add(retn.rawType, retn); } if (retn != null) { retn.references++; } return(retn); }
internal void Parse(TemplateRootNode templateRootNode, ProcessedType processedType) { TemplateAttribute templateAttr = processedType.templateAttr; string filePath = templateAttr.filePath; if (parsedFiles.TryGetValue(filePath, out TemplateShell rootNode)) { ParseContentTemplate(templateRootNode, rootNode, processedType); return; } TemplateShell shell = ParseOuterShell(templateAttr.filePath, templateAttr.source); parsedFiles.Add(filePath, shell); ParseContentTemplate(templateRootNode, shell, processedType); }
private string ResolveTemplateFilePath(ProcessedType processedType) { TemplateAttribute templateAttr = processedType.templateAttr; if (templateAttr.templateType == TemplateType.Internal) { return(templateAttr.filePath); } if (settings.filePathResolver != null) { return(settings.filePathResolver(processedType.rawType, templateAttr.templateId)); } string namespacePath = processedType.rawType.Namespace; if (namespacePath != null && namespacePath.Contains(".")) { namespacePath = namespacePath.Replace(".", Path.DirectorySeparatorChar.ToString()); } string xmlPath; // Special behavior for template attributes with no file path parameter. We figure out the whole path // based on a convention that looks for a given template like: // namespace My.Name.Space { [Template] public class MyElement : UIElement ... } // right here: // basepath + My/Name/Space/ClassName.xml if (templateAttr.templateType == TemplateType.DefaultFile) { string basePath = namespacePath == null ? processedType.rawType.Name : Path.Combine(namespacePath, processedType.rawType.Name); string relativePath = basePath + settings.templateFileExtension; xmlPath = Path.GetFullPath(Path.Combine(settings.templateResolutionBasePath, relativePath)); if (!File.Exists(xmlPath)) { throw new TemplateNotFoundException(processedType, xmlPath); } return(relativePath); } // first we try to find the template based on the resolution base path + a guessed namespace path if (namespacePath != null) { // namespace My.Name.Space.MyElement { [Template("MyElement.xml#id")] } // basepath + My/Name/Space/MyElement/MyElement.xml // templateRootNamespace = My/Name/Space xmlPath = Path.GetFullPath(Path.Combine(settings.templateResolutionBasePath, namespacePath, templateAttr.filePath)); if (File.Exists(xmlPath)) { return(Path.Combine(namespacePath, templateAttr.filePath)); } } // namespace My.Name.Space.MyElement { [Template("My/Name/Space/MyElement/MyElement.xml#id")] } // basepath + My/Name/Space/MyElement/MyElement.xml // templateRootNamespace = My/Name/Space // ------ // If the previous method didn't find a template we probably have a full path in the template attribute. // This should be the mode that is compatible with non-convention paths and namespaces xmlPath = Path.GetFullPath(Path.Combine(settings.templateResolutionBasePath, templateAttr.filePath)); if (File.Exists(xmlPath)) { return(templateAttr.filePath); } throw new TemplateNotFoundException(processedType, xmlPath); }
public RepeatNode(TemplateRootNode root, TemplateNode parent, ProcessedType processedType, StructList <AttributeDefinition> attributes, in TemplateLineInfo templateLineInfo) : base(root, parent, processedType, attributes, in templateLineInfo)
public ExpandedTemplateNode(TemplateRootNode root, TemplateNode parent, ProcessedType processedType, StructList <AttributeDefinition> attributes, in TemplateLineInfo templateLineInfo)
public SlotNode(TemplateRootNode root, TemplateNode parent, ProcessedType processedType, StructList <AttributeDefinition> attributes, in TemplateLineInfo templateLineInfo, string slotName, SlotType slotType)
// this might be getting called too many times since im not sure im caching the result private void ParseContentTemplate(TemplateRootNode templateRootNode, TemplateShell shell, ProcessedType processedType) { XElement root = shell.GetElementTemplateContent(processedType.templateAttr.templateId); if (root == null) { throw new TemplateNotFoundException(processedType.templateAttr.filePath, processedType.templateAttr.templateId); } IXmlLineInfo xmlLineInfo = root; StructList <AttributeDefinition> attributes = StructList <AttributeDefinition> .Get(); StructList <AttributeDefinition> injectedAttributes = StructList <AttributeDefinition> .Get(); ParseAttributes(shell, "Contents", root.Attributes(), attributes, injectedAttributes, out string genericTypeResolver, out string requireType); if (attributes.size == 0) { StructList <AttributeDefinition> .Release(ref attributes); } if (injectedAttributes.size == 0) { StructList <AttributeDefinition> .Release(ref injectedAttributes); } templateRootNode.attributes = ValidateRootAttributes(shell.filePath, attributes); templateRootNode.lineInfo = new TemplateLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); templateRootNode.genericTypeResolver = genericTypeResolver; templateRootNode.requireType = requireType; // always null I think ParseChildren(templateRootNode, templateRootNode, root.Nodes()); }
// Namespace resolution // if there is only one element with a name then no namespace is needed // if there are multiple elements with a name // namespace is required in order to match the correct one // using declarations can provide implicit namespaces public static ProcessedType ResolveTagName(string tagName, string namespacePrefix, IReadOnlyList <string> namespaces) { FilterAssemblies(); namespaces = namespaces ?? EmptyNamespaceList; if (string.IsNullOrEmpty(namespacePrefix)) { namespacePrefix = null; } if (string.IsNullOrWhiteSpace(namespacePrefix)) { namespacePrefix = null; } if (templateTypeMap.TryGetValue(tagName, out TypeList typeList)) { // if this is null we resolve using just the tag name if (namespacePrefix == null) { // if only one type has this tag name we can safely return it if (typeList.types == null) { return(typeList.mainType.Reference()); } // if there are multiple tags with this name, we need to search our namespaces // if only one match is found, we can return it. If multiple are found, throw // and ambiguous reference exception LightList <ProcessedType> resultList = LightList <ProcessedType> .Get(); for (int i = 0; i < namespaces.Count; i++) { for (int j = 0; j < typeList.types.Length; j++) { string namespaceName = namespaces[i]; ProcessedType testType = typeList.types[j]; if (namespaceName == testType.namespaceName) { resultList.Add(testType); } } } if (resultList.size == 1) { ProcessedType retn = resultList[0]; resultList.Release(); return(retn.Reference()); } List <string> list = resultList.Select((s) => s.namespaceName).ToList(); throw new ParseException("Ambiguous TagName reference: " + tagName + ". References found in namespaces " + StringUtil.ListToString(list, ", ")); } if (typeList.types == null) { if (namespacePrefix == typeList.mainType.namespaceName) { return(typeList.mainType.Reference()); } } else { // if prefix is not null we can only return a match for that namespace for (int j = 0; j < typeList.types.Length; j++) { ProcessedType testType = typeList.types[j]; if (namespacePrefix == testType.namespaceName) { return(testType.Reference()); } } } return(null); } if (s_GenericMap.TryGetValue(tagName, out ProcessedType processedType)) { return(processedType); } return(null); }
private static void FilterAssemblies() { if (processedTypes) { return; } processedTypes = true; Stopwatch watch = new Stopwatch(); watch.Start(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); int count = 0; for (int i = 0; i < assemblies.Length; i++) { Assembly assembly = assemblies[i]; if (assembly == null || assembly.IsDynamic) { continue; } bool filteredOut = !FilterAssembly(assembly); bool shouldProcessTypes = ShouldProcessTypes(assembly, filteredOut); if (!shouldProcessTypes) { continue; } count++; try { Type[] types = assembly.GetTypes(); for (int j = 0; j < types.Length; j++) { Type currentType = types[j]; // can be null if assembly referenced is unavailable // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (currentType == null) { continue; } if (!filteredOut && currentType.IsClass && currentType.Name[0] != '<' && currentType.IsGenericTypeDefinition) { if (currentType.IsSubclassOf(typeof(UIElement))) { Attribute[] attrs = Attribute.GetCustomAttributes(currentType, false); string tagName = GetTemplateAttribute(currentType, attrs, out TemplateAttribute templateAttr); // todo -- support namespaces in the look up map tagName = tagName.Split('`')[0]; ProcessedType processedType = new ProcessedType(currentType, templateAttr, tagName); processedType.IsUnresolvedGeneric = true; try { s_GenericMap.Add(tagName, processedType); } catch (Exception) { Debug.LogError($"UIForia does not support multiple elements with the same tag name. Tried to register type {processedType.rawType} for `{tagName}` " + $"but this tag name was already taken by type {s_GenericMap[tagName].rawType}. For generic overload types with multiple arguments you need to supply a unique [TagName] attribute"); continue; } typeMap[currentType] = processedType; if (!s_NamespaceMap.TryGetValue(currentType.Namespace ?? "null", out LightList <Assembly> namespaceList)) { namespaceList = new LightList <Assembly>(2); s_NamespaceMap.Add(currentType.Namespace ?? "null", namespaceList); } if (!namespaceList.Contains(assembly)) { namespaceList.Add(assembly); } continue; } } if (!filteredOut && currentType.IsClass && !currentType.IsGenericTypeDefinition) { Attribute[] attrs = Attribute.GetCustomAttributes(currentType, false); Application.ProcessClassAttributes(currentType, attrs); if (typeof(UIElement).IsAssignableFrom(currentType)) { string tagName = GetTemplateAttribute(currentType, attrs, out TemplateAttribute templateAttr); ProcessedType processedType = new ProcessedType(currentType, templateAttr, tagName); if (templateAttr != null) { templateTypes.Add(processedType); } // if (templateTypeMap.ContainsKey(tagName)) { // Debug.Log($"Tried to add template key `{tagName}` from type {currentType} but it was already defined by {templateTypeMap.GetOrDefault(tagName).rawType}"); // } if (templateTypeMap.TryGetValue(tagName, out TypeList typeList)) { if (typeList.types != null) { Array.Resize(ref typeList.types, typeList.types.Length + 1); typeList.types[typeList.types.Length - 1] = processedType; } else { typeList.types = new ProcessedType[2]; typeList.types[0] = typeList.mainType; typeList.types[1] = processedType; } } else { typeList.mainType = processedType; templateTypeMap[tagName] = typeList; } // templateTypeMap.Add(tagName, processedType); processedType.id = NextTypeId; typeMap[currentType] = processedType; } } if (filteredOut && !currentType.IsPublic) { continue; } if (!s_NamespaceMap.TryGetValue(currentType.Namespace ?? "null", out LightList <Assembly> list)) { list = new LightList <Assembly>(2); s_NamespaceMap.Add(currentType.Namespace ?? "null", list); } if (!list.Contains(assembly)) { list.Add(assembly); } } } catch (ReflectionTypeLoadException) { Debug.Log($"{assembly.FullName}"); throw; } } watch.Stop(); Debug.Log($"Loaded types in: {watch.ElapsedMilliseconds} ms from {count} assemblies"); }
public TemplateRootNode(string templateName, TemplateShell templateShell, ProcessedType processedType, StructList <AttributeDefinition> attributes, in TemplateLineInfo templateLineInfo) : base(null, null, processedType, attributes, in templateLineInfo)