Refactored parsing services into separate project
This commit is contained in:
95
Filtration.Parser/Filtration.Parser.csproj
Normal file
95
Filtration.Parser/Filtration.Parser.csproj
Normal file
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{10A7C2BC-EC6F-4A38-BDDA-E35935004C02}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Filtration.Parser</RootNamespace>
|
||||
<AssemblyName>Filtration.Parser</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Castle.Core.3.3.0\lib\net45\Castle.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Castle.Windsor, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Castle.Windsor.3.3.0\lib\net45\Castle.Windsor.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Filtration\Properties\Settings.Designer.cs">
|
||||
<Link>Properties\Settings.Designer.cs</Link>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\BlockGroupHierarchyBuilder.cs" />
|
||||
<Compile Include="Services\ItemFilterBlockTranslator.cs" />
|
||||
<Compile Include="Services\ItemFilterScriptTranslator.cs" />
|
||||
<Compile Include="WindsorInstaller.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\Filtration\Properties\Settings.settings">
|
||||
<Link>Properties\Settings.settings</Link>
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Filtration.Common\Filtration.Common.csproj">
|
||||
<Project>{8cb44f28-2956-4c2a-9314-72727262edd4}</Project>
|
||||
<Name>Filtration.Common</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Filtration.ObjectModel\Filtration.ObjectModel.csproj">
|
||||
<Project>{4aac3beb-1dc1-483e-9d11-0e9334e80227}</Project>
|
||||
<Name>Filtration.ObjectModel</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Filtration.Parser.Interface\Filtration.Parser.Interface.csproj">
|
||||
<Project>{46383f20-02df-48b4-b092-9088fa4acd5a}</Project>
|
||||
<Name>Filtration.Parser.Interface</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
39
Filtration.Parser/Properties/AssemblyInfo.cs
Normal file
39
Filtration.Parser/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Filtration.Parser")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Filtration.Parser")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("10a7c2bc-ec6f-4a38-bdda-e35935004c02")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: InternalsVisibleTo("Filtration.ItemFilterPreview.Tests")]
|
||||
[assembly: InternalsVisibleTo("Filtration.Parser.Tests")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
76
Filtration.Parser/Services/BlockGroupHierarchyBuilder.cs
Normal file
76
Filtration.Parser/Services/BlockGroupHierarchyBuilder.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Filtration.ObjectModel;
|
||||
using Filtration.Parser.Interface.Services;
|
||||
|
||||
namespace Filtration.Parser.Services
|
||||
{
|
||||
internal class BlockGroupHierarchyBuilder : IBlockGroupHierarchyBuilder
|
||||
{
|
||||
private ItemFilterBlockGroup _rootBlockGroup;
|
||||
|
||||
public void Initialise(ItemFilterBlockGroup rootBlockGroup)
|
||||
{
|
||||
_rootBlockGroup = rootBlockGroup;
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
_rootBlockGroup = null;
|
||||
}
|
||||
|
||||
public ItemFilterBlockGroup IntegrateStringListIntoBlockGroupHierarchy(IEnumerable<string> groupStrings)
|
||||
{
|
||||
if (_rootBlockGroup == null)
|
||||
{
|
||||
throw new Exception("BlockGroupHierarchyBuilder must be initialised with root BlockGroup before use");
|
||||
}
|
||||
return IntegrateStringListIntoBlockGroupHierarchy(groupStrings, _rootBlockGroup);
|
||||
}
|
||||
|
||||
public ItemFilterBlockGroup IntegrateStringListIntoBlockGroupHierarchy(IEnumerable<string> groupStrings, ItemFilterBlockGroup startItemGroup)
|
||||
{
|
||||
var inputGroups = groupStrings.ToList();
|
||||
var firstGroup = inputGroups.First().Trim();
|
||||
if (firstGroup.StartsWith("~"))
|
||||
{
|
||||
firstGroup = firstGroup.Substring(1);
|
||||
}
|
||||
|
||||
ItemFilterBlockGroup matchingChildItemGroup = null;
|
||||
if (startItemGroup.ChildGroups.Count(g => g.GroupName == firstGroup) > 0)
|
||||
{
|
||||
matchingChildItemGroup = startItemGroup.ChildGroups.First(c => c.GroupName == firstGroup);
|
||||
}
|
||||
|
||||
if (matchingChildItemGroup == null)
|
||||
{
|
||||
var newItemGroup = CreateBlockGroup(inputGroups.First().Trim(), startItemGroup);
|
||||
startItemGroup.ChildGroups.Add(newItemGroup);
|
||||
inputGroups = inputGroups.Skip(1).ToList();
|
||||
return inputGroups.Count > 0 ? IntegrateStringListIntoBlockGroupHierarchy(inputGroups, newItemGroup) : newItemGroup;
|
||||
}
|
||||
inputGroups = inputGroups.Skip(1).ToList();
|
||||
return inputGroups.Count > 0 ? IntegrateStringListIntoBlockGroupHierarchy(inputGroups, matchingChildItemGroup) : matchingChildItemGroup;
|
||||
}
|
||||
|
||||
private ItemFilterBlockGroup CreateBlockGroup(string groupNameString, ItemFilterBlockGroup parentGroup)
|
||||
{
|
||||
var advanced = false;
|
||||
|
||||
if (groupNameString.StartsWith("~"))
|
||||
{
|
||||
groupNameString = groupNameString.Substring(1);
|
||||
advanced = true;
|
||||
}
|
||||
|
||||
if (parentGroup.Advanced)
|
||||
{
|
||||
advanced = true;
|
||||
}
|
||||
|
||||
return new ItemFilterBlockGroup(groupNameString, parentGroup, advanced);
|
||||
}
|
||||
}
|
||||
}
|
||||
446
Filtration.Parser/Services/ItemFilterBlockTranslator.cs
Normal file
446
Filtration.Parser/Services/ItemFilterBlockTranslator.cs
Normal file
@@ -0,0 +1,446 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
using Filtration.Common.Utilities;
|
||||
using Filtration.ObjectModel;
|
||||
using Filtration.ObjectModel.BlockItemBaseTypes;
|
||||
using Filtration.ObjectModel.BlockItemTypes;
|
||||
using Filtration.ObjectModel.Enums;
|
||||
using Filtration.ObjectModel.Extensions;
|
||||
using Filtration.ObjectModel.ThemeEditor;
|
||||
using Filtration.Parser.Interface.Services;
|
||||
|
||||
namespace Filtration.Parser.Services
|
||||
{
|
||||
internal class ItemFilterBlockTranslator : IItemFilterBlockTranslator
|
||||
{
|
||||
private readonly IBlockGroupHierarchyBuilder _blockGroupHierarchyBuilder;
|
||||
private const string _indent = " ";
|
||||
private readonly string _newLine = Environment.NewLine + _indent;
|
||||
private readonly string _disabledNewLine = Environment.NewLine + "#" + _indent;
|
||||
private ThemeComponentCollection _masterComponentCollection;
|
||||
|
||||
public ItemFilterBlockTranslator(IBlockGroupHierarchyBuilder blockGroupHierarchyBuilder)
|
||||
{
|
||||
_blockGroupHierarchyBuilder = blockGroupHierarchyBuilder;
|
||||
}
|
||||
|
||||
// This method converts a string into a ItemFilterBlock. This is used for pasting ItemFilterBlocks
|
||||
// and reading ItemFilterScripts from a file.
|
||||
public IItemFilterBlock TranslateStringToItemFilterBlock(string inputString, ThemeComponentCollection masterComponentCollection)
|
||||
{
|
||||
_masterComponentCollection = masterComponentCollection;
|
||||
var block = new ItemFilterBlock();
|
||||
var showHideFound = false;
|
||||
|
||||
foreach (var line in new LineReader(() => new StringReader(inputString)))
|
||||
{
|
||||
|
||||
if (line.StartsWith(@"# Section:"))
|
||||
{
|
||||
var section = new ItemFilterSection
|
||||
{
|
||||
Description = line.Substring(line.IndexOf(":", StringComparison.Ordinal) + 1).Trim()
|
||||
};
|
||||
return section;
|
||||
}
|
||||
|
||||
if (line.StartsWith(@"#") && !showHideFound)
|
||||
{
|
||||
block.Description = line.TrimStart('#').TrimStart(' ');
|
||||
continue;
|
||||
}
|
||||
|
||||
var adjustedLine = line.Replace("#", " # ");
|
||||
var trimmedLine = adjustedLine.TrimStart(' ');
|
||||
|
||||
var spaceOrEndOfLinePos = trimmedLine.IndexOf(" ", StringComparison.Ordinal) > 0 ? trimmedLine.IndexOf(" ", StringComparison.Ordinal) : trimmedLine.Length;
|
||||
|
||||
var lineOption = trimmedLine.Substring(0, spaceOrEndOfLinePos);
|
||||
switch (lineOption)
|
||||
{
|
||||
case "Show":
|
||||
showHideFound = true;
|
||||
block.Action = BlockAction.Show;
|
||||
block.Enabled = true;
|
||||
AddBlockGroupToBlock(block, trimmedLine);
|
||||
break;
|
||||
case "Hide":
|
||||
showHideFound = true;
|
||||
block.Action = BlockAction.Hide;
|
||||
block.Enabled = true;
|
||||
AddBlockGroupToBlock(block, trimmedLine);
|
||||
break;
|
||||
case "ShowDisabled":
|
||||
showHideFound = true;
|
||||
block.Action = BlockAction.Show;
|
||||
block.Enabled = false;
|
||||
AddBlockGroupToBlock(block, trimmedLine);
|
||||
break;
|
||||
case "HideDisabled":
|
||||
showHideFound = true;
|
||||
block.Action = BlockAction.Hide;
|
||||
block.Enabled = false;
|
||||
AddBlockGroupToBlock(block, trimmedLine);
|
||||
break;
|
||||
case "ItemLevel":
|
||||
{
|
||||
AddNumericFilterPredicateItemToBlockItems<ItemLevelBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "DropLevel":
|
||||
{
|
||||
AddNumericFilterPredicateItemToBlockItems<DropLevelBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "Quality":
|
||||
{
|
||||
AddNumericFilterPredicateItemToBlockItems<QualityBlockItem>(block,trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "Rarity":
|
||||
{
|
||||
var blockItemValue = new RarityBlockItem();
|
||||
var result = Regex.Match(trimmedLine, @"^\w+\s+([><!=]{0,2})\s*(\w+)$");
|
||||
if (result.Groups.Count == 3)
|
||||
{
|
||||
blockItemValue.FilterPredicate.PredicateOperator =
|
||||
EnumHelper.GetEnumValueFromDescription<FilterPredicateOperator>(string.IsNullOrEmpty(result.Groups[1].Value) ? "=" : result.Groups[1].Value);
|
||||
blockItemValue.FilterPredicate.PredicateOperand =
|
||||
(int)EnumHelper.GetEnumValueFromDescription<ItemRarity>(result.Groups[2].Value);
|
||||
}
|
||||
block.BlockItems.Add(blockItemValue);
|
||||
break;
|
||||
}
|
||||
case "Class":
|
||||
{
|
||||
AddStringListItemToBlockItems<ClassBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "BaseType":
|
||||
{
|
||||
AddStringListItemToBlockItems<BaseTypeBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "Sockets":
|
||||
{
|
||||
AddNumericFilterPredicateItemToBlockItems<SocketsBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "LinkedSockets":
|
||||
{
|
||||
AddNumericFilterPredicateItemToBlockItems<LinkedSocketsBlockItem>(block,trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "Width":
|
||||
{
|
||||
AddNumericFilterPredicateItemToBlockItems<WidthBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "Height":
|
||||
{
|
||||
AddNumericFilterPredicateItemToBlockItems<HeightBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "SocketGroup":
|
||||
{
|
||||
AddStringListItemToBlockItems<SocketGroupBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "SetTextColor":
|
||||
{
|
||||
// Only ever use the last SetTextColor item encountered as multiples aren't valid.
|
||||
RemoveExistingBlockItemsOfType<TextColorBlockItem>(block);
|
||||
|
||||
AddColorItemToBlockItems<TextColorBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "SetBackgroundColor":
|
||||
{
|
||||
// Only ever use the last SetBackgroundColor item encountered as multiples aren't valid.
|
||||
RemoveExistingBlockItemsOfType<BackgroundColorBlockItem>(block);
|
||||
|
||||
AddColorItemToBlockItems<BackgroundColorBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "SetBorderColor":
|
||||
{
|
||||
// Only ever use the last SetBorderColor item encountered as multiples aren't valid.
|
||||
RemoveExistingBlockItemsOfType<BorderColorBlockItem>(block);
|
||||
|
||||
AddColorItemToBlockItems<BorderColorBlockItem>(block, trimmedLine);
|
||||
break;
|
||||
}
|
||||
case "SetFontSize":
|
||||
{
|
||||
// Only ever use the last SetFontSize item encountered as multiples aren't valid.
|
||||
RemoveExistingBlockItemsOfType<FontSizeBlockItem>(block);
|
||||
|
||||
var match = Regex.Match(trimmedLine, @"\s+(\d+)");
|
||||
if (match.Success)
|
||||
{
|
||||
var blockItemValue = new FontSizeBlockItem(Convert.ToInt16(match.Value));
|
||||
block.BlockItems.Add(blockItemValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "PlayAlertSound":
|
||||
{
|
||||
// Only ever use the last PlayAlertSound item encountered as multiples aren't valid.
|
||||
RemoveExistingBlockItemsOfType<SoundBlockItem>(block);
|
||||
|
||||
var matches = Regex.Matches(trimmedLine, @"\s+(\d+)");
|
||||
switch (matches.Count)
|
||||
{
|
||||
case 1:
|
||||
if (matches[0].Success)
|
||||
{
|
||||
var blockItemValue = new SoundBlockItem
|
||||
{
|
||||
Value = Convert.ToInt16(matches[0].Value),
|
||||
SecondValue = 79
|
||||
};
|
||||
block.BlockItems.Add(blockItemValue);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (matches[0].Success && matches[1].Success)
|
||||
{
|
||||
var blockItemValue = new SoundBlockItem
|
||||
{
|
||||
Value = Convert.ToInt16(matches[0].Value),
|
||||
SecondValue = Convert.ToInt16(matches[1].Value)
|
||||
};
|
||||
block.BlockItems.Add(blockItemValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
private static void RemoveExistingBlockItemsOfType<T>(IItemFilterBlock block)
|
||||
{
|
||||
var existingBlockItemCount = block.BlockItems.Count(b => b.GetType() == typeof(T));
|
||||
if (existingBlockItemCount > 0)
|
||||
{
|
||||
var existingBlockItem = block.BlockItems.First(b => b.GetType() == typeof(T));
|
||||
block.BlockItems.Remove(existingBlockItem);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddNumericFilterPredicateItemToBlockItems<T>(IItemFilterBlock block, string inputString) where T : NumericFilterPredicateBlockItem
|
||||
{
|
||||
var blockItem = Activator.CreateInstance<T>();
|
||||
|
||||
SetNumericFilterPredicateFromString(blockItem.FilterPredicate, inputString);
|
||||
block.BlockItems.Add(blockItem);
|
||||
}
|
||||
|
||||
private static void SetNumericFilterPredicateFromString(NumericFilterPredicate predicate, string inputString)
|
||||
{
|
||||
var result = Regex.Match(inputString, @"^\w+\s+([><=]{0,2})\s*(\d{0,3})$");
|
||||
if (result.Groups.Count != 3) return;
|
||||
|
||||
predicate.PredicateOperator =
|
||||
EnumHelper.GetEnumValueFromDescription<FilterPredicateOperator>(string.IsNullOrEmpty(result.Groups[1].Value) ? "=" : result.Groups[1].Value);
|
||||
predicate.PredicateOperand = Convert.ToInt16(result.Groups[2].Value);
|
||||
}
|
||||
|
||||
private static void AddStringListItemToBlockItems<T>(IItemFilterBlock block, string inputString) where T : StringListBlockItem
|
||||
{
|
||||
var blockItem = Activator.CreateInstance<T>();
|
||||
PopulateListFromString(blockItem.Items, inputString.Substring(inputString.IndexOf(" ", StringComparison.Ordinal) + 1).Trim());
|
||||
block.BlockItems.Add(blockItem);
|
||||
}
|
||||
|
||||
private static void PopulateListFromString(ICollection<string> list, string inputString)
|
||||
{
|
||||
var result = Regex.Matches(inputString, @"[^\s""]+|""([^""]*)""");
|
||||
foreach (Match match in result)
|
||||
{
|
||||
list.Add(match.Groups[1].Success
|
||||
? match.Groups[1].Value
|
||||
: match.Groups[0].Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddColorItemToBlockItems<T>(IItemFilterBlock block, string inputString) where T : ColorBlockItem
|
||||
{
|
||||
block.BlockItems.Add(GetColorBlockItemFromString<T>(inputString));
|
||||
}
|
||||
|
||||
private T GetColorBlockItemFromString<T>(string inputString) where T: ColorBlockItem
|
||||
{
|
||||
var blockItem = Activator.CreateInstance<T>();
|
||||
var result = Regex.Matches(inputString, @"([\w\s]*)[#]?(.*)");
|
||||
|
||||
blockItem.Color = GetColorFromString(result[0].Groups[1].Value);
|
||||
|
||||
var componentName = result[0].Groups[2].Value.Trim();
|
||||
if (!string.IsNullOrEmpty(componentName))
|
||||
{
|
||||
ThemeComponentType componentType;
|
||||
if (typeof(T) == typeof(TextColorBlockItem))
|
||||
{
|
||||
componentType = ThemeComponentType.TextColor;
|
||||
}
|
||||
else if (typeof(T) == typeof(BackgroundColorBlockItem))
|
||||
{
|
||||
componentType = ThemeComponentType.BackgroundColor;
|
||||
}
|
||||
else if (typeof(T) == typeof(BorderColorBlockItem))
|
||||
{
|
||||
componentType = ThemeComponentType.BorderColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Parsing error - unknown theme component type");
|
||||
}
|
||||
if (_masterComponentCollection != null)
|
||||
{
|
||||
blockItem.ThemeComponent = _masterComponentCollection.AddComponent(componentType, componentName,
|
||||
blockItem.Color);
|
||||
}
|
||||
}
|
||||
|
||||
return blockItem;
|
||||
}
|
||||
|
||||
public void ReplaceColorBlockItemsFromString(ObservableCollection<IItemFilterBlockItem> blockItems, string inputString)
|
||||
{
|
||||
// Reverse iterate to remove existing IAudioVisualBlockItems
|
||||
for (var idx = blockItems.Count - 1; idx >= 0; idx--)
|
||||
{
|
||||
if (blockItems[idx] is IAudioVisualBlockItem)
|
||||
{
|
||||
blockItems.RemoveAt(idx);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var line in new LineReader(() => new StringReader(inputString)))
|
||||
{
|
||||
var matches = Regex.Match(line, @"(\w+)");
|
||||
|
||||
switch (matches.Value)
|
||||
{
|
||||
case "SetTextColor":
|
||||
{
|
||||
blockItems.Add(GetColorBlockItemFromString<TextColorBlockItem>(line));
|
||||
break;
|
||||
}
|
||||
case "SetBackgroundColor":
|
||||
{
|
||||
blockItems.Add(GetColorBlockItemFromString<BackgroundColorBlockItem>(line));
|
||||
break;
|
||||
}
|
||||
case "SetBorderColor":
|
||||
{
|
||||
blockItems.Add(GetColorBlockItemFromString<BorderColorBlockItem>(line));
|
||||
break;
|
||||
}
|
||||
case "SetFontSize":
|
||||
{
|
||||
var match = Regex.Match(line, @"\s+(\d+)");
|
||||
if (!match.Success) break;
|
||||
blockItems.Add(new FontSizeBlockItem(Convert.ToInt16(match.Value)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddBlockGroupToBlock(IItemFilterBlock block, string inputString)
|
||||
{
|
||||
var blockGroupStart = inputString.IndexOf("#", StringComparison.Ordinal);
|
||||
if (blockGroupStart <= 0) return;
|
||||
|
||||
var blockGroupText = inputString.Substring(blockGroupStart + 1);
|
||||
var blockGroups = blockGroupText.Split('-').ToList();
|
||||
if (blockGroups.Count(b => !string.IsNullOrEmpty(b.Trim())) > 0)
|
||||
{
|
||||
block.BlockGroup = _blockGroupHierarchyBuilder.IntegrateStringListIntoBlockGroupHierarchy(blockGroups);
|
||||
block.BlockGroup.IsChecked = block.Action == BlockAction.Show;
|
||||
}
|
||||
}
|
||||
|
||||
private static Color GetColorFromString(string inputString)
|
||||
{
|
||||
var argbValues = Regex.Matches(inputString, @"\s+(\d+)");
|
||||
|
||||
switch (argbValues.Count)
|
||||
{
|
||||
case 3:
|
||||
return new Color
|
||||
{
|
||||
A = byte.MaxValue,
|
||||
R = Convert.ToByte(argbValues[0].Value),
|
||||
G = Convert.ToByte(argbValues[1].Value),
|
||||
B = Convert.ToByte(argbValues[2].Value)
|
||||
};
|
||||
case 4:
|
||||
return new Color
|
||||
{
|
||||
R = Convert.ToByte(argbValues[0].Value),
|
||||
G = Convert.ToByte(argbValues[1].Value),
|
||||
B = Convert.ToByte(argbValues[2].Value),
|
||||
A = Convert.ToByte(argbValues[3].Value)
|
||||
};
|
||||
}
|
||||
return new Color();
|
||||
}
|
||||
|
||||
// This method converts an ItemFilterBlock object into a string. This is used for copying a ItemFilterBlock
|
||||
// to the clipboard, and when saving a ItemFilterScript.
|
||||
public string TranslateItemFilterBlockToString(IItemFilterBlock block)
|
||||
{
|
||||
if (block.GetType() == typeof (ItemFilterSection))
|
||||
{
|
||||
return "# Section: " + block.Description;
|
||||
}
|
||||
|
||||
var outputString = string.Empty;
|
||||
|
||||
if (!block.Enabled)
|
||||
{
|
||||
outputString += "#Disabled Block Start" + Environment.NewLine;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(block.Description))
|
||||
{
|
||||
outputString += "# " + block.Description + Environment.NewLine;
|
||||
}
|
||||
|
||||
outputString += (!block.Enabled ? "#" : string.Empty) + block.Action.GetAttributeDescription();
|
||||
|
||||
if (block.BlockGroup != null)
|
||||
{
|
||||
outputString += " # " + block.BlockGroup;
|
||||
}
|
||||
|
||||
// ReSharper disable once LoopCanBeConvertedToQuery
|
||||
foreach (var blockItem in block.BlockItems.Where(b => b.GetType() != typeof(ActionBlockItem)).OrderBy(b => b.SortOrder))
|
||||
{
|
||||
if (blockItem.OutputText != string.Empty)
|
||||
{
|
||||
outputString += (!block.Enabled ? _disabledNewLine : _newLine) + blockItem.OutputText;
|
||||
}
|
||||
}
|
||||
|
||||
if (!block.Enabled)
|
||||
{
|
||||
outputString += Environment.NewLine + "#Disabled Block End";
|
||||
}
|
||||
|
||||
return outputString;
|
||||
}
|
||||
}
|
||||
}
|
||||
192
Filtration.Parser/Services/ItemFilterScriptTranslator.cs
Normal file
192
Filtration.Parser/Services/ItemFilterScriptTranslator.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Filtration.Common.Utilities;
|
||||
using Filtration.ObjectModel;
|
||||
using Filtration.Parser.Interface.Services;
|
||||
using Filtration.Properties;
|
||||
|
||||
namespace Filtration.Parser.Services
|
||||
{
|
||||
internal class ItemFilterScriptTranslator : IItemFilterScriptTranslator
|
||||
{
|
||||
private readonly IItemFilterBlockTranslator _blockTranslator;
|
||||
private readonly IBlockGroupHierarchyBuilder _blockGroupHierarchyBuilder;
|
||||
|
||||
public ItemFilterScriptTranslator(IItemFilterBlockTranslator blockTranslator,
|
||||
IBlockGroupHierarchyBuilder blockGroupHierarchyBuilder)
|
||||
{
|
||||
_blockTranslator = blockTranslator;
|
||||
_blockGroupHierarchyBuilder = blockGroupHierarchyBuilder;
|
||||
}
|
||||
|
||||
public string PreprocessDisabledBlocks(string inputString)
|
||||
{
|
||||
bool inDisabledBlock = false;
|
||||
var showHideFound = false;
|
||||
|
||||
var lines = Regex.Split(inputString, "\r\n|\r|\n").ToList();
|
||||
var linesToRemove = new List<int>();
|
||||
|
||||
for (var i = 0; i < lines.Count; i++)
|
||||
{
|
||||
if (lines[i].StartsWith("#Disabled Block Start"))
|
||||
{
|
||||
inDisabledBlock = true;
|
||||
linesToRemove.Add(i);
|
||||
continue;
|
||||
}
|
||||
if (inDisabledBlock)
|
||||
{
|
||||
if (lines[i].StartsWith("#Disabled Block End"))
|
||||
{
|
||||
inDisabledBlock = false;
|
||||
showHideFound = false;
|
||||
linesToRemove.Add(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
lines[i] = lines[i].TrimStart('#');
|
||||
lines[i] = lines[i].Replace("#", " # ");
|
||||
var spaceOrEndOfLinePos = lines[i].IndexOf(" ", StringComparison.Ordinal) > 0 ? lines[i].IndexOf(" ", StringComparison.Ordinal) : lines[i].Length;
|
||||
var lineOption = lines[i].Substring(0, spaceOrEndOfLinePos);
|
||||
|
||||
// If we haven't found a Show or Hide line yet, then this is probably the block comment.
|
||||
// Put its # back on and skip to the next line.
|
||||
if (lineOption != "Show" && lineOption != "Hide" && showHideFound == false)
|
||||
{
|
||||
lines[i] = "#" + lines[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lineOption == "Show")
|
||||
{
|
||||
lines[i] = lines[i].Replace("Show", "ShowDisabled");
|
||||
showHideFound = true;
|
||||
}
|
||||
else if (lineOption == "Hide")
|
||||
{
|
||||
lines[i] = lines[i].Replace("Hide", "HideDisabled");
|
||||
showHideFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = linesToRemove.Count - 1; i >= 0; i--)
|
||||
{
|
||||
lines.RemoveAt(linesToRemove[i]);
|
||||
}
|
||||
|
||||
return lines.Aggregate((c, n) => c + Environment.NewLine + n);
|
||||
}
|
||||
|
||||
public ItemFilterScript TranslateStringToItemFilterScript(string inputString)
|
||||
{
|
||||
var script = new ItemFilterScript();
|
||||
_blockGroupHierarchyBuilder.Initialise(script.ItemFilterBlockGroups.First());
|
||||
|
||||
inputString = inputString.Replace("\t", "");
|
||||
if (inputString.Contains("#Disabled Block Start"))
|
||||
{
|
||||
inputString = PreprocessDisabledBlocks(inputString);
|
||||
}
|
||||
|
||||
var conditionBoundaries = IdentifyBlockBoundaries(inputString);
|
||||
|
||||
var lines = Regex.Split(inputString, "\r\n|\r|\n");
|
||||
|
||||
// Process the script header
|
||||
for (var i = 0; i < conditionBoundaries.First.Value; i++)
|
||||
{
|
||||
if (lines[i].StartsWith("#"))
|
||||
{
|
||||
script.Description += lines[i].Substring(1).Trim(' ') + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(script.Description))
|
||||
{
|
||||
script.Description = script.Description.TrimEnd('\n').TrimEnd('\r');
|
||||
}
|
||||
|
||||
// Extract each block from between boundaries and translate it into a ItemFilterBlock object
|
||||
// and add that object to the ItemFilterBlocks list
|
||||
for (var boundary = conditionBoundaries.First; boundary != null; boundary = boundary.Next)
|
||||
{
|
||||
var begin = boundary.Value;
|
||||
var end = boundary.Next?.Value ?? lines.Length;
|
||||
var block = new string[end - begin];
|
||||
Array.Copy(lines, begin, block, 0, end - begin);
|
||||
var blockString = string.Join("\r\n", block);
|
||||
script.ItemFilterBlocks.Add(_blockTranslator.TranslateStringToItemFilterBlock(blockString, script.ThemeComponents));
|
||||
}
|
||||
|
||||
_blockGroupHierarchyBuilder.Cleanup();
|
||||
return script;
|
||||
}
|
||||
|
||||
private static LinkedList<int> IdentifyBlockBoundaries(string inputString)
|
||||
{
|
||||
var blockBoundaries = new LinkedList<int>();
|
||||
var previousLine = string.Empty;
|
||||
var currentLine = 0;
|
||||
|
||||
foreach (var line in new LineReader(() => new StringReader(inputString)))
|
||||
{
|
||||
currentLine++;
|
||||
var trimmedLine = line.TrimStart(' ').TrimEnd(' ');
|
||||
if (trimmedLine.StartsWith("Show") || trimmedLine.StartsWith("Hide") ||
|
||||
trimmedLine.StartsWith("# Section:"))
|
||||
{
|
||||
// If the line previous to the Show or Hide line is a comment then we should include that in the block
|
||||
// as it represents the block description.
|
||||
// currentLine > 2 caters for an edge case where the script description is a single line and the first
|
||||
// block has no description. This prevents the script description from being assigned to the first block's description.
|
||||
blockBoundaries.AddLast(previousLine.StartsWith("#") && !previousLine.StartsWith("# Section:") &&
|
||||
currentLine > 2
|
||||
? currentLine - 2
|
||||
: currentLine - 1);
|
||||
}
|
||||
previousLine = line;
|
||||
}
|
||||
|
||||
return blockBoundaries;
|
||||
}
|
||||
|
||||
public string TranslateItemFilterScriptToString(ItemFilterScript script)
|
||||
{
|
||||
var outputString = string.Empty;
|
||||
|
||||
outputString += "# Script edited with Filtration - https://github.com/ben-wallis/Filtration" +
|
||||
Environment.NewLine;
|
||||
|
||||
if (!string.IsNullOrEmpty(script.Description))
|
||||
{
|
||||
// ReSharper disable once LoopCanBeConvertedToQuery
|
||||
foreach (var line in new LineReader(() => new StringReader(script.Description)))
|
||||
{
|
||||
if (!line.Contains("Script edited with Filtration"))
|
||||
{
|
||||
outputString += "# " + line + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
outputString += Environment.NewLine;
|
||||
}
|
||||
|
||||
// ReSharper disable once LoopCanBeConvertedToQuery
|
||||
foreach (var block in script.ItemFilterBlocks)
|
||||
{
|
||||
outputString += _blockTranslator.TranslateItemFilterBlockToString(block) + Environment.NewLine;
|
||||
|
||||
if (Settings.Default.ExtraLineBetweenBlocks)
|
||||
{
|
||||
outputString += Environment.NewLine;
|
||||
}
|
||||
}
|
||||
|
||||
return outputString;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Filtration.Parser/WindsorInstaller.cs
Normal file
29
Filtration.Parser/WindsorInstaller.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Castle.MicroKernel.Registration;
|
||||
using Castle.MicroKernel.SubSystems.Configuration;
|
||||
using Castle.Windsor;
|
||||
using Filtration.Parser.Interface.Services;
|
||||
using Filtration.Parser.Services;
|
||||
|
||||
namespace Filtration.Parser
|
||||
{
|
||||
public class WindsorInstaller : IWindsorInstaller
|
||||
{
|
||||
public void Install(IWindsorContainer container, IConfigurationStore store)
|
||||
{
|
||||
container.Register(Component
|
||||
.For<IBlockGroupHierarchyBuilder>()
|
||||
.ImplementedBy<BlockGroupHierarchyBuilder>()
|
||||
.LifestyleSingleton());
|
||||
|
||||
container.Register(Component
|
||||
.For<IItemFilterBlockTranslator>()
|
||||
.ImplementedBy<ItemFilterBlockTranslator>()
|
||||
.LifestyleSingleton());
|
||||
|
||||
container.Register(Component
|
||||
.For<IItemFilterScriptTranslator>()
|
||||
.ImplementedBy<ItemFilterScriptTranslator>()
|
||||
.LifestyleSingleton());
|
||||
}
|
||||
}
|
||||
}
|
||||
5
Filtration.Parser/packages.config
Normal file
5
Filtration.Parser/packages.config
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="3.3.0" targetFramework="net461" />
|
||||
<package id="Castle.Windsor" version="3.3.0" targetFramework="net461" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user