Skip to content

Commit 534dade

Browse files
committed
[WIP] Hierarchy generation fully works now
All the types, including enums, are properly included and nested in their namespaces and classes. Issue: enums don't have their full managed names generated properly yet Issue: type index can't handle conflicts properly yet (e.g. we have two namespaces with different full native name but the same full managed name - Android.App. This is a legitimate scenario, need to come up with a way to handle it)
1 parent 047aab5 commit 534dade

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+975
-209
lines changed

api.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<api api-source="class-parse" platform="10">
2+
<api api-source="class-parse" platform="27">
33
<package name="android" jni-name="android">
44
<class abstract="false" deprecated="not deprecated" jni-extends="Ljava/lang/Object;" extends="java.lang.Object" extends-generic-aware="java.lang.Object" final="true" name="Manifest" jni-signature="Landroid/Manifest;" static="false" visibility="public">
55
<constructor deprecated="not deprecated" final="false" name="Manifest" static="false" visibility="public" bridge="false" synthetic="false" jni-signature="()V" />

metadata

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@
314314
<attr path="/api/package[@name='java.nio.channels']/class[@name='Channels']/method[@name='newChannel' and @return='java.nio.channels.WritableByteChannel']" name="managedName">NewWritableChannel</attr>
315315
<attr path="/api/package[@name='java.util']/class[@name='Dictionary']/method/parameter[@name='key']" name="type">K</attr>
316316
<attr path="/api/package[@name='java.util']/class[@name='Dictionary']/method/parameter[@name='value']" name="type">V</attr>
317-
<attr path="/api/package[@name='java.util']/class[@name='Dictionary']" name="name">Dictionary&lt;K, V&gt;</attr>
317+
<attr path="/api/package[@name='java.util']/class[@name='Dictionary']" name="name-generic-aware">Dictionary&lt;K, V&gt;</attr>
318318
<attr path="/api/package[@name='java.util']/class[@name='Hashtable']/method/parameter[@name='key']" name="type">K</attr>
319319
<attr path="/api/package[@name='java.util']/class[@name='Hashtable']/method/parameter[@name='value']" name="type">V</attr>
320320
<attr path="/api/package[@name='java.util']/class[@name='Hashtable']/implements[@name='java.util.Map']" name="name-generic-aware">java.util.Map&lt;K, V&gt;</attr>

src/Java.Interop.Bindings/Compiler/DefaultOutputPathProvider.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ public override FilesystemPath GetPathFor (string rootDirectory, HierarchyElemen
5555
relativePath = GetFilePath (iface);
5656
break;
5757

58+
case HierarchyEnum enm:
59+
relativePath = GetFilePath (enm);
60+
break;
61+
5862
default:
5963
throw new InvalidOperationException ($"Unsupported hierarchy type '{element.GetType ()}'");
6064
}

src/Java.Interop.Bindings/Compiler/Generator.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ protected virtual void GenerateNamespace (string outputDirectoryRoot, HierarchyN
107107
}
108108
}
109109

110-
protected virtual void GenerateNamespaceMember (HierarchyElement element, StreamWriter writer, string outputFileName)
110+
protected virtual void GenerateNamespaceMember (HierarchyElement element, TextWriter writer, string outputFileName)
111111
{
112112
Logger.Debug ($"Generating {element.GetManagedName (true)} in namespace output file: {outputFileName}");
113113
OutputNamespaceMember (element, writer, outputFileName);
@@ -117,7 +117,7 @@ protected virtual void GenerateNamespaceMember (HierarchyNamespace ns, Hierarchy
117117
{
118118
FilesystemPath path = Context.OutputPathProvider.GetPathFor (outputDirectory, element);
119119
if (String.IsNullOrEmpty (path?.FullPath)) {
120-
Logger.Warning ($"Unable to generate output for element {element.GetManagedName (true)} since no full path was given");
120+
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 ()})");
121121
return;
122122
}
123123

@@ -136,15 +136,15 @@ protected virtual void GenerateNamespaceMember (HierarchyNamespace ns, Hierarchy
136136
}
137137
}
138138

