Browse Source

Add ResourceToolkit AddIn (from hornung.dynalias.com/svn/resourcetoolkit/trunk, rev 70).

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@1866 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 20 years ago
parent
commit
a1b74dfb38
  1. BIN
      data/resources/StringResources.de.resources
  2. BIN
      data/resources/StringResources.es-mx.resources
  3. BIN
      data/resources/StringResources.es.resources
  4. BIN
      data/resources/StringResources.nl.resources
  5. BIN
      data/resources/StringResources.pl.resources
  6. BIN
      data/resources/StringResources.tr.resources
  7. 33
      src/AddIns/Misc/ResourceToolkit/Project/Configuration/AssemblyInfo.cs
  8. 135
      src/AddIns/Misc/ResourceToolkit/Project/Hornung.ResourceToolkit.addin
  9. 132
      src/AddIns/Misc/ResourceToolkit/Project/ResourceToolkit.csproj
  10. 73
      src/AddIns/Misc/ResourceToolkit/Project/Resources/EditStringResourceDialog.xfrm
  11. 59
      src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs
  12. 40
      src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/CSharpResourceCodeCompletionBinding.cs
  13. 69
      src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs
  14. 63
      src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/NewResourceCodeCompletionData.cs
  15. 70
      src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ResourceCodeCompletionData.cs
  16. 75
      src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ResourceCodeCompletionDataProvider.cs
  17. 40
      src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/VBNetResourceCodeCompletionBinding.cs
  18. 57
      src/AddIns/Misc/ResourceToolkit/Project/Src/Commands/RefactoringCommands.cs
  19. 150
      src/AddIns/Misc/ResourceToolkit/Project/Src/Commands/TextEditorContextMenuBuilder.cs
  20. 83
      src/AddIns/Misc/ResourceToolkit/Project/Src/Gui/EditStringResourceDialog.cs
  21. 316
      src/AddIns/Misc/ResourceToolkit/Project/Src/Gui/UnusedResourceKeysViewContent.cs
  22. 90
      src/AddIns/Misc/ResourceToolkit/Project/Src/ProjectFileDictionaryService.cs
  23. 83
      src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/AnyResourceReferenceFinder.cs
  24. 35
      src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/IResourceReferenceFinder.cs
  25. 364
      src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/ResourceRefactoringService.cs
  26. 93
      src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/SpecificResourceReferenceFinder.cs
  27. 112
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/AbstractResourceResolver.cs
  28. 458
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/BclNRefactoryResourceResolver.cs
  29. 154
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreNRefactoryResourceResolver.cs
  30. 299
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreResourceResolver.cs
  31. 44
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/INRefactoryResourceResolver.cs
  32. 56
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/IResourceResolver.cs
  33. 94
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryAstCacheService.cs
  34. 390
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryResourceResolver.cs
  35. 1144
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NodeTrackingAstVisitor.cs
  36. 142
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/PositionTrackingAstVisitor.cs
  37. 242
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/PropertyFieldAssociationVisitor.cs
  38. 80
      src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ResourceResolveResult.cs
  39. 63
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/DefaultBclResourceFileContentFactory.cs
  40. 109
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/DefaultFileLocalizedResourcesFinder.cs
  41. 26
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ILocalizedResourcesFinder.cs
  42. 26
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IMultiResourceFileContent.cs
  43. 82
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IResourceFileContent.cs
  44. 33
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IResourceFileContentFactory.cs
  45. 213
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/MergedResourceFileContent.cs
  46. 89
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResXResourceFileContent.cs
  47. 120
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResourceFileContentRegistry.cs
  48. 270
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResourcesResourceFileContent.cs
  49. 136
      src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceResolverService.cs
  50. 48
      src/AddIns/Misc/ResourceToolkit/Project/Src/ToolTips/ResourceToolTipProvider.cs
  51. 2
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  52. BIN
      src/Main/StartUp/Project/Resources/StringResources.resources
  53. 9
      src/SharpDevelop.sln

BIN
data/resources/StringResources.de.resources

Binary file not shown.

BIN
data/resources/StringResources.es-mx.resources

Binary file not shown.

BIN
data/resources/StringResources.es.resources

Binary file not shown.

BIN
data/resources/StringResources.nl.resources

Binary file not shown.

BIN
data/resources/StringResources.pl.resources

Binary file not shown.

BIN
data/resources/StringResources.tr.resources

Binary file not shown.

33
src/AddIns/Misc/ResourceToolkit/Project/Configuration/AssemblyInfo.cs

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Resources;
using System.Security.Permissions;
// Information about this assembly is defined by the following
// attributes.
//
// change them to the information which is associated with the assembly
// you compile.
[assembly: AssemblyTitle("Hornung.ResourceToolkit")]
[assembly: AssemblyDescription("Assists in working with resources")]
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
#endif
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution=true)]
[assembly: FileIOPermission(SecurityAction.RequestMinimum, AllLocalFiles=FileIOPermissionAccess.AllAccess)]
[assembly: UIPermission(SecurityAction.RequestMinimum, Clipboard=UIPermissionClipboard.OwnClipboard, Window=UIPermissionWindow.AllWindows)]

135
src/AddIns/Misc/ResourceToolkit/Project/Hornung.ResourceToolkit.addin

@ -0,0 +1,135 @@ @@ -0,0 +1,135 @@
<AddIn name = "Hornung.ResourceToolkit"
author = "Christian Hornung"
copyright = "2006 Christian Hornung"
url = "http://dev.hornung.dynalias.com/index.php?p=restk_home"
description = "Provides tooltips and code completion for resource entries to simplify working with localizable resources using the standard .NET framework classes or ICSharpCode.Core.">
<Manifest>
<Identity name="Hornung.ResourceToolkit" version="@Hornung.ResourceToolkit.dll" />
<Dependency addin="SharpDevelop" version="2.1.0.1751-2.1"/>
</Manifest>
<Runtime>
<Import assembly = "Hornung.ResourceToolkit.dll"/>
<Import assembly = ":ICSharpCode.SharpDevelop"/>
</Runtime>
<StringResources file = "Resources\StringResources.resources" />
<!-- Code completion -->
<Path name = "/AddIns/DefaultTextEditor/CodeCompletion">
<CodeCompletionBinding
id = "CSharpResources"
extensions = ".cs"
class = "Hornung.ResourceToolkit.CodeCompletion.CSharpResourceCodeCompletionBinding"
insertbefore = "CSharp"
/>
<CodeCompletionBinding
id = "VBNetResources"
extensions = ".vb"
class = "Hornung.ResourceToolkit.CodeCompletion.VBNetResourceCodeCompletionBinding"
insertbefore = "VBNet"
/>
<CodeCompletionBinding
id = "ICSharpCodeCoreResources"
class = "Hornung.ResourceToolkit.CodeCompletion.ICSharpCodeCoreResourceCodeCompletionBinding"
/>
</Path>
<!-- Context menus -->
<Path name = "/SharpDevelop/ViewContent/DefaultTextEditor/ContextMenu">
<MenuItem id = "StringResources" insertbefore = "Refactoring" type = "Builder" class = "Hornung.ResourceToolkit.Commands.TextEditorContextMenuBuilder" />
</Path>
<Path name = "/SharpDevelop/ViewContent/XmlEditor/ContextMenu">
<Include id = "StringResources" item = "/SharpDevelop/ViewContent/DefaultTextEditor/ContextMenu/StringResources" insertbefore = "Cut" />
</Path>
<!-- Tools menu -->
<Path name="/Workspace/Tools">
<MenuItem id="ResourceToolkit" label="${res:Hornung.ResourceToolkit.ToolsMenuLabel}" type="Menu">
<ComplexCondition action="Disable">
<Or>
<Condition name="SolutionOpen"/>
<Condition name="WindowOpen" openwindow="ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor.ITextEditorControlProvider"/>
</Or>
<MenuItem id = "FindMissingResourceKeys"
label = "${res:Hornung.ResourceToolkit.FindMissingResourceKeys}"
class = "Hornung.ResourceToolkit.Commands.FindMissingResourceKeysCommand" />
<MenuItem id = "FindUnusedResourceKeys"
label = "${res:Hornung.ResourceToolkit.FindUnusedResourceKeys}"
class = "Hornung.ResourceToolkit.Commands.FindUnusedResourceKeysCommand" />
</ComplexCondition>
</MenuItem>
</Path>
<!-- Unused resource keys toolbar -->
<Path name="/AddIns/ResourceToolkit/ViewContent/UnusedResourceKeys/Toolbar">
</Path>
<!-- Unused resource keys context menu -->
<Path name="/AddIns/ResourceToolkit/ViewContent/UnusedResourceKeys/ListViewContextMenu">
<Include id="Delete" item="/SharpDevelop/Workbench/MainMenu/Edit/Delete"/>
<MenuItem id="Separator1" type="Separator"/>
<Include id="SelectAll" item="/SharpDevelop/Workbench/MainMenu/Edit/SelectAll"/>
</Path>
<!-- Tooltips -->
<Path name="/SharpDevelop/ViewContent/DefaultTextEditor/ToolTips">
<Class id="ResourceToolTips" class="Hornung.ResourceToolkit.ToolTips.ResourceToolTipProvider" insertafter="DefaultDebuggerToolTips"/>
</Path>
<!-- Resource file content factories -->
<Path name="/AddIns/ResourceToolkit/ResourceFileContentFactories">
<Class id="DefaultBclResourceFileContentFactory" class="Hornung.ResourceToolkit.ResourceFileContent.DefaultBclResourceFileContentFactory"/>
</Path>
<!-- Resource resolvers -->
<Path name="/AddIns/ResourceToolkit/Resolvers">
<Class id="DefaultICSharpCodeCoreResourceResolver" class="Hornung.ResourceToolkit.Resolver.ICSharpCodeCoreResourceResolver"/>
<Class id="DefaultNRefactoryResourceResolver" class="Hornung.ResourceToolkit.Resolver.NRefactoryResourceResolver"/>
</Path>
<Path name="/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/HostResourcesLocations">
<String id="SharpDevelop" text="..\src\Main\StartUp\Project\Resources"/>
<String id="Alternative1" text="..\Startup\Project\Resources"/>
<String id="Alternative2" text="..\Startup\Resources"/>
<String id="ICSharpCodeCoreDemo" text="..\Startup"/>
</Path>
<Path name="/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/LocalResourcesLocations">
<String id="ProjectDir" text="."/>
<String id="ProjectResourcesDir" text="Resources"/>
</Path>
<Path name="/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/ResourceFileNames">
<String id="SharpDevelop" text="StringResources"/>
</Path>
<Path name="/AddIns/ResourceToolkit/NRefactoryResourceResolver/Resolvers">
<Class id="BclNRefactoryResourceResolver" class="Hornung.ResourceToolkit.Resolver.BclNRefactoryResourceResolver"/>
<Class id="ICSharpCodeCoreNRefactoryResourceResolver" class="Hornung.ResourceToolkit.Resolver.ICSharpCodeCoreNRefactoryResourceResolver"/>
</Path>
<!-- Localized resources finders -->
<Path name="/AddIns/ResourceToolkit/LocalizedResourcesFinders">
<Class id="DefaultFileLocalizedResourcesFinder" class="Hornung.ResourceToolkit.ResourceFileContent.DefaultFileLocalizedResourcesFinder"/>
</Path>
</AddIn>

132
src/AddIns/Misc/ResourceToolkit/Project/ResourceToolkit.csproj

@ -0,0 +1,132 @@ @@ -0,0 +1,132 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputType>Library</OutputType>
<RootNamespace>Hornung.ResourceToolkit</RootNamespace>
<AssemblyName>Hornung.ResourceToolkit</AssemblyName>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{461606BD-E824-4D0A-8CBA-01810B1F5E02}</ProjectGuid>
<OutputPath>..\..\..\..\..\AddIns\AddIns\Misc\ResourceToolkit\</OutputPath>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib>
<RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<BaseAddress>4194304</BaseAddress>
<PlatformTarget>AnyCPU</PlatformTarget>
<FileAlignment>4096</FileAlignment>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<IntermediateOutputPath>obj\Debug\</IntermediateOutputPath>
<Optimize>False</Optimize>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugSymbols>true</DebugSymbols>
<DebugType>Full</DebugType>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<IntermediateOutputPath>obj\Release\</IntermediateOutputPath>
<Optimize>True</Optimize>
<DefineConstants>TRACE</DefineConstants>
<DebugSymbols>False</DebugSymbols>
<DebugType>None</DebugType>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Drawing" />
</ItemGroup>
<ItemGroup>
<Compile Include="Src\CodeCompletion\AbstractNRefactoryResourceCodeCompletionBinding.cs" />
<Compile Include="Src\CodeCompletion\CSharpResourceCodeCompletionBinding.cs" />
<Compile Include="Src\CodeCompletion\ICSharpCodeCoreResourceCodeCompletionBinding.cs" />
<Compile Include="Src\CodeCompletion\NewResourceCodeCompletionData.cs" />
<Compile Include="Src\CodeCompletion\ResourceCodeCompletionData.cs" />
<Compile Include="Src\CodeCompletion\ResourceCodeCompletionDataProvider.cs" />
<Compile Include="Src\CodeCompletion\VBNetResourceCodeCompletionBinding.cs" />
<Compile Include="Src\Commands\RefactoringCommands.cs" />
<Compile Include="Src\Commands\TextEditorContextMenuBuilder.cs" />
<Compile Include="Src\Gui\EditStringResourceDialog.cs" />
<Compile Include="Src\Gui\UnusedResourceKeysViewContent.cs" />
<Compile Include="Src\Refactoring\AnyResourceReferenceFinder.cs" />
<Compile Include="Src\Refactoring\IResourceReferenceFinder.cs" />
<Compile Include="Src\Refactoring\ResourceRefactoringService.cs" />
<Compile Include="Src\Refactoring\SpecificResourceReferenceFinder.cs" />
<Compile Include="Src\Resolver\AbstractResourceResolver.cs" />
<Compile Include="Src\Resolver\BclNRefactoryResourceResolver.cs" />
<Compile Include="Src\Resolver\ICSharpCodeCoreNRefactoryResourceResolver.cs" />
<Compile Include="Src\Resolver\ICSharpCodeCoreResourceResolver.cs" />
<Compile Include="Src\Resolver\INRefactoryResourceResolver.cs" />
<Compile Include="Src\Resolver\IResourceResolver.cs" />
<Compile Include="Src\Resolver\NodeTrackingAstVisitor.cs" />
<Compile Include="Src\Resolver\NRefactoryAstCacheService.cs" />
<Compile Include="Src\Resolver\NRefactoryResourceResolver.cs" />
<Compile Include="Src\Resolver\PositionTrackingAstVisitor.cs" />
<Compile Include="Src\Resolver\PropertyFieldAssociationVisitor.cs" />
<Compile Include="Src\Resolver\ResourceResolveResult.cs" />
<Compile Include="Src\ResourceFileContent\DefaultBclResourceFileContentFactory.cs" />
<Compile Include="Src\ResourceFileContent\DefaultFileLocalizedResourcesFinder.cs" />
<Compile Include="Src\ResourceFileContent\ILocalizedResourcesFinder.cs" />
<Compile Include="Src\ResourceFileContent\IMultiResourceFileContent.cs" />
<Compile Include="Src\ResourceFileContent\IResourceFileContent.cs" />
<Compile Include="Src\ResourceFileContent\IResourceFileContentFactory.cs" />
<Compile Include="Src\ResourceFileContent\MergedResourceFileContent.cs" />
<Compile Include="Src\ResourceFileContent\ResourceFileContentRegistry.cs" />
<Compile Include="Src\ResourceFileContent\ResourcesResourceFileContent.cs" />
<Compile Include="Src\ResourceFileContent\ResXResourceFileContent.cs" />
<Compile Include="Src\ToolTips\ResourceToolTipProvider.cs" />
<Compile Include="Src\ProjectFileDictionaryService.cs" />
<Compile Include="Src\ResourceResolverService.cs" />
<Compile Include="Configuration\AssemblyInfo.cs" />
<Compile Include="..\..\..\..\Main\GlobalAssemblyInfo.cs">
<Link>Configuration\GlobalAssemblyInfo.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Main\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj">
<Project>{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}</Project>
<Name>ICSharpCode.SharpDevelop.Dom</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj">
<Project>{2748AD25-9C63-4E12-877B-4DCE96FBED54}</Project>
<Name>ICSharpCode.SharpDevelop</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Main\Core\Project\ICSharpCode.Core.csproj">
<Project>{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}</Project>
<Name>ICSharpCode.Core</Name>
<Private>False</Private>
</ProjectReference>
<Content Include="Hornung.ResourceToolkit.addin">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Folder Include="Src" />
<Folder Include="Src\CodeCompletion" />
<Folder Include="Src\Commands" />
<Folder Include="Src\Gui" />
<Folder Include="Src\Refactoring" />
<Folder Include="Src\Resolver" />
<Folder Include="Src\ResourceFileContent" />
<Folder Include="Src\ToolTips" />
<Folder Include="Resources" />
<Content Include="Resources\EditStringResourceDialog.xfrm" />
<Folder Include="Configuration" />
<ProjectReference Include="..\..\..\..\Libraries\ICSharpCode.TextEditor\Project\ICSharpCode.TextEditor.csproj">
<Project>{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}</Project>
<Name>ICSharpCode.TextEditor</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Libraries\NRefactory\Project\NRefactory.csproj">
<Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project>
<Name>NRefactory</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

73
src/AddIns/Misc/ResourceToolkit/Project/Resources/EditStringResourceDialog.xfrm

@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
<Components version="1.0">
<System.Windows.Forms.Form>
<Name value="EditStringResourceDialog" />
<FormBorderStyle value="FixedDialog" />
<AcceptButton value="okButton [System.Windows.Forms.Button], Text: ${res:Global.OKButtonText}" />
<MinimizeBox value="False" />
<ShowInTaskbar value="False" />
<Text value="${res:Hornung.ResourceToolkit.EditStringResourceDialog.Caption}" />
<MaximizeBox value="False" />
<ClientSize value="{Width=364, Height=224}" />
<CancelButton value="cancelButton [System.Windows.Forms.Button], Text: ${res:Global.CancelButtonText}" />
<ShowIcon value="False" />
<Controls>
<System.Windows.Forms.TextBox>
<Name value="valueTextBox" />
<TabIndex value="5" />
<Multiline value="True" />
<AcceptsReturn value="True" />
<Size value="{Width=340, Height=89}" />
<Location value="{X=12,Y=94}" />
<Anchor value="Top, Bottom, Left, Right" />
</System.Windows.Forms.TextBox>
<System.Windows.Forms.Label>
<Name value="valueLabel" />
<Location value="{X=12,Y=68}" />
<UseCompatibleTextRendering value="True" />
<Text value="${res:Hornung.ResourceToolkit.ValueLabel}" />
<Anchor value="Top, Left, Right" />
<Size value="{Width=340, Height=23}" />
<TabIndex value="4" />
</System.Windows.Forms.Label>
<System.Windows.Forms.TextBox>
<Name value="keyTextBox" />
<TabIndex value="3" />
<Size value="{Width=340, Height=21}" />
<Location value="{X=12,Y=35}" />
<Anchor value="Top, Left, Right" />
</System.Windows.Forms.TextBox>
<System.Windows.Forms.Label>
<Name value="keyLabel" />
<Location value="{X=12,Y=9}" />
<UseCompatibleTextRendering value="True" />
<Text value="${res:Hornung.ResourceToolkit.KeyLabel}" />
<Anchor value="Top, Left, Right" />
<Size value="{Width=340, Height=23}" />
<TabIndex value="2" />
</System.Windows.Forms.Label>
<System.Windows.Forms.Button>
<Name value="cancelButton" />
<DialogResult value="Cancel" />
<Location value="{X=277,Y=189}" />
<UseCompatibleTextRendering value="True" />
<Text value="${res:Global.CancelButtonText}" />
<Anchor value="Bottom, Right" />
<CausesValidation value="False" />
<UseVisualStyleBackColor value="True" />
<Size value="{Width=75, Height=23}" />
<TabIndex value="1" />
</System.Windows.Forms.Button>
<System.Windows.Forms.Button>
<Name value="okButton" />
<DialogResult value="OK" />
<Location value="{X=196,Y=189}" />
<UseCompatibleTextRendering value="True" />
<Text value="${res:Global.OKButtonText}" />
<Anchor value="Bottom, Right" />
<UseVisualStyleBackColor value="True" />
<Size value="{Width=75, Height=23}" />
<TabIndex value="0" />
</System.Windows.Forms.Button>
</Controls>
</System.Windows.Forms.Form>
</Components>

