Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] Emit an Error if a custom view cannot b…
Browse files Browse the repository at this point in the history
…e fixed up.

Context dotnet#1711

When using a custom view within a layout file we replace the
`namespace.classname` with an `md5hash.classname`. We do this
by using the `acwmap.txt` file to make known types onto the
`md5` hashed ones. We do this in a case sensitive manner. We
also only support Camel case and lower case type names.

	ClassLibrary1.CustomView=md5d6f7135293df7527c983d45d07471c5e.CustomTextView
	classlibrary1.CustomView=md5d6f7135293df7527c983d45d07471c5e.CustomTextView

Given that a user is able to type these manually it is highly
probable that typo's will occur. If for example a user types

	Classlibrary1.CustomView

this will NOT be fixed up. Instead the user will recieve the
following error at runtime.

	FATAL UNHANDLED EXCEPTION: Android.Views.InflateException: Binary XML file line #1: Binary XML file line #1: Error inflating class Classlibrary1.CustomTextView ---> Android.Views.InflateException: Binary XML file line #1: Error inflating class Classlibrary1.CustomTextView ---> Java.Lang.ClassNotFoundException: Didn't find class "Classlibrary1.CustomTextView"

if you are using the control in a number of places this runtime error
does nothing to help track down the problem.

Instead what we should be doing is detecting these issues and emitting
a build error. This will provide the user not only the problem but
also a link to the file causing the probem.

TODO
----

[ ] Fix up the name so it points to the file in `Resources` not `res`
[ ] Add a Unit test.
[ ] Add Error code and document.
  • Loading branch information
dellis1972 committed Jul 5, 2018
1 parent ba0db7d commit d2e98a9
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 7 deletions.
15 changes: 14 additions & 1 deletion src/Xamarin.Android.Build.Tasks/Tasks/ConvertResourcesCases.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (C) 2011 Xamarin, Inc. All rights reserved.

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -71,7 +72,19 @@ void FixupResources (ITaskItem item, Dictionary<string, string> acwMap)
MonoAndroidHelper.SetWriteable (tmpdest);
try {
AndroidResource.UpdateXmlResource (resdir, tmpdest, acwMap,
ResourceDirectories.Where (s => s != item).Select(s => s.ItemSpec));
ResourceDirectories.Where (s => s != item).Select(s => s.ItemSpec), (t, m) => {
switch (t) {
case TraceLevel.Error:
Log.LogCodedError ("XAXXXX", file: file, lineNumber: 0, message: m);
break;
case TraceLevel.Warning:
Log.LogCodedWarning ("XAXXXX", file: file, lineNumber: 0, message: m);
break;
default:
Log.LogDebugMessage (m);
break;
}
});

// We strip away an eventual UTF-8 BOM from the XML file.
// This is a requirement for the Android designer because the desktop Java renderer
Expand Down
21 changes: 15 additions & 6 deletions src/Xamarin.Android.Build.Tasks/Utilities/AndroidResource.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;

namespace Monodroid {
static class AndroidResource {

public static void UpdateXmlResource (string res, string filename, Dictionary<string, string> acwMap, IEnumerable<string> additionalDirectories = null)
public static void UpdateXmlResource (string res, string filename, Dictionary<string, string> acwMap, IEnumerable<string> additionalDirectories = null, Action<TraceLevel, string> logMessage = null)
{
// use a temporary file so we only update the real file if things actually changed
string tmpfile = filename + ".bk";
try {
XDocument doc = XDocument.Load (filename, LoadOptions.SetLineInfo);

UpdateXmlResource (res, doc.Root, acwMap, additionalDirectories);
UpdateXmlResource (res, doc.Root, acwMap, additionalDirectories, logMessage);
using (var stream = File.OpenWrite (tmpfile))
using (var xw = new LinePreservedXmlWriter (new StreamWriter (stream)))
xw.WriteNode (doc.CreateNavigator (), false);
Expand Down Expand Up @@ -59,10 +61,10 @@ static IEnumerable<T> Prepend<T> (this IEnumerable<T> l, T another) where T : XN
yield return e;
}

static void UpdateXmlResource (string resourcesBasePath, XElement e, Dictionary<string, string> acwMap, IEnumerable<string> additionalDirectories = null)
static void UpdateXmlResource (string resourcesBasePath, XElement e, Dictionary<string, string> acwMap, IEnumerable<string> additionalDirectories = null, Action<TraceLevel, string> logMessage = null)
{
foreach (var elem in GetElements (e).Prepend (e)) {
TryFixCustomView (elem, acwMap);
TryFixCustomView (elem, acwMap, logMessage);
}

foreach (var path in fixResourcesAliasPaths) {
Expand Down Expand Up @@ -217,15 +219,22 @@ private static bool TryFixResAuto (XAttribute attr, Dictionary<string, string> a
return false;
}

private static bool TryFixCustomView (XElement elem, Dictionary<string, string> acwMap)
private static bool TryFixCustomView (XElement elem, Dictionary<string, string> acwMap, Action<TraceLevel, string> logMessage = null)
{
// Looks for any <My.DotNet.Class ...
// and tries to change it to the ACW name
if (acwMap.ContainsKey (elem.Name.ToString ())) {
elem.Name = acwMap[elem.Name.ToString ()];
return true;
}

if (logMessage == null)
return false;
var matchingKey = acwMap.FirstOrDefault(x => String.Equals(x.Key, elem.Name.ToString (), StringComparison.OrdinalIgnoreCase));
if (matchingKey.Key != null) {
// we have elements with slightly different casing.
// lets issue a error.
logMessage (TraceLevel.Error, $"We found a matching key '{matchingKey.Key}' for '{elem.Name.ToString ()}'. But the casing was incorrect. Please correct the casing");
}
return false;
}

Expand Down

0 comments on commit d2e98a9

Please sign in to comment.