139-
protected virtual void OutputNamespaceMember (HierarchyElement element, StreamWriter writer, string fileName)
139+
protected virtual void OutputNamespaceMember (HierarchyElement element, TextWriter writer, string fileName)
140140
{
141141
}
142142

143-
protected virtual void WriteFileHeader (HierarchyNamespace ns, StreamWriter writer)
143+
protected virtual void WriteFileHeader (HierarchyNamespace ns, TextWriter writer)
144144
{
145145
}
146146

147-
protected virtual void WriteFileFooter (HierarchyNamespace ns, StreamWriter writer)
147+
protected virtual void WriteFileFooter (HierarchyNamespace ns, TextWriter writer)
148148
{
149149
}
150150

src/Java.Interop.Bindings/Compiler/Hierarchy.cs

Lines changed: 91 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,12 @@ public class Hierarchy : HierarchyBase
4444
public const string DefaultInterfaceBaseType = DefaultInterfaceBaseTypeNamespace + ".IJavaObject";
4545
public const string DefaultClassBaseType = DefaultClassBaseTypeNamespace + ".Object";
4646

47-
Dictionary<string, HierarchyObject> typeIndex;
4847
List<HierarchyNamespace> namespaces;
4948
List<HierarchyEnum> enums;
5049
Dictionary<string, List<HierarchyElement>> typeNameDependants;
5150

5251
public IList<HierarchyNamespace> Namespaces => namespaces;
53-
public IList<HierarchyEnum> Enums => enums;
54-
protected Dictionary<string, HierarchyObject> TypeIndex => typeIndex;
52+
protected HierarchyIndex TypeIndex { get; } = new HierarchyIndex ();
5553

5654
public void Build (IList<ApiElement> rawElements)
5755
{
@@ -92,69 +90,96 @@ public void Build (IList<ApiElement> rawElements)
9290
// interfaces that don't derive from other interfaces
9391
HierarchyInterface iJavaLangObject = androidRuntime.Members?.OfType <HierarchyInterface> ().Where (iface => String.Compare (DefaultInterfaceBaseType, iface?.FullName, StringComparison.OrdinalIgnoreCase) == 0).FirstOrDefault ();
9492
if (iJavaLangObject == null) {
95-
Logger.Verbose ("Synthetizing Android.Runtime.IJavaObject interface (not found after parsing API description)");
93+
Logger.Verbose ("Synthesizing Android.Runtime.IJavaObject interface (not found after parsing API description)");
9694
iJavaLangObject = CreateHierarchyElementInternal <HierarchyInterface> (androidRuntime);
9795
iJavaLangObject.Init ();
9896
iJavaLangObject.FullName = DefaultInterfaceBaseType;
9997
iJavaLangObject.Name = Helpers.GetBaseTypeName (iJavaLangObject.FullName);
10098
iJavaLangObject.IgnoreForCodeGeneration = true;
10199
iJavaLangObject.InvokerNotNeeded = true;
102100
iJavaLangObject.UseGlobal = true;
103-
AddTypeToIndex (iJavaLangObject);
101+
TypeIndex.Add (iJavaLangObject);
104102
}
105103

106-
// Pass 2: nest classes, enums and interfaces
107-
NestElements ();
104+
// Pass 2: nest classes, interfaces
105+
NestNamespaces ();
108106

109-
// Pass 3: generate and inject synthetic elements
107+
// Pass 3: generate managed names
108+
Helpers.ForEachNotNull (namespaces, (HierarchyNamespace ns) => GenerateManagedNames (ns));
109+
110+
// TODO: fix generation of full managed names for enums
111+
Helpers.ForEachNotNull (enums, (HierarchyEnum enm) => GenerateManagedNames (enm));
112+
113+
// Pass 4: nest enums (they need managed names)
114+
NestEnums ();
115+
116+
// Pass 5: generate and inject synthetic elements
110117
SynthesizeElements ();
111118

112-
// Pass 4: resolve base types since we have everything in place now
119+
// Pass 6: resolve base types since we have everything in place now
113120
ResolveBaseTypes ();
114121

115-
// Pass 5: generate managed names and rename all the related nested types accordingly
122+
// Pass 7: sort class members
123+
}
124+
125+
protected void GenerateManagedNames (HierarchyElement root)
126+
{
127+
if (root == null)
128+
return;
129+
130+
root.SetManagedNames ();
131+
if (root is HierarchyObject || root is HierarchyNamespace)
132+
TypeIndex.AddManaged (root);
133+
if (!root.HasMembers)
134+
return;
116135

117-
// Pass 6: sort class members
136+
Helpers.ForEachNotNull(root.Members, (HierarchyElement element) => GenerateManagedNames (element));
118137
}
119138

