protected virtual void GenerateNamespaceMember(HierarchyNamespace ns, HierarchyElement element, string outputDirectory) { FilesystemPath path = Context.OutputPathProvider.GetPathFor(outputDirectory, element); if (String.IsNullOrEmpty(path?.FullPath)) { Logger.Warning($"Unable to generate output for element {element.GetType ().FullName} ({element.GetManagedName (true) ?? element.FullName}) since no full path was given (at {element.GetLocation ()})"); return; } if (path.IsDirectory) { throw new InvalidOperationException($"Namespace member must be written to a file ({ns.FullName})"); } EnsureDirectory(Path.GetDirectoryName(path.FullPath)); CheckOverwrite(path.FullPath); using (Stream fs = File.Open(path.FullPath, FileMode.Create)) { using (StreamWriter writer = new StreamWriter(fs, Context.FileEncoding)) { WriteFileHeader(ns, writer); OutputNamespaceMember(element, writer, path.FullPath); WriteFileFooter(ns, writer); } } }
// A bit clumsy but better than full-blown reflection use or an open set of Create* methods for each // type we support now or in the future protected virtual T CreateHierarchyElement <T> (HierarchyBase parent) where T : HierarchyElement { Type type = typeof(T); HierarchyBase ret = null; if (type == typeof(HierarchyNamespace)) { ret = new HierarchyNamespace(Context, parent as Hierarchy); } else if (type == typeof(HierarchyClass)) { ret = new HierarchyClass(Context, parent as HierarchyNamespace); } else if (type == typeof(HierarchyImplements)) { ret = new HierarchyImplements(Context, parent as HierarchyObject); } else if (type == typeof(HierarchyMethod)) { ret = new HierarchyMethod(Context, parent as HierarchyObject); } else if (type == typeof(HierarchyConstructor)) { ret = new HierarchyConstructor(Context, parent as HierarchyObject); } else if (type == typeof(HierarchyException)) { ret = new HierarchyException(Context, parent as HierarchyMethod); } else if (type == typeof(HierarchyTypeParameter)) { ret = new HierarchyTypeParameter(Context, parent as HierarchyElement); } else if (type == typeof(HierarchyTypeParameterGenericConstraint)) { ret = new HierarchyTypeParameterGenericConstraint(Context, parent as HierarchyTypeParameter); } else if (type == typeof(HierarchyMethodParameter)) { ret = new HierarchyMethodParameter(Context, parent as HierarchyMethod); } else if (type == typeof(HierarchyField)) { ret = new HierarchyField(Context, parent as HierarchyObject); } else if (type == typeof(HierarchyInterface)) { ret = new HierarchyInterface(Context, parent as HierarchyNamespace); } else if (type == typeof(HierarchyEnum)) { ret = new HierarchyEnum(Context, parent as Hierarchy); } else { throw new InvalidOperationException($"Unsupported hierarchy element type {type}"); } return(ret as T); }
protected virtual void Process(HierarchyNamespace parent, ApiInterface iface) { var hierarchyInterface = CreateHierarchyElementInternal <HierarchyInterface> (parent); hierarchyInterface.Init(iface); AddLocationComment(iface, hierarchyInterface); Helpers.ForEachNotNull(iface.ChildElements, (ApiElement e) => { switch (e) { case ApiField field: Process(hierarchyInterface, field); break; case ApiMethod method: Process(hierarchyInterface, method); break; case ApiImplements implements: Process(hierarchyInterface, implements); break; case ApiTypeParameter typeParameter: Process(hierarchyInterface, typeParameter); break; default: Logger.Warning($"Unexpected member type for ApiInterface: '{e.GetType ().FullName}'"); break; } }); parent.AddMember(hierarchyInterface); TypeIndex.Add(hierarchyInterface); }
protected virtual void GenerateNamespace(string outputDirectoryRoot, HierarchyNamespace ns) { FilesystemPath path = Context.OutputPathProvider.GetPathFor(outputDirectoryRoot, ns); if (String.IsNullOrEmpty(path?.FullPath)) { return; } Stream nsFile = null; StreamWriter nsFileWriter = null; try { string targetKind; if (path.IsDirectory) { targetKind = "directory"; EnsureDirectory(path.FullPath); } else { targetKind = "file"; EnsureDirectory(Path.GetDirectoryName(path.FullPath)); CheckOverwrite(path.FullPath); nsFile = File.Open(path.FullPath, FileMode.Create); nsFileWriter = new StreamWriter(nsFile, Context.FileEncoding); } Logger.Debug($"Creating {targetKind} for namespace {ns.GetManagedName (true)}: {path.FullPath}"); Helpers.ForEachNotNull( ns.Members, (HierarchyElement nsm) => { if (nsFileWriter != null) { WriteFileHeader(ns, nsFileWriter); GenerateNamespaceMember(nsm, nsFileWriter, outputDirectoryRoot); WriteFileHeader(ns, nsFileWriter); } else { GenerateNamespaceMember(ns, nsm, outputDirectoryRoot); } } ); } finally { if (nsFileWriter != null) { nsFileWriter.Dispose(); nsFileWriter = null; } if (nsFile != null) { nsFile.Dispose(); nsFile = null; } } }
protected virtual void WriteFileFooter(HierarchyNamespace ns, TextWriter writer) { }
public void Build(IList <ApiElement> rawElements) { if (rawElements == null || rawElements.Count == 0) { throw new ArgumentException("must be a non-empty collection", nameof(rawElements)); } // Pass 1: build basic hierarchy (no class or interface nesting yet) Helpers.ForEachNotNull(rawElements, (ApiElement e) => { switch (e) { case ApiNameSpace ns: Process(ns); break; case ApiEnum enm: Process(enm); break; default: Logger.Warning($"Unsupported top-level element type: {e.GetType ().FullName}"); break; } }); HierarchyNamespace androidRuntime = namespaces?.Where(ns => String.Compare(DefaultInterfaceBaseTypeNamespace, ns?.FullName, StringComparison.OrdinalIgnoreCase) == 0).FirstOrDefault(); if (androidRuntime == null) { Logger.Verbose("Creating Android.Runtime namespace (not found after parsing API description)"); androidRuntime = CreateHierarchyElementInternal <HierarchyNamespace> (this); androidRuntime.Init(); androidRuntime.IgnoreForCodeGeneration = true; androidRuntime.FullName = DefaultInterfaceBaseTypeNamespace; androidRuntime.Name = Helpers.GetBaseTypeName(androidRuntime.FullName); androidRuntime.ManagedName = androidRuntime.Name; androidRuntime.FullManagedName = androidRuntime.FullName; Helpers.AddToList(androidRuntime, ref namespaces); } // IJavaObject is defined in Xamarin.Android's non-generated source and implmented by all // interfaces that don't derive from other interfaces HierarchyInterface iJavaLangObject = androidRuntime.Members?.OfType <HierarchyInterface> ().Where(iface => String.Compare(DefaultInterfaceBaseType, iface?.FullName, StringComparison.OrdinalIgnoreCase) == 0).FirstOrDefault(); if (iJavaLangObject == null) { Logger.Verbose("Synthesizing Android.Runtime.IJavaObject interface (not found after parsing API description)"); iJavaLangObject = CreateHierarchyElementInternal <HierarchyInterface> (androidRuntime); iJavaLangObject.Init(); iJavaLangObject.FullName = DefaultInterfaceBaseType; iJavaLangObject.Name = Helpers.GetBaseTypeName(iJavaLangObject.FullName); iJavaLangObject.IgnoreForCodeGeneration = true; iJavaLangObject.InvokerNotNeeded = true; iJavaLangObject.UseGlobal = true; TypeIndex.Add(iJavaLangObject); } // Pass 2: nest classes, interfaces NestNamespaces(); // Pass 3: generate managed names Helpers.ForEachNotNull(namespaces, (HierarchyNamespace ns) => GenerateManagedNames(ns)); // TODO: fix generation of full managed names for enums Helpers.ForEachNotNull(enums, (HierarchyEnum enm) => GenerateManagedNames(enm)); // Pass 4: nest enums (they need managed names) NestEnums(); // Pass 5: generate and inject synthetic elements SynthesizeElements(); // Pass 6: resolve base types since we have everything in place now ResolveBaseTypes(); // Pass 7: sort class members }