Skip to content

Commit fb8864d

Browse files
committed
[WIP] Output directory layout styles
1 parent 60c7870 commit fb8864d

17 files changed

+491
-46
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// BaseProvider.cs
3+
//
4+
// Author:
5+
// Marek Habersack <[email protected]>
6+
//
7+
// Copyright (c) 2018 Microsoft, Inc (http://microsoft.com/)
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in
17+
// all copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
// THE SOFTWARE.
26+
using System;
27+
28+
namespace Java.Interop.Bindings.Compiler
29+
{
30+
public abstract class BaseProvider
31+
{
32+
public abstract void Validate ();
33+
}
34+
}

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

Lines changed: 142 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,23 @@ namespace Java.Interop.Bindings.Compiler
3030
{
3131
public class DefaultOutputPathProvider : OutputPathProvider
3232
{
33-
public DefaultOutputPathProvider (GeneratorContext context) : base (context)
34-
{}
33+
public OutputTreeLayout TreeLayout { get; }
34+
35+
public DefaultOutputPathProvider (GeneratorContext context, OutputTreeLayout treeLayout) : base (context)
36+
{
37+
TreeLayout = treeLayout ?? throw new ArgumentNullException (nameof (treeLayout));
38+
}
39+
40+
public override void Validate ()
41+
{
42+
switch (TreeLayout.NestedTypesStyle) {
43+
case OutputNestedTypesStyle.SeparateFileDot:
44+
case OutputNestedTypesStyle.SeparateFileUnderscore:
45+
if (!Context.UsePartialClasses)
46+
throw new InvalidOperationException ("Nested types in separate files require partial classes");
47+
break;
48+
}
49+
}
3550

3651
public override FilesystemPath GetPathFor (string rootDirectory, HierarchyElement element)
3752
{
@@ -41,47 +56,149 @@ public override FilesystemPath GetPathFor (string rootDirectory, HierarchyElemen
4156
if (element == null)
4257
throw new ArgumentNullException (nameof (element));
4358

44-
FilesystemPath relativePath = null;
4559
switch (element) {
4660
case HierarchyNamespace ns:
47-
relativePath = new FilesystemPath (MakePath (ns.GetManagedName (true)), true);
61+
case HierarchyClass klass:
62+
case HierarchyInterface iface:
63+
case HierarchyEnum enm:
4864
break;
4965

50-
case HierarchyClass klass:
51-
relativePath = GetFilePath (klass);
66+
default:
67+
throw new InvalidOperationException ($"Unsupported hierarchy type '{element.GetType ()}'");
68+
}
69+
70+
Func<string, HierarchyElement, FilesystemPath> getter = null;
71+
switch (TreeLayout.NamespaceTreeStyle) {
72+
case OutputNamespaceTreeStyle.Single:
73+
getter = GetPathFor_Single;
5274
break;
5375

54-
case HierarchyInterface iface:
55-
relativePath = GetFilePath (iface);
76+
case OutputNamespaceTreeStyle.Shallow:
77+
getter = GetPathFor_Shallow;
5678
break;
5779

58-
case HierarchyEnum enm:
59-
relativePath = GetFilePath (enm);
80+
case OutputNamespaceTreeStyle.Deep:
81+
getter = GetPathFor_Deep;
82+
break;
83+
84+
case OutputNamespaceTreeStyle.FirstLevelThenFullShallow:
85+
getter = GetPathFor_FirstLevelThenFullShallow;
86+
break;
87+
88+
case OutputNamespaceTreeStyle.FirstLevelThenFullSingle:
89+
getter = GetPathFor_FirstLevelThenFullSingle;
90+
break;
91+
92+
case OutputNamespaceTreeStyle.FirstLevelThenShortShallow:
93+
getter = GetPathFor_FirstLevelThenShortShallow;
94+
break;
95+
96+
case OutputNamespaceTreeStyle.FirstLevelThenShortSingle:
97+
getter = GetPathFor_FirstLevelThenShortSingle;
6098
break;
6199

62100
default:
63-
throw new InvalidOperationException ($"Unsupported hierarchy type '{element.GetType ()}'");
101+
throw new InvalidOperationException ($"Unsupported namespace tree style {TreeLayout.NamespaceTreeStyle}");
64102
}
65103

66-
return relativePath;
104+
return getter (rootDirectory, element);
105+
}
67106

68-
string MakePath (string elementName, string extension = null)
69-
{
70-
string relPath = elementName?.Replace ('.', Path.DirectorySeparatorChar);
71-
if (relPath == null)
72-
return null;
107+
protected FilesystemPath GetPathFor_Single (string rootDirectory, HierarchyElement element)
108+
{
109+
if (element is HierarchyNamespace)
110+
return null;
73111

74-
string ret = Path.Combine (rootDirectory, relPath);
75-
if (!String.IsNullOrEmpty (extension))
76-
ret += $".{extension}";
112+
throw new NotImplementedException ();
113+
}
77114

78-
return ret;
79-
}
115+
protected FilesystemPath GetPathFor_Shallow (string rootDirectory, HierarchyElement element)
116+
{
117+
throw new NotImplementedException ();
118+
}
80119

81-
FilesystemPath GetFilePath (HierarchyElement e)
82-
{
83-
return new FilesystemPath (MakePath (e.GetManagedName (true), Context.CodeGenerator.FileExtension), false);
120+
protected FilesystemPath GetPathFor_Deep (string rootDirectory, HierarchyElement element)
121+
{
122+
throw new NotImplementedException ();
123+
}
124+
125+
protected FilesystemPath GetPathFor_FirstLevelThenFullShallow (string rootDirectory, HierarchyElement element)
126+
{
127+
string fullManagedName = element.GetManagedName (true);
128+
bool isDirectory;
129+
string path = GetFirstNameSegment (fullManagedName);
130+
131+
Logger.Debug ($"Element: {element.FullName} (managed: {fullManagedName})");
132+
Logger.Debug ($"Initial path: {path}");
133+
if (element is HierarchyNamespace) {
134+
Logger.Debug ("Is namespace");
135+
isDirectory = true;
136+
path = Path.Combine (path, fullManagedName);
137+
} else {
138+
var obj = element as HierarchyObject;
139+
if (obj == null)
140+
throw new InvalidOperationException ("Only HierarchyObject and HierarchyNamespace instances can be used to compute output file name");
141+
Logger.Debug ("Is type");
142+
isDirectory = false;
143+
path = Path.Combine (path, obj.GetNamespace (), obj.GetNameWithoutNamespace ());
84144
}
145+
Logger.Debug ($"Final relative path: {path}");
146+
Logger.Debug (String.Empty);
147+
return GetFilePath (rootDirectory, path, isDirectory);
148+
}
149+
150+
protected FilesystemPath GetPathFor_FirstLevelThenShortShallow (string rootDirectory, HierarchyElement element)
151+
{
152+
throw new NotImplementedException ();
153+
}
154+
155+
protected FilesystemPath GetPathFor_FirstLevelThenFullSingle (string rootDirectory, HierarchyElement element)
156+
{
157+
throw new NotImplementedException ();
158+
}
159+
160+
protected FilesystemPath GetPathFor_FirstLevelThenShortSingle (string rootDirectory, HierarchyElement element)
161+
{
162+
throw new NotImplementedException ();
163+
}
164+
165+
protected string MakePath (string rootDirectory, string elementName, string extension = null)
166+
{
167+
string ret = Path.Combine (rootDirectory, elementName);
168+
if (!String.IsNullOrEmpty (extension))
169+
ret += $".{extension}";
170+
171+
return ret;
172+
}
173+
174+
protected FilesystemPath GetFilePath (string rootDirectory, string fileName, bool isDirectory = false)
175+
{
176+
return new FilesystemPath (MakePath (rootDirectory, fileName, isDirectory ? null : Context.CodeGenerator.FileExtension), isDirectory);
177+
}
178+
179+
string GetLastNameSegment (string fullManagedName)
180+
{
181+
return GetNameSegment (fullManagedName, false);
182+
}
183+
184+
string GetFirstNameSegment (string fullManagedName)
185+
{
186+
return GetNameSegment (fullManagedName, true);
187+
}
188+
189+
string GetNameSegment (string fullManagedName, bool first)
190+
{
191+
if (String.IsNullOrEmpty (fullManagedName))
192+
throw new ArgumentException ("must not be null or empty", nameof (fullManagedName));
193+
194+
int dot = first ? fullManagedName.IndexOf ('.') : fullManagedName.LastIndexOf ('.');
195+
if (dot == 0)
196+
throw new InvalidOperationException ($"Full valid managed type name (including namespace) expected, got '{fullManagedName}'");
197+
198+
if (dot < 0)
199+
return fullManagedName;
200+
201+
return first ? fullManagedName.Substring (0, dot) : fullManagedName.Substring (dot + 1);
85202
}
86203
}
87204
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public FilesystemPath (string fullPath, bool isDirectory)
5555

5656
public override string ToString ()
5757
{
58-
return $"{GetType ()} ({FullPath}; {IsDirectory}";
58+
return $"{GetType ()} ({FullPath}; {IsDirectory})";
5959
}
6060
}
6161
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ protected virtual void GenerateNamespace (string outputDirectoryRoot, HierarchyN
8888
(HierarchyElement nsm) => {
8989
if (nsFileWriter != null) {
9090
WriteFileHeader (ns, nsFileWriter);
91-
GenerateNamespaceMember (nsm, nsFileWriter, path.FullPath);
91+
GenerateNamespaceMember (nsm, nsFileWriter, outputDirectoryRoot);
9292
WriteFileHeader (ns, nsFileWriter);
9393
} else
94-
GenerateNamespaceMember (ns, nsm, path.FullPath);
94+
GenerateNamespaceMember (ns, nsm, outputDirectoryRoot);
9595
}
9696
);
9797
} finally {

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

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,49 +41,52 @@ public class GeneratorContext
4141

4242
public NameTranslationProvider NameTranslationProvider {
4343
get => nameTranslationProvider;
44-
set => nameTranslationProvider = EnsureNotNull ("NameTranslationProvider", value);
44+
set => nameTranslationProvider = Helpers.EnsureNotNull ("NameTranslationProvider", value);
4545
}
4646

4747
public OutputPathProvider OutputPathProvider {
4848
get => outputPathProvider;
49-
set => outputPathProvider = EnsureNotNull ("OutputPathProvider", value);
49+
set => outputPathProvider = Helpers.EnsureNotNull ("OutputPathProvider", value);
5050
}
5151

5252
public FormattingCodeGenerator CodeGenerator {
5353
get => codeGenerator;
54-
set => codeGenerator = EnsureNotNull ("CodeGenerator", value);
54+
set => codeGenerator = Helpers.EnsureNotNull ("CodeGenerator", value);
5555
}
5656

5757
public Encoding FileEncoding {
5858
get => fileEncoding;
59-
set => fileEncoding = EnsureNotNull ("FileEncoding", value);
59+
set => fileEncoding = Helpers.EnsureNotNull ("FileEncoding", value);
6060
}
6161

6262
public Hierarchy HierarchyBuilder {
6363
get => hierarchyBuilder;
64-
set => hierarchyBuilder = EnsureNotNull ("HierarchyBuilder", value);
64+
set => hierarchyBuilder = Helpers.EnsureNotNull ("HierarchyBuilder", value);
6565
}
6666

6767
public string FileHeaderComment { get; set; }
6868
public bool DumpHierarchy { get; set; }
6969
public string HierarchyDumpFilePath { get; set; }
70+
public bool UsePartialClasses { get; set; } = true;
7071

7172
public GeneratorContext (FormattingCodeGenerator codeGenerator, Encoding fileEncoding = null)
7273
{
7374
CodeGenerator = codeGenerator ?? throw new ArgumentNullException (nameof (codeGenerator));
74-
OutputPathProvider = new DefaultOutputPathProvider (this);
7575
FileEncoding = fileEncoding ?? Encoding.UTF8;
7676
HierarchyBuilder = new XamarinAndroidHierarchy (this);
7777
NameTranslationProvider = new XamarinNameTranslationProvider ();
78+
OutputPathProvider = new DefaultOutputPathProvider (this, new OutputTreeLayout ());
7879
}
7980

8081
internal void AssertSaneEnvironment ()
8182
{
8283
if (NameTranslationProvider == null)
8384
throw new InvalidOperationException ("Name translation provider not defined");
85+
NameTranslationProvider.Validate ();
8486

8587
if (OutputPathProvider == null)
8688
throw new InvalidOperationException ("Output path provider not defined");
89+
OutputPathProvider.Validate ();
8790

8891
if (CodeGenerator == null)
8992
throw new InvalidOperationException ("Code generator not defined");
@@ -94,12 +97,5 @@ internal void AssertSaneEnvironment ()
9497
if (HierarchyBuilder == null)
9598
throw new InvalidOperationException ("Hierarchy builder not defined");
9699
}
97-
98-
T EnsureNotNull<T> (string name, T value) where T: class
99-
{
100-
if (value == null)
101-
throw new InvalidOperationException ($"{name} must not be null");
102-
return value;
103-
}
104100
}
105101
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ public override void Init (ApiElement apiElement)
4444
{
4545
base.Init (apiElement);
4646
apiEnum = EnsureApiElementType<ApiEnum> (apiElement);
47+
48+
// Enums are special - their name attribute is set to the full managed name
49+
int lastDot = apiElement.Name.LastIndexOf ('.');
50+
if (lastDot < 0)
51+
return;
52+
53+
if (lastDot == 0)
54+
throw new InvalidOperationException($"Type name ({apiElement.Name}) must not start with a dot");
55+
56+
FullName = Name;
57+
Name = apiElement.Name.Substring (lastDot + 1);
4758
}
4859
}
4960
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,17 @@ public class HierarchyInterface : HierarchyObject
3434