120139
protected void ResolveBaseTypes ()
121140
{
122-
if (namespaces == null || namespaces.Count == 0 || typeIndex == null)
141+
if (namespaces == null || namespaces.Count == 0)
123142
return;
124143

125144
foreach (HierarchyNamespace ns in namespaces) {
126145
if (ns == null || ns.Members == null || ns.Members.Count == 0)
127146
continue;
128147

129148
foreach (HierarchyObject type in ns.Members.OfType<HierarchyObject> ().Where (o => o != null)) {
130-
type.ResolveBaseTypes (typeIndex);
149+
type.ResolveBaseTypes (TypeIndex);
131150
}
132151
}
133152
}
134153

135154
protected virtual void SynthesizeElements ()
136155
{}
137156

138-
protected void NestElements ()
157+
void NestElements <TElement, TParent> (IList<TElement> elements, Dictionary <HierarchyElement, HierarchyElement> toReparent)
158+
where TElement: HierarchyElement
159+
where TParent: HierarchyElement
139160
{
140-
if (namespaces == null || namespaces.Count == 0)
161+
if (elements == null)
141162
return;
142163

143-
var toReparent = new Dictionary <HierarchyElement, HierarchyElement> ();
144-
foreach (HierarchyNamespace ns in namespaces) {
145-
if (ns == null || ns.Members == null || ns.Members.Count == 0)
164+
foreach (HierarchyElement element in elements) {
165+
TParent newParent = SelectNewParent <TParent> (element);
166+
if (newParent == null)
146167
continue;
147168

148-
foreach (HierarchyElement element in ns.Members) {
149-
HierarchyClass newParent = SelectNewParent (element);
150-
if (newParent == null)
151-
continue;
169+
if (toReparent.ContainsKey (element))
170+
Logger.Warning ($"Element {element.Name} ({element.GetType ()} was already re-parented");
152171

153-
if (toReparent.ContainsKey (element))
154-
Logger.Warning ($"Element {element.Name} ({element.GetType ()} was already re-parented");
155-
toReparent [element] = newParent;
156-
}
172+
toReparent [element] = newParent;
157173
}
174+
}
175+
176+
protected void NestElements (Action<Dictionary <HierarchyElement, HierarchyElement>> looper)
177+
{
178+
if (looper == null)
179+
throw new ArgumentNullException (nameof (looper));
180+
181+
var toReparent = new Dictionary <HierarchyElement, HierarchyElement> ();
182+
looper (toReparent);
158183

159184
if (toReparent.Count == 0)
160185
return;
@@ -168,12 +193,31 @@ protected void NestElements ()
168193
}
169194
}
170195

