Skip to content

Commit

Permalink
Added more patch classes: replace and insert as sibling
Browse files Browse the repository at this point in the history
  • Loading branch information
shdwp committed Apr 7, 2020
1 parent c9d2fac commit 3328100
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 17 deletions.
33 changes: 33 additions & 0 deletions UIExtenderLib/Prefab/Patches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ public interface IPrefabPatch
{
}

/**
* Custom patch on either whole XmlDocument (if T is XmlDocument) or Xpath specified node (if XmlNode is the generic argument)
*/
public abstract class CustomPatch<T> : IPrefabPatch where T: XmlNode
{
public abstract void Apply(T document);
}

/**
* Base class for insert patches
*/
public abstract class InsertPatch : IPrefabPatch
{
public static int PositionFirst = 0;
Expand All @@ -22,8 +28,35 @@ public abstract class InsertPatch : IPrefabPatch
public abstract int Position { get; }
}

/**
* Patch that inserts prefab extension (specified by `Name`) as a child in XPath specified node, at specific position (`Position` property)
*/
public abstract class PrefabExtensionInsertPatch: InsertPatch
{
public abstract string Name { get; }
}

/**
* Patch that replaces node specified by XPath with node from prefab extension
*/
public abstract class PrefabExtensionReplacePatch : IPrefabPatch
{
public abstract string Name { get; }
}

/**
* Patch that inserts prefab extension as a sibling to node specified by Xpath.
* Order is controlled by `Type` property.
*/
public abstract class PrefabExtensionInsertAsSiblingPatch
{
public enum InsertType
{
Prepend,
Append,
}

public virtual InsertType Type => InsertType.Append;
public abstract string Name { get; }
}
}
80 changes: 63 additions & 17 deletions UIExtenderLibModule/Prefab/PrefabComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal class PrefabComponent
internal void RegisterPatch(string movie, Action<XmlDocument> patcher)
{
Debug.Assert(movie != null && !movie.IsEmpty(), $"Invalid movie name: {movie}!");

_moviePatches.Get(movie, () => new List<Action<XmlDocument>>()).Add(patcher);
}

Expand All @@ -50,36 +50,61 @@ internal void RegisterPatch(string movie, string xpath, Action<XmlNode> patcher)
}

/**
* Register Gauntlet movie XML insert patch.
* Register XML insert patch
*/
internal void RegisterPatch(string movie, string xpath, PrefabExtensionInsertPatch patch)
{
Debug.Assert(_prefabExtensionPaths.ContainsKey(patch.Name), $"Prefab extension with name {patch.Name} does not exist!");

RegisterPatch(movie, xpath, (node) =>
{
var path = _prefabExtensionPaths[patch.Name];
var doc = new XmlDocument();

using (var reader = XmlReader.Create(path, new XmlReaderSettings
{
IgnoreComments = true,
IgnoreWhitespace = true,
}))
{
doc.Load(reader);
}

Debug.Assert(doc.HasChildNodes, $"Failed to parse extension ({patch.Name}) XML!");
var newNode = node.OwnerDocument.ImportNode(doc.DocumentElement, true);
var extensionNode = LoadPrefabExtension(patch.Name);
var importedExtensionNode = node.OwnerDocument.ImportNode(extensionNode, true);
var position = Math.Min(patch.Position, node.ChildNodes.Count - 1);
position = Math.Max(position, 0);
Debug.Assert(position >= 0 && position < node.ChildNodes.Count, $"Invalid position ({position}) for insert (patching in {patch.Name})");

node.InsertAfter(newNode, node.ChildNodes[position]);
node.InsertAfter(importedExtensionNode, node.ChildNodes[position]);
});
}

/**
* Register XML replace patch
*/
internal void RegisterPatch(string movie, string xpath, PrefabExtensionReplacePatch patch)
{
RegisterPatch(movie, xpath, (node) =>
{
var extensionNode = LoadPrefabExtension(patch.Name);
var importedExtensionNode = node.OwnerDocument.ImportNode(extensionNode, true);

node.ParentNode.ReplaceChild(importedExtensionNode, node);
});
}

/**
* Register XML sibling insert patch
*/
internal void RegisterPatch(string movie, string xpath, PrefabExtensionInsertAsSiblingPatch patch)
{
RegisterPatch(movie, xpath, (node) =>
{
var extensionNode = LoadPrefabExtension(patch.Name);
var importedExtensionNode = node.OwnerDocument.ImportNode(extensionNode, true);

switch (patch.Type)
{
case PrefabExtensionInsertAsSiblingPatch.InsertType.Append:
node.ParentNode.InsertAfter(importedExtensionNode, node);
break;

case PrefabExtensionInsertAsSiblingPatch.InsertType.Prepend:
node.ParentNode.InsertBefore(importedExtensionNode, node);
break;
}
});
}

/**
* Search loaded modules for PrefabExtensions and save them for further reference
*/
Expand All @@ -101,6 +126,27 @@ internal void FindPrefabExtensions()
}
}

/**
* Load and parse prefab extension
*/
private XmlNode LoadPrefabExtension(string name)
{
var path = _prefabExtensionPaths[name];
var doc = new XmlDocument();

using (var reader = XmlReader.Create(path, new XmlReaderSettings
{
IgnoreComments = true,
IgnoreWhitespace = true,
}))
{
doc.Load(reader);
}

Debug.Assert(doc.HasChildNodes, $"Failed to parse extension ({name}) XML!");
return doc.DocumentElement;
}

/**
* Make WidgetFactory reload Movies that were extended by _moviePatches.
*
Expand Down
8 changes: 8 additions & 0 deletions UIExtenderLibModule/UIExtenderLibModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ protected override void OnBeforeInitialModuleScreenSetAsRoot()
WidgetComponent.RegisterPatch(xmlExtension.Movie, xmlExtension.XPath, patch);
break;

case PrefabExtensionReplacePatch patch:
WidgetComponent.RegisterPatch(xmlExtension.Movie, xmlExtension.XPath, patch);
break;

case PrefabExtensionInsertAsSiblingPatch patch:
WidgetComponent.RegisterPatch(xmlExtension.Movie, xmlExtension.XPath, patch);
break;

case CustomPatch<XmlDocument> patch:
WidgetComponent.RegisterPatch(xmlExtension.Movie, patch.Apply);
break;
Expand Down

0 comments on commit 3328100

Please sign in to comment.