Skip to content

Commit

Permalink
Add AdvancedJSONMerge TargetType, assumed if not given
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael P. Starkweather committed Feb 8, 2019
1 parent 2705b7a commit 4911c81
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 58 deletions.
40 changes: 26 additions & 14 deletions ModTek/ModTek.cs
Expand Up @@ -891,7 +891,7 @@ private static IEnumerator<ProgressReport> HandleModManifestsLoop()
// StreamingAssets don't get default appendText
if (Path.GetExtension(modEntry.Path)?.ToLowerInvariant() == ".json" && modEntry.ShouldMergeJSON)
{
// this assumes that .json can only have a single type
// this assumes that vanilla .json can only have a single type
// typeCache will always contain this path
modEntry.Type = typeCache.GetTypes(modEntry.Id)[0];

Expand Down Expand Up @@ -932,29 +932,41 @@ private static IEnumerator<ProgressReport> HandleModManifestsLoop()
{
case "AdvancedJSONMerge":
{
var id = JSONMerger.GetTargetID(modEntry.Path);
var advancedJSONMerge = AdvancedJSONMerge.FromFile(modEntry.Path);
var id = advancedJSONMerge.TargetID;
var type = advancedJSONMerge.TargetType;

// need to add the types of the file to the typeCache, so that they can be used later
// if merging onto a file added by another mod, the type is already in the cache
var types = typeCache.GetTypes(id, CachedVersionManifest);
if (string.IsNullOrEmpty(type))
{
var types = typeCache.GetTypes(id, CachedVersionManifest);
if (types == null || types.Count == 0)
{
Log($"\tERROR: AdvancedJSONMerge: \"{GetRelativePath(modEntry.Path, ModsDirectory)}\" could not resolve type for ID: {id}. Skipping this merge");
continue;
}

// assume that only a single type
type = types[0];
}

if (types == null || types.Count == 0)
var entry = FindEntry(type, id);
if (entry == null)
{
Log($"\tERROR: AdvancedJSONMerge: \"{GetRelativePath(modEntry.Path, ModsDirectory)}\" has ID that doesn't match anything! Skipping this merge");
Log($"\tERROR: AdvancedJSONMerge: \"{GetRelativePath(modEntry.Path, ModsDirectory)}\" could not find entry {id} ({type}). Skipping this merge");
continue;
}

if (!merges.ContainsKey(types[0]))
merges[types[0]] = new Dictionary<string, List<string>>();
if (!merges.ContainsKey(type))
merges[type] = new Dictionary<string, List<string>>();

if (!merges[types[0]].ContainsKey(id))
merges[types[0]][id] = new List<string>();
if (!merges[type].ContainsKey(id))
merges[type][id] = new List<string>();

if (merges[types[0]][id].Contains(modEntry.Path)) // TODO: is this necessary?
if (merges[type][id].Contains(modEntry.Path)) // TODO: is this necessary?
continue;

Log($"\tAdvancedJSONMerge: \"{GetRelativePath(modEntry.Path, ModsDirectory)}\" ({types[0]})");
merges[types[0]][id].Add(modEntry.Path);
Log($"\tAdvancedJSONMerge: \"{GetRelativePath(modEntry.Path, ModsDirectory)}\" targeting '{id}' ({type})");
merges[type][id].Add(modEntry.Path);
continue;
}
}
Expand Down
97 changes: 54 additions & 43 deletions ModTek/Util/JSONMerger.cs
@@ -1,15 +1,40 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using static ModTek.Util.Logger;

namespace ModTek.Util
{
public static class JSONMerger
{
public enum AdvancedMergeAction
private static bool IsAdvancedJSONMerge(JObject merge)
{
return merge[nameof(AdvancedJSONMerge.TargetID)] != null && merge[nameof(AdvancedJSONMerge.Instructions)] != null;
}

private static void DoAdvancedMerge(JObject target, JObject merge)
{
var instructions = merge[nameof(AdvancedJSONMerge.Instructions)].ToObject<List<AdvancedJSONMerge.Instruction>>();
foreach (var instruction in instructions)
instruction.Process(target);
}

public static void MergeIntoTarget(JObject target, JObject merge)
{
if (IsAdvancedJSONMerge(merge))
DoAdvancedMerge(target, merge);
else
target.Merge(merge, new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Replace });
}
}

public class AdvancedJSONMerge
{
public enum MergeAction
{
ArrayAdd, // adds a given value to the end of the target array
ArrayAddAfter, // adds a given value after the target element in the array
Expand All @@ -20,15 +45,15 @@ public enum AdvancedMergeAction
Replace // replaces the target with a given value
}

public class AdvancedMergeInstruction
public class Instruction
{
[JsonProperty(Required = Required.Always)] [JsonConverter(typeof(StringEnumConverter))]
public AdvancedMergeAction Action;
[JsonProperty(Required = Required.Always)]
[JsonConverter(typeof(StringEnumConverter))]
public MergeAction Action;

[JsonProperty(Required = Required.Always)]
public string JSONPath;

// TODO add external JSON support
public JToken Value;

public void Process(JObject root)
Expand All @@ -38,7 +63,7 @@ public void Process(JObject root)
if (tokens.Count == 0)
throw new Exception("JSONPath does not point to anything");

if (Action == AdvancedMergeAction.Remove)
if (Action == MergeAction.Remove)
{
foreach (var jToken in tokens)
{
Expand All @@ -57,24 +82,24 @@ public void Process(JObject root)
var token = tokens[0];
switch (Action)
{
case AdvancedMergeAction.Replace:
case MergeAction.Replace:
token.Replace(Value);
break;
case AdvancedMergeAction.ArrayAdd:
case MergeAction.ArrayAdd:
{
if (!(token is JArray a))
throw new Exception("JSONPath needs to point an array");

a.Add(Value);
break;
}
case AdvancedMergeAction.ArrayAddAfter:
case MergeAction.ArrayAddAfter:
token.AddAfterSelf(Value);
break;
case AdvancedMergeAction.ArrayAddBefore:
case MergeAction.ArrayAddBefore:
token.AddBeforeSelf(Value);
break;
case AdvancedMergeAction.ObjectMerge:
case MergeAction.ObjectMerge:
{
if (!(token is JObject o1) || !(Value is JObject o2))
throw new Exception("JSONPath has to point to an object and Value has to be an object");
Expand All @@ -83,7 +108,7 @@ public void Process(JObject root)
o1.Merge(o2, new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Replace });
break;
}
case AdvancedMergeAction.ArrayConcat:
case MergeAction.ArrayConcat:
{
if (!(token is JArray a1) || !(Value is JArray a2))
throw new Exception("JSONPath has to point to an array and Value has to be an array");
Expand All @@ -97,42 +122,28 @@ public void Process(JObject root)
}
}

public static string GetTargetID(string modEntryPath)
{
var merge = ModTek.ParseGameJSONFile(modEntryPath);
return merge[nameof(AdvancedJSONMerge.TargetID)].ToString();
}
[JsonProperty(Required = Required.Always)]
public string TargetID;

private static bool IsAdvancedJSONMerge(JObject merge)
{
return merge[nameof(AdvancedJSONMerge.TargetID)] != null;
}
public string TargetType;

private static void DoAdvancedMerge(JObject target, JObject merge)
{
var instructions = merge[nameof(AdvancedJSONMerge.Instructions)].ToObject<List<AdvancedMergeInstruction>>();
foreach (var instruction in instructions)
instruction.Process(target);
}

public static void MergeIntoTarget(JObject target, JObject merge)
{
if (IsAdvancedJSONMerge(merge))
DoAdvancedMerge(target, merge);
else
target.Merge(merge, new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Replace });
}
[JsonProperty(Required = Required.Always)]
public List<Instruction> Instructions;

// unused, this level is parsed manually
#pragma warning disable CS0649
private class AdvancedJSONMerge
public static AdvancedJSONMerge FromFile(string path)
{
[JsonProperty(Required = Required.Always)]
public string TargetID;
if (!File.Exists(path))
return null;

[JsonProperty(Required = Required.Always)]
public List<AdvancedMergeInstruction> Instructions;
try
{
return JsonConvert.DeserializeObject<AdvancedJSONMerge>(File.ReadAllText(path));
}
catch (Exception e)
{
LogException($"\tCould not read AdvancedJSONMerge in path: {path}", e);
return null;
}
}
#pragma warning restore
}
}
2 changes: 1 addition & 1 deletion ModTekUnitTests/AdvancedJSONMergeInstructionTests.cs
Expand Up @@ -215,7 +215,7 @@ public void MergeNestedObject()

private void ProcessInstructionJSON(string json)
{
var instruction = JsonConvert.DeserializeObject<JSONMerger.AdvancedMergeInstruction>(json);
var instruction = JsonConvert.DeserializeObject<AdvancedJSONMerge.Instruction>(json);
instruction.Process(root);
}
}
Expand Down

0 comments on commit 4911c81

Please sign in to comment.