171-
protected virtual HierarchyClass SelectNewParent (HierarchyElement element)
196+
protected void NestNamespaces ()
197+
{
198+
NestElements (
199+
toReparent => {
200+
foreach (HierarchyNamespace ns in namespaces) {
201+
NestElements <HierarchyElement, HierarchyClass> (ns?.Members, toReparent);
202+
}
203+
}
204+
);
205+
}
206+
207+
protected void NestEnums ()
208+
{
209+
NestElements (toReparent => NestElements <HierarchyEnum, HierarchyNamespace> (enums, toReparent));
210+
}
211+
212+
protected virtual T SelectNewParent <T> (HierarchyElement element) where T: HierarchyElement
172213
{
173-
if (element == null || String.IsNullOrEmpty (element.FullName))
214+
if (element == null)
174215
return null;
175216

176-
if (typeIndex == null || typeIndex.Count == 0) {
217+
if (String.IsNullOrEmpty (element.FullName))
218+
throw new InvalidOperationException ("Each element must have a full name");
219+
220+
if (TypeIndex.Count == 0) {
177221
Logger.Warning ($"Unable to select new parent for element {element.Name}, type index does not exist");
178222
return null;
179223
}
@@ -185,15 +229,20 @@ protected virtual HierarchyClass SelectNewParent (HierarchyElement element)
185229
if (String.IsNullOrEmpty (nsOrTypeName))
186230
return null;
187231

188-
HierarchyObject maybeParent;
189-
if (!typeIndex.TryGetValue (nsOrTypeName, out maybeParent))
190-
return null;
232+
try {
233+
HierarchyElement maybeParent = TypeIndex.Lookup (nsOrTypeName);
234+
if (maybeParent == null)
235+
return null;
191236

192-
var klass = maybeParent as HierarchyClass;
193-
if (klass == null)
194-
return null;
237+
var ret = maybeParent as T;
238+
if (ret == null)
239+
return null;
195240

196-
return klass;
241+
return ret;
242+
} catch {
243+
Logger.Fatal ($"Error selecting new parent for element '{element.FullName}' ({element.GetLocation ()})");
244+
throw;
245+
}
197246
}
198247

199248
public void Dump (string outputFile)
@@ -204,7 +253,7 @@ public void Dump (string outputFile)
204253
string indent = String.Empty;
205254
using (var sw = new StreamWriter (outputFile, false, Encoding.UTF8)) {
206255
foreach (HierarchyNamespace ns in namespaces) {
207-
sw.WriteLine ($"Namespace: {ns.FullName}");
256+
sw.WriteLine ($"Namespace: [native: {ns.Name} ({ns.FullName})] [managed: {ns.ManagedName} ({ns.FullManagedName})]");
208257
Dump (sw, indent + "\t", ns.Members);
209258
}
210259
}
@@ -216,7 +265,7 @@ void Dump (StreamWriter sw, string indent, IList<HierarchyElement> members)
216265
return;
217266

218267
foreach (HierarchyElement element in members) {
219-
sw.WriteLine ($"{indent}{TypeToName (element)}: {element.FullName} [managed name: {element.ManagedName}; full managed name: {element.FullManagedName}]");
268+
sw.WriteLine ($"{indent}{TypeToName (element)}: [native: {element.Name} ({element.FullName})] [managed: {element.ManagedName} ({element.FullManagedName})]");
220269
Dump (sw, indent + "\t", element.Members);
221270
}
222271
}
@@ -280,6 +329,7 @@ protected virtual void Process (ApiNameSpace apiNameSpace)
280329
});
281330

282331
Helpers.AddToList (ns, ref namespaces);
332+
TypeIndex.Add (ns);
283333
}
284334

285335
void AddLocationComment (ApiElement element, HierarchyElement hierarchyElement)
@@ -331,7 +381,7 @@ protected virtual void Process (HierarchyNamespace parent, ApiClass klass)
331381
});
332382

333383
parent.AddMember (hierarchyClass);
334-
AddTypeToIndex (hierarchyClass);
384+
TypeIndex.Add (hierarchyClass);
335385
}
336386

337387
protected virtual void Process (HierarchyObject parent, ApiTypeParameter typeParameter)
@@ -473,48 +523,15 @@ protected virtual void Process (HierarchyNamespace parent, ApiInterface iface)
473523
});
474524

475525
parent.AddMember (hierarchyInterface);
476-
AddTypeToIndex (hierarchyInterface);
526+
TypeIndex.Add (hierarchyInterface);
477527
}
478528

