Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@1866 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
53 changed files with 6499 additions and 2 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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)] |
||||
@ -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> |
||||
@ -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> |
||||
@ -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> |
||||
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
} |
||||
} |
||||
@ -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() |
||||
{ |
||||
} |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
} |
||||
} |
||||
@ -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() |
||||
{ |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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() |
||||
{ |
||||
} |
||||
} |
||||
} |
||||
@ -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() |
||||
{ |
||||
} |
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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); |
||||
|
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -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>(); |
||||
} |
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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() |
||||
{ |
||||
} |
||||
} |
||||
} |
||||
@ -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
|
||||
/// <<paramref name="fileNameWithoutExtension"/>>.<culture><<paramref name="extension"/>>.
|
||||
/// </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() |
||||
{ |
||||
} |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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); |
||||
|
||||
} |
||||
} |
||||
@ -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); |
||||
|
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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(); |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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(); |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -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; |
||||
} |
||||
|
||||
} |
||||
} |
||||
Binary file not shown.
Loading…
Reference in new issue