59
src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.NRefactory.PrettyPrinter;
using ICSharpCode.TextEditor.Gui.CompletionWindow;
using Hornung.ResourceToolkit.Resolver;
namespace Hornung.ResourceToolkit.CodeCompletion
{
/// <summary>
/// Provides a base class for code completion for inserting resource keys using NRefactory.
/// </summary>
public abstract class AbstractNRefactoryResourceCodeCompletionBinding : DefaultCodeCompletionBinding
{
public override bool HandleKeyPress(SharpDevelopTextAreaControl editor, char ch)
{
if (this.CompletionPossible(editor, ch)) {
ResourceResolveResult result = ResourceResolverService.Resolve(editor);
if (result != null) {
if (result.ResourceFileContent != null) {
editor.ShowCompletionWindow(new ResourceCodeCompletionDataProvider(result.ResourceFileContent, this.OutputVisitor, result.CallingClass != null ? result.CallingClass.Name+"." : null), ch);
return true;
}
}
}
return false;
}
// ********************************************************************************************************************************
/// <summary>
/// Determines if the specified character should trigger resource resolve attempt and possibly code completion at the current position.
/// </summary>
protected abstract bool CompletionPossible(SharpDevelopTextAreaControl editor, char ch);
/// <summary>
/// Gets an NRefactory output visitor used to generate the inserted code.
/// </summary>
protected abstract IOutputAstVisitor OutputVisitor {
get;
}
}
}

40
src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/CSharpResourceCodeCompletionBinding.cs

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.NRefactory.PrettyPrinter;
namespace Hornung.ResourceToolkit.CodeCompletion
{
/// <summary>
/// Provides code completion for inserting resource keys in C#.
/// </summary>
public class CSharpResourceCodeCompletionBinding : AbstractNRefactoryResourceCodeCompletionBinding
{
/// <summary>
/// Determines if the specified character should trigger resource resolve attempt and possibly code completion at the current position.
/// </summary>
protected override bool CompletionPossible(SharpDevelopTextAreaControl editor, char ch)
{
return ch == '(' || ch == '[';
}
/// <summary>
/// Gets a CSharpOutputVisitor used to generate the inserted code.
/// </summary>
protected override IOutputAstVisitor OutputVisitor {
get {
return new CSharpOutputVisitor();
}
}
}
}

69
src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.TextEditor.Gui.CompletionWindow;
using Hornung.ResourceToolkit.Resolver;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.CodeCompletion
{
/// <summary>
/// Provides code completion for inserting resource keys
/// for ICSharpCode.Core resources references ("${res:...}").
/// </summary>
public class ICSharpCodeCoreResourceCodeCompletionBinding : DefaultCodeCompletionBinding
{
public override bool HandleKeyPress(SharpDevelopTextAreaControl editor, char ch)
{
if (ch == ':') {
if (editor.ActiveTextAreaControl.Caret.Offset >= 5 && editor.Document.GetText(editor.ActiveTextAreaControl.Caret.Offset-5, 5) == "${res") {
IResourceFileContent content = null;
string localFile = ICSharpCodeCoreResourceResolver.GetICSharpCodeCoreLocalResourceFileName(editor.FileName);
if (localFile != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: Found local ICSharpCode.Core resource file: "+localFile);
#endif
content = ResourceFileContentRegistry.GetResourceFileContent(localFile);
}
IResourceFileContent hostContent;
string hostFile = ICSharpCodeCoreResourceResolver.GetICSharpCodeCoreHostResourceFileName(editor.FileName);
if (hostFile != null && (hostContent = ResourceFileContentRegistry.GetResourceFileContent(hostFile)) != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: Found host ICSharpCode.Core resource file: "+hostFile);
#endif
if (content != null) {
content = new MergedResourceFileContent(content, new IResourceFileContent[] { hostContent });
} else {
content = hostContent;
}
}
if (content != null) {
editor.ShowCompletionWindow(new ResourceCodeCompletionDataProvider(content, null, null), ch);
return true;
}
}
}
return false;
}
}
}

63
src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/NewResourceCodeCompletionData.cs

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Globalization;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.NRefactory.PrettyPrinter;
using ICSharpCode.TextEditor;
using Hornung.ResourceToolkit.Gui;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.CodeCompletion
{
/// <summary>
/// Provides a code completion entry used to add a new string resource.
/// </summary>
public class NewResourceCodeCompletionData : ResourceCodeCompletionData
{
readonly IResourceFileContent content;
readonly string preEnteredName;
public NewResourceCodeCompletionData(IResourceFileContent content, IOutputAstVisitor outputVisitor, string preEnteredName)
: base(StringParser.Parse("${res:Hornung.ResourceToolkit.CodeCompletion.AddNewEntry}"), String.Format(CultureInfo.CurrentCulture, StringParser.Parse("${res:Hornung.ResourceToolkit.CodeCompletion.AddNewDescription}"), content.FileName), outputVisitor)
{
this.content = content;
this.preEnteredName = preEnteredName;
}
/// <summary>
/// Present a form to the user where he enters the name for the new
/// string resource and then insert the key value into the text editor.
/// </summary>
/// <param name="textArea">TextArea to insert the completion data in.</param>
/// <param name="ch">Character that should be inserted after the completion data.
/// \0 when no character should be inserted.</param>
/// <returns>Returns true when the insert action has processed the character
/// <paramref name="ch"/>; false when the character was not processed.</returns>
public override bool InsertAction(TextArea textArea, char ch)
{
EditStringResourceDialog dialog = new EditStringResourceDialog(this.content, this.preEnteredName, null, true);
dialog.Text = this.Description;
if (dialog.ShowDialog(WorkbenchSingleton.MainForm) != DialogResult.OK) {
return false;
}
this.Text = dialog.Key;
this.content.Add(dialog.Key, dialog.Value);
return base.InsertAction(textArea, ch);
}
}
}

70
src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ResourceCodeCompletionData.cs

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Globalization;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.PrettyPrinter;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Gui.CompletionWindow;
namespace Hornung.ResourceToolkit.CodeCompletion
{
/// <summary>
/// Represents a code completion data entry for resource keys.
/// </summary>
public class ResourceCodeCompletionData : DefaultCompletionData
{
readonly IOutputAstVisitor outputVisitor;
/// <summary>
/// Initializes a new instance of the <see cref="ResourceCodeCompletionData" /> class.
/// </summary>
/// <param name="key">The resource key.</param>
/// <param name="description">The resource description.</param>
/// <param name="outputVisitor">The NRefactory output visitor to be used to generate the inserted code. If <c>null</c>, the key is inserted literally.</param>
public ResourceCodeCompletionData(string key, string description, IOutputAstVisitor outputVisitor)
: base(key, description, ClassBrowserIconService.GotoArrowIndex)
{
this.outputVisitor = outputVisitor;
}
/// <summary>
/// Insert the element represented by the completion data into the text
/// editor.
/// </summary>
/// <param name="textArea">TextArea to insert the completion data in.</param>
/// <param name="ch">Character that should be inserted after the completion data.
/// \0 when no character should be inserted.</param>
/// <returns>Returns true when the insert action has processed the character
/// <paramref name="ch"/>; false when the character was not processed.</returns>
public override bool InsertAction(TextArea textArea, char ch)
{
string insertString;
if (this.outputVisitor != null) {
PrimitiveExpression pre = new PrimitiveExpression(this.Text, this.Text);
pre.AcceptVisitor(this.outputVisitor, null);
insertString = this.outputVisitor.Text;
} else {
insertString = this.Text;
}
textArea.InsertString(insertString);
if (ch == insertString[insertString.Length - 1]) {
return true;
}
return false;
}
}
}

75
src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ResourceCodeCompletionDataProvider.cs

@ -0,0 +1,75 @@ @@ -0,0 +1,75 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.NRefactory.PrettyPrinter;
using ICSharpCode.TextEditor.Gui.CompletionWindow;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.CodeCompletion
{
/// <summary>
/// Provides code completion data for resource keys.
/// </summary>
public class ResourceCodeCompletionDataProvider : AbstractCompletionDataProvider
{
readonly IResourceFileContent content;
readonly IOutputAstVisitor outputVisitor;
readonly string preEnteredName;
/// <summary>
/// Initializes a new instance of the <see cref="ResourceCodeCompletionDataProvider" /> class.
/// </summary>
/// <param name="content">The resource file content to be presented to the user.</param>
/// <param name="outputVisitor">The NRefactory output visitor to be used to generate the inserted code. If <c>null</c>, the key is inserted literally.</param>
/// <param name="preEnteredName">The type name which should be pre-entered in the 'add new' dialog box if the user selects the 'add new' entry.</param>
public ResourceCodeCompletionDataProvider(IResourceFileContent content, IOutputAstVisitor outputVisitor, string preEnteredName)
{
if (content == null) {
throw new ArgumentNullException("content");
}
this.content = content;
this.outputVisitor = outputVisitor;
this.preEnteredName = preEnteredName;
this.InsertSpace = false;
}
/// <summary>
/// Generates the completion data. This method is called by the text editor control.
/// </summary>
public override ICompletionData[] GenerateCompletionData(string fileName, ICSharpCode.TextEditor.TextArea textArea, char charTyped)
{
List<ICompletionData> list = new List<ICompletionData>();
list.Add(new NewResourceCodeCompletionData(this.content, this.outputVisitor, this.preEnteredName));
foreach (KeyValuePair<string, object> entry in this.content.Data) {
list.Add(new ResourceCodeCompletionData(entry.Key, ResourceResolverService.FormatResourceDescription(this.content, entry.Key), this.outputVisitor));
}
return list.ToArray();
}
/// <summary>
/// Gets if pressing 'key' should trigger the insertion of the currently selected element.
/// </summary>
public override CompletionDataProviderKeyResult ProcessKey(char key)
{
if (key == '.') {
// don't auto-complete on pressing '.' (this character is commonly used in resource key names)
return CompletionDataProviderKeyResult.NormalKey;
}
return base.ProcessKey(key);
}
}
}

40
src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/VBNetResourceCodeCompletionBinding.cs

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.NRefactory.PrettyPrinter;
namespace Hornung.ResourceToolkit.CodeCompletion
{
/// <summary>
/// Provides code completion for inserting resource keys in VB.
/// </summary>
public class VBNetResourceCodeCompletionBinding : AbstractNRefactoryResourceCodeCompletionBinding
{
/// <summary>
/// Determines if the specified character should trigger resource resolve attempt and possibly code completion at the current position.
/// </summary>
protected override bool CompletionPossible(SharpDevelopTextAreaControl editor, char ch)
{
return ch == '(';
}
/// <summary>
/// Gets a VBNetOutputVisitor used to generate the inserted code.
/// </summary>
protected override IOutputAstVisitor OutputVisitor {
get {
return new VBNetOutputVisitor();
}
}
}
}

57
src/AddIns/Misc/ResourceToolkit/Project/Src/Commands/RefactoringCommands.cs

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Refactoring;
using Hornung.ResourceToolkit.Gui;
using Hornung.ResourceToolkit.Refactoring;
namespace Hornung.ResourceToolkit.Commands
{
/// <summary>
/// Find missing resource keys in the whole solution.
/// </summary>
public class FindMissingResourceKeysCommand : AbstractMenuCommand
{
public override void Run()
{
FindReferencesAndRenameHelper.ShowAsSearchResults(StringParser.Parse("${res:Hornung.ResourceToolkit.ReferencesToMissingKeys}"),
ResourceRefactoringService.FindReferencesToMissingKeys());
}
}
/// <summary>
/// Find unused resource keys in the whole solution.
/// </summary>
public class FindUnusedResourceKeysCommand : AbstractMenuCommand
{
public override void Run()
{
ICollection<KeyValuePair<string, string>> unusedKeys = ResourceRefactoringService.FindUnusedKeys();
if (unusedKeys == null) {
return;
}
if (unusedKeys.Count == 0) {
MessageService.ShowMessage("${res:Hornung.ResourceToolkit.UnusedResourceKeys.NotFound}");
return;
}
IWorkbench workbench = WorkbenchSingleton.Workbench;
if (workbench != null) {
UnusedResourceKeysViewContent vc = new UnusedResourceKeysViewContent(unusedKeys);
workbench.ShowView(vc);
}
}
}
}

150
src/AddIns/Misc/ResourceToolkit/Project/Src/Commands/TextEditorContextMenuBuilder.cs

@ -0,0 +1,150 @@ @@ -0,0 +1,150 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Refactoring;
using ICSharpCode.TextEditor;
using Hornung.ResourceToolkit.Gui;
using Hornung.ResourceToolkit.Refactoring;
using Hornung.ResourceToolkit.Resolver;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.Commands
{
/// <summary>
/// Builds context menu for editing string resources.
/// </summary>
public class TextEditorContextMenuBuilder : ISubmenuBuilder
{
public ToolStripItem[] BuildSubmenu(Codon codon, object owner)
{
TextEditorControl editor = owner as TextEditorControl;
if (editor == null) {
return new ToolStripItem[0];
}
ResourceResolveResult result = ResourceResolverService.Resolve(editor);
if (result != null && result.ResourceFileContent != null && result.Key != null) {
List<ToolStripItem> items = new List<ToolStripItem>();
MenuCommand cmd;
// add resource (if key does not exist) / edit resource (if key exists)
if (result.ResourceFileContent.ContainsKey(result.Key)) {
cmd = new MenuCommand("${res:Hornung.ResourceToolkit.TextEditorContextMenu.EditResource}", this.EditResource);
} else {
cmd = new MenuCommand("${res:Hornung.ResourceToolkit.TextEditorContextMenu.AddResource}", this.EditResource);
}
cmd.Tag = result;
items.Add(cmd);
// find references
cmd = new MenuCommand("${res:SharpDevelop.Refactoring.FindReferencesCommand}", this.FindReferences);
cmd.Tag = result;
items.Add(cmd);
// rename
cmd = new MenuCommand("${res:SharpDevelop.Refactoring.RenameCommand}", this.Rename);
cmd.Tag = result;
items.Add(cmd);
// put the resource menu items into a submenu
// with the resource key as title
ToolStripMenuItem subMenu = new ToolStripMenuItem(result.Key);
subMenu.DropDownItems.AddRange(items.ToArray());
return new ToolStripItem[] { subMenu, new MenuSeparator() };
}
return new ToolStripItem[0];
}
// ********************************************************************************************************************************
void EditResource(object sender, EventArgs e)
{
MenuCommand cmd = sender as MenuCommand;
if (cmd == null) {
return;
}
ResourceResolveResult result = cmd.Tag as ResourceResolveResult;
if (result == null) {
return;
}
object value;
string svalue = null;
if (result.ResourceFileContent.TryGetValue(result.Key, out value)) {
svalue = value as string;
if (svalue == null) {
MessageService.ShowWarning("${res:Hornung.ResourceToolkit.ResourceTypeNotSupported}");
return;
}
}
EditStringResourceDialog dialog = new EditStringResourceDialog(result.ResourceFileContent, result.Key, svalue, false);
if (svalue == null) {
dialog.Text = String.Format(CultureInfo.CurrentCulture, StringParser.Parse("${res:Hornung.ResourceToolkit.CodeCompletion.AddNewDescription}"), result.ResourceFileContent.FileName);
}
if (dialog.ShowDialog(WorkbenchSingleton.MainForm) == DialogResult.OK) {
if (svalue == null) {
// Add new resource.
result.ResourceFileContent.Add(dialog.Key, dialog.Value);
} else {
// Modify existing resource.
result.ResourceFileContent.SetValue(result.Key, dialog.Value);
}
}
}
// ********************************************************************************************************************************
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", MessageId = "Body")]
void FindReferences(object sender, EventArgs e)
{
MenuCommand cmd = sender as MenuCommand;
if (cmd == null) {
return;
}
ResourceResolveResult result = cmd.Tag as ResourceResolveResult;
if (result == null) {
return;
}
FindReferencesAndRenameHelper.ShowAsSearchResults(StringParser.Parse("${res:Hornung.ResourceToolkit.ReferencesToResource}", new string[,] { {"ResourceFileName", System.IO.Path.GetFileName(result.FileName)}, {"ResourceKey", result.Key} }),
ResourceRefactoringService.FindReferences(result.FileName, result.Key));
}
void Rename(object sender, EventArgs e)
{
MenuCommand cmd = sender as MenuCommand;
if (cmd == null) {
return;
}
ResourceResolveResult result = cmd.Tag as ResourceResolveResult;
if (result == null) {
return;
}
ResourceRefactoringService.Rename(result);
}
}
}

83
src/AddIns/Misc/ResourceToolkit/Project/Src/Gui/EditStringResourceDialog.cs

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.ComponentModel;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui.XmlForms;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.Gui
{
/// <summary>
/// A dialog where the user can edit a string resource key and value.
/// </summary>
public class EditStringResourceDialog : BaseSharpDevelopForm
{
readonly IResourceFileContent content;
public EditStringResourceDialog(IResourceFileContent content, string key, string value, bool allowEditKey) : base()
{
this.content = content;
key = key ?? String.Empty;
value = value ?? String.Empty;
InitializeComponent();
if (allowEditKey) {
this.Get<TextBox>("key").Validating += this.KeyValidating;
} else {
this.Get<TextBox>("key").ReadOnly = true;
}
this.Get<TextBox>("key").Text = key;
this.Get<TextBox>("value").Text = value;
if (allowEditKey) {
this.Get<TextBox>("key").Select();
this.Get<TextBox>("key").Select(key.Length, 0);
} else {
this.Get<TextBox>("value").Select();
this.Get<TextBox>("value").Select(value.Length, 0);
}
}
void InitializeComponent()
{
SetupFromXmlStream(this.GetType().Assembly.GetManifestResourceStream("Hornung.ResourceToolkit.Resources.EditStringResourceDialog.xfrm"));
}
void KeyValidating(object sender, CancelEventArgs e)
{
TextBox textBox = (TextBox)sender;
if (textBox.Text.Trim().Length == 0) {
e.Cancel = true;
MessageService.ShowWarning("${res:Hornung.ResourceToolkit.EditStringResourceDialog.KeyIsEmpty}");
} else if (this.content.ContainsKey(textBox.Text.Trim())) {
e.Cancel = true;
MessageService.ShowWarning("${res:Hornung.ResourceToolkit.EditStringResourceDialog.DuplicateKey}");
}
}
public string Key {
get {
return this.Get<TextBox>("key").Text.Trim();
}
}
public string Value {
get {
return this.Get<TextBox>("value").Text;
}
}
}
}

316
src/AddIns/Misc/ResourceToolkit/Project/Src/Gui/UnusedResourceKeysViewContent.cs

