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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
<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 @@ |
|||||||
|
<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 @@ |
|||||||
|
<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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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 @@ |
|||||||
|
// <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