summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI/Framework
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2016-11-05 16:20:31 -0400
committerJesse Plamondon-Willard <github@jplamondonw.com>2016-11-05 16:20:31 -0400
commit8d8b640779061a5cd001890c15f6f00a602df463 (patch)
tree02f746c96cc09e64075b799782c4cd232a0b3473 /src/StardewModdingAPI/Framework
parent0749fdcbe5d991df9e7c1ad91fd48c198fcc1dbb (diff)
downloadSMAPI-8d8b640779061a5cd001890c15f6f00a602df463.tar.gz
SMAPI-8d8b640779061a5cd001890c15f6f00a602df463.tar.bz2
SMAPI-8d8b640779061a5cd001890c15f6f00a602df463.zip
add deprecation warnings (#165)
Diffstat (limited to 'src/StardewModdingAPI/Framework')
-rw-r--r--src/StardewModdingAPI/Framework/DeprecationManager.cs124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/StardewModdingAPI/Framework/DeprecationManager.cs b/src/StardewModdingAPI/Framework/DeprecationManager.cs
new file mode 100644
index 00000000..2d4ff614
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/DeprecationManager.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+
+namespace StardewModdingAPI.Framework
+{
+ /// <summary>Manages deprecation warnings.</summary>
+ internal class DeprecationManager
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>The friendly mod names treated as deprecation warning sources (assembly full name => mod name).</summary>
+ private readonly IDictionary<string, string> ModNamesByAssembly = new Dictionary<string, string>();
+
+ /// <summary>The deprecations which have already been logged (as 'mod name::noun phrase::version').</summary>
+ private readonly HashSet<string> LoggedDeprecations = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Register a mod as a possible source of deprecation warnings.</summary>
+ /// <param name="assembly">The mod assembly.</param>
+ /// <param name="name">The mod's friendly name.</param>
+ public void AddMod(Assembly assembly, string name)
+ {
+ this.ModNamesByAssembly[assembly.FullName] = name;
+ }
+
+ /// <summary>Log a deprecation warning.</summary>
+ /// <param name="nounPhrase">A noun phrase describing what is deprecated.</param>
+ /// <param name="version">The SMAPI version which deprecated it.</param>
+ public void Warn(string nounPhrase, string version)
+ {
+ this.Warn(this.GetSourceNameFromStack(), nounPhrase, version);
+ }
+
+ /// <summary>Log a deprecation warning.</summary>
+ /// <param name="source">The friendly mod name which used the deprecated code.</param>
+ /// <param name="nounPhrase">A noun phrase describing what is deprecated.</param>
+ /// <param name="version">The SMAPI version which deprecated it.</param>
+ public void Warn(string source, string nounPhrase, string version)
+ {
+ if (source != null && !this.MarkWarned(source, nounPhrase, version))
+ return;
+
+ Log.Debug(source != null
+ ? $"NOTE: {source} used {nounPhrase}, which is deprecated since SMAPI {version}. It will work fine for now, but may be removed in a future version of SMAPI."
+ : $"NOTE: an unknown mod used {nounPhrase}, which is deprecated since SMAPI {version}. It will work fine for now, but may be removed in a future version of SMAPI.\n{Environment.StackTrace}"
+ );
+ }
+
+ /// <summary>Mark a deprecation warning as already logged.</summary>
+ /// <param name="nounPhrase">A noun phrase describing what is deprecated (e.g. "the Extensions.AsInt32 method").</param>
+ /// <param name="version">The SMAPI version which deprecated it.</param>
+ /// <returns>Returns whether the deprecation was successfully marked as warned. Returns <c>false</c> if it was already marked.</returns>
+ public bool MarkWarned(string nounPhrase, string version)
+ {
+ return this.MarkWarned(this.GetSourceNameFromStack(), nounPhrase, version);
+ }
+
+ /// <summary>Mark a deprecation warning as already logged.</summary>
+ /// <param name="source">The friendly name of the assembly which used the deprecated code.</param>
+ /// <param name="nounPhrase">A noun phrase describing what is deprecated (e.g. "the Extensions.AsInt32 method").</param>
+ /// <param name="version">The SMAPI version which deprecated it.</param>
+ /// <returns>Returns whether the deprecation was successfully marked as warned. Returns <c>false</c> if it was already marked.</returns>
+ public bool MarkWarned(string source, string nounPhrase, string version)
+ {
+ if (string.IsNullOrWhiteSpace(source))
+ throw new InvalidOperationException("The deprecation source cannot be empty.");
+
+ string key = $"{source}::{nounPhrase}::{version}";
+ if (this.LoggedDeprecations.Contains(key))
+ return false;
+ this.LoggedDeprecations.Add(key);
+ return true;
+ }
+
+ /// <summary>Get whether a type implements the given virtual method.</summary>
+ /// <param name="subtype">The type to check.</param>
+ /// <param name="baseType">The base type which declares the virtual method.</param>
+ /// <param name="name">The method name.</param>
+ public bool IsVirtualMethodImplemented(Type subtype, Type baseType, string name)
+ {
+ MethodInfo method = subtype.GetMethod(nameof(Mod.Entry), new[] { typeof(object[]) });
+ return method.DeclaringType != baseType;
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Get the friendly name for the closest assembly registered as a source of deprecation warnings.</summary>
+ /// <returns>Returns the source name, or <c>null</c> if no registered assemblies were found.</returns>
+ private string GetSourceNameFromStack()
+ {
+ // get stack frames
+ StackTrace stack = new StackTrace();
+ StackFrame[] frames = stack.GetFrames();
+ if (frames == null)
+ return null;
+
+ // search stack for a source assembly
+ foreach (StackFrame frame in frames)
+ {
+ // get assembly name
+ MethodBase method = frame.GetMethod();
+ Type type = method.ReflectedType;
+ if (type == null)
+ continue;
+ string assemblyName = type.Assembly.FullName;
+
+ // get name if it's a registered source
+ if (this.ModNamesByAssembly.ContainsKey(assemblyName))
+ return this.ModNamesByAssembly[assemblyName];
+ }
+
+ // no known assembly found
+ return null;
+ }
+ }
+}