@ -0,0 +1,316 @@ @@ -0,0 +1,316 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.Gui
{
/// <summary>
/// Displays unused resource keys in a list and allows the user to delete them.
/// </summary>
public class UnusedResourceKeysViewContent : AbstractViewContent, IClipboardHandler
{
readonly ICollection<KeyValuePair<string, string>> unusedKeys;
Panel panel;
ListView listView;
public override System.Windows.Forms.Control Control {
get {
return this.panel;
}
}
/// <summary>
/// Gets the ListView control that shows the unused resource keys.
/// </summary>
public System.Windows.Forms.ListView ListView {
get {
return this.listView;
}
}
/// <summary>
/// Gets a collection of key/value pairs where the values are the resource file names and the keys are the unused resource keys.
/// </summary>
public ICollection<KeyValuePair<string, string>> UnusedKeys {
get {
return this.unusedKeys;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="UnusedResourceKeysViewContent"/> class.
/// </summary>
/// <param name="unusedKeys">A collection of key/value pairs where the values are the resource file names and the keys are the unused resource keys.</param>
public UnusedResourceKeysViewContent(ICollection<KeyValuePair<string, string>> unusedKeys)
: base(StringParser.Parse("${res:Hornung.ResourceToolkit.UnusedResourceKeys.Title}"))
{
LoggingService.Debug("ResourceToolkit: Creating new UnusedResourceKeysViewContent");
if (unusedKeys == null) {
throw new ArgumentNullException("unusedKeys");
}
this.unusedKeys = unusedKeys;
this.panel = new Panel();
this.panel.SuspendLayout();
this.panel.Dock = DockStyle.Fill;
this.listView = new ListView();
this.ListView.Columns.Add(StringParser.Parse("${res:Hornung.ResourceToolkit.FileName}"), 60);
this.ListView.Columns.Add(StringParser.Parse("${res:Hornung.ResourceToolkit.Key}"), 140);
this.ListView.Columns.Add(StringParser.Parse("${res:Hornung.ResourceToolkit.Value}"), 140);
this.ListView.CheckBoxes = true;
this.ListView.View = View.Details;
this.ListView.FullRowSelect = true;
this.ListView.ShowItemToolTips = true;
this.ListView.ListViewItemSorter = new ResourceListViewItemComparer();
this.ListView.Dock = DockStyle.Fill;
this.ListView.Resize += delegate {
if (this.ListView != null && !this.ListView.IsDisposed && !this.ListView.Disposing && this.ListView.Columns.Count >= 3) {
this.ListView.Columns[0].Width = Convert.ToInt32(this.ListView.Width * 0.20);
this.ListView.Columns[1].Width = Convert.ToInt32(this.ListView.Width * 0.45);
this.ListView.Columns[2].Width = Convert.ToInt32(this.ListView.Width * 0.30);
}
};
this.ListView.HandleCreated += this.ListViewHandleCreated;
this.ListView.ContextMenuStrip = MenuService.CreateContextMenu(this, "/AddIns/ResourceToolkit/ViewContent/UnusedResourceKeys/ListViewContextMenu");
ToolStrip toolStrip = ToolbarService.CreateToolStrip(this, "/AddIns/ResourceToolkit/ViewContent/UnusedResourceKeys/Toolbar");
toolStrip.Dock = DockStyle.Top;
toolStrip.Stretch = true;
this.panel.Controls.Add(this.ListView);
this.panel.Controls.Add(toolStrip);
this.panel.ResumeLayout();
}
void ListViewHandleCreated(object sender, EventArgs e)
{
this.ListView.HandleCreated -= this.ListViewHandleCreated;
this.ListView.BeginInvoke(new Action<ICollection<KeyValuePair<string, string>>>(this.FillListView), this.UnusedKeys);
}
public override void Dispose()
{
this.panel.Controls.Clear();
this.ListView.Dispose();
this.listView = null;
this.panel.Dispose();
this.panel = null;
base.Dispose();
}
// ********************************************************************************************************************************
class ResourceListViewItemComparer : System.Collections.IComparer
{
public int Compare(object x, object y)
{
ListViewItem a = (ListViewItem)x;
ListViewItem b = (ListViewItem)y;
return String.Compare(String.Concat(a.Text, a.SubItems[1].Text), String.Concat(b.Text, b.SubItems[1].Text));
}
}
/// <summary>
/// Fills the list view with the specified resource keys.
/// </summary>
/// <param name="resources">A collection of key/value pairs where the values are the resource file names and the keys are the resource keys.</param>
public void FillListView(ICollection<KeyValuePair<string, string>> resources)
{
Application.DoEvents();
Cursor oldCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
try {
this.ListView.Items.Clear();
this.ListView.Groups.Clear();
this.ListView.BeginUpdate();
Dictionary<string, ListViewGroup> fileGroups = new Dictionary<string, ListViewGroup>();
// Create the ListViewItems.
foreach (KeyValuePair<string, string> entry in resources) {
IResourceFileContent c = ResourceFileContentRegistry.GetResourceFileContent(entry.Value);
object o;
// only add the file name to save space
// and show the full path as tooltip
ListViewItem item = new ListViewItem(Path.GetFileName(entry.Value));
item.ToolTipText = entry.Value;
item.SubItems.Add(entry.Key);
if (c.TryGetValue(entry.Key, out o)) {
item.SubItems.Add((o ?? (object)"<<null>>").ToString());
} else {
throw new InvalidOperationException("The key '"+entry.Key+"' in file '"+entry.Value+"' does not exist although it was reported as unused.");
}
// Use ListViewGroups to group by file names
ListViewGroup grp;
if (!fileGroups.TryGetValue(entry.Value, out grp)) {
grp = new ListViewGroup(entry.Value);
fileGroups.Add(entry.Value, grp);
this.ListView.Groups.Add(grp);
}
grp.Items.Add(item);
this.ListView.Items.Add(item);
}
this.ListView.EndUpdate();
} finally {
Cursor.Current = oldCursor;
}
}
// ********************************************************************************************************************************
#region IClipboardHandler implementation
public bool EnableCut {
get {
return false;
}
}
public bool EnableCopy {
get {
return false;
}
}
public bool EnablePaste {
get {
return false;
}
}
public bool EnableDelete {
get {
return this.ListView.SelectedItems.Count > 0;
}
}
public bool EnableSelectAll {
get {
return this.ListView.Items.Count > 0;
}
}
public void Cut()
{
throw new NotImplementedException();
}
public void Copy()
{
throw new NotImplementedException();
}
public void Paste()
{
throw new NotImplementedException();
}
public void Delete()
{
if (this.ListView.SelectedItems.Count > 0) {
bool ok;
if (this.ListView.SelectedItems.Count == 1) {
ok = MessageService.AskQuestion(StringParser.Parse("${res:Hornung.ResourceToolkit.DeleteSingleResourceKeyQuestion}", new string[,] { {"Key", this.ListView.SelectedItems[0].SubItems[1].Text}, {"FileName", this.ListView.SelectedItems[0].Group.Header} }));
} else {
ok = MessageService.AskQuestion(StringParser.Parse("${res:Hornung.ResourceToolkit.DeleteAllSelectedResourceKeysQuestion}", new string[,] { {"Count", this.ListView.SelectedItems.Count.ToString(CultureInfo.CurrentCulture)} }));
}
if (ok) {
this.DeleteResources(this.ListView.SelectedItems);
}
}
}
public void SelectAll()
{
foreach (ListViewItem item in this.ListView.Items) {
item.Selected = true;
}
}
#endregion
// ********************************************************************************************************************************
/// <summary>
/// Deletes the resource keys represented by the specified ListViewItems.
/// </summary>
public void DeleteResources(System.Collections.IEnumerable itemsToDelete)
{
Application.DoEvents();
Cursor oldCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
try {
// The collection must not be modified during the enumeration.
// -> Save the items that should be deleted in a separate list.
List<ListViewItem> items = new List<ListViewItem>();
foreach (ListViewItem item in itemsToDelete) {
DeleteResourceKey(item.Group.Header, item.SubItems[1].Text);
items.Add(item);
}
items.ForEach(this.ListView.Items.Remove);
} finally {
Cursor.Current = oldCursor;
}
}
/// <summary>
/// Deletes the specified resource key in the resource file and all dependent localized
/// resource files permanently.
/// </summary>
/// <param name="fileName">The master resource file that contains the key to be deleted.</param>
/// <param name="key">The key to be deleted.</param>
protected static void DeleteResourceKey(string fileName, string key)
{
IResourceFileContent content = ResourceFileContentRegistry.GetResourceFileContent(fileName);
if (content != null) {
if (content.ContainsKey(key)) {
LoggingService.Debug("ResourceToolkit: Remove key '"+key+"' from resource file '"+fileName+"'");
content.RemoveKey(key);
} else {
MessageService.ShowWarningFormatted("${res:Hornung.ResourceToolkit.KeyNotFoundWarning}", key, fileName);
}
} else {
MessageService.ShowWarning("ResoureToolkit: Could not get ResourceFileContent for '"+fileName+"' key +'"+key+"'.");
}
foreach (KeyValuePair<string, IResourceFileContent> entry in ResourceFileContentRegistry.GetLocalizedContents(fileName)) {
LoggingService.Debug("ResourceToolkit: Looking in localized resource file: '"+entry.Value.FileName+"'");
if (entry.Value.ContainsKey(key)) {
LoggingService.Debug("ResourceToolkit: -> Key found, removing.");
entry.Value.RemoveKey(key);
}
}
}
}
}

90
src/AddIns/Misc/ResourceToolkit/Project/Src/ProjectFileDictionaryService.cs

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Project;
namespace Hornung.ResourceToolkit
{
/// <summary>
/// Provides a way to find out which project a certain file belongs to
/// and caches the results to improve performance.
/// </summary>
public static class ProjectFileDictionaryService
{
static Dictionary<string, IProject> files = new Dictionary<string, IProject>();
static ProjectFileDictionaryService()
{
// Remove file from dictionary when file is removed from project
ProjectService.ProjectItemRemoved += delegate(object sender, ProjectItemEventArgs e) {
if (e.ProjectItem != null && e.ProjectItem.FileName != null) {
files.Remove(e.ProjectItem.FileName);
}
};
// Clear cache when solution is closed
ProjectService.SolutionClosed += delegate { files.Clear(); };
}
/// <summary>
/// Gets the project that contains the specified file.
/// </summary>
/// <param name="fileName">The file to find the project for.</param>
/// <returns>The project that contains the specified file. If the file is not found in any project or there is no open project, the current project is returned, which may be <c>null</c>.</returns>
public static IProject GetProjectForFile(string fileName)
{
if (!String.IsNullOrEmpty(fileName)) {
IProject p;
if (files.TryGetValue(fileName, out p)) {
return p;
}
if ((p = GetProjectForFileInternal(fileName)) != null) {
files[fileName] = p;
return p;
}
}
LoggingService.Debug("ResourceToolkit: ProjectFileDictionary: Could not determine project for file '"+(fileName ?? "<null>")+"'.");
return ProjectService.CurrentProject;
}
/// <summary>
/// Gets the project that contains the specified file.
/// </summary>
/// <param name="fileName">The file to find the project for.</param>
/// <returns>The project that contains the specified file. If the file is not found in any project or there is no open project, <c>null</c> is returned.</returns>
static IProject GetProjectForFileInternal(string fileName)
{
if (ProjectService.OpenSolution != null) {
IProject p;
if ((p = ProjectService.OpenSolution.FindProjectContainingFile(fileName)) != null) {
return p;
}
}
return null;
}
/// <summary>
/// Adds a file name to the project dictionary or sets the file's project if
/// it is already listed.
/// </summary>
/// <param name="file">The file to add.</param>
/// <param name="project">The project the specified file belongs to.</param>
public static void AddFile(string file, IProject project)
{
files[file] = project;
}
}
}

83
src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/AnyResourceReferenceFinder.cs

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using Hornung.ResourceToolkit.Resolver;
namespace Hornung.ResourceToolkit.Refactoring
{
/// <summary>
/// Finds references to resources in a text document.
/// </summary>
public class AnyResourceReferenceFinder : IResourceReferenceFinder
{
/// <summary>
/// Returns the offset of the next possible resource reference in the file
/// after prevOffset.
/// Returns -1, if there are no more possible references.
/// </summary>
/// <param name="fileName">The name of the file that is currently being searched in.</param>
/// <param name="fileContent">The text content of the file.</param>
/// <param name="prevOffset">The offset of the last found reference or -1, if this is the first call in the current file.</param>
public int GetNextPossibleOffset(string fileName, string fileContent, int prevOffset)
{
int pos = -1;
int i;
foreach (string pattern in GetPossiblePatternsForFile(fileName)) {
if ((i = fileContent.IndexOf(pattern, prevOffset+1, StringComparison.InvariantCultureIgnoreCase)) >= 0) {
if (pos == -1 || i < pos) {
pos = i;
}
}
}
return pos;
}
/// <summary>
/// Determines whether the specified ResourceResolveResult describes
/// a resource that should be included in the search result.
/// </summary>
public bool IsReferenceToResource(ResourceResolveResult result)
{
return true;
}
/// <summary>
/// Gets a list of patterns that can be searched for in the specified file
/// to find possible resource references that are supported by any
/// resolver.
/// </summary>
/// <param name="fileName">The name of the file to get a list of possible patterns for.</param>
public static IEnumerable<string> GetPossiblePatternsForFile(string fileName)
{
List<string> patterns = new List<string>();
foreach (IResourceResolver resolver in ResourceResolverService.Resolvers) {
if (resolver.SupportsFile(fileName)) {
foreach (string pattern in resolver.GetPossiblePatternsForFile(fileName)) {
if (!patterns.Contains(pattern)) {
patterns.Add(pattern);
}
}
}
}
return patterns;
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="AnyResourceReferenceFinder"/> class.
/// </summary>
public AnyResourceReferenceFinder()
{
}
}
}

35
src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/IResourceReferenceFinder.cs

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using Hornung.ResourceToolkit.Resolver;
namespace Hornung.ResourceToolkit.Refactoring
{
/// <summary>
/// Describes an object that finds resource references in a text document.
/// </summary>
public interface IResourceReferenceFinder
{
/// <summary>
/// Returns the offset of the next possible resource reference in the file
/// after prevOffset.
/// Returns -1, if there are no more possible references.
/// </summary>
/// <param name="fileName">The name of the file that is currently being searched in.</param>
/// <param name="fileContent">The text content of the file.</param>
/// <param name="prevOffset">The offset of the last found reference or -1, if this is the first call in the current file.</param>
int GetNextPossibleOffset(string fileName, string fileContent, int prevOffset);
/// <summary>
/// Determines whether the specified ResourceResolveResult describes
/// a resource that should be included in the search result.
/// </summary>
bool IsReferenceToResource(ResourceResolveResult result);
}
}

364
src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/ResourceRefactoringService.cs

@ -0,0 +1,364 @@ @@ -0,0 +1,364 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Refactoring;
using ICSharpCode.TextEditor.Document;
using Hornung.ResourceToolkit.Resolver;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.Refactoring
{
/// <summary>
/// Provides facilities for refactoring resources.
/// </summary>
public static class ResourceRefactoringService
{
/// <summary>
/// Finds all references to the specified resource (except the definition).
/// </summary>
/// <param name="resourceFileName">The name of the resource file that contains the resource key to find.</param>
/// <param name="key">The resource key to find.</param>
/// <returns>A list of references to this resource.</returns>
public static List<Reference> FindReferences(string resourceFileName, string key)
{
return FindReferences(new SpecificResourceReferenceFinder(resourceFileName, key));
}
/// <summary>
/// Finds all references to resources (except the definition) using the specified
/// <see cref="IResourceReferenceFinder"/> object.
/// </summary>
/// <param name="finder">The <see cref="IResourceReferenceFinder"/> to use to find resource references.</param>
/// <returns>A list of references to resources.</returns>
public static List<Reference> FindReferences(IResourceReferenceFinder finder)
{
if (finder == null) {
throw new ArgumentNullException("finder");
}
if (ParserService.LoadSolutionProjectsThreadRunning) {
MessageService.ShowMessage("${res:SharpDevelop.Refactoring.LoadSolutionProjectsThreadRunning}");
return null;
}
DateTime startTime = DateTime.UtcNow;
List<Reference> references = new List<Reference>();
try {
NRefactoryAstCacheService.EnableCache();
foreach (string fileName in GetPossibleFiles()) {
IDocument doc = null;
try {
// The following line throws an exception if the file does not exist.
// But the file may be in an unsaved view content (which would be found by GetDocumentInformation),
// so we cannot simply loop on !File.Exists(...).
doc = FindReferencesAndRenameHelper.GetDocumentInformation(fileName).CreateDocument();
} catch (FileNotFoundException) {
}
if (doc == null) {
continue;
}
string fileContent = doc.TextContent;
if (String.IsNullOrEmpty(fileContent)) {
continue;
}
int pos = -1;
while ((pos = finder.GetNextPossibleOffset(fileName, fileContent, pos)) >= 0) {
Point docPos = doc.OffsetToPosition(pos);
ResourceResolveResult rrr = ResourceResolverService.Resolve(fileName, doc, docPos.Y, docPos.X);
if (rrr != null && rrr.ResourceFileContent != null && rrr.Key != null) {
if (finder.IsReferenceToResource(rrr)) {
// The actual location of the key string may be after 'pos' because
// the resolvers may find an expression just before it.
string keyString = rrr.Key;
int keyPos = fileContent.IndexOf(keyString, pos, StringComparison.InvariantCultureIgnoreCase);
if (keyPos < pos) {
// The key may be escaped in some way in the document.
// Try using the code generator to find this out.
keyPos = FindStringLiteral(fileName, fileContent, rrr.Key, pos, out keyString);
}
if (keyPos < pos) {
MessageService.ShowWarning("ResourceToolkit: The key '"+rrr.Key+"' could not be located at the resolved position in the file '"+fileName+"'.");
} else {
references.Add(new Reference(fileName, keyPos, keyString.Length, keyString, rrr));
}
}
}
}
}
LoggingService.Info("ResourceToolkit: FindReferences finished in "+(DateTime.UtcNow - startTime).TotalSeconds.ToString(System.Globalization.CultureInfo.CurrentCulture)+"s");
} finally {
NRefactoryAstCacheService.DisableCache();
}
return references;
}
/// <summary>
/// Finds all references to resources (except the definitions).
/// </summary>
/// <returns>A list of references to resources.</returns>
public static List<Reference> FindAllReferences()
{
return FindReferences(new AnyResourceReferenceFinder());
}
/// <summary>
/// Finds all references to missing resource keys.
/// </summary>
/// <returns>A list of all references to missing resource keys.</returns>
public static List<Reference> FindReferencesToMissingKeys()
{
List<Reference> references = FindAllReferences();
if (references == null) {
return null;
}
return references.FindAll(IsReferenceToMissingKey);
}
/// <summary>
/// Determines whether the specified reference is a resource reference
/// to a missing key.
/// </summary>
/// <param name="reference">The reference to examine.</param>
/// <returns><c>true</c>, if the specified reference is a resource reference to a missing key, otherwise <c>false</c>.</returns>
public static bool IsReferenceToMissingKey(Reference reference)
{
ResourceResolveResult rrr = reference.ResolveResult as ResourceResolveResult;
if (rrr == null || rrr.Key == null) {
return false;
}
if (rrr.ResourceFileContent == null) {
return true;
}
return !rrr.ResourceFileContent.ContainsKey(rrr.Key);
}
/// <summary>
/// Finds all unused resource keys in all resource files that are referenced
/// in code at least once in the whole solution.
/// </summary>
/// <returns>A collection of key/value pairs where the values are the resource file names and the keys are the unused resource keys.</returns>
public static ICollection<KeyValuePair<string, string>> FindUnusedKeys()
{
List<Reference> references = FindAllReferences();
if (references == null) {
return null;
}
DateTime startTime = DateTime.UtcNow;
List<KeyValuePair<string, string>> unused = new List<KeyValuePair<string, string>>();
// Get a list of all referenced resource files.
// Generate a dictonary of resource file names and the
// corresponding referenced keys.
Dictionary<string, List<string>> referencedKeys = new Dictionary<string, List<string>>();
foreach (Reference reference in references) {
ResourceResolveResult rrr = (ResourceResolveResult)reference.ResolveResult;
if (rrr.ResourceFileContent != null) {
string fileName = rrr.FileName;
if (!referencedKeys.ContainsKey(fileName)) {
referencedKeys.Add(fileName, new List<string>());
}
if (rrr.Key != null && !referencedKeys[fileName].Contains(rrr.Key)) {
referencedKeys[fileName].Add(rrr.Key);
}
} else {
MessageService.ShowWarning("Found a resource reference that could not be resolved."+Environment.NewLine+(reference.FileName ?? "<null>")+":"+reference.Offset+Environment.NewLine+"Expression: "+(reference.Expression ?? "<null>"));
}
}
// Find keys that are not referenced anywhere.
foreach (string fileName in referencedKeys.Keys) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: FindUnusedKeys: Referenced resource file '"+fileName+"'");
#endif
foreach (KeyValuePair<string, object> entry in ResourceFileContentRegistry.GetResourceFileContent(fileName).Data) {
if (!referencedKeys[fileName].Contains(entry.Key)) {
unused.Add(new KeyValuePair<string, string>(entry.Key, fileName));
}
}
}
LoggingService.Info("ResourceToolkit: FindUnusedKeys finished in "+(DateTime.UtcNow - startTime).TotalSeconds.ToString(System.Globalization.CultureInfo.CurrentCulture)+"s");
return unused.AsReadOnly();
}
// ********************************************************************************************************************************
/// <summary>
/// Renames all references to a resource including the definition.
/// Asks the user for a new name.
/// </summary>
/// <param name="rrr">The resource to be renamed.</param>
public static void Rename(ResourceResolveResult rrr)
{
string newKey = MessageService.ShowInputBox("${res:SharpDevelop.Refactoring.Rename}", "${res:Hornung.ResourceToolkit.RenameResourceText}", rrr.Key);
if (!String.IsNullOrEmpty(newKey) && !newKey.Equals(rrr.Key)) {
Rename(rrr, newKey);
}
}
/// <summary>
/// Renames all references to a resource including the definition.
/// </summary>
/// <param name="rrr">The resource to be renamed.</param>
/// <param name="newKey">The new name of the resource key.</param>
public static void Rename(ResourceResolveResult rrr, string newKey)
{
// Prevent duplicate key names
if (rrr.ResourceFileContent.ContainsKey(newKey)) {
MessageService.ShowWarning("${res:Hornung.ResourceToolkit.EditStringResourceDialog.DuplicateKey}");
return;
}
List<Reference> references = FindReferences(rrr.FileName, rrr.Key);
if (references == null) {
return;
}
// rename references
// FIXME: RenameReferences does not enforce escaping rules. May be a problem if someone uses double-quotes in the new resource key name.
FindReferencesAndRenameHelper.RenameReferences(references, newKey);
// rename definition (if present)
if (rrr.ResourceFileContent.ContainsKey(rrr.Key)) {
rrr.ResourceFileContent.RenameKey(rrr.Key, newKey);
} else {
MessageService.ShowWarning("${res:Hornung.ResourceToolkit.RenameKeyDefinitionNotFoundWarning}");
}
// rename definitions in localized resource files
foreach (KeyValuePair<string, IResourceFileContent> entry in ResourceFileContentRegistry.GetLocalizedContents(rrr.FileName)) {
if (entry.Value.ContainsKey(rrr.Key)) {
entry.Value.RenameKey(rrr.Key, newKey);
}
}
}
// ********************************************************************************************************************************
/// <summary>
/// Gets a list of names of files which can possibly contain resource references.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
public static List<string> GetPossibleFiles()
{
List<string> files = new List<string>();
if (ProjectService.OpenSolution == null) {
foreach (IViewContent vc in WorkbenchSingleton.Workbench.ViewContentCollection) {
string name = vc.FileName ?? vc.UntitledName;
if (IsPossibleFile(name)) {
files.Add(name);
}
}
} else {
foreach (IProject p in ProjectService.OpenSolution.Projects) {
foreach (ProjectItem pi in p.Items) {
if (pi is FileProjectItem) {
string name = pi.FileName;
if (IsPossibleFile(name)) {
files.Add(name);
// Add the file to the project dictionary here.
// This saves the lookup time when the corresponding project
// is needed later.
ProjectFileDictionaryService.AddFile(name, p);
}
}
}
}
}
return files;
}
/// <summary>
/// Determines whether the specified file could possibly contain resource references
/// that can be detected by at least one registered resource resolver.
/// </summary>
public static bool IsPossibleFile(string name)
{
foreach (IResourceResolver resolver in ResourceResolverService.Resolvers) {
if (resolver.SupportsFile(name)) {
return true;
}
}
return false;
}
// ********************************************************************************************************************************
/// <summary>
/// Finds a string literal in a source code file with respect to escaping rules
/// of the language.
/// </summary>
/// <param name="fileName">The name of the file to search in.</param>
/// <param name="fileContent">The text content of the file.</param>
/// <param name="literal">The string literal to find.</param>
/// <param name="startOffset">The position to start searching at.</param>
/// <param name="code">Receives the unquoted program code that represents the specified string literal in the language this file is written in.</param>
/// <returns>The next index where the specified string literal appears, or -1 if there is no match or the language cannot be determined.</returns>
public static int FindStringLiteral(string fileName, string fileContent, string literal, int startOffset, out string code)
{
ICSharpCode.SharpDevelop.Dom.LanguageProperties lp = NRefactoryResourceResolver.GetLanguagePropertiesForFile(fileName);
if (lp != null && lp.CodeGenerator != null) {
code = lp.CodeGenerator.GenerateCode(new PrimitiveExpression(literal, literal), String.Empty);
if (!String.IsNullOrEmpty(code)) {
// Unquote the string if possible.
if (code.StartsWith("\"") || code.StartsWith("'")) {
code = code.Substring(1);
}
if (code.EndsWith("\"") || code.EndsWith("'")) {
code = code.Remove(code.Length-1);
}
return fileContent.IndexOf(code, startOffset, StringComparison.InvariantCultureIgnoreCase);
}
}
code = null;
return -1;
}
}
}

93
src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/SpecificResourceReferenceFinder.cs

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.Core;
using Hornung.ResourceToolkit.Resolver;
namespace Hornung.ResourceToolkit.Refactoring
{
/// <summary>
/// Finds references to a specific resource in a text document.
/// </summary>
public class SpecificResourceReferenceFinder : IResourceReferenceFinder
{
readonly string resourceFileName;
readonly string key;
/// <summary>
/// Gets the name of the resource file that contains the resource to find.
/// </summary>
public string ResourceFileName {
get {
return resourceFileName;
}
}
/// <summary>
/// Gets the resource key to find.
/// </summary>
public string Key {
get {
return key;
}
}
// ********************************************************************************************************************************
/// <summary>
/// Returns the offset of the next possible resource reference in the file
/// after prevOffset.
/// Returns -1, if there are no more possible references.
/// </summary>
/// <param name="fileName">The name of the file that is currently being searched in.</param>
/// <param name="fileContent">The text content of the file.</param>
/// <param name="prevOffset">The offset of the last found reference or -1, if this is the first call in the current file.</param>
public int GetNextPossibleOffset(string fileName, string fileContent, int prevOffset)
{
string code;
int pos = ResourceRefactoringService.FindStringLiteral(fileName, fileContent, this.Key, prevOffset+1, out code);
if (pos == -1) {
// if the code generator search fails, try a direct search
pos = fileContent.IndexOf(this.Key, prevOffset+1, StringComparison.InvariantCultureIgnoreCase);
}
return pos;
}
/// <summary>
/// Determines whether the specified ResourceResolveResult describes
/// a resource that should be included in the search result.
/// </summary>
public bool IsReferenceToResource(ResourceResolveResult result)
{
return FileUtility.IsEqualFileName(this.ResourceFileName, result.FileName) &&
result.Key.Equals(this.Key, StringComparison.InvariantCultureIgnoreCase);
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="SpecificResourceReferenceFinder"/> class.
/// </summary>
/// <param name="resourceFileName">The name of the resource file that contains the resource to find.</param>
/// <param name="key">The resource key to find.</param>
public SpecificResourceReferenceFinder(string resourceFileName, string key)
{
if (resourceFileName == null) {
throw new ArgumentNullException("resourceFileName");
}
if (key == null) {
throw new ArgumentNullException("key");
}
this.resourceFileName = resourceFileName;
this.key = key;
}
}
}

112
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/AbstractResourceResolver.cs

@ -0,0 +1,112 @@ @@ -0,0 +1,112 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Document;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Abstract base class for resource resolvers.
/// </summary>
public abstract class AbstractResourceResolver : IResourceResolver
{
/// <summary>
/// Attempts to resolve a reference to a resource.
/// </summary>
/// <param name="fileName">The name of the file that contains the expression to be resolved.</param>
/// <param name="document">The document that contains the expression to be resolved.</param>
/// <param name="caretLine">The 0-based line in the file that contains the expression to be resolved.</param>
/// <param name="caretColumn">The 0-based column position of the expression to be resolved.</param>
/// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the specified position in the specified file, or <c>null</c> if that expression does not reference a (known) resource or if the specified position is invalid.</returns>
public ResourceResolveResult Resolve(string fileName, IDocument document, int caretLine, int caretColumn)
{
if (fileName == null || document == null) {
LoggingService.Debug("ResourceToolkit: "+this.GetType().ToString()+".Resolve called with null fileName or document argument");
return null;
}
if (caretLine < 0 || caretColumn < 0 || caretLine >= document.TotalNumberOfLines || caretColumn >= document.GetLineSegment(caretLine).TotalLength) {
LoggingService.Debug("ResourceToolkit: "+this.GetType().ToString()+".Resolve called with invalid position arguments");
return null;
}
return this.Resolve(fileName, document, caretLine, caretColumn, document.PositionToOffset(new Point(caretColumn, caretLine)));
}
/// <summary>
/// Attempts to resolve a reference to a resource.
/// </summary>
/// <param name="editor">The text editor for which a resource resolution attempt should be performed.</param>
/// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the caret in the specified editor, or <c>null</c> if that expression does not reference a (known) resource.</returns>
public ResourceResolveResult Resolve(TextEditorControl editor)
{
return this.Resolve(editor.FileName, editor.Document, editor.ActiveTextAreaControl.Caret.Line, editor.ActiveTextAreaControl.Caret.Column);
}
// ********************************************************************************************************************************
/// <summary>
/// Attempts to resolve a reference to a resource.
/// </summary>
/// <param name="fileName">The name of the file that contains the expression to be resolved.</param>
/// <param name="document">The document that contains the expression to be resolved.</param>
/// <param name="caretLine">The 0-based line in the file that contains the expression to be resolved.</param>
/// <param name="caretColumn">The 0-based column position of the expression to be resolved.</param>
/// <param name="caretOffset">The offset of the position of the expression to be resolved.</param>
/// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the specified position in the specified file, or <c>null</c> if that expression does not reference a (known) resource.</returns>
protected abstract ResourceResolveResult Resolve(string fileName, IDocument document, int caretLine, int caretColumn, int caretOffset);
/// <summary>
/// Determines whether this resolver supports resolving resources in the given file.
/// </summary>
/// <param name="fileName">The name of the file to examine.</param>
/// <returns><c>true</c>, if this resolver supports resolving resources in the given file, <c>false</c> otherwise.</returns>
public abstract bool SupportsFile(string fileName);
/// <summary>
/// Gets a list of patterns that can be searched for in the specified file
/// to find possible resource references that are supported by this
/// resolver.
/// </summary>
/// <param name="fileName">The name of the file to get a list of possible patterns for.</param>
public abstract IEnumerable<string> GetPossiblePatternsForFile(string fileName);
// ********************************************************************************************************************************
/// <summary>
/// Tries to find a resource file with the given name in the given directory by
/// trying all known resource file extensions.
/// </summary>
protected static string FindResourceFileName(string fileName)
{
string f;
if (File.Exists(f = Path.ChangeExtension(fileName, ".resources"))) {
return f;
}
if (File.Exists(f = Path.ChangeExtension(fileName, ".resx"))) {
return f;
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="AbstractResourceResolver"/> class.
/// </summary>
protected AbstractResourceResolver()
{
}
}
}

458
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/BclNRefactoryResourceResolver.cs

@ -0,0 +1,458 @@ @@ -0,0 +1,458 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Parser;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Detects and resolves resource references using the standard .NET
/// framework provided resource access methods (ResourceManager and derived
/// classes).
/// </summary>
public class BclNRefactoryResourceResolver : INRefactoryResourceResolver
{
/// <summary>
/// Tries to find a resource reference in the specified expression.
/// </summary>
/// <param name="expressionResult">The ExpressionResult for the expression.</param>
/// <param name="expr">The AST representation of the expression.</param>
/// <param name="resolveResult">SharpDevelop's ResolveResult for the expression.</param>
/// <param name="caretLine">The line where the expression is located.</param>
/// <param name="caretColumn">The column where the expression is located.</param>
/// <param name="fileName">The name of the source file where the expression is located.</param>
/// <param name="fileContent">The content of the source file where the expression is located.</param>
/// <returns>A ResourceResolveResult describing the referenced resource, or <c>null</c>, if this expression does not reference a resource using the standard .NET framework classes.</returns>
public ResourceResolveResult Resolve(ExpressionResult expressionResult, Expression expr, ResolveResult resolveResult, int caretLine, int caretColumn, string fileName, string fileContent)
{
IResourceFileContent rfc = null;
MemberResolveResult mrr = resolveResult as MemberResolveResult;
if (mrr != null) {
rfc = ResolveResourceFileContent(mrr.ResolvedMember);
}
LocalResolveResult lrr = resolveResult as LocalResolveResult;
if (lrr != null) {
if (!lrr.IsParameter) {
rfc = ResolveResourceFileContent(lrr.Field);
}
}
if (rfc != null) {
string key = GetKeyFromExpression(expr);
// TODO: Add information about return type (of the resource, if present).
return new ResourceResolveResult(resolveResult.CallingClass, resolveResult.CallingMember, null, rfc, key);
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Tries to determine the resource file content which is referenced by the
/// resource manager which is assigned to the specified member.
/// </summary>
/// <returns>
/// The IResourceFileContent, if successful, or a null reference, if the
/// specified member is not a resource manager or if the
/// resource file cannot be determined.
/// </returns>
static IResourceFileContent ResolveResourceFileContent(IMember member)
{
if (member != null && member.ReturnType != null) {
if (IsResourceManager(member.ReturnType) && member.DeclaringType != null && member.DeclaringType.CompilationUnit != null) {
string declaringFileName = member.DeclaringType.CompilationUnit.FileName;
if (declaringFileName != null) {
SupportedLanguage? language = NRefactoryResourceResolver.GetFileLanguage(declaringFileName);
if (language == null) {
return null;
}
CompilationUnit cu = NRefactoryAstCacheService.GetFullAst(language.Value, declaringFileName);
if (cu != null) {
ResourceManagerInitializationFindVisitor visitor = new ResourceManagerInitializationFindVisitor(member);
cu.AcceptVisitor(visitor, null);
if (visitor.FoundFileName != null) {
return ResourceFileContentRegistry.GetResourceFileContent(visitor.FoundFileName);
}
}
}
}
}
return null;
}
/// <summary>
/// Determines if the specified type is a ResourceManager type that can
/// be handled by this resolver.
/// </summary>
static bool IsResourceManager(IReturnType type)
{
IClass resourceManager = ParserService.CurrentProjectContent.GetClass("System.Resources.ResourceManager");
if (resourceManager == null) {
return false;
}
IClass c = type.GetUnderlyingClass();
if (c == null) {
return false;
}
return (c.CompareTo(resourceManager) == 0 || c.IsTypeInInheritanceTree(resourceManager));
}
// ********************************************************************************************************************************
#region ResourceManagerInitializationFindVisitor
/// <summary>
/// Finds an initialization statement for a resource manager member and
/// tries to infer the referenced resource file from the parameters
/// given to the resource manager constructor.
/// </summary>
class ResourceManagerInitializationFindVisitor : PositionTrackingAstVisitor
{
readonly IMember resourceManagerMember;
readonly bool isLocalVariable;
CompilationUnit compilationUnit;
string foundFileName;
/// <summary>
/// Gets the resource file name which the resource manager accesses, or a null reference if the file could not be determined or does not exist.
/// </summary>
public string FoundFileName {
get {
return this.foundFileName;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ResourceManagerInitializationFindVisitor" /> class.
/// </summary>
/// <param name="resourceManagerMember">The member which the resource manager to be found is assigned to.</param>
public ResourceManagerInitializationFindVisitor(IMember resourceManagerMember) : base()
{
this.resourceManagerMember = resourceManagerMember;
IField resourceManagerField = resourceManagerMember as IField;
if (resourceManagerField != null && resourceManagerField.IsLocalVariable) {
this.isLocalVariable = true;
}
}
public override object TrackedVisit(CompilationUnit compilationUnit, object data)
{
this.compilationUnit = compilationUnit;
return base.TrackedVisit(compilationUnit, data);
}
public override object TrackedVisit(LocalVariableDeclaration localVariableDeclaration, object data)
{
return base.TrackedVisit(localVariableDeclaration, localVariableDeclaration);
}
public override object TrackedVisit(FieldDeclaration fieldDeclaration, object data)
{
return base.TrackedVisit(fieldDeclaration, fieldDeclaration);
}
public override object TrackedVisit(VariableDeclaration variableDeclaration, object data)
{
LocalVariableDeclaration localVariableDeclaration = data as LocalVariableDeclaration;
if (this.isLocalVariable && localVariableDeclaration != null) {
if (variableDeclaration.Name == this.resourceManagerMember.Name) {
// Make sure we got the right declaration by comparing the positions.
// Both must have the same start position.
if (localVariableDeclaration.StartLocation.X == this.resourceManagerMember.Region.BeginColumn && localVariableDeclaration.StartLocation.Y == this.resourceManagerMember.Region.BeginLine) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found local variable declaration: "+localVariableDeclaration.ToString()+" at "+localVariableDeclaration.StartLocation.ToString());
#endif
data = true;
}
}
}
FieldDeclaration fieldDeclaration = data as FieldDeclaration;
if (!this.isLocalVariable && fieldDeclaration != null) {
if (variableDeclaration.Name == this.resourceManagerMember.Name) {
// Make sure we got the right declaration by comparing the positions.
// Both must have the same start position.
if (fieldDeclaration.StartLocation.X == this.resourceManagerMember.Region.BeginColumn && fieldDeclaration.StartLocation.Y == this.resourceManagerMember.Region.BeginLine) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found field declaration: "+fieldDeclaration.ToString()+" at "+fieldDeclaration.StartLocation.ToString());
#endif
data = true;
}
}
}
return base.TrackedVisit(variableDeclaration, data);
}
public override object TrackedVisit(AssignmentExpression assignmentExpression, object data)
{
if (this.FoundFileName == null && // skip if already found to improve performance
assignmentExpression.Op == AssignmentOperatorType.Assign && this.PositionAvailable &&
(!this.isLocalVariable || this.resourceManagerMember.Region.IsInside(this.CurrentNodeStartLocation.Y, this.CurrentNodeStartLocation.X)) // skip if local variable is out of scope
) {
MemberResolveResult mrr = this.Resolve(assignmentExpression.Left, this.resourceManagerMember) as MemberResolveResult;
if (mrr != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver: Resolved member: "+mrr.ResolvedMember.ToString());
#endif
// HACK: The GetType()s are necessary because the DOM IComparable implementations try to cast the parameter object to their own interface type which may fail.
if (mrr.ResolvedMember.GetType().Equals(this.resourceManagerMember.GetType()) && mrr.ResolvedMember.CompareTo(this.resourceManagerMember) == 0) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found assignment to field: "+assignmentExpression.ToString());
#endif
data = true;
// Resolving the property association only makes sense if
// there is a possible relationship between the return types
// of the resolved member and the member we are looking for.
} else if (this.compilationUnit != null && !this.isLocalVariable &&
(
mrr.ResolvedMember.ReturnType.Equals(this.resourceManagerMember.ReturnType) ||
(
mrr.ResolvedMember.ReturnType.GetUnderlyingClass() != null && this.resourceManagerMember.ReturnType.GetUnderlyingClass() != null &&
(
mrr.ResolvedMember.ReturnType.GetUnderlyingClass().IsTypeInInheritanceTree(this.resourceManagerMember.ReturnType.GetUnderlyingClass()) ||
this.resourceManagerMember.ReturnType.GetUnderlyingClass().IsTypeInInheritanceTree(mrr.ResolvedMember.ReturnType.GetUnderlyingClass())
)
)
)) {
if (this.resourceManagerMember is IProperty && mrr.ResolvedMember is IField) {
// Find out if the resourceManagerMember is a property whose get block returns the value of the resolved member.
PropertyFieldAssociationVisitor visitor = new PropertyFieldAssociationVisitor((IProperty)this.resourceManagerMember);
this.compilationUnit.AcceptVisitor(visitor, null);
if (visitor.AssociatedField != null && visitor.AssociatedField.CompareTo(mrr.ResolvedMember) == 0) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found assignment to field: "+assignmentExpression.ToString());
#endif
data = true;
}
} else if (this.resourceManagerMember is IField && mrr.ResolvedMember is IProperty) {
// Find out if the resolved member is a property whose set block assigns the value to the resourceManagerMember.
PropertyFieldAssociationVisitor visitor = new PropertyFieldAssociationVisitor((IField)this.resourceManagerMember);
this.compilationUnit.AcceptVisitor(visitor, null);
if (visitor.AssociatedProperty != null && visitor.AssociatedProperty.CompareTo(mrr.ResolvedMember) == 0) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found assignment to property: "+assignmentExpression.ToString());
#endif
data = true;
}
}
}
}
}
return base.TrackedVisit(assignmentExpression, data);
}
public override object TrackedVisit(ObjectCreateExpression objectCreateExpression, object data)
{
if (data as bool? ?? false) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found object initialization: "+objectCreateExpression.ToString());
#endif
// Resolve the constructor.
// A type derived from the declaration type is also allowed.
MemberResolveResult mrr = this.Resolve(objectCreateExpression, this.resourceManagerMember) as MemberResolveResult;
#if DEBUG
if (mrr != null) {
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver: resolved constructor: "+mrr.ResolvedMember.ToString());
}
#endif
if (mrr != null &&
mrr.ResolvedMember is IMethod &&
(mrr.ResolvedMember.DeclaringType.CompareTo(this.resourceManagerMember.ReturnType.GetUnderlyingClass()) == 0 ||
mrr.ResolvedMember.DeclaringType.IsTypeInInheritanceTree(this.resourceManagerMember.ReturnType.GetUnderlyingClass()))
) {
// This most probably is the resource manager initialization we are looking for.
// Find a parameter that indicates the resources being referenced.
foreach (Expression param in objectCreateExpression.Parameters) {
PrimitiveExpression p = param as PrimitiveExpression;
if (p != null) {
string pValue = p.Value as string;
if (pValue != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found string parameter: '"+pValue+"'");
#endif
string fileName = NRefactoryResourceResolver.GetResourceFileNameByResourceName(this.resourceManagerMember.DeclaringType.CompilationUnit.FileName, pValue);
if (fileName != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found resource file: "+fileName);
#endif
this.foundFileName = fileName;
break;
}
}
continue;
}
// Support typeof(...)
TypeOfExpression t = param as TypeOfExpression;
if (t != null && this.PositionAvailable) {
TypeResolveResult trr = this.Resolve(new TypeReferenceExpression(t.TypeReference), this.resourceManagerMember) as TypeResolveResult;
if (trr != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found typeof(...) parameter, type: '"+trr.ResolvedType.ToString()+"'");
#endif
string fileName = NRefactoryResourceResolver.GetResourceFileNameByResourceName(this.resourceManagerMember.DeclaringType.CompilationUnit.FileName, trr.ResolvedType.FullyQualifiedName);
if (fileName != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found resource file: "+fileName);
#endif
this.foundFileName = fileName;
break;
}
}
}
}
}
}
return base.TrackedVisit(objectCreateExpression, data);
}
}
#endregion
// ********************************************************************************************************************************
/// <summary>
/// Tries to infer the resource key being referenced from the given expression.
/// </summary>
static string GetKeyFromExpression(Expression expr)
{
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver trying to get key from expression: "+expr.ToString());
#endif
IndexerExpression indexer = expr as IndexerExpression;
if (indexer != null) {
foreach (Expression index in indexer.Indexes) {
PrimitiveExpression p = index as PrimitiveExpression;
if (p != null) {
string key = p.Value as string;
if (key != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found key: "+key);
#endif
return key;
}
}
}
}
InvocationExpression invocation = expr as InvocationExpression;
if (invocation != null) {
FieldReferenceExpression fre = invocation.TargetObject as FieldReferenceExpression;
if (fre != null) {
if (fre.FieldName == "GetString" || fre.FieldName == "GetObject" || fre.FieldName == "GetStream") {
if (invocation.Arguments.Count > 0) {
PrimitiveExpression p = invocation.Arguments[0] as PrimitiveExpression;
if (p != null) {
string key = p.Value as string;
if (key != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: BclNRefactoryResourceResolver found key: "+key);
#endif
return key;
}
}
}
}
}
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Gets a list of patterns that can be searched for in the specified file
/// to find possible resource references that are supported by this
/// resolver.
/// </summary>
/// <param name="fileName">The name of the file to get a list of possible patterns for.</param>
public IEnumerable<string> GetPossiblePatternsForFile(string fileName)
{
return new string[] {
"GetString",
"GetObject",
"GetStream",
(NRefactoryResourceResolver.GetLanguagePropertiesForFile(fileName) ?? LanguageProperties.None).IndexerExpressionStartToken
};
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="BclNRefactoryResourceResolver"/> class.
/// </summary>
public BclNRefactoryResourceResolver()
{
}
}
}

154
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreNRefactoryResourceResolver.cs

@ -0,0 +1,154 @@ @@ -0,0 +1,154 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Dom;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Detects and resolves resource references when using the ICSharpCode.Core
/// ResourceService class.
/// </summary>
public class ICSharpCodeCoreNRefactoryResourceResolver : INRefactoryResourceResolver
{
/// <summary>
/// Tries to find a resource reference in the specified expression.
/// </summary>
/// <param name="expressionResult">The ExpressionResult for the expression.</param>
/// <param name="expr">The AST representation of the expression.</param>
/// <param name="resolveResult">SharpDevelop's ResolveResult for the expression.</param>
/// <param name="caretLine">The line where the expression is located.</param>
/// <param name="caretColumn">The column where the expression is located.</param>
/// <param name="fileName">The name of the source file where the expression is located.</param>
/// <param name="fileContent">The content of the source file where the expression is located.</param>
/// <returns>A ResourceResolveResult describing the referenced resource, or <c>null</c>, if this expression does not reference a resource using the ICSharpCode.Core.ResourceService class.</returns>
public ResourceResolveResult Resolve(ExpressionResult expressionResult, Expression expr, ResolveResult resolveResult, int caretLine, int caretColumn, string fileName, string fileContent)
{
IMember member = null;
// "ResourceService.GetString(..." may be a MemberResolveResult or
// MethodResolveResult, dependent on how much of the expression
// has already been typed.
MemberResolveResult mrr = resolveResult as MemberResolveResult;
if (mrr != null) {
member = mrr.ResolvedMember;
} else {
MethodResolveResult methrr = resolveResult as MethodResolveResult;
if (methrr != null) {
member = methrr.GetMethodIfSingleOverload();
}
}
if (member is IMethod &&
LanguageProperties.CSharp.NameComparer.Equals(member.FullyQualifiedName, "ICSharpCode.Core.ResourceService.GetString")
) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: ICSharpCodeCoreNRefactoryResourceResolver: ResourceService resource access detected");
#endif
string key = GetKeyFromExpression(expr);
string localResourceFileName = ICSharpCodeCoreResourceResolver.GetICSharpCodeCoreLocalResourceFileName(fileName);
string hostResourceFileName = ICSharpCodeCoreResourceResolver.GetICSharpCodeCoreHostResourceFileName(fileName);
IResourceFileContent content = null;
// Merge the local and host resource file contents if available.
if (!String.IsNullOrEmpty(localResourceFileName)) {
content = ResourceFileContentRegistry.GetResourceFileContent(localResourceFileName);
}
if (!String.IsNullOrEmpty(hostResourceFileName)) {
if (content == null) {
content = ResourceFileContentRegistry.GetResourceFileContent(hostResourceFileName);
} else {
IResourceFileContent hostContent = ResourceFileContentRegistry.GetResourceFileContent(hostResourceFileName);
if (hostContent != null) {
content = new MergedResourceFileContent(content, new IResourceFileContent[] { hostContent });
}
}
}
if (content != null) {
// TODO: Add information about return type (of the resource, if present).
return new ResourceResolveResult(resolveResult.CallingClass, resolveResult.CallingMember, null, content, key);
}
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Tries to infer the resource key being referenced from the given expression.
/// </summary>
static string GetKeyFromExpression(Expression expr)
{
#if DEBUG
LoggingService.Debug("ResourceToolkit: ICSharpCodeCoreNRefactoryResourceResolver trying to get key from expression: "+expr.ToString());
#endif
InvocationExpression invocation = expr as InvocationExpression;
if (invocation != null) {
FieldReferenceExpression fre = invocation.TargetObject as FieldReferenceExpression;
if (fre != null) {
if (fre.FieldName == "GetString") {
if (invocation.Arguments.Count > 0) {
PrimitiveExpression p = invocation.Arguments[0] as PrimitiveExpression;
if (p != null) {
string key = p.Value as string;
if (key != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: ICSharpCodeCoreNRefactoryResourceResolver found key: "+key);
#endif
return key;
}
}
}
}
}
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Gets a list of patterns that can be searched for in the specified file
/// to find possible resource references that are supported by this
/// resolver.
/// </summary>
/// <param name="fileName">The name of the file to get a list of possible patterns for.</param>
public IEnumerable<string> GetPossiblePatternsForFile(string fileName)
{
return new string[] {
"GetString"
};
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="ICSharpCodeCoreNRefactoryResourceResolver"/> class.
/// </summary>
public ICSharpCodeCoreNRefactoryResourceResolver()
{
}
}
}

299
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreResourceResolver.cs

@ -0,0 +1,299 @@ @@ -0,0 +1,299 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TextEditor.Document;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Resolves references to resources that are accessed using ICSharpCode.Core
/// ("${res:...}").
/// </summary>
public class ICSharpCodeCoreResourceResolver : AbstractResourceResolver
{
/// <summary>
/// Initializes a new instance of the <see cref="ICSharpCodeCoreResourceResolver"/> class.
/// </summary>
public ICSharpCodeCoreResourceResolver() : base()
{
}
// ********************************************************************************************************************************
/// <summary>
/// Determines whether this resolver supports resolving resources in the given file.
/// </summary>
/// <param name="fileName">The name of the file to examine.</param>
/// <returns><c>true</c>, if this resolver supports resolving resources in the given file, <c>false</c> otherwise.</returns>
public override bool SupportsFile(string fileName)
{
// Any parseable source code file may contain references
if (ICSharpCode.SharpDevelop.ParserService.GetParser(fileName) != null) {
return true;
}
// Support additional files by extension
switch(Path.GetExtension(fileName).ToLowerInvariant()) {
case ".addin":
case ".xfrm":
case ".xml":
return true;
default:
break;
}
return false;
}
static readonly string[] possiblePatterns = new string[] {
"${res:"
};
/// <summary>
/// Gets a list of patterns that can be searched for in the specified file
/// to find possible resource references that are supported by this
/// resolver.
/// </summary>
/// <param name="fileName">The name of the file to get a list of possible patterns for.</param>
public override IEnumerable<string> GetPossiblePatternsForFile(string fileName)
{
if (this.SupportsFile(fileName)) {
return possiblePatterns;
}
return new string[0];
}
// ********************************************************************************************************************************
/// <summary>
/// The token that indicates a reference to an ICSharpCode.Core resource.
/// </summary>
public const string ResourceReferenceToken = @"${res:";
/// <summary>
/// Attempts to resolve a reference to a resource.
/// </summary>
/// <param name="fileName">The name of the file that contains the expression to be resolved.</param>
/// <param name="document">The document that contains the expression to be resolved.</param>
/// <param name="caretLine">The 0-based line in the file that contains the expression to be resolved.</param>
/// <param name="caretColumn">The 0-based column position of the expression to be resolved.</param>
/// <param name="caretOffset">The offset of the position of the expression to be resolved.</param>
/// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the specified position in the specified file, or <c>null</c> if that expression does not reference a (known) resource.</returns>
protected override ResourceResolveResult Resolve(string fileName, IDocument document, int caretLine, int caretColumn, int caretOffset)
{
// Find $ character to the left of the caret.
caretOffset += 1;
char ch;
do {
ch = document.GetCharAt(--caretOffset);
} while (!Char.IsWhiteSpace(ch) && ch != '$' && caretOffset > 0);
if (caretOffset + 6 >= document.TextLength || document.GetText(caretOffset, 6) != ResourceReferenceToken) {
return null;
}
caretOffset += 6;
// Read resource key.
StringBuilder key = new StringBuilder();
while (caretOffset < document.TextLength && !Char.IsWhiteSpace(ch = document.GetCharAt(caretOffset++)) && ch != '}') {
key.Append(ch);
}
if (ch != '}') {
key = null;
#if DEBUG
} else {
LoggingService.Debug("ResourceToolkit: ICSharpCodeCoreResourceResolver found resource key: "+key.ToString());
#endif
}
string resourceFile = ResolveICSharpCodeCoreResourceFileName(key == null ? null : key.ToString(), fileName);
if (resourceFile != null) {
// TODO: Add information about callingClass, callingMember, returnType
return new ResourceResolveResult(null, null, null, ResourceFileContentRegistry.GetResourceFileContent(resourceFile), key == null ? null : key.ToString());
} else {
LoggingService.Info("ResourceToolkit: ICSharpCodeCoreResourceResolver: Could not find the ICSharpCode.Core resource file name for the source file '"+fileName+"', key '"+(key == null ? "<null>" : key.ToString())+"'.");
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Tries to find the name of the resource file which serves as source for
/// the resources accessed by the ICSharpCode.Core.
/// </summary>
/// <param name="key">The resource key to look for. May be null.</param>
/// <param name="sourceFileName">The name of the source code file that contains the reference to the resource.</param>
static string ResolveICSharpCodeCoreResourceFileName(string key, string sourceFileName)
{
// As there is no easy way to find out the actual location of the resources
// based on the source code, we just look in some standard directories.
// Local file (SD addin or standalone application with standard directory structure)
string localFile = GetICSharpCodeCoreLocalResourceFileName(sourceFileName);
// Prefer local file, especially if the key is there.
if (localFile != null) {
if (key != null) {
IResourceFileContent localContent = ResourceFileContentRegistry.GetResourceFileContent(localFile);
if (localContent != null) {
if (localContent.ContainsKey(key)) {
return localFile;
}
}
} else {
return localFile;
}
}
// Resource file of the host application
string hostFile = GetICSharpCodeCoreHostResourceFileName(sourceFileName);
if (key != null) {
if (hostFile != null) {
IResourceFileContent hostContent = ResourceFileContentRegistry.GetResourceFileContent(hostFile);
if (hostContent != null) {
if (hostContent.ContainsKey(key)) {
return hostFile;
}
}
}
}
// Use local file also if the key is not there
// (allows adding of a new key)
return localFile == null ? hostFile : localFile;
}
/// <summary>
/// Tries to find an ICSharpCode.Core resource file in the given path
/// according to the file names defined in the AddIn tree.
/// </summary>
static string FindICSharpCodeCoreResourceFile(string path)
{
string file;
foreach (string fileName in AddInTree.BuildItems<string>("/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/ResourceFileNames", null, false)) {
if ((file = FindResourceFileName(Path.Combine(path, fileName))) != null) {
return file;
}
}
return null;
}
/// <summary>
/// Tries to find the local string resource file used for ICSharpCode.Core resource access.
/// </summary>
/// <param name="sourceFileName">The name of the source code file which to find the ICSharpCode.Core resource file for.</param>
public static string GetICSharpCodeCoreLocalResourceFileName(string sourceFileName)
{
IProject project = ProjectFileDictionaryService.GetProjectForFile(sourceFileName);
if (project == null || String.IsNullOrEmpty(project.Directory)) {
return null;
}
string localFile;
foreach (string relativePath in AddInTree.BuildItems<string>("/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/LocalResourcesLocations", null, false)) {
if ((localFile = FindICSharpCodeCoreResourceFile(Path.GetFullPath(Path.Combine(project.Directory, relativePath)))) != null) {
return localFile;
}
}
return null;
}
/// <summary>
/// Tries to find the string resource file of the host application for ICSharpCode.Core resource access.
/// </summary>
/// <param name="sourceFileName">The name of the source code file which to find the ICSharpCode.Core resource file for.</param>
public static string GetICSharpCodeCoreHostResourceFileName(string sourceFileName)
{
IProject project = ProjectFileDictionaryService.GetProjectForFile(sourceFileName);
if (project == null || String.IsNullOrEmpty(project.Directory)) {
return null;
}
// Get SD directory using the reference to ICSharpCode.Core
string coreAssemblyFullPath = GetICSharpCodeCoreFullPath(project);
if (coreAssemblyFullPath == null) {
// Look for the ICSharpCode.Core project using all available projects.
if (ProjectService.OpenSolution != null) {
foreach (IProject p in ProjectService.OpenSolution.Projects) {
if ((coreAssemblyFullPath = GetICSharpCodeCoreFullPath(p)) != null) {
break;
}
}
}
}
if (coreAssemblyFullPath != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: ICSharpCodeCoreResourceResolver coreAssemblyFullPath = "+coreAssemblyFullPath);
#endif
string hostFile;
foreach (string relativePath in AddInTree.BuildItems<string>("/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/HostResourcesLocations", null, false)) {
if ((hostFile = FindICSharpCodeCoreResourceFile(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(coreAssemblyFullPath), relativePath)))) != null) {
return hostFile;
}
}
}
return null;
}
static string GetICSharpCodeCoreFullPath(IProject sourceProject)
{
string coreAssemblyFullPath = null;
if (sourceProject.Name.Equals("ICSharpCode.Core", StringComparison.InvariantCultureIgnoreCase)) {
// This is the ICSharpCode.Core project itself.
coreAssemblyFullPath = sourceProject.OutputAssemblyFullPath;
} else {
// Get the ICSharpCode.Core.dll path by using the project reference.
foreach (ProjectItem item in sourceProject.Items) {
ProjectReferenceProjectItem prpi = item as ProjectReferenceProjectItem;
if (prpi != null) {
if (prpi.ReferencedProject != null) {
if (prpi.ReferencedProject.Name.Equals("ICSharpCode.Core", StringComparison.InvariantCultureIgnoreCase) && prpi.ReferencedProject.OutputAssemblyFullPath != null) {
coreAssemblyFullPath = prpi.ReferencedProject.OutputAssemblyFullPath;
break;
}
}
}
ReferenceProjectItem rpi = item as ReferenceProjectItem;
if (rpi != null) {
if (rpi.Name.Equals("ICSharpCode.Core", StringComparison.InvariantCultureIgnoreCase) && rpi.FileName != null) {
coreAssemblyFullPath = rpi.FileName;
break;
}
}
}
}
return coreAssemblyFullPath;
}
}
}

44
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/INRefactoryResourceResolver.cs

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Dom;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Detects and resolves resource references using SharpDevelop.Dom and
/// NRefactory. At this stage the parser and resolver have already been
/// called.
/// </summary>
public interface INRefactoryResourceResolver
{
/// <summary>
/// Tries to find a resource reference in the specified expression.
/// </summary>
/// <param name="expressionResult">The ExpressionResult for the expression.</param>
/// <param name="expr">The AST representation of the expression.</param>
/// <param name="resolveResult">SharpDevelop's ResolveResult for the expression.</param>
/// <param name="caretLine">The line where the expression is located.</param>
/// <param name="caretColumn">The column where the expression is located.</param>
/// <param name="fileName">The name of the source file where the expression is located.</param>
/// <param name="fileContent">The content of the source file where the expression is located.</param>
/// <returns>A ResourceResolveResult describing the referenced resource, or <c>null</c>, if this expression does not reference a resource in a known way.</returns>
ResourceResolveResult Resolve(ExpressionResult expressionResult, Expression expr, ResolveResult resolveResult, int caretLine, int caretColumn, string fileName, string fileContent);
/// <summary>
/// Gets a list of patterns that can be searched for in the specified file
/// to find possible resource references that are supported by this
/// resolver.
/// </summary>
/// <param name="fileName">The name of the file to get a list of possible patterns for.</param>
IEnumerable<string> GetPossiblePatternsForFile(string fileName);
}
}

56
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/IResourceResolver.cs

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Document;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Describes an object that is able to find out if an expression
/// references a resource and if so, in which file and which key.
/// </summary>
public interface IResourceResolver
{
/// <summary>
/// Attempts to resolve a reference to a resource.
/// </summary>
/// <param name="fileName">The name of the file that contains the expression to be resolved.</param>
/// <param name="document">The document that contains the expression to be resolved.</param>
/// <param name="caretLine">The 1-based line in the file that contains the expression to be resolved.</param>
/// <param name="caretColumn">The 1-based column position of the expression to be resolved.</param>
/// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the specified position in the specified file, or <c>null</c> if that expression does not reference a (known) resource.</returns>
ResourceResolveResult Resolve(string fileName, IDocument document, int caretLine, int caretColumn);
/// <summary>
/// Attempts to resolve a reference to a resource.
/// </summary>
/// <param name="editor">The text editor for which a resource resolution attempt should be performed.</param>
/// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the caret in the specified editor, or <c>null</c> if that expression does not reference a (known) resource.</returns>
ResourceResolveResult Resolve(TextEditorControl editor);
/// <summary>
/// Determines whether this resolver supports resolving resources in the given file.
/// </summary>
/// <param name="fileName">The name of the file to examine.</param>
/// <returns><c>true</c>, if this resolver supports resolving resources in the given file, <c>false</c> otherwise.</returns>
bool SupportsFile(string fileName);
/// <summary>
/// Gets a list of patterns that can be searched for in the specified file
/// to find possible resource references that are supported by this
/// resolver.
/// </summary>
/// <param name="fileName">The name of the file to get a list of possible patterns for.</param>
IEnumerable<string> GetPossiblePatternsForFile(string fileName);
}
}

94
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryAstCacheService.cs

@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Parser;
using ICSharpCode.SharpDevelop;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Parses files using NRefactory and caches the AST on demand.
/// </summary>
public static class NRefactoryAstCacheService
{
static bool cacheEnabled = false;
static Dictionary<string, CompilationUnit> cachedAstInfo = new Dictionary<string, CompilationUnit>();
/// <summary>
/// Gets a flag that indicates whether the AST cache is currently enabled.
/// </summary>
public static bool CacheEnabled {
get {
return cacheEnabled;
}
}
/// <summary>
/// Enables the AST cache.
/// </summary>
/// <exception cref="InvalidOperationException">The AST cache is already enabled.</exception>
public static void EnableCache()
{
if (CacheEnabled) {
throw new InvalidOperationException("The AST cache is already enabled.");
}
cacheEnabled = true;
LoggingService.Info("ResourceToolkit: NRefactoryAstCacheService cache enabled");
}
/// <summary>
/// Disables the AST cache.
/// </summary>
public static void DisableCache()
{
cacheEnabled = false;
cachedAstInfo.Clear();
LoggingService.Info("ResourceToolkit: NRefactoryAstCacheService cache disabled and cleared");
}
/// <summary>
/// Gets the complete NRefactory AST for the specified file.
/// </summary>
/// <param name="language">The language of the file.</param>
/// <param name="fileName">The file to get the AST for.</param>
/// <returns>A <see cref="CompilationUnit"/> that contains the AST for the specified file, or <c>null</c> if the file cannot be parsed.</returns>
/// <remarks>Between calls to <see cref="EnableCache"/> and <see cref="DisableCache"/> the file is parsed only once. On subsequent accesses the AST is retrieved from the cache.</remarks>
public static CompilationUnit GetFullAst(SupportedLanguage language, string fileName)
{
CompilationUnit cu;
if (!CacheEnabled || !cachedAstInfo.TryGetValue(fileName, out cu)) {
cu = Parse(language, fileName);
if (cu != null && CacheEnabled) {
cachedAstInfo.Add(fileName, cu);
}
}
return cu;
}
static CompilationUnit Parse(SupportedLanguage language, string fileName)
{
using(ICSharpCode.NRefactory.IParser parser = ParserFactory.CreateParser(language, new StringReader(ParserService.GetParseableFileContent(fileName)))) {
if (parser != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: NRefactoryAstCacheService: Parsing file '"+fileName+"'");
#endif
parser.ParseMethodBodies = true;
parser.Parse();
return parser.CompilationUnit;
}
}
return null;
}
}
}

390
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryResourceResolver.cs

@ -0,0 +1,390 @@ @@ -0,0 +1,390 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Parser;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.TextEditor.Document;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Resolves resource references using NRefactory.
/// </summary>
public class NRefactoryResourceResolver : AbstractResourceResolver
{
/// <summary>
/// The AddIn tree path where the NRefactory resource resolvers are registered.
/// </summary>
public const string NRefactoryResourceResolversAddInTreePath = "/AddIns/ResourceToolkit/NRefactoryResourceResolver/Resolvers";
// ********************************************************************************************************************************
static List<INRefactoryResourceResolver> resolvers;
/// <summary>
/// Gets a list of all registered NRefactory resource resolvers.
/// </summary>
public static IEnumerable<INRefactoryResourceResolver> Resolvers {
get {
if (resolvers == null) {
resolvers = AddInTree.BuildItems<INRefactoryResourceResolver>(NRefactoryResourceResolversAddInTreePath, null, false);
}
return resolvers;
}
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="NRefactoryResourceResolver"/> class.
/// </summary>
public NRefactoryResourceResolver() : base()
{
}
// ********************************************************************************************************************************
/// <summary>
/// Determines whether this resolver supports resolving resources in the given file.
/// </summary>
/// <param name="fileName">The name of the file to examine.</param>
/// <returns><c>true</c>, if this resolver supports resolving resources in the given file, <c>false</c> otherwise.</returns>
public override bool SupportsFile(string fileName)
{
// Any source code file supported by NRefactory is supported
return (GetFileLanguage(fileName) != null);
}
/// <summary>
/// Gets a list of patterns that can be searched for in the specified file
/// to find possible resource references that are supported by this
/// resolver.
/// </summary>
/// <param name="fileName">The name of the file to get a list of possible patterns for.</param>
public override IEnumerable<string> GetPossiblePatternsForFile(string fileName)
{
if (this.SupportsFile(fileName)) {
List<string> patterns = new List<string>();
foreach (INRefactoryResourceResolver resolver in Resolvers) {
foreach (string pattern in resolver.GetPossiblePatternsForFile(fileName)) {
if (!patterns.Contains(pattern)) {
patterns.Add(pattern);
}
}
}
return patterns;
}
return new string[0];
}
// ********************************************************************************************************************************
/// <summary>
/// Attempts to resolve a reference to a resource.
/// </summary>
/// <param name="fileName">The name of the file that contains the expression to be resolved.</param>
/// <param name="document">The document that contains the expression to be resolved.</param>
/// <param name="caretLine">The 0-based line in the file that contains the expression to be resolved.</param>
/// <param name="caretColumn">The 0-based column position of the expression to be resolved.</param>
/// <param name="caretOffset">The offset of the position of the expression to be resolved.</param>
/// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the specified position in the specified file, or <c>null</c> if that expression does not reference a (known) resource.</returns>
protected override ResourceResolveResult Resolve(string fileName, IDocument document, int caretLine, int caretColumn, int caretOffset)
{
IExpressionFinder ef = ParserService.GetExpressionFinder(fileName);
if (ef == null) {
return null;
}
ExpressionResult result = ef.FindFullExpression(document.TextContent, caretOffset);
if (result.Expression == null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: NRefactoryResourceResolver could not find expression on first try");
#endif
// may happen if in string
while (--caretOffset > 0 && (result = ef.FindFullExpression(document.TextContent, caretOffset)).Expression == null) {
if (document.GetLineNumberForOffset(caretOffset) != caretLine) {
// only look in same line
break;
}
}
}
if (result.Expression != null) {
ResourceResolveResult rrr = null;
// The full expression is parsed here because
// we will need to modify the result in the next step.
Expression expr = null;
SupportedLanguage? language = GetFileLanguage(fileName);
if (language != null) {
using(ICSharpCode.NRefactory.IParser parser = ParserFactory.CreateParser(language.Value, new StringReader(result.Expression))) {
if (parser != null) {
expr = parser.ParseExpression();
}
}
}
// The resolve routine needs the member which contains the actual member being referenced.
// If a complete expression is given, the expression needs to be reduced to
// the member reference.
while (result.Expression != null && result.Expression.Length > 0) {
if ((rrr = TryResolve(result, expr, caretLine, caretColumn, fileName, document.TextContent)) != null) {
break;
}
result.Expression = ef.RemoveLastPart(result.Expression);
}
return rrr;
} else {
#if DEBUG
LoggingService.Debug("ResourceToolkit: NRefactoryResourceResolver could not find an expression");
#endif
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Tries to resolve the resource reference using all available
/// NRefactory resource resolvers.
/// </summary>
static ResourceResolveResult TryResolve(ExpressionResult result, Expression expr, int caretLine, int caretColumn, string fileName, string fileContent)
{
#if DEBUG
LoggingService.Debug("ResourceToolkit: NRefactoryResourceResolver trying to resolve expression: "+result.ToString());
#endif
ResolveResult rr = ParserService.Resolve(result, caretLine, caretColumn, fileName, fileContent);
if (rr != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: NRefactoryResourceResolver: The expression resolved to: "+rr.ToString());
#endif
ResourceResolveResult rrr;
foreach (INRefactoryResourceResolver resolver in Resolvers) {
if ((rrr = resolver.Resolve(result, expr, rr, caretLine, caretColumn, fileName, fileContent)) != null) {
return rrr;
}
}
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Determines the file which contains the resources referenced by the specified manifest resource name.
/// </summary>
/// <param name="sourceFileName">The name of the source code file which the reference occurs in.</param>
/// <param name="resourceName">The manifest resource name to find the resource file for.</param>
/// <returns>The name of the file that contains the resources with the specified manifest resource name, or <c>null</c> if the file name cannot be determined.</returns>
/// <exception cref="ArgumentNullException">The <paramref name="resourceName"/> parameter is <c>null</c>.</exception>
public static string GetResourceFileNameByResourceName(string sourceFileName, string resourceName)
{
if (resourceName == null) {
throw new ArgumentNullException("resourceName");
}
IProject p = ProjectFileDictionaryService.GetProjectForFile(sourceFileName);
if (p != null) {
string fileName = null;
if (resourceName.StartsWith(p.RootNamespace, StringComparison.InvariantCultureIgnoreCase)) {
// Look for a resource file in the project with the exact name.
if ((fileName = FindResourceFileName(Path.Combine(p.Directory, resourceName.Substring(p.RootNamespace.Length+1).Replace('.', Path.DirectorySeparatorChar)))) != null) {
return fileName;
}
}
// SharpDevelop silently strips the (hard-coded) folder names
// "src" and "source" when generating the default namespace name
// for new files.
// When MSBuild generates the manifest resource names for the
// forms designer resources, it uses the type name of the
// first class in the file. So we should find all files
// that contain a type with the name in resourceName
// and then look for dependent resource files or resource files
// with the same name in the same directory as the source files.
// Find all source files that contain a type with the same
// name as the resource we are looking for.
List<string> possibleSourceFiles = new List<string>();
IProjectContent pc = ParserService.GetProjectContent(p);
if (pc != null) {
IClass resourceClass = pc.GetClass(resourceName);
if (resourceClass != null) {
CompoundClass cc = resourceClass.GetCompoundClass() as CompoundClass;
foreach (IClass c in (cc == null ? new IClass[] { resourceClass } : (IEnumerable<IClass>)cc.Parts)) {
if (c.CompilationUnit != null && c.CompilationUnit.FileName != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: NRefactoryResourceResolver found file '"+c.CompilationUnit.FileName+"' to contain the type '"+resourceName+"'");
#endif
possibleSourceFiles.Add(c.CompilationUnit.FileName);
}
}
}
}
foreach (string possibleSourceFile in possibleSourceFiles) {
string possibleSourceFileName = Path.GetFileName(possibleSourceFile);
// Find resource files dependent on these source files.
foreach (ProjectItem pi in p.Items) {
FileProjectItem fpi = pi as FileProjectItem;
if (fpi != null) {
if (fpi.DependentUpon != null &&
(fpi.ItemType == ItemType.EmbeddedResource || fpi.ItemType == ItemType.Resource || fpi.ItemType == ItemType.None) &&
FileUtility.IsEqualFileName(fpi.DependentUpon, possibleSourceFileName)) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: NRefactoryResourceResolver trying to use dependent file '"+fpi.FileName+"' as resource file");
#endif
if ((fileName = FindResourceFileName(fpi.FileName)) != null) {
return fileName;
}
}
}
}
// Find resource files with the same name as the source file
// and in the same directory.
if ((fileName = FindResourceFileName(possibleSourceFile)) != null) {
return fileName;
}
}
} else {
LoggingService.Info("ResourceToolkit: NRefactoryResourceResolver.GetResourceFileNameByResourceName could not determine the project for the source file '"+(sourceFileName ?? "<null>")+"'.");
if (sourceFileName != null) {
// The project could not be determined.
// Try a simple file search.
string directory = Path.GetDirectoryName(sourceFileName);
string resourcePart = resourceName;
string fileName;
while (true) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: NRefactoryResourceResolver.GetResourceFileNameByResourceName: looking for a resource file like '"+Path.Combine(directory, resourcePart)+"'");
#endif
if ((fileName = FindResourceFileName(Path.Combine(directory, resourcePart.Replace('.', Path.DirectorySeparatorChar)))) != null) {
return fileName;
}
if ((fileName = FindResourceFileName(Path.Combine(directory, resourcePart))) != null) {
return fileName;
}
if (resourcePart.Contains(".")) {
resourcePart = resourcePart.Substring(resourcePart.IndexOf('.')+1);
} else {
break;
}
}
}
}
LoggingService.Info("ResourceToolkit: NRefactoryResourceResolver.GetResourceFileNameByResourceName is unable to find a suitable resource file for '"+resourceName+"'");
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Gets the NRefactory language for the specified file name.
/// </summary>
public static SupportedLanguage? GetFileLanguage(string fileName)
{
string ext = Path.GetExtension(fileName);
if (ext.Equals(".cs", StringComparison.InvariantCultureIgnoreCase))
return SupportedLanguage.CSharp;
if (ext.Equals(".vb", StringComparison.InvariantCultureIgnoreCase))
return SupportedLanguage.VBNet;
return null;
}
/// <summary>
/// Gets the language properties for the project the specified member
/// belongs to.
/// Returns <c>null</c> if the language cannot be determined.
/// </summary>
public static LanguageProperties GetLanguagePropertiesForMember(IMember member)
{
if (member == null) {
return null;
}
if (member.DeclaringType == null) {
return null;
}
if (member.DeclaringType.CompilationUnit == null) {
return null;
}
if (member.DeclaringType.CompilationUnit.ProjectContent == null) {
return null;
}
return member.DeclaringType.CompilationUnit.ProjectContent.Language;
}
/// <summary>
/// Gets the language properties for the specified file.
/// </summary>
/// <param name="fileName">The file to get the language properties for.</param>
/// <returns>The language properties of the specified file, or <c>null</c> if the language cannot be determined.</returns>
public static LanguageProperties GetLanguagePropertiesForFile(string fileName)
{
ICSharpCode.SharpDevelop.Dom.IParser p = ParserService.GetParser(fileName);
if (p == null) {
return null;
}
return p.Language;
}
}
}

1144
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NodeTrackingAstVisitor.cs

File diff suppressed because it is too large Load Diff

142
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/PositionTrackingAstVisitor.cs

@ -0,0 +1,142 @@ @@ -0,0 +1,142 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Drawing;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Provides contextual position information while iterating through
/// the AST and the ability to resolve expressions in-place.
/// </summary>
public abstract class PositionTrackingAstVisitor : NodeTrackingAstVisitor
{
private Stack<INode> parentNodes;
protected override void BeginVisit(INode node)
{
base.BeginVisit(node);
// Only push nodes on the stack which have valid position information.
if (node != null &&
node.StartLocation.X >= 1 && node.StartLocation.Y >= 1 &&
node.EndLocation.X >= 1 && node.EndLocation.Y >= 1) {
this.parentNodes.Push(node);
}
}
protected override void EndVisit(INode node)
{
base.EndVisit(node);
// Only remove those nodes which have actually been pushed before.
if (this.parentNodes.Count > 0 && INode.ReferenceEquals(this.parentNodes.Peek(), node)) {
this.parentNodes.Pop();
}
}
// ********************************************************************************************************************************
/// <summary>
/// Gets a flag that indicates whether the current node is located
/// inside a block which position information is available for.
/// </summary>
protected bool PositionAvailable {
get {
return this.parentNodes.Count > 0;
}
}
/// <summary>
/// Gets the start location of the current innermost node with valid position information
/// as 1-based coordinates in the parsed document.
/// X = column number, Y = line number.
/// </summary>
protected Location CurrentNodeStartLocation {
get {
return this.parentNodes.Peek().StartLocation;
}
}
/// <summary>
/// Gets the end location of the current innermost node with valid position information
/// as 1-based coordinates in the parsed document.
/// X = column number, Y = line number.
/// </summary>
protected Location CurrentNodeEndLocation {
get {
return this.parentNodes.Peek().EndLocation;
}
}
// ********************************************************************************************************************************
/// <summary>
/// Resolves an expression in the current node's context.
/// </summary>
/// <param name="expression">The expression to be resolved.</param>
/// <param name="memberInThisFile">Any member declared in the source file in question. Used to get the language, file name and file content.</param>
public ResolveResult Resolve(Expression expression, IMember memberInThisFile)
{
if (!this.PositionAvailable) {
LoggingService.Debug("ResourceToolkit: PositionTrackingAstVisitor: Resolve failed due to position information being unavailable. Expression: "+expression.ToString());
return null;
}
// In order to resolve expression, we need the original code.
// HACK: To get the code belonging to this expression, we pass it through the code generator.
// (Is there a better way?)
string code = null;
LanguageProperties lp = NRefactoryResourceResolver.GetLanguagePropertiesForMember(memberInThisFile);
if (lp != null && lp.CodeGenerator != null) {
code = lp.CodeGenerator.GenerateCode(expression, String.Empty);
}
if (!String.IsNullOrEmpty(code)) {
// Now resolve the expression in the current context.
#if DEBUG
LoggingService.Debug("ResourceToolkit: PositionTrackingAstVisitor: Resolving an expression, code (re-generated): '"+code+"'");
#endif
ResolveResult rr = ParserService.Resolve(new ExpressionResult(code, ExpressionContext.Default), this.CurrentNodeStartLocation.Y-1, this.CurrentNodeStartLocation.X, memberInThisFile.DeclaringType.CompilationUnit.FileName, ParserService.GetParseableFileContent(memberInThisFile.DeclaringType.CompilationUnit.FileName));
#if DEBUG
if (rr != null) {
LoggingService.Debug("ResourceToolkit: PositionTrackingAstVisitor: The expression resolved to: "+rr.ToString());
} else {
LoggingService.Debug("ResourceToolkit: PositionTrackingAstVisitor: The expression could not be resolved.");
}
#endif
return rr;
} else {
LoggingService.Debug("ResourceToolkit: PositionTrackingAstVisitor could not re-generate code for the expression: "+expression.ToString());
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="PositionTrackingAstVisitor"/> class.
/// </summary>
protected PositionTrackingAstVisitor() : base()
{
this.parentNodes = new Stack<INode>();
}
}
}

242
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/PropertyFieldAssociationVisitor.cs

@ -0,0 +1,242 @@ @@ -0,0 +1,242 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Finds connections between fields and properties.
/// TODO: This currently only works if the field and the property are declared in the same source file.
/// </summary>
public class PropertyFieldAssociationVisitor : PositionTrackingAstVisitor
{
IMember memberToFind;
IMember associatedMember;
/// <summary>
/// Gets the field that has been found to be associated with the property specified at the constructor call.
/// </summary>
public IField AssociatedField {
get {
return this.associatedMember as IField;
}
}
/// <summary>
/// Gets the property that has been found to be associated with the field specified at the constructor call.
/// </summary>
public IProperty AssociatedProperty {
get {
return this.associatedMember as IProperty;
}
}
// ********************************************************************************************************************************
private enum VisitorContext
{
Default,
PropertyGetRegion,
PropertySetRegion
}
private VisitorContext currentContext = VisitorContext.Default;
protected override void BeginVisit(INode node)
{
base.BeginVisit(node);
if (node is PropertyGetRegion) {
this.currentContext = VisitorContext.PropertyGetRegion;
} else if (node is PropertySetRegion) {
this.currentContext = VisitorContext.PropertySetRegion;
}
}
protected override void EndVisit(INode node)
{
if (node is PropertyGetRegion || node is PropertySetRegion) {
this.currentContext = VisitorContext.Default;
}
base.EndVisit(node);
}
public override object TrackedVisit(PropertyDeclaration propertyDeclaration, object data)
{
if (this.memberToFind is IProperty) {
// If we are looking for a specified property:
// find out if this is the one by comparing the location.
if (propertyDeclaration.StartLocation.X == this.memberToFind.Region.BeginColumn &&
propertyDeclaration.StartLocation.Y == this.memberToFind.Region.BeginLine) {
data = true;
}
} else if (this.memberToFind is IField) {
// If we are looking for a specifield field:
// store the property info for future reference.
data = propertyDeclaration;
}
return base.TrackedVisit(propertyDeclaration, data);
}
public override object TrackedVisit(ReturnStatement returnStatement, object data)
{
// If we are in a property get region,
// this may be the statement where the field value is returned.
if (this.associatedMember == null && // skip if already found to improve performance
this.currentContext == VisitorContext.PropertyGetRegion && data != null) {
// Resolve the expression.
MemberResolveResult mrr = this.Resolve(returnStatement.Expression, this.memberToFind) as MemberResolveResult;
if (mrr != null && mrr.ResolvedMember is IField) {
PropertyDeclaration pd;
#if DEBUG
LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertyGetRegion, resolved field: "+mrr.ResolvedMember.ToString());
#endif
if (data as bool? ?? false) {
// We are looking for this property.
#if DEBUG
LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertyGetRegion, this property seems to reference field "+mrr.ResolvedMember.ToString());
#endif
this.associatedMember = mrr.ResolvedMember;
} else if ((pd = (data as PropertyDeclaration)) != null) {
// We are looking for the field in this.memberToFind.
if (this.memberToFind.CompareTo(mrr.ResolvedMember) == 0) {
// Resolve the property.
MemberResolveResult prr = ParserService.Resolve(new ExpressionResult(pd.Name, ExpressionContext.Default), pd.StartLocation.Y-1, pd.StartLocation.X, this.memberToFind.DeclaringType.CompilationUnit.FileName, ParserService.GetParseableFileContent(this.memberToFind.DeclaringType.CompilationUnit.FileName)) as MemberResolveResult;
if (prr != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertyGetRegion, resolved property: "+prr.ResolvedMember.ToString());
#endif
if (prr.ResolvedMember is IProperty) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertyGetRegion, property "+prr.ResolvedMember.ToString()+" seems to reference field "+mrr.ResolvedMember.ToString());
#endif
this.associatedMember = prr.ResolvedMember;
}
}
}
}
}
}
return base.TrackedVisit(returnStatement, data);
}
public override object TrackedVisit(AssignmentExpression assignmentExpression, object data)
{
// If we are in a property set region,
// this may be the statement where the field value is assigned.
if (this.associatedMember == null && // skip if already found to improve performance
this.currentContext == VisitorContext.PropertySetRegion &&
assignmentExpression.Op == AssignmentOperatorType.Assign && data != null) {
// Resolve the expression.
MemberResolveResult mrr = this.Resolve(assignmentExpression.Left, this.memberToFind) as MemberResolveResult;
if (mrr != null && mrr.ResolvedMember is IField && !((IField)mrr.ResolvedMember).IsLocalVariable) {
PropertyDeclaration pd;
#if DEBUG
LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertySetRegion, resolved field: "+mrr.ResolvedMember.ToString());
#endif
if (data as bool? ?? false) {
// We are looking for this property.
#if DEBUG
LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertySetRegion, this property seems to reference field "+mrr.ResolvedMember.ToString());
#endif
this.associatedMember = mrr.ResolvedMember;
} else if ((pd = (data as PropertyDeclaration)) != null) {
// We are looking for the field in this.memberToFind.
if (this.memberToFind.CompareTo(mrr.ResolvedMember) == 0) {
// Resolve the property.
MemberResolveResult prr = ParserService.Resolve(new ExpressionResult(pd.Name, ExpressionContext.Default), pd.StartLocation.Y-1, pd.StartLocation.X, this.memberToFind.DeclaringType.CompilationUnit.FileName, ParserService.GetParseableFileContent(this.memberToFind.DeclaringType.CompilationUnit.FileName)) as MemberResolveResult;
if (prr != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertySetRegion, resolved property: "+prr.ResolvedMember.ToString());
#endif
if (prr.ResolvedMember is IProperty) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: PropertyFieldAssociationVisitor, inside PropertySetRegion, property "+prr.ResolvedMember.ToString()+" seems to reference field "+mrr.ResolvedMember.ToString());
#endif
this.associatedMember = prr.ResolvedMember;
}
}
}
}
}
}
return base.TrackedVisit(assignmentExpression, data);
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="PropertyFieldAssociationVisitor"/> class.
/// </summary>
/// <param name="property">The property to find the associated field for.</param>
public PropertyFieldAssociationVisitor(IProperty property) : base()
{
if (property == null) {
throw new ArgumentNullException("property");
}
this.memberToFind = property;
}
/// <summary>
/// Initializes a new instance of the <see cref="PropertyFieldAssociationVisitor"/> class.
/// </summary>
/// <param name="field">The field to find the associated property for.</param>
public PropertyFieldAssociationVisitor(IField field) : base()
{
if (field == null) {
throw new ArgumentNullException("field");
} else if (field.IsLocalVariable) {
throw new ArgumentException("The specified IField must not be a local variable.", "field");
}
this.memberToFind = field;
}
}
}

80
src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ResourceResolveResult.cs

@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.SharpDevelop.Dom;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit.Resolver
{
/// <summary>
/// Describes a reference to a resource.
/// </summary>
public class ResourceResolveResult : ResolveResult
{
IResourceFileContent resourceFileContent;
string key;
/// <summary>
/// Gets the <see cref="IResourceFileContent" /> of the resource being referenced.
/// </summary>
public IResourceFileContent ResourceFileContent {
get {
return this.resourceFileContent;
}
}
/// <summary>
/// Gets the resource key being referenced. May be null if the key is unknown/not yet typed.
/// </summary>
public string Key {
get {
return this.key;
}
}
/// <summary>
/// Gets the resource file name that contains the resource being referenced.
/// Only valid if both <see cref="ResourceFileContent"/> and <see cref="Key"/> are not <c>null</c>.
/// </summary>
public string FileName {
get {
if (this.ResourceFileContent == null || this.Key == null) {
return null;
}
IMultiResourceFileContent mrfc = this.ResourceFileContent as IMultiResourceFileContent;
if (mrfc != null) {
return mrfc.GetFileNameForKey(this.Key);
} else {
return this.ResourceFileContent.FileName;
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ResourceResolveResult"/> class.
/// </summary>
/// <param name="callingClass">The class that contains the reference to the resource.</param>
/// <param name="callingMember">The member that contains the reference to the resource.</param>
/// <param name="returnType">The type of the resource being referenced.</param>
/// <param name="resourceFileContent">The <see cref="IResourceFileContent"/> that contains the resource being referenced.</param>
/// <param name="key">The resource key being referenced.</param>
public ResourceResolveResult(IClass callingClass, IMember callingMember, IReturnType returnType, IResourceFileContent resourceFileContent, string key)
: base(callingClass, callingMember, returnType)
{
this.resourceFileContent = resourceFileContent;
this.key = key;
}
}
}

63
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/DefaultBclResourceFileContentFactory.cs

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.IO;
namespace Hornung.ResourceToolkit.ResourceFileContent
{
/// <summary>
/// Creates resource file contents for .resources and .resx files.
/// </summary>
public class DefaultBclResourceFileContentFactory : IResourceFileContentFactory
{
/// <summary>
/// Determines whether this factory can create a resource file content
/// for the specified file.
/// </summary>
/// <param name="fileName">The file name to examine.</param>
/// <returns><c>true</c>, if this factory can create a resource file content for the specified file, otherwise <c>false</c>.</returns>
public bool CanCreateContentForFile(string fileName)
{
string ext = Path.GetExtension(fileName);
if (ext.Equals(".resources", StringComparison.OrdinalIgnoreCase) ||
ext.Equals(".resx", StringComparison.OrdinalIgnoreCase)) {
return true;
}
return false;
}
/// <summary>
/// Creates a resource file content for the specified file.
/// </summary>
/// <param name="fileName">The name of the file to create the resource file content for.</param>
/// <returns>A new instance of a class that implements <see cref="IResourceFileContent"/> and represents the content of the specified file, or <c>null</c>, if this class cannot handle the file format.</returns>
public IResourceFileContent CreateContentForFile(string fileName)
{
string ext = Path.GetExtension(fileName);
if (ext.Equals(".resources", StringComparison.OrdinalIgnoreCase)) {
return new ResourcesResourceFileContent(fileName);
} else if (ext.Equals(".resx", StringComparison.OrdinalIgnoreCase)) {
return new ResXResourceFileContent(fileName);
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="DefaultBclResourceFileContentFactory"/> class.
/// </summary>
public DefaultBclResourceFileContentFactory()
{
}
}
}

109
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/DefaultFileLocalizedResourcesFinder.cs

@ -0,0 +1,109 @@ @@ -0,0 +1,109 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using ICSharpCode.Core;
namespace Hornung.ResourceToolkit.ResourceFileContent
{
/// <summary>
/// Finds localized resources that follow the standard .NET pattern
/// MyResources.(culture).(extension).
/// </summary>
public class DefaultFileLocalizedResourcesFinder : ILocalizedResourcesFinder
{
/// <summary>
/// Gets localized resources that belong to the master resource file.
/// </summary>
/// <param name="fileName">The name of the master resource file.</param>
/// <returns>A dictionary of culture names and associated resource file contents, or <c>null</c>, if there are none.</returns>
public IDictionary<string, IResourceFileContent> GetLocalizedContents(string fileName)
{
#if DEBUG
LoggingService.Debug("ResourceToolkit: DefaultFileLocalizedResourcesFinder.GetLocalizedContents called, fileName: '"+fileName+"'");
#endif
string fileNameWithoutExtension = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName));
string culture = Path.GetExtension(fileNameWithoutExtension);
if (!String.IsNullOrEmpty(culture)) {
try {
CultureInfo dummy = CultureInfo.GetCultureInfo(culture);
// the specified file is a localized resource file itself
LoggingService.Debug("ResourceToolkit: DefaultFileLocalizedResourcesFinder.GetLocalizedContents: Returning null for file '"+fileName+"' because it has been detected as being a localized resource file itself.");
return null;
} catch (ArgumentException) {
}
}
return FindLocalizedResourceFiles(fileNameWithoutExtension, Path.GetExtension(fileName));
}
/// <summary>
/// Finds all localized resource files that follow the pattern
/// &lt;<paramref name="fileNameWithoutExtension"/>&gt;.&lt;culture&gt;&lt;<paramref name="extension"/>&gt;.
/// </summary>
/// <param name="fileNameWithoutExtension">The full path and name of the master resource file without extension.</param>
/// <param name="extension">The extension of the master resource file (with leading dot).</param>
/// <returns>A dictionary of culture names and associated resource file contents.</returns>
public static IDictionary<string, IResourceFileContent> FindLocalizedResourceFiles(string fileNameWithoutExtension, string extension)
{
Dictionary<string, IResourceFileContent> list = new Dictionary<string, IResourceFileContent>();
#if DEBUG
LoggingService.Debug("ResourceToolkit: Finding localized resource files (DefaultFileLocalizedResourcesFinder.FindLocalizedResourceFiles).");
LoggingService.Debug(" -> fileNameWithoutExtension: '"+fileNameWithoutExtension+"'");
LoggingService.Debug(" -> extension: '"+extension+"'");
#endif
foreach (string fileName in Directory.GetFiles(Path.GetDirectoryName(fileNameWithoutExtension), Path.GetFileName(fileNameWithoutExtension)+".*"+extension)) {
#if DEBUG
LoggingService.Debug(" -> possible file: '"+fileName+"'");
#endif
string culture = Path.GetExtension(Path.GetFileNameWithoutExtension(fileName));
#if DEBUG
LoggingService.Debug(" -> culture = '"+(culture ?? "<null>")+"'");
#endif
if (!String.IsNullOrEmpty(culture)) {
try {
CultureInfo ci = CultureInfo.GetCultureInfo(culture.Substring(1)); // need to remove leading dot from culture
IResourceFileContent content = ResourceFileContentRegistry.GetResourceFileContent(fileName);
if (content != null) {
#if DEBUG
LoggingService.Debug(" -> culture name: '"+ci.Name+"'");
#endif
list.Add(ci.Name, content);
}
} catch (ArgumentException) {
continue;
}
}
}
return list;
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes a new instance of the <see cref="DefaultFileLocalizedResourcesFinder"/> class.
/// </summary>
public DefaultFileLocalizedResourcesFinder()
{
}
}
}

26
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ILocalizedResourcesFinder.cs

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
namespace Hornung.ResourceToolkit.ResourceFileContent
{
/// <summary>
/// Describes an object that can find localized resources that belong to a
/// master resource.
/// </summary>
public interface ILocalizedResourcesFinder
{
/// <summary>
/// Gets localized resources that belong to the master resource file.
/// </summary>
/// <param name="fileName">The name of the master resource file.</param>
/// <returns>A dictionary of culture names and associated resource file contents, or <c>null</c>, if there are none.</returns>
IDictionary<string, IResourceFileContent> GetLocalizedContents(string fileName);
}
}

26
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IMultiResourceFileContent.cs

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Hornung.ResourceToolkit.ResourceFileContent
{
/// <summary>
/// Describes the content of multiple resource files.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
public interface IMultiResourceFileContent : IResourceFileContent
{
/// <summary>
/// Gets the file name of the resource file the specified key is in.
/// </summary>
/// <returns>The name of the resource file the specified key is in, or <c>null</c> if the key cannot be found in any resource file this instance represents.</returns>
string GetFileNameForKey(string key);
}
}

82
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IResourceFileContent.cs

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Hornung.ResourceToolkit.ResourceFileContent
{
/// <summary>
/// Describes the content of a resource file.
/// </summary>
public interface IResourceFileContent
{
/// <summary>
/// Gets the file name of the resource file this instance represents.
/// If this instance represents multiple resource files, it's the name
/// of the resource file which new entries are added to.
/// </summary>
string FileName {
get;
}
/// <summary>
/// Gets the culture of the resource file this instance represents.
/// </summary>
CultureInfo Culture {
get;
}
/// <summary>
/// Gets an iterator that can be used to iterate over all key/value pairs in this resource.
/// </summary>
IEnumerable<KeyValuePair<string, object>> Data {
get;
}
/// <summary>
/// Determines if the resource file this instance represents contains the specified key.
/// </summary>
bool ContainsKey(string key);
/// <summary>
/// Tries to get the value of the resource with the specified key.
/// </summary>
/// <returns><c>true</c>, if the key exists, otherwise <c>false</c>.</returns>
bool TryGetValue(string key, out object value);
/// <summary>
/// Adds a new key to the resource file.
/// </summary>
/// <exception cref="ArgumentException">A key with the same name already exists.</exception>
void Add(string key, object value);
/// <summary>
/// Modify the value of an existing entry.
/// </summary>
/// <exception cref="ArgumentException">The specified key does not exist.</exception>
void SetValue(string key, object value);
/// <summary>
/// Renames a resource key.
/// </summary>
/// <param name="oldName">The old name of the resource key to rename.</param>
/// <param name="newName">The new name of the resource key.</param>
/// <exception cref="ArgumentException">The specified key does not exist.</exception>
void RenameKey(string oldName, string newName);
/// <summary>
/// Removes the specified resource key permanently.
/// </summary>
/// <param name="key">The resource key to remove.</param>
/// <exception cref="ArgumentException">The specified key does not exist.</exception>
void RemoveKey(string key);
}
}

33
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IResourceFileContentFactory.cs

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Hornung.ResourceToolkit.ResourceFileContent
{
/// <summary>
/// Describes an object that can create instances of classes that
/// implement <see cref="IResourceFileContent"/>.
/// </summary>
public interface IResourceFileContentFactory
{
/// <summary>
/// Determines whether this factory can create a resource file content
/// for the specified file.
/// </summary>
/// <param name="fileName">The file name to examine.</param>
/// <returns><c>true</c>, if this factory can create a resource file content for the specified file, otherwise <c>false</c>.</returns>
bool CanCreateContentForFile(string fileName);
/// <summary>
/// Creates a resource file content for the specified file.
/// </summary>
/// <param name="fileName">The name of the file to create the resource file content for.</param>
/// <returns>A new instance of a class that implements <see cref="IResourceFileContent"/> and represents the content of the specified file, or <c>null</c>, if this class cannot handle the file format.</returns>
IResourceFileContent CreateContentForFile(string fileName);
}
}

213
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/MergedResourceFileContent.cs

@ -0,0 +1,213 @@ @@ -0,0 +1,213 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Hornung.ResourceToolkit.ResourceFileContent
{
/// <summary>
/// Makes multiple <see cref="IResourceFileContent" /> implementations accessible
/// through a single one. All contents are merged into a single dictionary.
/// When adding new entries, they are added to the master content.
/// </summary>
public class MergedResourceFileContent : IMultiResourceFileContent
{
IResourceFileContent masterContent;
IResourceFileContent[] otherContents;
/// <summary>
/// Initializes a new instance of the <see cref="MergedResourceFileContent" /> class.
/// </summary>
/// <param name="masterContent">The master resource file content.</param>
/// <param name="otherContents">Additional resource file contents.</param>
/// <exception cref="ArgumentException">The cultures of the specified resource file contents do not match.</exception>
public MergedResourceFileContent(IResourceFileContent masterContent, IResourceFileContent[] otherContents)
{
this.masterContent = masterContent;
this.otherContents = otherContents;
// Ensure that all contents are for the same culture
foreach (IResourceFileContent c in this.otherContents) {
if (!c.Culture.Equals(this.masterContent.Culture)) {
throw new ArgumentException("The cultures of the specified resource file contents do not match.");
}
}
}
// ********************************************************************************************************************************
/// <summary>
/// Gets the file name of the master resource file.
/// </summary>
public string FileName {
get {
return this.masterContent.FileName;
}
}
/// <summary>
/// Gets the culture of the resource files this instance represents.
/// </summary>
public CultureInfo Culture {
get {
return this.masterContent.Culture;
}
}
/// <summary>
/// Gets an iterator that can be used to iterate over all key/value pairs in all resource files this instance represents.
/// </summary>
public IEnumerable<KeyValuePair<string, object>> Data {
get {
foreach (KeyValuePair<string, object> entry in this.masterContent.Data) {
yield return entry;
}
foreach (IResourceFileContent c in this.otherContents) {
foreach (KeyValuePair<string, object> entry in c.Data) {
yield return entry;
}
}
}
}
/// <summary>
/// Determines if any of the resource files this instance represents contains the specified key.
/// </summary>
public bool ContainsKey(string key)
{
if (this.masterContent.ContainsKey(key)) {
return true;
}
foreach (IResourceFileContent c in this.otherContents) {
if (c.ContainsKey(key)) {
return true;
}
}
return false;
}
/// <summary>
/// Tries to get the value of the resource with the specified key.
/// </summary>
/// <returns><c>true</c>, if the key exists, otherwise <c>false</c>.</returns>
public bool TryGetValue(string key, out object value)
{
if (this.masterContent.TryGetValue(key, out value)) {
return true;
}
foreach (IResourceFileContent c in this.otherContents) {
if (c.TryGetValue(key, out value)) {
return true;
}
}
return false;
}
/// <summary>
/// Adds a new key to the master resource file.
/// </summary>
/// <exception cref="ArgumentException">A key with the same name already exists.</exception>
public void Add(string key, object value)
{
this.masterContent.Add(key, value);
}
/// <summary>
/// Modify the value of an existing entry.
/// </summary>
/// <exception cref="ArgumentException">The specified key does not exist.</exception>
public void SetValue(string key, object value)
{
if (this.masterContent.ContainsKey(key)) {
this.masterContent.SetValue(key, value);
return;
} else {
foreach (IResourceFileContent c in this.otherContents) {
if (c.ContainsKey(key)) {
c.SetValue(key, value);
return;
}
}
}
throw new ArgumentException("The key '"+key+"' does not exist.", "key");
}
/// <summary>
/// Renames a resource key.
/// </summary>
/// <param name="oldName">The old name of the resource key to rename.</param>
/// <param name="newName">The new name of the resource key.</param>
/// <exception cref="ArgumentException">The specified key does not exist or the new key does already exist.</exception>
public void RenameKey(string oldName, string newName)
{
if (this.masterContent.ContainsKey(oldName)) {
this.masterContent.RenameKey(oldName, newName);
return;
} else {
foreach (IResourceFileContent c in this.otherContents) {
if (c.ContainsKey(oldName)) {
c.RenameKey(oldName, newName);
return;
}
}
}
throw new ArgumentException("The key '"+oldName+"' does not exist.", "oldName");
}
/// <summary>
/// Removes the specified resource key permanently.
/// </summary>
/// <param name="key">The resource key to remove.</param>
/// <exception cref="ArgumentException">The specified key does not exist.</exception>
public void RemoveKey(string key)
{
if (this.masterContent.ContainsKey(key)) {
this.masterContent.RemoveKey(key);
return;
} else {
foreach (IResourceFileContent c in this.otherContents) {
if (c.ContainsKey(key)) {
c.RemoveKey(key);
return;
}
}
}
throw new ArgumentException("The key '"+key+"' does not exist.", "key");
}
/// <summary>
/// Gets the file name of the resource file the specified key is in.
/// </summary>
/// <returns>The name of the resource file the specified key is in, or <c>null</c> if the key cannot be found in any resource file this instance represents.</returns>
public string GetFileNameForKey(string key)
{
string fileName;
IMultiResourceFileContent mrfc;
if ((mrfc = (this.masterContent as IMultiResourceFileContent)) != null) {
if ((fileName = mrfc.GetFileNameForKey(key)) != null) {
return fileName;
}
} else if (this.masterContent.ContainsKey(key)) {
return this.masterContent.FileName;
}
foreach (IResourceFileContent c in this.otherContents) {
if ((mrfc = (c as IMultiResourceFileContent)) != null) {
if ((fileName = mrfc.GetFileNameForKey(key)) != null) {
return fileName;
}
} else if (c.ContainsKey(key)) {
return c.FileName;
}
}
return null;
}
}
}

89
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResXResourceFileContent.cs

@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections;
using System.Collections.Generic;
using System.Resources;
namespace Hornung.ResourceToolkit.ResourceFileContent
{
/// <summary>
/// Describes the content of a .resx resource file.
/// </summary>
public class ResXResourceFileContent : ResourcesResourceFileContent
{
Dictionary<string, object> metadata;
/// <summary>
/// Initializes a new instance of the <see cref="ResXResourceFileContent" /> class.
/// </summary>
/// <param name="fileName">The file name of the resource file this instance represents.</param>
public ResXResourceFileContent(string fileName) : base(fileName)
{
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes all instance fields in preparation for loading of the file content.
/// </summary>
protected override void InitializeContent()
{
base.InitializeContent();
this.metadata = new Dictionary<string, object>();
}
/// <summary>
/// Loads the content of the specified <see cref="IResourceReader" /> into the cache.
/// </summary>
/// <param name="reader">The <see cref="IResourceReader" /> to be used to read the resource content.</param>
protected override void LoadContent(IResourceReader reader)
{
base.LoadContent(reader);
IDictionaryEnumerator en = ((ResXResourceReader)reader).GetMetadataEnumerator();
while (en.MoveNext()) {
this.metadata.Add((string)en.Key, en.Value);
}
}
/// <summary>
/// Gets a resx resource reader for the resource file represented by this instance.
/// </summary>
/// <returns>A resx resource reader for the resource file represented by this instance.</returns>
protected override IResourceReader GetResourceReader()
{
return new ResXResourceReader(this.FileName);
}
// ********************************************************************************************************************************
/// <summary>
/// Save changes done to the resource content to disk.
/// </summary>
/// <param name="writer">The <see cref="IResourceWriter" /> to be used to save the resource content.</param>
protected override void SaveContent(IResourceWriter writer)
{
base.SaveContent(writer);
ResXResourceWriter w = (ResXResourceWriter)writer;
foreach (KeyValuePair<string, object> entry in this.metadata) {
w.AddMetadata(entry.Key, entry.Value);
}
}
/// <summary>
/// Gets a resx resource writer for the resource file represented by this instance.
/// </summary>
/// <returns>A resx resource writer for the resource file represented by this instance.</returns>
protected override IResourceWriter GetResourceWriter()
{
return new ResXResourceWriter(this.FileName);
}
}
}

120
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResourceFileContentRegistry.cs

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Core;
namespace Hornung.ResourceToolkit.ResourceFileContent
{
/// <summary>
/// Provides facilities to load and cache the contents of resource files.
/// </summary>
public static class ResourceFileContentRegistry
{
/// <summary>
/// The AddIn tree path where the resource file content factories are registered.
/// </summary>
public const string ResourceFileContentFactoriesAddInTreePath = "/AddIns/ResourceToolkit/ResourceFileContentFactories";
static List<IResourceFileContentFactory> factories;
/// <summary>
/// Gets a list of all registered resource file content factories.
/// </summary>
public static IEnumerable<IResourceFileContentFactory> Factories {
get {
if (factories == null) {
factories = AddInTree.BuildItems<IResourceFileContentFactory>(ResourceFileContentFactoriesAddInTreePath, null, false);
}
return factories;
}
}
static Dictionary<string, IResourceFileContent> resourceFileContents = new Dictionary<string, IResourceFileContent>();
/// <summary>
/// Gets the resource content for the specified file.
/// </summary>
/// <param name="fileName">The name of the file to get a resource content for.</param>
/// <returns>The resource content for the specified file.</returns>
/// <exception cref="NotSupportedException">The format of the specified resource file cannot be handled.</exception>
public static IResourceFileContent GetResourceFileContent(string fileName)
{
IResourceFileContent c;
if (!resourceFileContents.TryGetValue(fileName, out c)) {
c = CreateResourceFileContent(fileName);
if (c == null) {
throw new NotSupportedException("The format of the resource file '"+fileName+"' cannot be handled by any registered resource file content factory.");
}
resourceFileContents[fileName] = c;
}
return c;
}
/// <summary>
/// Creates the resource content for the specified file.
/// </summary>
/// <param name="fileName">The name of the file to create a resource content for.</param>
/// <returns>The resource content for the specified file, or <c>null</c>, if the resource file format cannot be handled.</returns>
static IResourceFileContent CreateResourceFileContent(string fileName)
{
foreach (IResourceFileContentFactory factory in Factories) {
if (factory.CanCreateContentForFile(fileName)) {
return factory.CreateContentForFile(fileName);
}
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// The AddIn tree path where the localized resource finders are registered.
/// </summary>
public const string LocalizedResourcesFindersAddInTreePath = "/AddIns/ResourceToolkit/LocalizedResourcesFinders";
static List<ILocalizedResourcesFinder> localizedResourcesFinders;
/// <summary>
/// Gets a list of all registered localized resources finders.
/// </summary>
public static IEnumerable<ILocalizedResourcesFinder> LocalizedResourcesFinders {
get {
if (localizedResourcesFinders == null) {
localizedResourcesFinders = AddInTree.BuildItems<ILocalizedResourcesFinder>(LocalizedResourcesFindersAddInTreePath, null, false);
}
return localizedResourcesFinders;
}
}
/// <summary>
/// Gets localized resources that belong to the master resource file.
/// </summary>
/// <param name="fileName">The name of the master resource file.</param>
/// <returns>A dictionary of culture names and associated resource file contents.</returns>
public static IDictionary<string, IResourceFileContent> GetLocalizedContents(string fileName)
{
Dictionary<string, IResourceFileContent> list = new Dictionary<string, IResourceFileContent>();
foreach (ILocalizedResourcesFinder finder in LocalizedResourcesFinders) {
IDictionary<string, IResourceFileContent> l = finder.GetLocalizedContents(fileName);
if (l != null) {
foreach (KeyValuePair<string, IResourceFileContent> entry in l) {
if (!list.ContainsKey(entry.Key)) {
list.Add(entry.Key, entry.Value);
}
}
}
}
return list;
}
}
}

270
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResourcesResourceFileContent.cs

@ -0,0 +1,270 @@ @@ -0,0 +1,270 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Resources;
using ICSharpCode.Core;
namespace Hornung.ResourceToolkit.ResourceFileContent
{
/// <summary>
/// Describes the content of a .resources resource file.
/// </summary>
public class ResourcesResourceFileContent : IResourceFileContent
{
readonly string fileName;
readonly CultureInfo culture;
DateTime lastWriteTimeUtc;
Dictionary<string, object> data;
/// <summary>
/// Gets the file name of the resource file this instance represents.
/// </summary>
public string FileName {
get {
return this.fileName;
}
}
/// <summary>
/// Gets the culture of the resource file this instance represents.
/// </summary>
public CultureInfo Culture {
get {
return this.culture;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ResourcesFileContent" /> class.
/// </summary>
/// <param name="fileName">The file name of the resource file this instance represents.</param>
public ResourcesResourceFileContent(string fileName)
{
this.fileName = fileName;
// Determine culture from file name
string cultureExt = Path.GetExtension(Path.GetFileNameWithoutExtension(fileName));
if (!String.IsNullOrEmpty(cultureExt)) {
try {
this.culture = CultureInfo.GetCultureInfo(cultureExt.Substring(1)); // need to remove leading dot from cultureExt
} catch (ArgumentException) {
this.culture = CultureInfo.InvariantCulture;
}
} else {
this.culture = CultureInfo.InvariantCulture;
}
#if DEBUG
LoggingService.Debug("ResourceToolkit: Created ResourceFileContent, file '"+fileName+"', culture name: '"+this.Culture.Name+"'");
#endif
}
/// <summary>
/// Synchronises the cache with the content of the actual file on disk.
/// </summary>
protected void EnsureLoaded()
{
if (this.data == null || File.GetLastWriteTimeUtc(this.FileName) != this.lastWriteTimeUtc) {
this.InitializeContent();
this.LoadContent();
this.lastWriteTimeUtc = File.GetLastWriteTimeUtc(this.FileName);
}
}
// ********************************************************************************************************************************
/// <summary>
/// Initializes all instance fields in preparation for loading of the file content.
/// </summary>
protected virtual void InitializeContent()
{
this.data = new Dictionary<string, object>();
}
/// <summary>
/// Loads the content of the specified <see cref="IResourceReader" /> into the cache.
/// </summary>
/// <param name="reader">The <see cref="IResourceReader" /> to be used to read the resource content.</param>
protected virtual void LoadContent(IResourceReader reader)
{
IDictionaryEnumerator en = reader.GetEnumerator();
while (en.MoveNext()) {
this.data.Add((string)en.Key, en.Value);
}
}
/// <summary>
/// Loads the content of the file into the cache.
/// </summary>
protected virtual void LoadContent()
{
using(IResourceReader reader = this.GetResourceReader()) {
if (reader != null) {
this.LoadContent(reader);
reader.Close();
}
}
}
/// <summary>
/// Gets a resource reader for the resource file represented by this instance.
/// </summary>
/// <returns>A resource reader for the resource file represented by this instance.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
protected virtual IResourceReader GetResourceReader()
{
return new ResourceReader(this.FileName);
}
// ********************************************************************************************************************************
/// <summary>
/// Save changes done to the resource content to disk.
/// </summary>
protected void Save()
{
this.SaveContent();
this.lastWriteTimeUtc = File.GetLastWriteTimeUtc(this.FileName);
}
/// <summary>
/// Save changes done to the resource content to disk.
/// </summary>
protected virtual void SaveContent()
{
using(IResourceWriter writer = this.GetResourceWriter()) {
if (writer != null) {
this.SaveContent(writer);
writer.Close();
}
}
}
/// <summary>
/// Save changes done to the resource content to disk.
/// </summary>
/// <param name="writer">The <see cref="IResourceWriter" /> to be used to save the resource content.</param>
protected virtual void SaveContent(IResourceWriter writer)
{
foreach (KeyValuePair<string, object> entry in this.data) {
writer.AddResource(entry.Key, entry.Value);
}
}
/// <summary>
/// Gets a resource writer for the resource file represented by this instance.
/// </summary>
/// <returns>A resource writer for the resource file represented by this instance.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
protected virtual IResourceWriter GetResourceWriter()
{
return new ResourceWriter(this.FileName);
}
// ********************************************************************************************************************************
/// <summary>
/// Gets an iterator that can be used to iterate over all key/value pairs in this resource.
/// </summary>
public IEnumerable<KeyValuePair<string, object>> Data {
get {
this.EnsureLoaded();
return this.data;
}
}
/// <summary>
/// Determines if the resource file this instance represents contains the specified key.
/// </summary>
public bool ContainsKey(string key)
{
this.EnsureLoaded();
return this.data.ContainsKey(key);
}
/// <summary>
/// Tries to get the value of the resource with the specified key.
/// </summary>
/// <returns><c>true</c>, if the key exists, otherwise <c>false</c>.</returns>
public bool TryGetValue(string key, out object value)
{
this.EnsureLoaded();
return this.data.TryGetValue(key, out value);
}
/// <summary>
/// Adds a new key to the resource file.
/// </summary>
/// <exception cref="ArgumentException">A key with the same name already exists.</exception>
public void Add(string key, object value)
{
this.EnsureLoaded();
if (this.data.ContainsKey(key)) {
throw new ArgumentException("A key with the name '"+key+"' already exists.", "key");
}
this.data.Add(key, value);
this.Save();
}
/// <summary>
/// Modify the value of an existing entry.
/// </summary>
/// <exception cref="ArgumentException">The specified key does not exist.</exception>
public void SetValue(string key, object value)
{
this.EnsureLoaded();
if (!this.data.ContainsKey(key)) {
throw new ArgumentException("The key '"+key+"' does not exist.", "key");
}
this.data[key] = value;
this.Save();
}
/// <summary>
/// Renames a resource key.
/// </summary>
/// <param name="oldName">The old name of the resource key to rename.</param>
/// <param name="newName">The new name of the resource key.</param>
/// <exception cref="ArgumentException">The specified key does not exist or the new key does already exist.</exception>
public void RenameKey(string oldName, string newName)
{
this.EnsureLoaded();
if (!this.data.ContainsKey(oldName)) {
throw new ArgumentException("The key '"+oldName+"' does not exist.", "oldName");
}
if (this.data.ContainsKey(newName)) {
throw new ArgumentException("The key '"+newName+"' already exists.", "newName");
}
this.data.Add(newName, this.data[oldName]);
this.data.Remove(oldName);
this.Save();
}
/// <summary>
/// Removes the specified resource key permanently.
/// </summary>
/// <param name="key">The resource key to remove.</param>
/// <exception cref="ArgumentException">The specified key does not exist.</exception>
public void RemoveKey(string key)
{
this.EnsureLoaded();
if (!this.data.ContainsKey(key)) {
throw new ArgumentException("The key '"+key+"' does not exist.", "key");
}
this.data.Remove(key);
this.Save();
}
}
}

136
src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceResolverService.cs

@ -0,0 +1,136 @@ @@ -0,0 +1,136 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Text;
using ICSharpCode.Core;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Document;
using Hornung.ResourceToolkit.Resolver;
using Hornung.ResourceToolkit.ResourceFileContent;
namespace Hornung.ResourceToolkit
{
/// <summary>
/// Provides facilities to find and resolve expressions referencing resources.
/// </summary>
public static class ResourceResolverService
{
/// <summary>
/// The AddIn tree path where the resource resolvers are registered.
/// </summary>
public const string ResourceResolversAddInTreePath = "/AddIns/ResourceToolkit/Resolvers";
// ********************************************************************************************************************************
static List<IResourceResolver> resolvers;
/// <summary>
/// Gets a list of all registered resource resolvers.
/// </summary>
public static IEnumerable<IResourceResolver> Resolvers {
get {
if (resolvers == null) {
resolvers = AddInTree.BuildItems<IResourceResolver>(ResourceResolversAddInTreePath, null, false);
}
return resolvers;
}
}
// ********************************************************************************************************************************
/// <summary>
/// Attempts to resolve a reference to a resource using all registered resolvers.
/// </summary>
/// <param name="editor">The text editor for which a resource resolution attempt should be performed.</param>
/// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the caret in the specified editor, or <c>null</c> if all registered resolvers return <c>null</c>.</returns>
public static ResourceResolveResult Resolve(TextEditorControl editor)
{
ResourceResolveResult result;
foreach (IResourceResolver resolver in Resolvers) {
if ((result = resolver.Resolve(editor)) != null) {
return result;
}
}
return null;
}
/// <summary>
/// Attempts to resolve a reference to a resource using all registered resolvers.
/// </summary>
/// <param name="fileName">The name of the file that contains the expression to be resolved.</param>
/// <param name="document">The document that contains the expression to be resolved.</param>
/// <param name="caretLine">The 0-based line in the file that contains the expression to be resolved.</param>
/// <param name="caretColumn">The 0-based column position of the expression to be resolved.</param>
/// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the caret in the specified editor, or <c>null</c> if all registered resolvers return <c>null</c>.</returns>
public static ResourceResolveResult Resolve(string fileName, IDocument document, int caretLine, int caretColumn)
{
ResourceResolveResult result;
foreach (IResourceResolver resolver in Resolvers) {
if ((result = resolver.Resolve(fileName, document, caretLine, caretColumn)) != null) {
return result;
}
}
return null;
}
// ********************************************************************************************************************************
/// <summary>
/// Builds the formatted description string for the specified resource.
/// </summary>
public static string FormatResourceDescription(IResourceFileContent content, string key)
{
StringBuilder sb = new StringBuilder();
IMultiResourceFileContent mrfc;
if (key != null && (mrfc = (content as IMultiResourceFileContent)) != null) {
string file = mrfc.GetFileNameForKey(key);
if (file == null) {
file = content.FileName;
}
sb.AppendFormat(StringParser.Parse("${res:Hornung.ResourceToolkit.ToolTips.PlaceMessage}"), file);
} else {
sb.AppendFormat(StringParser.Parse("${res:Hornung.ResourceToolkit.ToolTips.PlaceMessage}"), content.FileName);
}
sb.AppendLine();
sb.Append(StringParser.Parse("${res:Hornung.ResourceToolkit.KeyLabel}"));
sb.Append(' ');
if (key != null) {
sb.AppendLine(key);
sb.AppendLine();
sb.AppendLine(StringParser.Parse("${res:Hornung.ResourceToolkit.ValueLabel}"));
object value;
if (content.TryGetValue(key, out value)) {
if (value is string) {
sb.Append(value);
} else {
sb.AppendFormat(StringParser.Parse("${res:Hornung.ResourceToolkit.ToolTips.TypeMessage}"), value.GetType().ToString());
sb.Append(' ');
sb.Append(value.ToString());
}
} else {
sb.Append(StringParser.Parse("${res:Hornung.ResourceToolkit.ToolTips.KeyNotFound}"));
}
} else {
sb.Append(StringParser.Parse("${res:Hornung.ResourceToolkit.ToolTips.UnknownKey}"));
}
return sb.ToString();
}
}
}

48
src/AddIns/Misc/ResourceToolkit/Project/Src/ToolTips/ResourceToolTipProvider.cs

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
// <file>
// <copyright see="prj:///Doc/copyright.txt"/>
// <license see="prj:///Doc/license.txt"/>
// <owner name="Christian Hornung" email="c-hornung@gmx.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Drawing;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
using ICSharpCode.TextEditor;
using ICSharpCode.TextEditor.Document;
using Hornung.ResourceToolkit.Resolver;
namespace Hornung.ResourceToolkit.ToolTips
{
/// <summary>
/// Provides tooltips for resources.
/// </summary>
public class ResourceToolTipProvider : ITextAreaToolTipProvider
{
public ToolTipInfo GetToolTipInfo(TextArea textArea, ToolTipRequestEventArgs e)
{
Point logicPos = e.LogicalPosition;
IDocument doc = textArea.Document;
if (logicPos.X > doc.GetLineSegment(logicPos.Y).Length - 1) {
return null;
}
ResourceResolveResult result = ResourceResolverService.Resolve(textArea.MotherTextEditorControl.FileName, doc, logicPos.Y, logicPos.X);
if (result != null && result.ResourceFileContent != null) {
#if DEBUG
LoggingService.Debug("ResourceToolkit: Providing ToolTipInfo");
#endif
return new ToolTipInfo(ResourceResolverService.FormatResourceDescription(result.ResourceFileContent, result.Key));
}
return null;
}
}
}

2
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -744,4 +744,4 @@ @@ -744,4 +744,4 @@
<Folder Include="Src\Gui\Dialogs\SolutionConfiguration" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
</Project>
</Project>

BIN
src/Main/StartUp/Project/Resources/StringResources.resources

Binary file not shown.

9
src/SharpDevelop.sln

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
Microsoft Visual Studio Solution File, Format Version 9.00
# SharpDevelop 2.1.0.1793
# SharpDevelop 2.1.0.1865
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AddIns", "AddIns", "{14A277EE-7DF1-4529-B639-7D1EF334C1C5}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
@ -56,6 +56,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{CE5B42B7-6 @@ -56,6 +56,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{CE5B42B7-6
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceToolkit", "AddIns\Misc\ResourceToolkit\Project\ResourceToolkit.csproj", "{461606BD-E824-4D0A-8CBA-01810B1F5E02}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpDbTools", "AddIns\Misc\SharpServerTools\SharpDbTools\SharpDbTools.csproj", "{F2CC804D-5A9C-4875-A423-4FB9A5D07018}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpServerTools", "AddIns\Misc\SharpServerTools\SharpServerTools.csproj", "{E8922383-B6F5-4107-AB82-49662D98B2FE}"
@ -376,6 +378,10 @@ Global @@ -376,6 +378,10 @@ Global
{F2CC804D-5A9C-4875-A423-4FB9A5D07018}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F2CC804D-5A9C-4875-A423-4FB9A5D07018}.Release|Any CPU.Build.0 = Release|Any CPU
{F2CC804D-5A9C-4875-A423-4FB9A5D07018}.Release|Any CPU.ActiveCfg = Release|Any CPU
{461606BD-E824-4D0A-8CBA-01810B1F5E02}.Debug|Any CPU.Build.0 = Debug|Any CPU
{461606BD-E824-4D0A-8CBA-01810B1F5E02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{461606BD-E824-4D0A-8CBA-01810B1F5E02}.Release|Any CPU.Build.0 = Release|Any CPU
{461606BD-E824-4D0A-8CBA-01810B1F5E02}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{CE5B42B7-6E8C-4385-9E97-F4023FC16BF2} = {14A277EE-7DF1-4529-B639-7D1EF334C1C5}
@ -419,6 +425,7 @@ Global @@ -419,6 +425,7 @@ Global
{BDDDCD01-D2FE-4EAD-9425-4B6B91922C7C} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2}
{E8922383-B6F5-4107-AB82-49662D98B2FE} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2}
{F2CC804D-5A9C-4875-A423-4FB9A5D07018} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2}
{461606BD-E824-4D0A-8CBA-01810B1F5E02} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2}
{000E4F64-5D0D-4EB1-B0BF-1A62ADBC6EAD} = {BDDDCD01-D2FE-4EAD-9425-4B6B91922C7C}
{869951D5-A0D6-4DC6-9F1D-E6B9A12AC446} = {BDDDCD01-D2FE-4EAD-9425-4B6B91922C7C}
{E6F4983F-DE41-4AEC-88E7-1FA9AFB4E6FF} = {BDDDCD01-D2FE-4EAD-9425-4B6B91922C7C}

Loading…
Cancel
Save