3535
public HierarchyInterface (GeneratorContext context, HierarchyElement parent) : base (context, parent)
3636
{}
37+
38+
protected override (string ManagedName, string FullManagedName) GenerateManagedNames ()
39+
{
40+
if (String.IsNullOrEmpty (Name))
41+
throw new InvalidOperationException ("Name is required");
42+
43+
string managedName = GetManagedName ();
44+
if (managedName [0] != 'I')
45+
managedName = $"I{managedName}";
46+
47+
return (managedName, BuildFullName (managedName, (HierarchyElement parent) => parent.GetManagedName ()));
48+
}
3749
}
3850
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,19 @@
2424
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2525
// THE SOFTWARE.
2626
using System;
27-
using System.Collections.Generic;
27+
28+
using Java.Interop.Bindings.Syntax;
2829

2930
namespace Java.Interop.Bindings.Compiler
3031
{
31-
// TODO: invoker managed names must be generated after we have the interface name resolved - add amechanism to
32-
// do that in Hierarchy
3332
public class HierarchyInterfaceInvoker : HierarchyClass
3433
{
3534
HierarchyInterface InvokedInterface { get; }
3635

3736
public HierarchyInterfaceInvoker (GeneratorContext context, HierarchyElement parent, HierarchyInterface invokedInterface) : base (context, parent)
3837
{
3938
InvokedInterface = invokedInterface ?? throw new ArgumentNullException (nameof (invokedInterface));
39+
Visibility = ApiVisibility.Internal;
4040
}
4141

4242
public override void Init ()
@@ -61,6 +61,9 @@ string MakeInvokerName (string baseName)
6161
if (String.IsNullOrEmpty (baseName))
6262
return String.Empty;
6363

64+
if (baseName.EndsWith ("Invoker", StringComparison.Ordinal))
65+
return baseName;
66+
6467
return $"{baseName}Invoker";
6568
}
6669
}

0 commit comments

Comments
 (0)