479529
protected virtual void Process (ApiEnum apiEnum)
480530
{
481531
var enm = CreateHierarchyElementInternal <HierarchyEnum> (this);
482532
enm.Init (apiEnum);
483533
Helpers.AddToList (enm, ref enums);
484-
}
485-
486-
protected void AddTypeToIndex (HierarchyObject type)
487-
{
488-
if (type == null)
489-
throw new ArgumentNullException (nameof (type));
490-
491-
// TODO: add aliasing for types that have NameGenericAware (AppImplements) and their generic
492-
// name is different than the regular one - map the generic one to the type desribed by the
493-
// non-generic name
494-
AddTypeToIndex (type.FullName, type);
495-
AddTypeToIndex (type.FullManagedName, type);
496-
AddTypeToIndex (type.NameGenericAware, type);
497-
}
498-
499-
void AddTypeToIndex (string typeName, HierarchyObject type)
500-
{
501-
if (String.IsNullOrEmpty (typeName))
502-
return;
503-
504-
if (String.Compare ("java.lang.Comparable", typeName, StringComparison.OrdinalIgnoreCase) == 0) {
505-
Logger.Debug ($"java.lang.Comparable being added: {type} ({type.FullName})");
506-
}
507-
508-
if (typeIndex == null)
509-
typeIndex = new Dictionary<string, HierarchyObject> (StringComparer.Ordinal);
510-
511-
HierarchyObject t;
512-
if (typeIndex.TryGetValue (typeName, out t) && t != null) {
513-
if (t.GetType () != type.GetType ())
514-
throw new InvalidOperationException ($"Conflicting type index entry. Type '{typeName} ({type.GetType ().FullName})' must not replace '{t.FullName} ({t.GetType ().FullName})' since they are of different types");
515-
}
516-
517-
typeIndex [typeName] = type;
534+
TypeIndex.Add (enm);
518535
}
519536

520537
protected T CreateHierarchyElementInternal <T> (HierarchyBase parent) where T: HierarchyElement
@@ -536,8 +553,6 @@ void AddTypeToIndex (string typeName, HierarchyObject type)
536553
ret = new HierarchyNamespace (parent as Hierarchy);
537554
else if (type == typeof (HierarchyClass))
538555
ret = new HierarchyClass (parent as HierarchyNamespace);
539-
else if (type == typeof (HierarchyTypeMember))
540-
ret = new HierarchyTypeMember (parent as HierarchyObject);
541556
else if (type == typeof (HierarchyImplements))
542557
ret = new HierarchyImplements (parent as HierarchyObject);
543558
else if (type == typeof (HierarchyMethod))

src/Java.Interop.Bindings/Compiler/HierarchyAttributeAnnotationGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public abstract class HierarchyAttributeAnnotationGenerator : HierarchyCustomAtt
3333
protected HierarchyAttributeAnnotationGenerator (HierarchyAttributeAnnotation attribute) : base (attribute)
3434
{}
3535

36-
protected override void OutputParameters (Stream output)
36+
protected override void OutputParameters (TextWriter output)
3737
{
3838
OutputParameter (output, Attribute.JavaName);
3939
}

src/Java.Interop.Bindings/Compiler/HierarchyAttributeIntDefinitionGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public abstract class HierarchyAttributeIntDefinitionGenerator : HierarchyCustom
3333
protected HierarchyAttributeIntDefinitionGenerator (HierarchyAttributeIntDefinition attribute) : base (attribute)
3434
{}
3535

36-
protected override void OutputParameters (Stream output)
36+
protected override void OutputParameters (TextWriter output)
3737
{
3838
OutputParameter (output, Attribute.ConstantMember);
3939
if (!String.IsNullOrEmpty (Attribute.JniField))

src/Java.Interop.Bindings/Compiler/HierarchyAttributeNamespaceMappingGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public abstract class HierarchyAttributeNamespaceMappingGenerator : HierarchyCus
3333
protected HierarchyAttributeNamespaceMappingGenerator (HierarchyAttributeNamespaceMapping attribute) : base (attribute)
3434
{}
3535

36-
protected override void OutputParameters (Stream output)
36+
protected override void OutputParameters (TextWriter output)
3737
{
3838
bool haveJava = !String.IsNullOrEmpty (Attribute.Java);
3939
bool haveManaged = !String.IsNullOrEmpty (Attribute.Managed);

src/Java.Interop.Bindings/Compiler/HierarchyAttributeObsoleteGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public abstract class HierarchyAttributeObsoleteGenerator : HierarchyCustomAttri
3333
protected HierarchyAttributeObsoleteGenerator (HierarchyAttributeObsolete attribute) : base (attribute)
3434
{}
3535

36-
protected override void OutputParameters (Stream output)
36+
protected override void OutputParameters (TextWriter output)
3737
{
3838
if (!Attribute.IsError && String.IsNullOrEmpty (Attribute.Message))
3939
return;

0 commit comments

Comments
 (0)