diff --git a/data/resources/StringResources.de.resources b/data/resources/StringResources.de.resources
index bbb6088615..f45b1abf80 100644
Binary files a/data/resources/StringResources.de.resources and b/data/resources/StringResources.de.resources differ
diff --git a/data/resources/StringResources.es-mx.resources b/data/resources/StringResources.es-mx.resources
index 4e86aac426..ab73446e75 100644
Binary files a/data/resources/StringResources.es-mx.resources and b/data/resources/StringResources.es-mx.resources differ
diff --git a/data/resources/StringResources.es.resources b/data/resources/StringResources.es.resources
index 2f71791f0d..62b4870f85 100644
Binary files a/data/resources/StringResources.es.resources and b/data/resources/StringResources.es.resources differ
diff --git a/data/resources/StringResources.nl.resources b/data/resources/StringResources.nl.resources
index 4f396745eb..f0e775ea4c 100644
Binary files a/data/resources/StringResources.nl.resources and b/data/resources/StringResources.nl.resources differ
diff --git a/data/resources/StringResources.pl.resources b/data/resources/StringResources.pl.resources
index 0486ae93f4..159c0b85ef 100644
Binary files a/data/resources/StringResources.pl.resources and b/data/resources/StringResources.pl.resources differ
diff --git a/data/resources/StringResources.tr.resources b/data/resources/StringResources.tr.resources
index 7afc1121db..82925c681d 100644
Binary files a/data/resources/StringResources.tr.resources and b/data/resources/StringResources.tr.resources differ
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Configuration/AssemblyInfo.cs b/src/AddIns/Misc/ResourceToolkit/Project/Configuration/AssemblyInfo.cs
new file mode 100644
index 0000000000..c88b5563e3
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Configuration/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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)]
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Hornung.ResourceToolkit.addin b/src/AddIns/Misc/ResourceToolkit/Project/Hornung.ResourceToolkit.addin
new file mode 100644
index 0000000000..a2863466fa
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Hornung.ResourceToolkit.addin
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/ResourceToolkit.csproj b/src/AddIns/Misc/ResourceToolkit/Project/ResourceToolkit.csproj
new file mode 100644
index 0000000000..2c2c0740ba
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/ResourceToolkit.csproj
@@ -0,0 +1,132 @@
+
+
+ Library
+ Hornung.ResourceToolkit
+ Hornung.ResourceToolkit
+ Debug
+ AnyCPU
+ {461606BD-E824-4D0A-8CBA-01810B1F5E02}
+ ..\..\..\..\..\AddIns\AddIns\Misc\ResourceToolkit\
+ False
+ False
+ False
+ Auto
+ 4194304
+ AnyCPU
+ 4096
+ 4
+ false
+
+
+ obj\
+ obj\Debug\
+ False
+ DEBUG;TRACE
+ true
+ Full
+ True
+
+
+ obj\
+ obj\Release\
+ True
+ TRACE
+ False
+ None
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Configuration\GlobalAssemblyInfo.cs
+
+
+
+
+ {924EE450-603D-49C1-A8E5-4AFAA31CE6F3}
+ ICSharpCode.SharpDevelop.Dom
+ False
+
+
+ {2748AD25-9C63-4E12-877B-4DCE96FBED54}
+ ICSharpCode.SharpDevelop
+ False
+
+
+ {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
+ ICSharpCode.Core
+ False
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}
+ ICSharpCode.TextEditor
+ False
+
+
+ {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}
+ NRefactory
+ False
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Resources/EditStringResourceDialog.xfrm b/src/AddIns/Misc/ResourceToolkit/Project/Resources/EditStringResourceDialog.xfrm
new file mode 100644
index 0000000000..7af0720b43
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Resources/EditStringResourceDialog.xfrm
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs
new file mode 100644
index 0000000000..713f933375
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/AbstractNRefactoryResourceCodeCompletionBinding.cs
@@ -0,0 +1,59 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Provides a base class for code completion for inserting resource keys using NRefactory.
+ ///
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Determines if the specified character should trigger resource resolve attempt and possibly code completion at the current position.
+ ///
+ protected abstract bool CompletionPossible(SharpDevelopTextAreaControl editor, char ch);
+
+ ///
+ /// Gets an NRefactory output visitor used to generate the inserted code.
+ ///
+ protected abstract IOutputAstVisitor OutputVisitor {
+ get;
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/CSharpResourceCodeCompletionBinding.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/CSharpResourceCodeCompletionBinding.cs
new file mode 100644
index 0000000000..0f9b6f3f47
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/CSharpResourceCodeCompletionBinding.cs
@@ -0,0 +1,40 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using ICSharpCode.Core;
+using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
+using ICSharpCode.NRefactory.PrettyPrinter;
+
+namespace Hornung.ResourceToolkit.CodeCompletion
+{
+ ///
+ /// Provides code completion for inserting resource keys in C#.
+ ///
+ public class CSharpResourceCodeCompletionBinding : AbstractNRefactoryResourceCodeCompletionBinding
+ {
+
+ ///
+ /// Determines if the specified character should trigger resource resolve attempt and possibly code completion at the current position.
+ ///
+ protected override bool CompletionPossible(SharpDevelopTextAreaControl editor, char ch)
+ {
+ return ch == '(' || ch == '[';
+ }
+
+ ///
+ /// Gets a CSharpOutputVisitor used to generate the inserted code.
+ ///
+ protected override IOutputAstVisitor OutputVisitor {
+ get {
+ return new CSharpOutputVisitor();
+ }
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs
new file mode 100644
index 0000000000..3af384223a
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ICSharpCodeCoreResourceCodeCompletionBinding.cs
@@ -0,0 +1,69 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Provides code completion for inserting resource keys
+ /// for ICSharpCode.Core resources references ("${res:...}").
+ ///
+ 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;
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/NewResourceCodeCompletionData.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/NewResourceCodeCompletionData.cs
new file mode 100644
index 0000000000..3dfc8adadf
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/NewResourceCodeCompletionData.cs
@@ -0,0 +1,63 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Provides a code completion entry used to add a new string resource.
+ ///
+ 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;
+ }
+
+ ///
+ /// 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.
+ ///
+ /// TextArea to insert the completion data in.
+ /// Character that should be inserted after the completion data.
+ /// \0 when no character should be inserted.
+ /// Returns true when the insert action has processed the character
+ /// ; false when the character was not processed.
+ 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);
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ResourceCodeCompletionData.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ResourceCodeCompletionData.cs
new file mode 100644
index 0000000000..95a762996e
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ResourceCodeCompletionData.cs
@@ -0,0 +1,70 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Represents a code completion data entry for resource keys.
+ ///
+ public class ResourceCodeCompletionData : DefaultCompletionData
+ {
+
+ readonly IOutputAstVisitor outputVisitor;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The resource key.
+ /// The resource description.
+ /// The NRefactory output visitor to be used to generate the inserted code. If null, the key is inserted literally.
+ public ResourceCodeCompletionData(string key, string description, IOutputAstVisitor outputVisitor)
+ : base(key, description, ClassBrowserIconService.GotoArrowIndex)
+ {
+ this.outputVisitor = outputVisitor;
+ }
+
+ ///
+ /// Insert the element represented by the completion data into the text
+ /// editor.
+ ///
+ /// TextArea to insert the completion data in.
+ /// Character that should be inserted after the completion data.
+ /// \0 when no character should be inserted.
+ /// Returns true when the insert action has processed the character
+ /// ; false when the character was not processed.
+ 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;
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ResourceCodeCompletionDataProvider.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ResourceCodeCompletionDataProvider.cs
new file mode 100644
index 0000000000..49bbb545c8
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/ResourceCodeCompletionDataProvider.cs
@@ -0,0 +1,75 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Provides code completion data for resource keys.
+ ///
+ public class ResourceCodeCompletionDataProvider : AbstractCompletionDataProvider
+ {
+ readonly IResourceFileContent content;
+ readonly IOutputAstVisitor outputVisitor;
+ readonly string preEnteredName;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The resource file content to be presented to the user.
+ /// The NRefactory output visitor to be used to generate the inserted code. If null, the key is inserted literally.
+ /// The type name which should be pre-entered in the 'add new' dialog box if the user selects the 'add new' entry.
+ 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;
+ }
+
+ ///
+ /// Generates the completion data. This method is called by the text editor control.
+ ///
+ public override ICompletionData[] GenerateCompletionData(string fileName, ICSharpCode.TextEditor.TextArea textArea, char charTyped)
+ {
+ List list = new List();
+
+ list.Add(new NewResourceCodeCompletionData(this.content, this.outputVisitor, this.preEnteredName));
+
+ foreach (KeyValuePair entry in this.content.Data) {
+ list.Add(new ResourceCodeCompletionData(entry.Key, ResourceResolverService.FormatResourceDescription(this.content, entry.Key), this.outputVisitor));
+ }
+
+ return list.ToArray();
+ }
+
+ ///
+ /// Gets if pressing 'key' should trigger the insertion of the currently selected element.
+ ///
+ 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);
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/VBNetResourceCodeCompletionBinding.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/VBNetResourceCodeCompletionBinding.cs
new file mode 100644
index 0000000000..41d5b231a7
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/CodeCompletion/VBNetResourceCodeCompletionBinding.cs
@@ -0,0 +1,40 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using ICSharpCode.Core;
+using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor;
+using ICSharpCode.NRefactory.PrettyPrinter;
+
+namespace Hornung.ResourceToolkit.CodeCompletion
+{
+ ///
+ /// Provides code completion for inserting resource keys in VB.
+ ///
+ public class VBNetResourceCodeCompletionBinding : AbstractNRefactoryResourceCodeCompletionBinding
+ {
+
+ ///
+ /// Determines if the specified character should trigger resource resolve attempt and possibly code completion at the current position.
+ ///
+ protected override bool CompletionPossible(SharpDevelopTextAreaControl editor, char ch)
+ {
+ return ch == '(';
+ }
+
+ ///
+ /// Gets a VBNetOutputVisitor used to generate the inserted code.
+ ///
+ protected override IOutputAstVisitor OutputVisitor {
+ get {
+ return new VBNetOutputVisitor();
+ }
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Commands/RefactoringCommands.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Commands/RefactoringCommands.cs
new file mode 100644
index 0000000000..7502bda4bc
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Commands/RefactoringCommands.cs
@@ -0,0 +1,57 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Find missing resource keys in the whole solution.
+ ///
+ public class FindMissingResourceKeysCommand : AbstractMenuCommand
+ {
+ public override void Run()
+ {
+ FindReferencesAndRenameHelper.ShowAsSearchResults(StringParser.Parse("${res:Hornung.ResourceToolkit.ReferencesToMissingKeys}"),
+ ResourceRefactoringService.FindReferencesToMissingKeys());
+ }
+ }
+
+ ///
+ /// Find unused resource keys in the whole solution.
+ ///
+ public class FindUnusedResourceKeysCommand : AbstractMenuCommand
+ {
+ public override void Run()
+ {
+ ICollection> 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);
+ }
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Commands/TextEditorContextMenuBuilder.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Commands/TextEditorContextMenuBuilder.cs
new file mode 100644
index 0000000000..d959b9f0dc
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Commands/TextEditorContextMenuBuilder.cs
@@ -0,0 +1,150 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Builds context menu for editing string resources.
+ ///
+ 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 items = new List();
+ 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);
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Gui/EditStringResourceDialog.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Gui/EditStringResourceDialog.cs
new file mode 100644
index 0000000000..ca3c78caf8
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Gui/EditStringResourceDialog.cs
@@ -0,0 +1,83 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// A dialog where the user can edit a string resource key and value.
+ ///
+ 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("key").Validating += this.KeyValidating;
+ } else {
+ this.Get("key").ReadOnly = true;
+ }
+
+ this.Get("key").Text = key;
+ this.Get("value").Text = value;
+
+ if (allowEditKey) {
+ this.Get("key").Select();
+ this.Get("key").Select(key.Length, 0);
+ } else {
+ this.Get("value").Select();
+ this.Get("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("key").Text.Trim();
+ }
+ }
+
+ public string Value {
+ get {
+ return this.Get("value").Text;
+ }
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Gui/UnusedResourceKeysViewContent.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Gui/UnusedResourceKeysViewContent.cs
new file mode 100644
index 0000000000..880a6b6fbf
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Gui/UnusedResourceKeysViewContent.cs
@@ -0,0 +1,316 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Displays unused resource keys in a list and allows the user to delete them.
+ ///
+ public class UnusedResourceKeysViewContent : AbstractViewContent, IClipboardHandler
+ {
+ readonly ICollection> unusedKeys;
+ Panel panel;
+ ListView listView;
+
+ public override System.Windows.Forms.Control Control {
+ get {
+ return this.panel;
+ }
+ }
+
+ ///
+ /// Gets the ListView control that shows the unused resource keys.
+ ///
+ public System.Windows.Forms.ListView ListView {
+ get {
+ return this.listView;
+ }
+ }
+
+ ///
+ /// Gets a collection of key/value pairs where the values are the resource file names and the keys are the unused resource keys.
+ ///
+ public ICollection> UnusedKeys {
+ get {
+ return this.unusedKeys;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A collection of key/value pairs where the values are the resource file names and the keys are the unused resource keys.
+ public UnusedResourceKeysViewContent(ICollection> 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>>(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));
+ }
+ }
+
+ ///
+ /// Fills the list view with the specified resource keys.
+ ///
+ /// A collection of key/value pairs where the values are the resource file names and the keys are the resource keys.
+ public void FillListView(ICollection> resources)
+ {
+ Application.DoEvents();
+ Cursor oldCursor = Cursor.Current;
+ Cursor.Current = Cursors.WaitCursor;
+ try {
+
+ this.ListView.Items.Clear();
+ this.ListView.Groups.Clear();
+ this.ListView.BeginUpdate();
+
+ Dictionary fileGroups = new Dictionary();
+
+ // Create the ListViewItems.
+ foreach (KeyValuePair 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)"<>").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
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Deletes the resource keys represented by the specified ListViewItems.
+ ///
+ 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 items = new List();
+ 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;
+ }
+ }
+
+ ///
+ /// Deletes the specified resource key in the resource file and all dependent localized
+ /// resource files permanently.
+ ///
+ /// The master resource file that contains the key to be deleted.
+ /// The key to be deleted.
+ 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 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);
+ }
+ }
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ProjectFileDictionaryService.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ProjectFileDictionaryService.cs
new file mode 100644
index 0000000000..2836df1538
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ProjectFileDictionaryService.cs
@@ -0,0 +1,90 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections.Generic;
+
+using ICSharpCode.Core;
+using ICSharpCode.SharpDevelop.Project;
+
+namespace Hornung.ResourceToolkit
+{
+ ///
+ /// Provides a way to find out which project a certain file belongs to
+ /// and caches the results to improve performance.
+ ///
+ public static class ProjectFileDictionaryService
+ {
+ static Dictionary files = new Dictionary();
+
+ 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(); };
+ }
+
+ ///
+ /// Gets the project that contains the specified file.
+ ///
+ /// The file to find the project for.
+ /// 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 null.
+ 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 ?? "")+"'.");
+
+ return ProjectService.CurrentProject;
+ }
+
+ ///
+ /// Gets the project that contains the specified file.
+ ///
+ /// The file to find the project for.
+ /// The project that contains the specified file. If the file is not found in any project or there is no open project, null is returned.
+ static IProject GetProjectForFileInternal(string fileName)
+ {
+ if (ProjectService.OpenSolution != null) {
+ IProject p;
+ if ((p = ProjectService.OpenSolution.FindProjectContainingFile(fileName)) != null) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Adds a file name to the project dictionary or sets the file's project if
+ /// it is already listed.
+ ///
+ /// The file to add.
+ /// The project the specified file belongs to.
+ public static void AddFile(string file, IProject project)
+ {
+ files[file] = project;
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/AnyResourceReferenceFinder.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/AnyResourceReferenceFinder.cs
new file mode 100644
index 0000000000..18e3afef48
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/AnyResourceReferenceFinder.cs
@@ -0,0 +1,83 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections.Generic;
+
+using Hornung.ResourceToolkit.Resolver;
+
+namespace Hornung.ResourceToolkit.Refactoring
+{
+ ///
+ /// Finds references to resources in a text document.
+ ///
+ public class AnyResourceReferenceFinder : IResourceReferenceFinder
+ {
+ ///
+ /// Returns the offset of the next possible resource reference in the file
+ /// after prevOffset.
+ /// Returns -1, if there are no more possible references.
+ ///
+ /// The name of the file that is currently being searched in.
+ /// The text content of the file.
+ /// The offset of the last found reference or -1, if this is the first call in the current file.
+ 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;
+ }
+
+ ///
+ /// Determines whether the specified ResourceResolveResult describes
+ /// a resource that should be included in the search result.
+ ///
+ public bool IsReferenceToResource(ResourceResolveResult result)
+ {
+ return true;
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The name of the file to get a list of possible patterns for.
+ public static IEnumerable GetPossiblePatternsForFile(string fileName)
+ {
+ List patterns = new List();
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AnyResourceReferenceFinder()
+ {
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/IResourceReferenceFinder.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/IResourceReferenceFinder.cs
new file mode 100644
index 0000000000..4e72e4644c
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/IResourceReferenceFinder.cs
@@ -0,0 +1,35 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+
+using Hornung.ResourceToolkit.Resolver;
+
+namespace Hornung.ResourceToolkit.Refactoring
+{
+ ///
+ /// Describes an object that finds resource references in a text document.
+ ///
+ public interface IResourceReferenceFinder
+ {
+ ///
+ /// Returns the offset of the next possible resource reference in the file
+ /// after prevOffset.
+ /// Returns -1, if there are no more possible references.
+ ///
+ /// The name of the file that is currently being searched in.
+ /// The text content of the file.
+ /// The offset of the last found reference or -1, if this is the first call in the current file.
+ int GetNextPossibleOffset(string fileName, string fileContent, int prevOffset);
+
+ ///
+ /// Determines whether the specified ResourceResolveResult describes
+ /// a resource that should be included in the search result.
+ ///
+ bool IsReferenceToResource(ResourceResolveResult result);
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/ResourceRefactoringService.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/ResourceRefactoringService.cs
new file mode 100644
index 0000000000..76eefe6566
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/ResourceRefactoringService.cs
@@ -0,0 +1,364 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Provides facilities for refactoring resources.
+ ///
+ public static class ResourceRefactoringService
+ {
+
+ ///
+ /// Finds all references to the specified resource (except the definition).
+ ///
+ /// The name of the resource file that contains the resource key to find.
+ /// The resource key to find.
+ /// A list of references to this resource.
+ public static List FindReferences(string resourceFileName, string key)
+ {
+ return FindReferences(new SpecificResourceReferenceFinder(resourceFileName, key));
+ }
+
+ ///
+ /// Finds all references to resources (except the definition) using the specified
+ /// object.
+ ///
+ /// The to use to find resource references.
+ /// A list of references to resources.
+ public static List 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 references = new List();
+
+ 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;
+ }
+
+ ///
+ /// Finds all references to resources (except the definitions).
+ ///
+ /// A list of references to resources.
+ public static List FindAllReferences()
+ {
+ return FindReferences(new AnyResourceReferenceFinder());
+ }
+
+ ///
+ /// Finds all references to missing resource keys.
+ ///
+ /// A list of all references to missing resource keys.
+ public static List FindReferencesToMissingKeys()
+ {
+ List references = FindAllReferences();
+ if (references == null) {
+ return null;
+ }
+ return references.FindAll(IsReferenceToMissingKey);
+ }
+
+ ///
+ /// Determines whether the specified reference is a resource reference
+ /// to a missing key.
+ ///
+ /// The reference to examine.
+ /// true, if the specified reference is a resource reference to a missing key, otherwise false.
+ 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);
+ }
+
+ ///
+ /// Finds all unused resource keys in all resource files that are referenced
+ /// in code at least once in the whole solution.
+ ///
+ /// A collection of key/value pairs where the values are the resource file names and the keys are the unused resource keys.
+ public static ICollection> FindUnusedKeys()
+ {
+ List references = FindAllReferences();
+ if (references == null) {
+ return null;
+ }
+
+ DateTime startTime = DateTime.UtcNow;
+ List> unused = new List>();
+
+ // Get a list of all referenced resource files.
+ // Generate a dictonary of resource file names and the
+ // corresponding referenced keys.
+ Dictionary> referencedKeys = new Dictionary>();
+ 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());
+ }
+ 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 ?? "")+":"+reference.Offset+Environment.NewLine+"Expression: "+(reference.Expression ?? ""));
+ }
+ }
+
+ // 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 entry in ResourceFileContentRegistry.GetResourceFileContent(fileName).Data) {
+ if (!referencedKeys[fileName].Contains(entry.Key)) {
+ unused.Add(new KeyValuePair(entry.Key, fileName));
+ }
+ }
+ }
+
+ LoggingService.Info("ResourceToolkit: FindUnusedKeys finished in "+(DateTime.UtcNow - startTime).TotalSeconds.ToString(System.Globalization.CultureInfo.CurrentCulture)+"s");
+
+ return unused.AsReadOnly();
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Renames all references to a resource including the definition.
+ /// Asks the user for a new name.
+ ///
+ /// The resource to be renamed.
+ 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);
+ }
+ }
+
+ ///
+ /// Renames all references to a resource including the definition.
+ ///
+ /// The resource to be renamed.
+ /// The new name of the resource key.
+ 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 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 entry in ResourceFileContentRegistry.GetLocalizedContents(rrr.FileName)) {
+ if (entry.Value.ContainsKey(rrr.Key)) {
+ entry.Value.RenameKey(rrr.Key, newKey);
+ }
+ }
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Gets a list of names of files which can possibly contain resource references.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ public static List GetPossibleFiles()
+ {
+ List files = new List();
+
+ 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;
+ }
+
+ ///
+ /// Determines whether the specified file could possibly contain resource references
+ /// that can be detected by at least one registered resource resolver.
+ ///
+ public static bool IsPossibleFile(string name)
+ {
+ foreach (IResourceResolver resolver in ResourceResolverService.Resolvers) {
+ if (resolver.SupportsFile(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Finds a string literal in a source code file with respect to escaping rules
+ /// of the language.
+ ///
+ /// The name of the file to search in.
+ /// The text content of the file.
+ /// The string literal to find.
+ /// The position to start searching at.
+ /// Receives the unquoted program code that represents the specified string literal in the language this file is written in.
+ /// The next index where the specified string literal appears, or -1 if there is no match or the language cannot be determined.
+ 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;
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/SpecificResourceReferenceFinder.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/SpecificResourceReferenceFinder.cs
new file mode 100644
index 0000000000..bad0467907
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/SpecificResourceReferenceFinder.cs
@@ -0,0 +1,93 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+
+using ICSharpCode.Core;
+
+using Hornung.ResourceToolkit.Resolver;
+
+namespace Hornung.ResourceToolkit.Refactoring
+{
+ ///
+ /// Finds references to a specific resource in a text document.
+ ///
+ public class SpecificResourceReferenceFinder : IResourceReferenceFinder
+ {
+ readonly string resourceFileName;
+ readonly string key;
+
+ ///
+ /// Gets the name of the resource file that contains the resource to find.
+ ///
+ public string ResourceFileName {
+ get {
+ return resourceFileName;
+ }
+ }
+
+ ///
+ /// Gets the resource key to find.
+ ///
+ public string Key {
+ get {
+ return key;
+ }
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Returns the offset of the next possible resource reference in the file
+ /// after prevOffset.
+ /// Returns -1, if there are no more possible references.
+ ///
+ /// The name of the file that is currently being searched in.
+ /// The text content of the file.
+ /// The offset of the last found reference or -1, if this is the first call in the current file.
+ 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;
+ }
+
+ ///
+ /// Determines whether the specified ResourceResolveResult describes
+ /// a resource that should be included in the search result.
+ ///
+ public bool IsReferenceToResource(ResourceResolveResult result)
+ {
+ return FileUtility.IsEqualFileName(this.ResourceFileName, result.FileName) &&
+ result.Key.Equals(this.Key, StringComparison.InvariantCultureIgnoreCase);
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the resource file that contains the resource to find.
+ /// The resource key to find.
+ 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;
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/AbstractResourceResolver.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/AbstractResourceResolver.cs
new file mode 100644
index 0000000000..1f37f44535
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/AbstractResourceResolver.cs
@@ -0,0 +1,112 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Abstract base class for resource resolvers.
+ ///
+ public abstract class AbstractResourceResolver : IResourceResolver
+ {
+
+ ///
+ /// Attempts to resolve a reference to a resource.
+ ///
+ /// The name of the file that contains the expression to be resolved.
+ /// The document that contains the expression to be resolved.
+ /// The 0-based line in the file that contains the expression to be resolved.
+ /// The 0-based column position of the expression to be resolved.
+ /// A that describes which resource is referenced by the expression at the specified position in the specified file, or null if that expression does not reference a (known) resource or if the specified position is invalid.
+ 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)));
+ }
+
+ ///
+ /// Attempts to resolve a reference to a resource.
+ ///
+ /// The text editor for which a resource resolution attempt should be performed.
+ /// A that describes which resource is referenced by the expression at the caret in the specified editor, or null if that expression does not reference a (known) resource.
+ public ResourceResolveResult Resolve(TextEditorControl editor)
+ {
+ return this.Resolve(editor.FileName, editor.Document, editor.ActiveTextAreaControl.Caret.Line, editor.ActiveTextAreaControl.Caret.Column);
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Attempts to resolve a reference to a resource.
+ ///
+ /// The name of the file that contains the expression to be resolved.
+ /// The document that contains the expression to be resolved.
+ /// The 0-based line in the file that contains the expression to be resolved.
+ /// The 0-based column position of the expression to be resolved.
+ /// The offset of the position of the expression to be resolved.
+ /// A that describes which resource is referenced by the expression at the specified position in the specified file, or null if that expression does not reference a (known) resource.
+ protected abstract ResourceResolveResult Resolve(string fileName, IDocument document, int caretLine, int caretColumn, int caretOffset);
+
+ ///
+ /// Determines whether this resolver supports resolving resources in the given file.
+ ///
+ /// The name of the file to examine.
+ /// true, if this resolver supports resolving resources in the given file, false otherwise.
+ public abstract bool SupportsFile(string fileName);
+
+ ///
+ /// 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.
+ ///
+ /// The name of the file to get a list of possible patterns for.
+ public abstract IEnumerable GetPossiblePatternsForFile(string fileName);
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Tries to find a resource file with the given name in the given directory by
+ /// trying all known resource file extensions.
+ ///
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected AbstractResourceResolver()
+ {
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/BclNRefactoryResourceResolver.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/BclNRefactoryResourceResolver.cs
new file mode 100644
index 0000000000..d0d4a79a52
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/BclNRefactoryResourceResolver.cs
@@ -0,0 +1,458 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Detects and resolves resource references using the standard .NET
+ /// framework provided resource access methods (ResourceManager and derived
+ /// classes).
+ ///
+ public class BclNRefactoryResourceResolver : INRefactoryResourceResolver
+ {
+
+ ///
+ /// Tries to find a resource reference in the specified expression.
+ ///
+ /// The ExpressionResult for the expression.
+ /// The AST representation of the expression.
+ /// SharpDevelop's ResolveResult for the expression.
+ /// The line where the expression is located.
+ /// The column where the expression is located.
+ /// The name of the source file where the expression is located.
+ /// The content of the source file where the expression is located.
+ /// A ResourceResolveResult describing the referenced resource, or null, if this expression does not reference a resource using the standard .NET framework classes.
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Tries to determine the resource file content which is referenced by the
+ /// resource manager which is assigned to the specified member.
+ ///
+ ///
+ /// 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.
+ ///
+ 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;
+ }
+
+ ///
+ /// Determines if the specified type is a ResourceManager type that can
+ /// be handled by this resolver.
+ ///
+ 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
+
+ ///
+ /// 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.
+ ///
+ class ResourceManagerInitializationFindVisitor : PositionTrackingAstVisitor
+ {
+
+ readonly IMember resourceManagerMember;
+ readonly bool isLocalVariable;
+
+ CompilationUnit compilationUnit;
+
+ string foundFileName;
+
+ ///
+ /// 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.
+ ///
+ public string FoundFileName {
+ get {
+ return this.foundFileName;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The member which the resource manager to be found is assigned to.
+ 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
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Tries to infer the resource key being referenced from the given expression.
+ ///
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// 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.
+ ///
+ /// The name of the file to get a list of possible patterns for.
+ public IEnumerable GetPossiblePatternsForFile(string fileName)
+ {
+ return new string[] {
+ "GetString",
+ "GetObject",
+ "GetStream",
+ (NRefactoryResourceResolver.GetLanguagePropertiesForFile(fileName) ?? LanguageProperties.None).IndexerExpressionStartToken
+ };
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BclNRefactoryResourceResolver()
+ {
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreNRefactoryResourceResolver.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreNRefactoryResourceResolver.cs
new file mode 100644
index 0000000000..d6bb2abc6b
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreNRefactoryResourceResolver.cs
@@ -0,0 +1,154 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Detects and resolves resource references when using the ICSharpCode.Core
+ /// ResourceService class.
+ ///
+ public class ICSharpCodeCoreNRefactoryResourceResolver : INRefactoryResourceResolver
+ {
+
+ ///
+ /// Tries to find a resource reference in the specified expression.
+ ///
+ /// The ExpressionResult for the expression.
+ /// The AST representation of the expression.
+ /// SharpDevelop's ResolveResult for the expression.
+ /// The line where the expression is located.
+ /// The column where the expression is located.
+ /// The name of the source file where the expression is located.
+ /// The content of the source file where the expression is located.
+ /// A ResourceResolveResult describing the referenced resource, or null, if this expression does not reference a resource using the ICSharpCode.Core.ResourceService class.
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Tries to infer the resource key being referenced from the given expression.
+ ///
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// 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.
+ ///
+ /// The name of the file to get a list of possible patterns for.
+ public IEnumerable GetPossiblePatternsForFile(string fileName)
+ {
+ return new string[] {
+ "GetString"
+ };
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ICSharpCodeCoreNRefactoryResourceResolver()
+ {
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreResourceResolver.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreResourceResolver.cs
new file mode 100644
index 0000000000..f356c99296
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreResourceResolver.cs
@@ -0,0 +1,299 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Resolves references to resources that are accessed using ICSharpCode.Core
+ /// ("${res:...}").
+ ///
+ public class ICSharpCodeCoreResourceResolver : AbstractResourceResolver
+ {
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ICSharpCodeCoreResourceResolver() : base()
+ {
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Determines whether this resolver supports resolving resources in the given file.
+ ///
+ /// The name of the file to examine.
+ /// true, if this resolver supports resolving resources in the given file, false otherwise.
+ 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:"
+ };
+
+ ///
+ /// 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.
+ ///
+ /// The name of the file to get a list of possible patterns for.
+ public override IEnumerable GetPossiblePatternsForFile(string fileName)
+ {
+ if (this.SupportsFile(fileName)) {
+ return possiblePatterns;
+ }
+ return new string[0];
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// The token that indicates a reference to an ICSharpCode.Core resource.
+ ///
+ public const string ResourceReferenceToken = @"${res:";
+
+ ///
+ /// Attempts to resolve a reference to a resource.
+ ///
+ /// The name of the file that contains the expression to be resolved.
+ /// The document that contains the expression to be resolved.
+ /// The 0-based line in the file that contains the expression to be resolved.
+ /// The 0-based column position of the expression to be resolved.
+ /// The offset of the position of the expression to be resolved.
+ /// A that describes which resource is referenced by the expression at the specified position in the specified file, or null if that expression does not reference a (known) resource.
+ 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 ? "" : key.ToString())+"'.");
+ }
+
+ return null;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Tries to find the name of the resource file which serves as source for
+ /// the resources accessed by the ICSharpCode.Core.
+ ///
+ /// The resource key to look for. May be null.
+ /// The name of the source code file that contains the reference to the resource.
+ 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;
+ }
+
+ ///
+ /// Tries to find an ICSharpCode.Core resource file in the given path
+ /// according to the file names defined in the AddIn tree.
+ ///
+ static string FindICSharpCodeCoreResourceFile(string path)
+ {
+ string file;
+ foreach (string fileName in AddInTree.BuildItems("/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/ResourceFileNames", null, false)) {
+ if ((file = FindResourceFileName(Path.Combine(path, fileName))) != null) {
+ return file;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Tries to find the local string resource file used for ICSharpCode.Core resource access.
+ ///
+ /// The name of the source code file which to find the ICSharpCode.Core resource file for.
+ 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("/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/LocalResourcesLocations", null, false)) {
+ if ((localFile = FindICSharpCodeCoreResourceFile(Path.GetFullPath(Path.Combine(project.Directory, relativePath)))) != null) {
+ return localFile;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Tries to find the string resource file of the host application for ICSharpCode.Core resource access.
+ ///
+ /// The name of the source code file which to find the ICSharpCode.Core resource file for.
+ 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("/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;
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/INRefactoryResourceResolver.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/INRefactoryResourceResolver.cs
new file mode 100644
index 0000000000..986999e35a
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/INRefactoryResourceResolver.cs
@@ -0,0 +1,44 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections.Generic;
+
+using ICSharpCode.NRefactory.Ast;
+using ICSharpCode.SharpDevelop.Dom;
+
+namespace Hornung.ResourceToolkit.Resolver
+{
+ ///
+ /// Detects and resolves resource references using SharpDevelop.Dom and
+ /// NRefactory. At this stage the parser and resolver have already been
+ /// called.
+ ///
+ public interface INRefactoryResourceResolver
+ {
+ ///
+ /// Tries to find a resource reference in the specified expression.
+ ///
+ /// The ExpressionResult for the expression.
+ /// The AST representation of the expression.
+ /// SharpDevelop's ResolveResult for the expression.
+ /// The line where the expression is located.
+ /// The column where the expression is located.
+ /// The name of the source file where the expression is located.
+ /// The content of the source file where the expression is located.
+ /// A ResourceResolveResult describing the referenced resource, or null, if this expression does not reference a resource in a known way.
+ ResourceResolveResult Resolve(ExpressionResult expressionResult, Expression expr, ResolveResult resolveResult, int caretLine, int caretColumn, string fileName, string fileContent);
+
+ ///
+ /// 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.
+ ///
+ /// The name of the file to get a list of possible patterns for.
+ IEnumerable GetPossiblePatternsForFile(string fileName);
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/IResourceResolver.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/IResourceResolver.cs
new file mode 100644
index 0000000000..a5b922525d
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/IResourceResolver.cs
@@ -0,0 +1,56 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections.Generic;
+
+using ICSharpCode.TextEditor;
+using ICSharpCode.TextEditor.Document;
+
+namespace Hornung.ResourceToolkit.Resolver
+{
+ ///
+ /// Describes an object that is able to find out if an expression
+ /// references a resource and if so, in which file and which key.
+ ///
+ public interface IResourceResolver
+ {
+
+ ///
+ /// Attempts to resolve a reference to a resource.
+ ///
+ /// The name of the file that contains the expression to be resolved.
+ /// The document that contains the expression to be resolved.
+ /// The 1-based line in the file that contains the expression to be resolved.
+ /// The 1-based column position of the expression to be resolved.
+ /// A that describes which resource is referenced by the expression at the specified position in the specified file, or null if that expression does not reference a (known) resource.
+ ResourceResolveResult Resolve(string fileName, IDocument document, int caretLine, int caretColumn);
+
+ ///
+ /// Attempts to resolve a reference to a resource.
+ ///
+ /// The text editor for which a resource resolution attempt should be performed.
+ /// A that describes which resource is referenced by the expression at the caret in the specified editor, or null if that expression does not reference a (known) resource.
+ ResourceResolveResult Resolve(TextEditorControl editor);
+
+ ///
+ /// Determines whether this resolver supports resolving resources in the given file.
+ ///
+ /// The name of the file to examine.
+ /// true, if this resolver supports resolving resources in the given file, false otherwise.
+ bool SupportsFile(string fileName);
+
+ ///
+ /// 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.
+ ///
+ /// The name of the file to get a list of possible patterns for.
+ IEnumerable GetPossiblePatternsForFile(string fileName);
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryAstCacheService.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryAstCacheService.cs
new file mode 100644
index 0000000000..188e66a02b
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryAstCacheService.cs
@@ -0,0 +1,94 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Parses files using NRefactory and caches the AST on demand.
+ ///
+ public static class NRefactoryAstCacheService
+ {
+ static bool cacheEnabled = false;
+ static Dictionary cachedAstInfo = new Dictionary();
+
+ ///
+ /// Gets a flag that indicates whether the AST cache is currently enabled.
+ ///
+ public static bool CacheEnabled {
+ get {
+ return cacheEnabled;
+ }
+ }
+
+ ///
+ /// Enables the AST cache.
+ ///
+ /// The AST cache is already enabled.
+ public static void EnableCache()
+ {
+ if (CacheEnabled) {
+ throw new InvalidOperationException("The AST cache is already enabled.");
+ }
+ cacheEnabled = true;
+ LoggingService.Info("ResourceToolkit: NRefactoryAstCacheService cache enabled");
+ }
+
+ ///
+ /// Disables the AST cache.
+ ///
+ public static void DisableCache()
+ {
+ cacheEnabled = false;
+ cachedAstInfo.Clear();
+ LoggingService.Info("ResourceToolkit: NRefactoryAstCacheService cache disabled and cleared");
+ }
+
+ ///
+ /// Gets the complete NRefactory AST for the specified file.
+ ///
+ /// The language of the file.
+ /// The file to get the AST for.
+ /// A that contains the AST for the specified file, or null if the file cannot be parsed.
+ /// Between calls to and the file is parsed only once. On subsequent accesses the AST is retrieved from the cache.
+ 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;
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryResourceResolver.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryResourceResolver.cs
new file mode 100644
index 0000000000..161fb2e3b2
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NRefactoryResourceResolver.cs
@@ -0,0 +1,390 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Resolves resource references using NRefactory.
+ ///
+ public class NRefactoryResourceResolver : AbstractResourceResolver
+ {
+
+ ///
+ /// The AddIn tree path where the NRefactory resource resolvers are registered.
+ ///
+ public const string NRefactoryResourceResolversAddInTreePath = "/AddIns/ResourceToolkit/NRefactoryResourceResolver/Resolvers";
+
+ // ********************************************************************************************************************************
+
+ static List resolvers;
+
+ ///
+ /// Gets a list of all registered NRefactory resource resolvers.
+ ///
+ public static IEnumerable Resolvers {
+ get {
+ if (resolvers == null) {
+ resolvers = AddInTree.BuildItems(NRefactoryResourceResolversAddInTreePath, null, false);
+ }
+ return resolvers;
+ }
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public NRefactoryResourceResolver() : base()
+ {
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Determines whether this resolver supports resolving resources in the given file.
+ ///
+ /// The name of the file to examine.
+ /// true, if this resolver supports resolving resources in the given file, false otherwise.
+ public override bool SupportsFile(string fileName)
+ {
+ // Any source code file supported by NRefactory is supported
+ return (GetFileLanguage(fileName) != null);
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The name of the file to get a list of possible patterns for.
+ public override IEnumerable GetPossiblePatternsForFile(string fileName)
+ {
+ if (this.SupportsFile(fileName)) {
+ List patterns = new List();
+ 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];
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Attempts to resolve a reference to a resource.
+ ///
+ /// The name of the file that contains the expression to be resolved.
+ /// The document that contains the expression to be resolved.
+ /// The 0-based line in the file that contains the expression to be resolved.
+ /// The 0-based column position of the expression to be resolved.
+ /// The offset of the position of the expression to be resolved.
+ /// A that describes which resource is referenced by the expression at the specified position in the specified file, or null if that expression does not reference a (known) resource.
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Tries to resolve the resource reference using all available
+ /// NRefactory resource resolvers.
+ ///
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Determines the file which contains the resources referenced by the specified manifest resource name.
+ ///
+ /// The name of the source code file which the reference occurs in.
+ /// The manifest resource name to find the resource file for.
+ /// The name of the file that contains the resources with the specified manifest resource name, or null if the file name cannot be determined.
+ /// The parameter is null.
+ 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 possibleSourceFiles = new List();
+ 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)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 ?? "")+"'.");
+
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Gets the NRefactory language for the specified file name.
+ ///
+ 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;
+ }
+
+ ///
+ /// Gets the language properties for the project the specified member
+ /// belongs to.
+ /// Returns null if the language cannot be determined.
+ ///
+ 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;
+ }
+
+ ///
+ /// Gets the language properties for the specified file.
+ ///
+ /// The file to get the language properties for.
+ /// The language properties of the specified file, or null if the language cannot be determined.
+ public static LanguageProperties GetLanguagePropertiesForFile(string fileName)
+ {
+ ICSharpCode.SharpDevelop.Dom.IParser p = ParserService.GetParser(fileName);
+ if (p == null) {
+ return null;
+ }
+ return p.Language;
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NodeTrackingAstVisitor.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NodeTrackingAstVisitor.cs
new file mode 100644
index 0000000000..3e3443b1a5
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/NodeTrackingAstVisitor.cs
@@ -0,0 +1,1144 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.42
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Hornung.ResourceToolkit.Resolver {
+ using System;
+ using ICSharpCode.NRefactory.Ast;
+
+
+ ///
+ /// The NodeTrackingAstVisitor will iterate through the whole AST,
+ /// just like the AbstractAstVisitor, and calls the virtual methods
+ /// BeginVisit and EndVisit for each node being visited.
+ ///
+ ///
+ /// To enable the tracking functionality, you must override
+ /// the TrackedVisit methods instead of the Visit methods!
+ ///
+ public abstract class NodeTrackingAstVisitor : ICSharpCode.NRefactory.Visitors.AbstractAstVisitor {
+
+ protected virtual void BeginVisit(INode node) {
+ }
+
+ protected virtual void EndVisit(INode node) {
+ }
+
+ public override object VisitAddHandlerStatement(AddHandlerStatement addHandlerStatement, object data) {
+ this.BeginVisit(addHandlerStatement);
+ object result = this.TrackedVisit(addHandlerStatement, data);
+ this.EndVisit(addHandlerStatement);
+ return result;
+ }
+
+ public override object VisitAddressOfExpression(AddressOfExpression addressOfExpression, object data) {
+ this.BeginVisit(addressOfExpression);
+ object result = this.TrackedVisit(addressOfExpression, data);
+ this.EndVisit(addressOfExpression);
+ return result;
+ }
+
+ public override object VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, object data) {
+ this.BeginVisit(anonymousMethodExpression);
+ object result = this.TrackedVisit(anonymousMethodExpression, data);
+ this.EndVisit(anonymousMethodExpression);
+ return result;
+ }
+
+ public override object VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, object data) {
+ this.BeginVisit(arrayCreateExpression);
+ object result = this.TrackedVisit(arrayCreateExpression, data);
+ this.EndVisit(arrayCreateExpression);
+ return result;
+ }
+
+ public override object VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, object data) {
+ this.BeginVisit(arrayInitializerExpression);
+ object result = this.TrackedVisit(arrayInitializerExpression, data);
+ this.EndVisit(arrayInitializerExpression);
+ return result;
+ }
+
+ public override object VisitAssignmentExpression(AssignmentExpression assignmentExpression, object data) {
+ this.BeginVisit(assignmentExpression);
+ object result = this.TrackedVisit(assignmentExpression, data);
+ this.EndVisit(assignmentExpression);
+ return result;
+ }
+
+ public override object VisitAttribute(ICSharpCode.NRefactory.Ast.Attribute attribute, object data) {
+ this.BeginVisit(attribute);
+ object result = this.TrackedVisit(attribute, data);
+ this.EndVisit(attribute);
+ return result;
+ }
+
+ public override object VisitAttributeSection(AttributeSection attributeSection, object data) {
+ this.BeginVisit(attributeSection);
+ object result = this.TrackedVisit(attributeSection, data);
+ this.EndVisit(attributeSection);
+ return result;
+ }
+
+ public override object VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, object data) {
+ this.BeginVisit(baseReferenceExpression);
+ object result = this.TrackedVisit(baseReferenceExpression, data);
+ this.EndVisit(baseReferenceExpression);
+ return result;
+ }
+
+ public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) {
+ this.BeginVisit(binaryOperatorExpression);
+ object result = this.TrackedVisit(binaryOperatorExpression, data);
+ this.EndVisit(binaryOperatorExpression);
+ return result;
+ }
+
+ public override object VisitBlockStatement(BlockStatement blockStatement, object data) {
+ this.BeginVisit(blockStatement);
+ object result = this.TrackedVisit(blockStatement, data);
+ this.EndVisit(blockStatement);
+ return result;
+ }
+
+ public override object VisitBreakStatement(BreakStatement breakStatement, object data) {
+ this.BeginVisit(breakStatement);
+ object result = this.TrackedVisit(breakStatement, data);
+ this.EndVisit(breakStatement);
+ return result;
+ }
+
+ public override object VisitCaseLabel(CaseLabel caseLabel, object data) {
+ this.BeginVisit(caseLabel);
+ object result = this.TrackedVisit(caseLabel, data);
+ this.EndVisit(caseLabel);
+ return result;
+ }
+
+ public override object VisitCastExpression(CastExpression castExpression, object data) {
+ this.BeginVisit(castExpression);
+ object result = this.TrackedVisit(castExpression, data);
+ this.EndVisit(castExpression);
+ return result;
+ }
+
+ public override object VisitCatchClause(CatchClause catchClause, object data) {
+ this.BeginVisit(catchClause);
+ object result = this.TrackedVisit(catchClause, data);
+ this.EndVisit(catchClause);
+ return result;
+ }
+
+ public override object VisitCheckedExpression(CheckedExpression checkedExpression, object data) {
+ this.BeginVisit(checkedExpression);
+ object result = this.TrackedVisit(checkedExpression, data);
+ this.EndVisit(checkedExpression);
+ return result;
+ }
+
+ public override object VisitCheckedStatement(CheckedStatement checkedStatement, object data) {
+ this.BeginVisit(checkedStatement);
+ object result = this.TrackedVisit(checkedStatement, data);
+ this.EndVisit(checkedStatement);
+ return result;
+ }
+
+ public override object VisitClassReferenceExpression(ClassReferenceExpression classReferenceExpression, object data) {
+ this.BeginVisit(classReferenceExpression);
+ object result = this.TrackedVisit(classReferenceExpression, data);
+ this.EndVisit(classReferenceExpression);
+ return result;
+ }
+
+ public override object VisitCompilationUnit(CompilationUnit compilationUnit, object data) {
+ this.BeginVisit(compilationUnit);
+ object result = this.TrackedVisit(compilationUnit, data);
+ this.EndVisit(compilationUnit);
+ return result;
+ }
+
+ public override object VisitConditionalExpression(ConditionalExpression conditionalExpression, object data) {
+ this.BeginVisit(conditionalExpression);
+ object result = this.TrackedVisit(conditionalExpression, data);
+ this.EndVisit(conditionalExpression);
+ return result;
+ }
+
+ public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) {
+ this.BeginVisit(constructorDeclaration);
+ object result = this.TrackedVisit(constructorDeclaration, data);
+ this.EndVisit(constructorDeclaration);
+ return result;
+ }
+
+ public override object VisitConstructorInitializer(ConstructorInitializer constructorInitializer, object data) {
+ this.BeginVisit(constructorInitializer);
+ object result = this.TrackedVisit(constructorInitializer, data);
+ this.EndVisit(constructorInitializer);
+ return result;
+ }
+
+ public override object VisitContinueStatement(ContinueStatement continueStatement, object data) {
+ this.BeginVisit(continueStatement);
+ object result = this.TrackedVisit(continueStatement, data);
+ this.EndVisit(continueStatement);
+ return result;
+ }
+
+ public override object VisitDeclareDeclaration(DeclareDeclaration declareDeclaration, object data) {
+ this.BeginVisit(declareDeclaration);
+ object result = this.TrackedVisit(declareDeclaration, data);
+ this.EndVisit(declareDeclaration);
+ return result;
+ }
+
+ public override object VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, object data) {
+ this.BeginVisit(defaultValueExpression);
+ object result = this.TrackedVisit(defaultValueExpression, data);
+ this.EndVisit(defaultValueExpression);
+ return result;
+ }
+
+ public override object VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, object data) {
+ this.BeginVisit(delegateDeclaration);
+ object result = this.TrackedVisit(delegateDeclaration, data);
+ this.EndVisit(delegateDeclaration);
+ return result;
+ }
+
+ public override object VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, object data) {
+ this.BeginVisit(destructorDeclaration);
+ object result = this.TrackedVisit(destructorDeclaration, data);
+ this.EndVisit(destructorDeclaration);
+ return result;
+ }
+
+ public override object VisitDirectionExpression(DirectionExpression directionExpression, object data) {
+ this.BeginVisit(directionExpression);
+ object result = this.TrackedVisit(directionExpression, data);
+ this.EndVisit(directionExpression);
+ return result;
+ }
+
+ public override object VisitDoLoopStatement(DoLoopStatement doLoopStatement, object data) {
+ this.BeginVisit(doLoopStatement);
+ object result = this.TrackedVisit(doLoopStatement, data);
+ this.EndVisit(doLoopStatement);
+ return result;
+ }
+
+ public override object VisitElseIfSection(ElseIfSection elseIfSection, object data) {
+ this.BeginVisit(elseIfSection);
+ object result = this.TrackedVisit(elseIfSection, data);
+ this.EndVisit(elseIfSection);
+ return result;
+ }
+
+ public override object VisitEmptyStatement(EmptyStatement emptyStatement, object data) {
+ this.BeginVisit(emptyStatement);
+ object result = this.TrackedVisit(emptyStatement, data);
+ this.EndVisit(emptyStatement);
+ return result;
+ }
+
+ public override object VisitEndStatement(EndStatement endStatement, object data) {
+ this.BeginVisit(endStatement);
+ object result = this.TrackedVisit(endStatement, data);
+ this.EndVisit(endStatement);
+ return result;
+ }
+
+ public override object VisitEraseStatement(EraseStatement eraseStatement, object data) {
+ this.BeginVisit(eraseStatement);
+ object result = this.TrackedVisit(eraseStatement, data);
+ this.EndVisit(eraseStatement);
+ return result;
+ }
+
+ public override object VisitErrorStatement(ErrorStatement errorStatement, object data) {
+ this.BeginVisit(errorStatement);
+ object result = this.TrackedVisit(errorStatement, data);
+ this.EndVisit(errorStatement);
+ return result;
+ }
+
+ public override object VisitEventAddRegion(EventAddRegion eventAddRegion, object data) {
+ this.BeginVisit(eventAddRegion);
+ object result = this.TrackedVisit(eventAddRegion, data);
+ this.EndVisit(eventAddRegion);
+ return result;
+ }
+
+ public override object VisitEventDeclaration(EventDeclaration eventDeclaration, object data) {
+ this.BeginVisit(eventDeclaration);
+ object result = this.TrackedVisit(eventDeclaration, data);
+ this.EndVisit(eventDeclaration);
+ return result;
+ }
+
+ public override object VisitEventRaiseRegion(EventRaiseRegion eventRaiseRegion, object data) {
+ this.BeginVisit(eventRaiseRegion);
+ object result = this.TrackedVisit(eventRaiseRegion, data);
+ this.EndVisit(eventRaiseRegion);
+ return result;
+ }
+
+ public override object VisitEventRemoveRegion(EventRemoveRegion eventRemoveRegion, object data) {
+ this.BeginVisit(eventRemoveRegion);
+ object result = this.TrackedVisit(eventRemoveRegion, data);
+ this.EndVisit(eventRemoveRegion);
+ return result;
+ }
+
+ public override object VisitExitStatement(ExitStatement exitStatement, object data) {
+ this.BeginVisit(exitStatement);
+ object result = this.TrackedVisit(exitStatement, data);
+ this.EndVisit(exitStatement);
+ return result;
+ }
+
+ public override object VisitExpressionStatement(ExpressionStatement expressionStatement, object data) {
+ this.BeginVisit(expressionStatement);
+ object result = this.TrackedVisit(expressionStatement, data);
+ this.EndVisit(expressionStatement);
+ return result;
+ }
+
+ public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) {
+ this.BeginVisit(fieldDeclaration);
+ object result = this.TrackedVisit(fieldDeclaration, data);
+ this.EndVisit(fieldDeclaration);
+ return result;
+ }
+
+ public override object VisitFieldReferenceExpression(FieldReferenceExpression fieldReferenceExpression, object data) {
+ this.BeginVisit(fieldReferenceExpression);
+ object result = this.TrackedVisit(fieldReferenceExpression, data);
+ this.EndVisit(fieldReferenceExpression);
+ return result;
+ }
+
+ public override object VisitFixedStatement(FixedStatement fixedStatement, object data) {
+ this.BeginVisit(fixedStatement);
+ object result = this.TrackedVisit(fixedStatement, data);
+ this.EndVisit(fixedStatement);
+ return result;
+ }
+
+ public override object VisitForeachStatement(ForeachStatement foreachStatement, object data) {
+ this.BeginVisit(foreachStatement);
+ object result = this.TrackedVisit(foreachStatement, data);
+ this.EndVisit(foreachStatement);
+ return result;
+ }
+
+ public override object VisitForNextStatement(ForNextStatement forNextStatement, object data) {
+ this.BeginVisit(forNextStatement);
+ object result = this.TrackedVisit(forNextStatement, data);
+ this.EndVisit(forNextStatement);
+ return result;
+ }
+
+ public override object VisitForStatement(ForStatement forStatement, object data) {
+ this.BeginVisit(forStatement);
+ object result = this.TrackedVisit(forStatement, data);
+ this.EndVisit(forStatement);
+ return result;
+ }
+
+ public override object VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, object data) {
+ this.BeginVisit(gotoCaseStatement);
+ object result = this.TrackedVisit(gotoCaseStatement, data);
+ this.EndVisit(gotoCaseStatement);
+ return result;
+ }
+
+ public override object VisitGotoStatement(GotoStatement gotoStatement, object data) {
+ this.BeginVisit(gotoStatement);
+ object result = this.TrackedVisit(gotoStatement, data);
+ this.EndVisit(gotoStatement);
+ return result;
+ }
+
+ public override object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) {
+ this.BeginVisit(identifierExpression);
+ object result = this.TrackedVisit(identifierExpression, data);
+ this.EndVisit(identifierExpression);
+ return result;
+ }
+
+ public override object VisitIfElseStatement(IfElseStatement ifElseStatement, object data) {
+ this.BeginVisit(ifElseStatement);
+ object result = this.TrackedVisit(ifElseStatement, data);
+ this.EndVisit(ifElseStatement);
+ return result;
+ }
+
+ public override object VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, object data) {
+ this.BeginVisit(indexerDeclaration);
+ object result = this.TrackedVisit(indexerDeclaration, data);
+ this.EndVisit(indexerDeclaration);
+ return result;
+ }
+
+ public override object VisitIndexerExpression(IndexerExpression indexerExpression, object data) {
+ this.BeginVisit(indexerExpression);
+ object result = this.TrackedVisit(indexerExpression, data);
+ this.EndVisit(indexerExpression);
+ return result;
+ }
+
+ public override object VisitInnerClassTypeReference(InnerClassTypeReference innerClassTypeReference, object data) {
+ this.BeginVisit(innerClassTypeReference);
+ object result = this.TrackedVisit(innerClassTypeReference, data);
+ this.EndVisit(innerClassTypeReference);
+ return result;
+ }
+
+ public override object VisitInterfaceImplementation(InterfaceImplementation interfaceImplementation, object data) {
+ this.BeginVisit(interfaceImplementation);
+ object result = this.TrackedVisit(interfaceImplementation, data);
+ this.EndVisit(interfaceImplementation);
+ return result;
+ }
+
+ public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) {
+ this.BeginVisit(invocationExpression);
+ object result = this.TrackedVisit(invocationExpression, data);
+ this.EndVisit(invocationExpression);
+ return result;
+ }
+
+ public override object VisitLabelStatement(LabelStatement labelStatement, object data) {
+ this.BeginVisit(labelStatement);
+ object result = this.TrackedVisit(labelStatement, data);
+ this.EndVisit(labelStatement);
+ return result;
+ }
+
+ public override object VisitLocalVariableDeclaration(LocalVariableDeclaration localVariableDeclaration, object data) {
+ this.BeginVisit(localVariableDeclaration);
+ object result = this.TrackedVisit(localVariableDeclaration, data);
+ this.EndVisit(localVariableDeclaration);
+ return result;
+ }
+
+ public override object VisitLockStatement(LockStatement lockStatement, object data) {
+ this.BeginVisit(lockStatement);
+ object result = this.TrackedVisit(lockStatement, data);
+ this.EndVisit(lockStatement);
+ return result;
+ }
+
+ public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) {
+ this.BeginVisit(methodDeclaration);
+ object result = this.TrackedVisit(methodDeclaration, data);
+ this.EndVisit(methodDeclaration);
+ return result;
+ }
+
+ public override object VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, object data) {
+ this.BeginVisit(namedArgumentExpression);
+ object result = this.TrackedVisit(namedArgumentExpression, data);
+ this.EndVisit(namedArgumentExpression);
+ return result;
+ }
+
+ public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) {
+ this.BeginVisit(namespaceDeclaration);
+ object result = this.TrackedVisit(namespaceDeclaration, data);
+ this.EndVisit(namespaceDeclaration);
+ return result;
+ }
+
+ public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) {
+ this.BeginVisit(objectCreateExpression);
+ object result = this.TrackedVisit(objectCreateExpression, data);
+ this.EndVisit(objectCreateExpression);
+ return result;
+ }
+
+ public override object VisitOnErrorStatement(OnErrorStatement onErrorStatement, object data) {
+ this.BeginVisit(onErrorStatement);
+ object result = this.TrackedVisit(onErrorStatement, data);
+ this.EndVisit(onErrorStatement);
+ return result;
+ }
+
+ public override object VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data) {
+ this.BeginVisit(operatorDeclaration);
+ object result = this.TrackedVisit(operatorDeclaration, data);
+ this.EndVisit(operatorDeclaration);
+ return result;
+ }
+
+ public override object VisitOptionDeclaration(OptionDeclaration optionDeclaration, object data) {
+ this.BeginVisit(optionDeclaration);
+ object result = this.TrackedVisit(optionDeclaration, data);
+ this.EndVisit(optionDeclaration);
+ return result;
+ }
+
+ public override object VisitParameterDeclarationExpression(ParameterDeclarationExpression parameterDeclarationExpression, object data) {
+ this.BeginVisit(parameterDeclarationExpression);
+ object result = this.TrackedVisit(parameterDeclarationExpression, data);
+ this.EndVisit(parameterDeclarationExpression);
+ return result;
+ }
+
+ public override object VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, object data) {
+ this.BeginVisit(parenthesizedExpression);
+ object result = this.TrackedVisit(parenthesizedExpression, data);
+ this.EndVisit(parenthesizedExpression);
+ return result;
+ }
+
+ public override object VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, object data) {
+ this.BeginVisit(pointerReferenceExpression);
+ object result = this.TrackedVisit(pointerReferenceExpression, data);
+ this.EndVisit(pointerReferenceExpression);
+ return result;
+ }
+
+ public override object VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, object data) {
+ this.BeginVisit(primitiveExpression);
+ object result = this.TrackedVisit(primitiveExpression, data);
+ this.EndVisit(primitiveExpression);
+ return result;
+ }
+
+ public override object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) {
+ this.BeginVisit(propertyDeclaration);
+ object result = this.TrackedVisit(propertyDeclaration, data);
+ this.EndVisit(propertyDeclaration);
+ return result;
+ }
+
+ public override object VisitPropertyGetRegion(PropertyGetRegion propertyGetRegion, object data) {
+ this.BeginVisit(propertyGetRegion);
+ object result = this.TrackedVisit(propertyGetRegion, data);
+ this.EndVisit(propertyGetRegion);
+ return result;
+ }
+
+ public override object VisitPropertySetRegion(PropertySetRegion propertySetRegion, object data) {
+ this.BeginVisit(propertySetRegion);
+ object result = this.TrackedVisit(propertySetRegion, data);
+ this.EndVisit(propertySetRegion);
+ return result;
+ }
+
+ public override object VisitRaiseEventStatement(RaiseEventStatement raiseEventStatement, object data) {
+ this.BeginVisit(raiseEventStatement);
+ object result = this.TrackedVisit(raiseEventStatement, data);
+ this.EndVisit(raiseEventStatement);
+ return result;
+ }
+
+ public override object VisitReDimStatement(ReDimStatement reDimStatement, object data) {
+ this.BeginVisit(reDimStatement);
+ object result = this.TrackedVisit(reDimStatement, data);
+ this.EndVisit(reDimStatement);
+ return result;
+ }
+
+ public override object VisitRemoveHandlerStatement(RemoveHandlerStatement removeHandlerStatement, object data) {
+ this.BeginVisit(removeHandlerStatement);
+ object result = this.TrackedVisit(removeHandlerStatement, data);
+ this.EndVisit(removeHandlerStatement);
+ return result;
+ }
+
+ public override object VisitResumeStatement(ResumeStatement resumeStatement, object data) {
+ this.BeginVisit(resumeStatement);
+ object result = this.TrackedVisit(resumeStatement, data);
+ this.EndVisit(resumeStatement);
+ return result;
+ }
+
+ public override object VisitReturnStatement(ReturnStatement returnStatement, object data) {
+ this.BeginVisit(returnStatement);
+ object result = this.TrackedVisit(returnStatement, data);
+ this.EndVisit(returnStatement);
+ return result;
+ }
+
+ public override object VisitSizeOfExpression(SizeOfExpression sizeOfExpression, object data) {
+ this.BeginVisit(sizeOfExpression);
+ object result = this.TrackedVisit(sizeOfExpression, data);
+ this.EndVisit(sizeOfExpression);
+ return result;
+ }
+
+ public override object VisitStackAllocExpression(StackAllocExpression stackAllocExpression, object data) {
+ this.BeginVisit(stackAllocExpression);
+ object result = this.TrackedVisit(stackAllocExpression, data);
+ this.EndVisit(stackAllocExpression);
+ return result;
+ }
+
+ public override object VisitStopStatement(StopStatement stopStatement, object data) {
+ this.BeginVisit(stopStatement);
+ object result = this.TrackedVisit(stopStatement, data);
+ this.EndVisit(stopStatement);
+ return result;
+ }
+
+ public override object VisitSwitchSection(SwitchSection switchSection, object data) {
+ this.BeginVisit(switchSection);
+ object result = this.TrackedVisit(switchSection, data);
+ this.EndVisit(switchSection);
+ return result;
+ }
+
+ public override object VisitSwitchStatement(SwitchStatement switchStatement, object data) {
+ this.BeginVisit(switchStatement);
+ object result = this.TrackedVisit(switchStatement, data);
+ this.EndVisit(switchStatement);
+ return result;
+ }
+
+ public override object VisitTemplateDefinition(TemplateDefinition templateDefinition, object data) {
+ this.BeginVisit(templateDefinition);
+ object result = this.TrackedVisit(templateDefinition, data);
+ this.EndVisit(templateDefinition);
+ return result;
+ }
+
+ public override object VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, object data) {
+ this.BeginVisit(thisReferenceExpression);
+ object result = this.TrackedVisit(thisReferenceExpression, data);
+ this.EndVisit(thisReferenceExpression);
+ return result;
+ }
+
+ public override object VisitThrowStatement(ThrowStatement throwStatement, object data) {
+ this.BeginVisit(throwStatement);
+ object result = this.TrackedVisit(throwStatement, data);
+ this.EndVisit(throwStatement);
+ return result;
+ }
+
+ public override object VisitTryCatchStatement(TryCatchStatement tryCatchStatement, object data) {
+ this.BeginVisit(tryCatchStatement);
+ object result = this.TrackedVisit(tryCatchStatement, data);
+ this.EndVisit(tryCatchStatement);
+ return result;
+ }
+
+ public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) {
+ this.BeginVisit(typeDeclaration);
+ object result = this.TrackedVisit(typeDeclaration, data);
+ this.EndVisit(typeDeclaration);
+ return result;
+ }
+
+ public override object VisitTypeOfExpression(TypeOfExpression typeOfExpression, object data) {
+ this.BeginVisit(typeOfExpression);
+ object result = this.TrackedVisit(typeOfExpression, data);
+ this.EndVisit(typeOfExpression);
+ return result;
+ }
+
+ public override object VisitTypeOfIsExpression(TypeOfIsExpression typeOfIsExpression, object data) {
+ this.BeginVisit(typeOfIsExpression);
+ object result = this.TrackedVisit(typeOfIsExpression, data);
+ this.EndVisit(typeOfIsExpression);
+ return result;
+ }
+
+ public override object VisitTypeReference(TypeReference typeReference, object data) {
+ this.BeginVisit(typeReference);
+ object result = this.TrackedVisit(typeReference, data);
+ this.EndVisit(typeReference);
+ return result;
+ }
+
+ public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data) {
+ this.BeginVisit(typeReferenceExpression);
+ object result = this.TrackedVisit(typeReferenceExpression, data);
+ this.EndVisit(typeReferenceExpression);
+ return result;
+ }
+
+ public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) {
+ this.BeginVisit(unaryOperatorExpression);
+ object result = this.TrackedVisit(unaryOperatorExpression, data);
+ this.EndVisit(unaryOperatorExpression);
+ return result;
+ }
+
+ public override object VisitUncheckedExpression(UncheckedExpression uncheckedExpression, object data) {
+ this.BeginVisit(uncheckedExpression);
+ object result = this.TrackedVisit(uncheckedExpression, data);
+ this.EndVisit(uncheckedExpression);
+ return result;
+ }
+
+ public override object VisitUncheckedStatement(UncheckedStatement uncheckedStatement, object data) {
+ this.BeginVisit(uncheckedStatement);
+ object result = this.TrackedVisit(uncheckedStatement, data);
+ this.EndVisit(uncheckedStatement);
+ return result;
+ }
+
+ public override object VisitUnsafeStatement(UnsafeStatement unsafeStatement, object data) {
+ this.BeginVisit(unsafeStatement);
+ object result = this.TrackedVisit(unsafeStatement, data);
+ this.EndVisit(unsafeStatement);
+ return result;
+ }
+
+ public override object VisitUsing(Using @using, object data) {
+ this.BeginVisit(@using);
+ object result = this.TrackedVisit(@using, data);
+ this.EndVisit(@using);
+ return result;
+ }
+
+ public override object VisitUsingDeclaration(UsingDeclaration usingDeclaration, object data) {
+ this.BeginVisit(usingDeclaration);
+ object result = this.TrackedVisit(usingDeclaration, data);
+ this.EndVisit(usingDeclaration);
+ return result;
+ }
+
+ public override object VisitUsingStatement(UsingStatement usingStatement, object data) {
+ this.BeginVisit(usingStatement);
+ object result = this.TrackedVisit(usingStatement, data);
+ this.EndVisit(usingStatement);
+ return result;
+ }
+
+ public override object VisitVariableDeclaration(VariableDeclaration variableDeclaration, object data) {
+ this.BeginVisit(variableDeclaration);
+ object result = this.TrackedVisit(variableDeclaration, data);
+ this.EndVisit(variableDeclaration);
+ return result;
+ }
+
+ public override object VisitWithStatement(WithStatement withStatement, object data) {
+ this.BeginVisit(withStatement);
+ object result = this.TrackedVisit(withStatement, data);
+ this.EndVisit(withStatement);
+ return result;
+ }
+
+ public override object VisitYieldStatement(YieldStatement yieldStatement, object data) {
+ this.BeginVisit(yieldStatement);
+ object result = this.TrackedVisit(yieldStatement, data);
+ this.EndVisit(yieldStatement);
+ return result;
+ }
+
+ public virtual object TrackedVisit(AddHandlerStatement addHandlerStatement, object data) {
+ return base.VisitAddHandlerStatement(addHandlerStatement, data);
+ }
+
+ public virtual object TrackedVisit(AddressOfExpression addressOfExpression, object data) {
+ return base.VisitAddressOfExpression(addressOfExpression, data);
+ }
+
+ public virtual object TrackedVisit(AnonymousMethodExpression anonymousMethodExpression, object data) {
+ return base.VisitAnonymousMethodExpression(anonymousMethodExpression, data);
+ }
+
+ public virtual object TrackedVisit(ArrayCreateExpression arrayCreateExpression, object data) {
+ return base.VisitArrayCreateExpression(arrayCreateExpression, data);
+ }
+
+ public virtual object TrackedVisit(ArrayInitializerExpression arrayInitializerExpression, object data) {
+ return base.VisitArrayInitializerExpression(arrayInitializerExpression, data);
+ }
+
+ public virtual object TrackedVisit(AssignmentExpression assignmentExpression, object data) {
+ return base.VisitAssignmentExpression(assignmentExpression, data);
+ }
+
+ public virtual object TrackedVisit(ICSharpCode.NRefactory.Ast.Attribute attribute, object data) {
+ return base.VisitAttribute(attribute, data);
+ }
+
+ public virtual object TrackedVisit(AttributeSection attributeSection, object data) {
+ return base.VisitAttributeSection(attributeSection, data);
+ }
+
+ public virtual object TrackedVisit(BaseReferenceExpression baseReferenceExpression, object data) {
+ return base.VisitBaseReferenceExpression(baseReferenceExpression, data);
+ }
+
+ public virtual object TrackedVisit(BinaryOperatorExpression binaryOperatorExpression, object data) {
+ return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
+ }
+
+ public virtual object TrackedVisit(BlockStatement blockStatement, object data) {
+ return base.VisitBlockStatement(blockStatement, data);
+ }
+
+ public virtual object TrackedVisit(BreakStatement breakStatement, object data) {
+ return base.VisitBreakStatement(breakStatement, data);
+ }
+
+ public virtual object TrackedVisit(CaseLabel caseLabel, object data) {
+ return base.VisitCaseLabel(caseLabel, data);
+ }
+
+ public virtual object TrackedVisit(CastExpression castExpression, object data) {
+ return base.VisitCastExpression(castExpression, data);
+ }
+
+ public virtual object TrackedVisit(CatchClause catchClause, object data) {
+ return base.VisitCatchClause(catchClause, data);
+ }
+
+ public virtual object TrackedVisit(CheckedExpression checkedExpression, object data) {
+ return base.VisitCheckedExpression(checkedExpression, data);
+ }
+
+ public virtual object TrackedVisit(CheckedStatement checkedStatement, object data) {
+ return base.VisitCheckedStatement(checkedStatement, data);
+ }
+
+ public virtual object TrackedVisit(ClassReferenceExpression classReferenceExpression, object data) {
+ return base.VisitClassReferenceExpression(classReferenceExpression, data);
+ }
+
+ public virtual object TrackedVisit(CompilationUnit compilationUnit, object data) {
+ return base.VisitCompilationUnit(compilationUnit, data);
+ }
+
+ public virtual object TrackedVisit(ConditionalExpression conditionalExpression, object data) {
+ return base.VisitConditionalExpression(conditionalExpression, data);
+ }
+
+ public virtual object TrackedVisit(ConstructorDeclaration constructorDeclaration, object data) {
+ return base.VisitConstructorDeclaration(constructorDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(ConstructorInitializer constructorInitializer, object data) {
+ return base.VisitConstructorInitializer(constructorInitializer, data);
+ }
+
+ public virtual object TrackedVisit(ContinueStatement continueStatement, object data) {
+ return base.VisitContinueStatement(continueStatement, data);
+ }
+
+ public virtual object TrackedVisit(DeclareDeclaration declareDeclaration, object data) {
+ return base.VisitDeclareDeclaration(declareDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(DefaultValueExpression defaultValueExpression, object data) {
+ return base.VisitDefaultValueExpression(defaultValueExpression, data);
+ }
+
+ public virtual object TrackedVisit(DelegateDeclaration delegateDeclaration, object data) {
+ return base.VisitDelegateDeclaration(delegateDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(DestructorDeclaration destructorDeclaration, object data) {
+ return base.VisitDestructorDeclaration(destructorDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(DirectionExpression directionExpression, object data) {
+ return base.VisitDirectionExpression(directionExpression, data);
+ }
+
+ public virtual object TrackedVisit(DoLoopStatement doLoopStatement, object data) {
+ return base.VisitDoLoopStatement(doLoopStatement, data);
+ }
+
+ public virtual object TrackedVisit(ElseIfSection elseIfSection, object data) {
+ return base.VisitElseIfSection(elseIfSection, data);
+ }
+
+ public virtual object TrackedVisit(EmptyStatement emptyStatement, object data) {
+ return base.VisitEmptyStatement(emptyStatement, data);
+ }
+
+ public virtual object TrackedVisit(EndStatement endStatement, object data) {
+ return base.VisitEndStatement(endStatement, data);
+ }
+
+ public virtual object TrackedVisit(EraseStatement eraseStatement, object data) {
+ return base.VisitEraseStatement(eraseStatement, data);
+ }
+
+ public virtual object TrackedVisit(ErrorStatement errorStatement, object data) {
+ return base.VisitErrorStatement(errorStatement, data);
+ }
+
+ public virtual object TrackedVisit(EventAddRegion eventAddRegion, object data) {
+ return base.VisitEventAddRegion(eventAddRegion, data);
+ }
+
+ public virtual object TrackedVisit(EventDeclaration eventDeclaration, object data) {
+ return base.VisitEventDeclaration(eventDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(EventRaiseRegion eventRaiseRegion, object data) {
+ return base.VisitEventRaiseRegion(eventRaiseRegion, data);
+ }
+
+ public virtual object TrackedVisit(EventRemoveRegion eventRemoveRegion, object data) {
+ return base.VisitEventRemoveRegion(eventRemoveRegion, data);
+ }
+
+ public virtual object TrackedVisit(ExitStatement exitStatement, object data) {
+ return base.VisitExitStatement(exitStatement, data);
+ }
+
+ public virtual object TrackedVisit(ExpressionStatement expressionStatement, object data) {
+ return base.VisitExpressionStatement(expressionStatement, data);
+ }
+
+ public virtual object TrackedVisit(FieldDeclaration fieldDeclaration, object data) {
+ return base.VisitFieldDeclaration(fieldDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(FieldReferenceExpression fieldReferenceExpression, object data) {
+ return base.VisitFieldReferenceExpression(fieldReferenceExpression, data);
+ }
+
+ public virtual object TrackedVisit(FixedStatement fixedStatement, object data) {
+ return base.VisitFixedStatement(fixedStatement, data);
+ }
+
+ public virtual object TrackedVisit(ForeachStatement foreachStatement, object data) {
+ return base.VisitForeachStatement(foreachStatement, data);
+ }
+
+ public virtual object TrackedVisit(ForNextStatement forNextStatement, object data) {
+ return base.VisitForNextStatement(forNextStatement, data);
+ }
+
+ public virtual object TrackedVisit(ForStatement forStatement, object data) {
+ return base.VisitForStatement(forStatement, data);
+ }
+
+ public virtual object TrackedVisit(GotoCaseStatement gotoCaseStatement, object data) {
+ return base.VisitGotoCaseStatement(gotoCaseStatement, data);
+ }
+
+ public virtual object TrackedVisit(GotoStatement gotoStatement, object data) {
+ return base.VisitGotoStatement(gotoStatement, data);
+ }
+
+ public virtual object TrackedVisit(IdentifierExpression identifierExpression, object data) {
+ return base.VisitIdentifierExpression(identifierExpression, data);
+ }
+
+ public virtual object TrackedVisit(IfElseStatement ifElseStatement, object data) {
+ return base.VisitIfElseStatement(ifElseStatement, data);
+ }
+
+ public virtual object TrackedVisit(IndexerDeclaration indexerDeclaration, object data) {
+ return base.VisitIndexerDeclaration(indexerDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(IndexerExpression indexerExpression, object data) {
+ return base.VisitIndexerExpression(indexerExpression, data);
+ }
+
+ public virtual object TrackedVisit(InnerClassTypeReference innerClassTypeReference, object data) {
+ return base.VisitInnerClassTypeReference(innerClassTypeReference, data);
+ }
+
+ public virtual object TrackedVisit(InterfaceImplementation interfaceImplementation, object data) {
+ return base.VisitInterfaceImplementation(interfaceImplementation, data);
+ }
+
+ public virtual object TrackedVisit(InvocationExpression invocationExpression, object data) {
+ return base.VisitInvocationExpression(invocationExpression, data);
+ }
+
+ public virtual object TrackedVisit(LabelStatement labelStatement, object data) {
+ return base.VisitLabelStatement(labelStatement, data);
+ }
+
+ public virtual object TrackedVisit(LocalVariableDeclaration localVariableDeclaration, object data) {
+ return base.VisitLocalVariableDeclaration(localVariableDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(LockStatement lockStatement, object data) {
+ return base.VisitLockStatement(lockStatement, data);
+ }
+
+ public virtual object TrackedVisit(MethodDeclaration methodDeclaration, object data) {
+ return base.VisitMethodDeclaration(methodDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(NamedArgumentExpression namedArgumentExpression, object data) {
+ return base.VisitNamedArgumentExpression(namedArgumentExpression, data);
+ }
+
+ public virtual object TrackedVisit(NamespaceDeclaration namespaceDeclaration, object data) {
+ return base.VisitNamespaceDeclaration(namespaceDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(ObjectCreateExpression objectCreateExpression, object data) {
+ return base.VisitObjectCreateExpression(objectCreateExpression, data);
+ }
+
+ public virtual object TrackedVisit(OnErrorStatement onErrorStatement, object data) {
+ return base.VisitOnErrorStatement(onErrorStatement, data);
+ }
+
+ public virtual object TrackedVisit(OperatorDeclaration operatorDeclaration, object data) {
+ return base.VisitOperatorDeclaration(operatorDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(OptionDeclaration optionDeclaration, object data) {
+ return base.VisitOptionDeclaration(optionDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(ParameterDeclarationExpression parameterDeclarationExpression, object data) {
+ return base.VisitParameterDeclarationExpression(parameterDeclarationExpression, data);
+ }
+
+ public virtual object TrackedVisit(ParenthesizedExpression parenthesizedExpression, object data) {
+ return base.VisitParenthesizedExpression(parenthesizedExpression, data);
+ }
+
+ public virtual object TrackedVisit(PointerReferenceExpression pointerReferenceExpression, object data) {
+ return base.VisitPointerReferenceExpression(pointerReferenceExpression, data);
+ }
+
+ public virtual object TrackedVisit(PrimitiveExpression primitiveExpression, object data) {
+ return base.VisitPrimitiveExpression(primitiveExpression, data);
+ }
+
+ public virtual object TrackedVisit(PropertyDeclaration propertyDeclaration, object data) {
+ return base.VisitPropertyDeclaration(propertyDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(PropertyGetRegion propertyGetRegion, object data) {
+ return base.VisitPropertyGetRegion(propertyGetRegion, data);
+ }
+
+ public virtual object TrackedVisit(PropertySetRegion propertySetRegion, object data) {
+ return base.VisitPropertySetRegion(propertySetRegion, data);
+ }
+
+ public virtual object TrackedVisit(RaiseEventStatement raiseEventStatement, object data) {
+ return base.VisitRaiseEventStatement(raiseEventStatement, data);
+ }
+
+ public virtual object TrackedVisit(ReDimStatement reDimStatement, object data) {
+ return base.VisitReDimStatement(reDimStatement, data);
+ }
+
+ public virtual object TrackedVisit(RemoveHandlerStatement removeHandlerStatement, object data) {
+ return base.VisitRemoveHandlerStatement(removeHandlerStatement, data);
+ }
+
+ public virtual object TrackedVisit(ResumeStatement resumeStatement, object data) {
+ return base.VisitResumeStatement(resumeStatement, data);
+ }
+
+ public virtual object TrackedVisit(ReturnStatement returnStatement, object data) {
+ return base.VisitReturnStatement(returnStatement, data);
+ }
+
+ public virtual object TrackedVisit(SizeOfExpression sizeOfExpression, object data) {
+ return base.VisitSizeOfExpression(sizeOfExpression, data);
+ }
+
+ public virtual object TrackedVisit(StackAllocExpression stackAllocExpression, object data) {
+ return base.VisitStackAllocExpression(stackAllocExpression, data);
+ }
+
+ public virtual object TrackedVisit(StopStatement stopStatement, object data) {
+ return base.VisitStopStatement(stopStatement, data);
+ }
+
+ public virtual object TrackedVisit(SwitchSection switchSection, object data) {
+ return base.VisitSwitchSection(switchSection, data);
+ }
+
+ public virtual object TrackedVisit(SwitchStatement switchStatement, object data) {
+ return base.VisitSwitchStatement(switchStatement, data);
+ }
+
+ public virtual object TrackedVisit(TemplateDefinition templateDefinition, object data) {
+ return base.VisitTemplateDefinition(templateDefinition, data);
+ }
+
+ public virtual object TrackedVisit(ThisReferenceExpression thisReferenceExpression, object data) {
+ return base.VisitThisReferenceExpression(thisReferenceExpression, data);
+ }
+
+ public virtual object TrackedVisit(ThrowStatement throwStatement, object data) {
+ return base.VisitThrowStatement(throwStatement, data);
+ }
+
+ public virtual object TrackedVisit(TryCatchStatement tryCatchStatement, object data) {
+ return base.VisitTryCatchStatement(tryCatchStatement, data);
+ }
+
+ public virtual object TrackedVisit(TypeDeclaration typeDeclaration, object data) {
+ return base.VisitTypeDeclaration(typeDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(TypeOfExpression typeOfExpression, object data) {
+ return base.VisitTypeOfExpression(typeOfExpression, data);
+ }
+
+ public virtual object TrackedVisit(TypeOfIsExpression typeOfIsExpression, object data) {
+ return base.VisitTypeOfIsExpression(typeOfIsExpression, data);
+ }
+
+ public virtual object TrackedVisit(TypeReference typeReference, object data) {
+ return base.VisitTypeReference(typeReference, data);
+ }
+
+ public virtual object TrackedVisit(TypeReferenceExpression typeReferenceExpression, object data) {
+ return base.VisitTypeReferenceExpression(typeReferenceExpression, data);
+ }
+
+ public virtual object TrackedVisit(UnaryOperatorExpression unaryOperatorExpression, object data) {
+ return base.VisitUnaryOperatorExpression(unaryOperatorExpression, data);
+ }
+
+ public virtual object TrackedVisit(UncheckedExpression uncheckedExpression, object data) {
+ return base.VisitUncheckedExpression(uncheckedExpression, data);
+ }
+
+ public virtual object TrackedVisit(UncheckedStatement uncheckedStatement, object data) {
+ return base.VisitUncheckedStatement(uncheckedStatement, data);
+ }
+
+ public virtual object TrackedVisit(UnsafeStatement unsafeStatement, object data) {
+ return base.VisitUnsafeStatement(unsafeStatement, data);
+ }
+
+ public virtual object TrackedVisit(Using @using, object data) {
+ return base.VisitUsing(@using, data);
+ }
+
+ public virtual object TrackedVisit(UsingDeclaration usingDeclaration, object data) {
+ return base.VisitUsingDeclaration(usingDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(UsingStatement usingStatement, object data) {
+ return base.VisitUsingStatement(usingStatement, data);
+ }
+
+ public virtual object TrackedVisit(VariableDeclaration variableDeclaration, object data) {
+ return base.VisitVariableDeclaration(variableDeclaration, data);
+ }
+
+ public virtual object TrackedVisit(WithStatement withStatement, object data) {
+ return base.VisitWithStatement(withStatement, data);
+ }
+
+ public virtual object TrackedVisit(YieldStatement yieldStatement, object data) {
+ return base.VisitYieldStatement(yieldStatement, data);
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/PositionTrackingAstVisitor.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/PositionTrackingAstVisitor.cs
new file mode 100644
index 0000000000..562e55579e
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/PositionTrackingAstVisitor.cs
@@ -0,0 +1,142 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Provides contextual position information while iterating through
+ /// the AST and the ability to resolve expressions in-place.
+ ///
+ public abstract class PositionTrackingAstVisitor : NodeTrackingAstVisitor
+ {
+
+ private Stack 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();
+ }
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Gets a flag that indicates whether the current node is located
+ /// inside a block which position information is available for.
+ ///
+ protected bool PositionAvailable {
+ get {
+ return this.parentNodes.Count > 0;
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ protected Location CurrentNodeStartLocation {
+ get {
+ return this.parentNodes.Peek().StartLocation;
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ protected Location CurrentNodeEndLocation {
+ get {
+ return this.parentNodes.Peek().EndLocation;
+ }
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Resolves an expression in the current node's context.
+ ///
+ /// The expression to be resolved.
+ /// Any member declared in the source file in question. Used to get the language, file name and file content.
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected PositionTrackingAstVisitor() : base()
+ {
+ this.parentNodes = new Stack();
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/PropertyFieldAssociationVisitor.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/PropertyFieldAssociationVisitor.cs
new file mode 100644
index 0000000000..ad4f0cbedb
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/PropertyFieldAssociationVisitor.cs
@@ -0,0 +1,242 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+
+using ICSharpCode.Core;
+using ICSharpCode.NRefactory.Ast;
+using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.Dom;
+
+namespace Hornung.ResourceToolkit.Resolver
+{
+ ///
+ /// Finds connections between fields and properties.
+ /// TODO: This currently only works if the field and the property are declared in the same source file.
+ ///
+ public class PropertyFieldAssociationVisitor : PositionTrackingAstVisitor
+ {
+ IMember memberToFind;
+ IMember associatedMember;
+
+ ///
+ /// Gets the field that has been found to be associated with the property specified at the constructor call.
+ ///
+ public IField AssociatedField {
+ get {
+ return this.associatedMember as IField;
+ }
+ }
+
+ ///
+ /// Gets the property that has been found to be associated with the field specified at the constructor call.
+ ///
+ 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);
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The property to find the associated field for.
+ public PropertyFieldAssociationVisitor(IProperty property) : base()
+ {
+ if (property == null) {
+ throw new ArgumentNullException("property");
+ }
+ this.memberToFind = property;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The field to find the associated property for.
+ 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;
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ResourceResolveResult.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ResourceResolveResult.cs
new file mode 100644
index 0000000000..ed17274fd1
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ResourceResolveResult.cs
@@ -0,0 +1,80 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+
+using ICSharpCode.SharpDevelop.Dom;
+
+using Hornung.ResourceToolkit.ResourceFileContent;
+
+namespace Hornung.ResourceToolkit.Resolver
+{
+ ///
+ /// Describes a reference to a resource.
+ ///
+ public class ResourceResolveResult : ResolveResult
+ {
+
+ IResourceFileContent resourceFileContent;
+ string key;
+
+ ///
+ /// Gets the of the resource being referenced.
+ ///
+ public IResourceFileContent ResourceFileContent {
+ get {
+ return this.resourceFileContent;
+ }
+ }
+
+ ///
+ /// Gets the resource key being referenced. May be null if the key is unknown/not yet typed.
+ ///
+ public string Key {
+ get {
+ return this.key;
+ }
+ }
+
+ ///
+ /// Gets the resource file name that contains the resource being referenced.
+ /// Only valid if both and are not null.
+ ///
+ 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;
+ }
+
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The class that contains the reference to the resource.
+ /// The member that contains the reference to the resource.
+ /// The type of the resource being referenced.
+ /// The that contains the resource being referenced.
+ /// The resource key being referenced.
+ public ResourceResolveResult(IClass callingClass, IMember callingMember, IReturnType returnType, IResourceFileContent resourceFileContent, string key)
+ : base(callingClass, callingMember, returnType)
+ {
+ this.resourceFileContent = resourceFileContent;
+ this.key = key;
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/DefaultBclResourceFileContentFactory.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/DefaultBclResourceFileContentFactory.cs
new file mode 100644
index 0000000000..02721d1e1d
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/DefaultBclResourceFileContentFactory.cs
@@ -0,0 +1,63 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.IO;
+
+namespace Hornung.ResourceToolkit.ResourceFileContent
+{
+ ///
+ /// Creates resource file contents for .resources and .resx files.
+ ///
+ public class DefaultBclResourceFileContentFactory : IResourceFileContentFactory
+ {
+ ///
+ /// Determines whether this factory can create a resource file content
+ /// for the specified file.
+ ///
+ /// The file name to examine.
+ /// true, if this factory can create a resource file content for the specified file, otherwise false.
+ 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;
+ }
+
+ ///
+ /// Creates a resource file content for the specified file.
+ ///
+ /// The name of the file to create the resource file content for.
+ /// A new instance of a class that implements and represents the content of the specified file, or null, if this class cannot handle the file format.
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DefaultBclResourceFileContentFactory()
+ {
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/DefaultFileLocalizedResourcesFinder.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/DefaultFileLocalizedResourcesFinder.cs
new file mode 100644
index 0000000000..e560c776f8
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/DefaultFileLocalizedResourcesFinder.cs
@@ -0,0 +1,109 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+
+using ICSharpCode.Core;
+
+namespace Hornung.ResourceToolkit.ResourceFileContent
+{
+ ///
+ /// Finds localized resources that follow the standard .NET pattern
+ /// MyResources.(culture).(extension).
+ ///
+ public class DefaultFileLocalizedResourcesFinder : ILocalizedResourcesFinder
+ {
+ ///
+ /// Gets localized resources that belong to the master resource file.
+ ///
+ /// The name of the master resource file.
+ /// A dictionary of culture names and associated resource file contents, or null, if there are none.
+ public IDictionary 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));
+ }
+
+ ///
+ /// Finds all localized resource files that follow the pattern
+ /// <>.<culture><>.
+ ///
+ /// The full path and name of the master resource file without extension.
+ /// The extension of the master resource file (with leading dot).
+ /// A dictionary of culture names and associated resource file contents.
+ public static IDictionary FindLocalizedResourceFiles(string fileNameWithoutExtension, string extension)
+ {
+ Dictionary list = new Dictionary();
+
+ #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 ?? "")+"'");
+ #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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DefaultFileLocalizedResourcesFinder()
+ {
+ }
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ILocalizedResourcesFinder.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ILocalizedResourcesFinder.cs
new file mode 100644
index 0000000000..e18a88ec6c
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ILocalizedResourcesFinder.cs
@@ -0,0 +1,26 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace Hornung.ResourceToolkit.ResourceFileContent
+{
+ ///
+ /// Describes an object that can find localized resources that belong to a
+ /// master resource.
+ ///
+ public interface ILocalizedResourcesFinder
+ {
+ ///
+ /// Gets localized resources that belong to the master resource file.
+ ///
+ /// The name of the master resource file.
+ /// A dictionary of culture names and associated resource file contents, or null, if there are none.
+ IDictionary GetLocalizedContents(string fileName);
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IMultiResourceFileContent.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IMultiResourceFileContent.cs
new file mode 100644
index 0000000000..e4595ad6ea
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IMultiResourceFileContent.cs
@@ -0,0 +1,26 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+
+namespace Hornung.ResourceToolkit.ResourceFileContent
+{
+ ///
+ /// Describes the content of multiple resource files.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
+ public interface IMultiResourceFileContent : IResourceFileContent
+ {
+
+ ///
+ /// Gets the file name of the resource file the specified key is in.
+ ///
+ /// The name of the resource file the specified key is in, or null if the key cannot be found in any resource file this instance represents.
+ string GetFileNameForKey(string key);
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IResourceFileContent.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IResourceFileContent.cs
new file mode 100644
index 0000000000..738cc3e942
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IResourceFileContent.cs
@@ -0,0 +1,82 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace Hornung.ResourceToolkit.ResourceFileContent
+{
+ ///
+ /// Describes the content of a resource file.
+ ///
+ public interface IResourceFileContent
+ {
+
+ ///
+ /// 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.
+ ///
+ string FileName {
+ get;
+ }
+
+ ///
+ /// Gets the culture of the resource file this instance represents.
+ ///
+ CultureInfo Culture {
+ get;
+ }
+
+ ///
+ /// Gets an iterator that can be used to iterate over all key/value pairs in this resource.
+ ///
+ IEnumerable> Data {
+ get;
+ }
+
+ ///
+ /// Determines if the resource file this instance represents contains the specified key.
+ ///
+ bool ContainsKey(string key);
+
+ ///
+ /// Tries to get the value of the resource with the specified key.
+ ///
+ /// true, if the key exists, otherwise false.
+ bool TryGetValue(string key, out object value);
+
+ ///
+ /// Adds a new key to the resource file.
+ ///
+ /// A key with the same name already exists.
+ void Add(string key, object value);
+
+ ///
+ /// Modify the value of an existing entry.
+ ///
+ /// The specified key does not exist.
+ void SetValue(string key, object value);
+
+ ///
+ /// Renames a resource key.
+ ///
+ /// The old name of the resource key to rename.
+ /// The new name of the resource key.
+ /// The specified key does not exist.
+ void RenameKey(string oldName, string newName);
+
+ ///
+ /// Removes the specified resource key permanently.
+ ///
+ /// The resource key to remove.
+ /// The specified key does not exist.
+ void RemoveKey(string key);
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IResourceFileContentFactory.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IResourceFileContentFactory.cs
new file mode 100644
index 0000000000..c45367e017
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/IResourceFileContentFactory.cs
@@ -0,0 +1,33 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+
+namespace Hornung.ResourceToolkit.ResourceFileContent
+{
+ ///
+ /// Describes an object that can create instances of classes that
+ /// implement .
+ ///
+ public interface IResourceFileContentFactory
+ {
+ ///
+ /// Determines whether this factory can create a resource file content
+ /// for the specified file.
+ ///
+ /// The file name to examine.
+ /// true, if this factory can create a resource file content for the specified file, otherwise false.
+ bool CanCreateContentForFile(string fileName);
+
+ ///
+ /// Creates a resource file content for the specified file.
+ ///
+ /// The name of the file to create the resource file content for.
+ /// A new instance of a class that implements and represents the content of the specified file, or null, if this class cannot handle the file format.
+ IResourceFileContent CreateContentForFile(string fileName);
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/MergedResourceFileContent.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/MergedResourceFileContent.cs
new file mode 100644
index 0000000000..ce57402cd1
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/MergedResourceFileContent.cs
@@ -0,0 +1,213 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace Hornung.ResourceToolkit.ResourceFileContent
+{
+ ///
+ /// Makes multiple 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.
+ ///
+ public class MergedResourceFileContent : IMultiResourceFileContent
+ {
+
+ IResourceFileContent masterContent;
+ IResourceFileContent[] otherContents;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The master resource file content.
+ /// Additional resource file contents.
+ /// The cultures of the specified resource file contents do not match.
+ 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.");
+ }
+ }
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Gets the file name of the master resource file.
+ ///
+ public string FileName {
+ get {
+ return this.masterContent.FileName;
+ }
+ }
+
+ ///
+ /// Gets the culture of the resource files this instance represents.
+ ///
+ public CultureInfo Culture {
+ get {
+ return this.masterContent.Culture;
+ }
+ }
+
+ ///
+ /// Gets an iterator that can be used to iterate over all key/value pairs in all resource files this instance represents.
+ ///
+ public IEnumerable> Data {
+ get {
+ foreach (KeyValuePair entry in this.masterContent.Data) {
+ yield return entry;
+ }
+ foreach (IResourceFileContent c in this.otherContents) {
+ foreach (KeyValuePair entry in c.Data) {
+ yield return entry;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Determines if any of the resource files this instance represents contains the specified key.
+ ///
+ 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;
+ }
+
+ ///
+ /// Tries to get the value of the resource with the specified key.
+ ///
+ /// true, if the key exists, otherwise false.
+ 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;
+ }
+
+ ///
+ /// Adds a new key to the master resource file.
+ ///
+ /// A key with the same name already exists.
+ public void Add(string key, object value)
+ {
+ this.masterContent.Add(key, value);
+ }
+
+ ///
+ /// Modify the value of an existing entry.
+ ///
+ /// The specified key does not exist.
+ 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");
+ }
+
+ ///
+ /// Renames a resource key.
+ ///
+ /// The old name of the resource key to rename.
+ /// The new name of the resource key.
+ /// The specified key does not exist or the new key does already exist.
+ 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");
+ }
+
+ ///
+ /// Removes the specified resource key permanently.
+ ///
+ /// The resource key to remove.
+ /// The specified key does not exist.
+ 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");
+ }
+
+ ///
+ /// Gets the file name of the resource file the specified key is in.
+ ///
+ /// The name of the resource file the specified key is in, or null if the key cannot be found in any resource file this instance represents.
+ 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;
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResXResourceFileContent.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResXResourceFileContent.cs
new file mode 100644
index 0000000000..e6de768a3d
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResXResourceFileContent.cs
@@ -0,0 +1,89 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Resources;
+
+namespace Hornung.ResourceToolkit.ResourceFileContent
+{
+ ///
+ /// Describes the content of a .resx resource file.
+ ///
+ public class ResXResourceFileContent : ResourcesResourceFileContent
+ {
+
+ Dictionary metadata;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The file name of the resource file this instance represents.
+ public ResXResourceFileContent(string fileName) : base(fileName)
+ {
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes all instance fields in preparation for loading of the file content.
+ ///
+ protected override void InitializeContent()
+ {
+ base.InitializeContent();
+ this.metadata = new Dictionary();
+ }
+
+ ///
+ /// Loads the content of the specified into the cache.
+ ///
+ /// The to be used to read the resource content.
+ 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);
+ }
+ }
+
+ ///
+ /// Gets a resx resource reader for the resource file represented by this instance.
+ ///
+ /// A resx resource reader for the resource file represented by this instance.
+ protected override IResourceReader GetResourceReader()
+ {
+ return new ResXResourceReader(this.FileName);
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Save changes done to the resource content to disk.
+ ///
+ /// The to be used to save the resource content.
+ protected override void SaveContent(IResourceWriter writer)
+ {
+ base.SaveContent(writer);
+ ResXResourceWriter w = (ResXResourceWriter)writer;
+ foreach (KeyValuePair entry in this.metadata) {
+ w.AddMetadata(entry.Key, entry.Value);
+ }
+ }
+
+ ///
+ /// Gets a resx resource writer for the resource file represented by this instance.
+ ///
+ /// A resx resource writer for the resource file represented by this instance.
+ protected override IResourceWriter GetResourceWriter()
+ {
+ return new ResXResourceWriter(this.FileName);
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResourceFileContentRegistry.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResourceFileContentRegistry.cs
new file mode 100644
index 0000000000..c167e3bcff
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResourceFileContentRegistry.cs
@@ -0,0 +1,120 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using ICSharpCode.Core;
+
+namespace Hornung.ResourceToolkit.ResourceFileContent
+{
+ ///
+ /// Provides facilities to load and cache the contents of resource files.
+ ///
+ public static class ResourceFileContentRegistry
+ {
+ ///
+ /// The AddIn tree path where the resource file content factories are registered.
+ ///
+ public const string ResourceFileContentFactoriesAddInTreePath = "/AddIns/ResourceToolkit/ResourceFileContentFactories";
+
+ static List factories;
+
+ ///
+ /// Gets a list of all registered resource file content factories.
+ ///
+ public static IEnumerable Factories {
+ get {
+ if (factories == null) {
+ factories = AddInTree.BuildItems(ResourceFileContentFactoriesAddInTreePath, null, false);
+ }
+ return factories;
+ }
+ }
+
+
+ static Dictionary resourceFileContents = new Dictionary();
+
+ ///
+ /// Gets the resource content for the specified file.
+ ///
+ /// The name of the file to get a resource content for.
+ /// The resource content for the specified file.
+ /// The format of the specified resource file cannot be handled.
+ 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;
+ }
+
+ ///
+ /// Creates the resource content for the specified file.
+ ///
+ /// The name of the file to create a resource content for.
+ /// The resource content for the specified file, or null, if the resource file format cannot be handled.
+ static IResourceFileContent CreateResourceFileContent(string fileName)
+ {
+ foreach (IResourceFileContentFactory factory in Factories) {
+ if (factory.CanCreateContentForFile(fileName)) {
+ return factory.CreateContentForFile(fileName);
+ }
+ }
+ return null;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// The AddIn tree path where the localized resource finders are registered.
+ ///
+ public const string LocalizedResourcesFindersAddInTreePath = "/AddIns/ResourceToolkit/LocalizedResourcesFinders";
+
+ static List localizedResourcesFinders;
+
+ ///
+ /// Gets a list of all registered localized resources finders.
+ ///
+ public static IEnumerable LocalizedResourcesFinders {
+ get {
+ if (localizedResourcesFinders == null) {
+ localizedResourcesFinders = AddInTree.BuildItems(LocalizedResourcesFindersAddInTreePath, null, false);
+ }
+ return localizedResourcesFinders;
+ }
+ }
+
+ ///
+ /// Gets localized resources that belong to the master resource file.
+ ///
+ /// The name of the master resource file.
+ /// A dictionary of culture names and associated resource file contents.
+ public static IDictionary GetLocalizedContents(string fileName)
+ {
+ Dictionary list = new Dictionary();
+ foreach (ILocalizedResourcesFinder finder in LocalizedResourcesFinders) {
+ IDictionary l = finder.GetLocalizedContents(fileName);
+ if (l != null) {
+ foreach (KeyValuePair entry in l) {
+ if (!list.ContainsKey(entry.Key)) {
+ list.Add(entry.Key, entry.Value);
+ }
+ }
+ }
+ }
+ return list;
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResourcesResourceFileContent.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResourcesResourceFileContent.cs
new file mode 100644
index 0000000000..e5d27e3043
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceFileContent/ResourcesResourceFileContent.cs
@@ -0,0 +1,270 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Describes the content of a .resources resource file.
+ ///
+ public class ResourcesResourceFileContent : IResourceFileContent
+ {
+
+ readonly string fileName;
+ readonly CultureInfo culture;
+
+ DateTime lastWriteTimeUtc;
+ Dictionary data;
+
+ ///
+ /// Gets the file name of the resource file this instance represents.
+ ///
+ public string FileName {
+ get {
+ return this.fileName;
+ }
+ }
+
+ ///
+ /// Gets the culture of the resource file this instance represents.
+ ///
+ public CultureInfo Culture {
+ get {
+ return this.culture;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The file name of the resource file this instance represents.
+ 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
+ }
+
+ ///
+ /// Synchronises the cache with the content of the actual file on disk.
+ ///
+ protected void EnsureLoaded()
+ {
+ if (this.data == null || File.GetLastWriteTimeUtc(this.FileName) != this.lastWriteTimeUtc) {
+ this.InitializeContent();
+ this.LoadContent();
+ this.lastWriteTimeUtc = File.GetLastWriteTimeUtc(this.FileName);
+ }
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Initializes all instance fields in preparation for loading of the file content.
+ ///
+ protected virtual void InitializeContent()
+ {
+ this.data = new Dictionary();
+ }
+
+ ///
+ /// Loads the content of the specified into the cache.
+ ///
+ /// The to be used to read the resource content.
+ protected virtual void LoadContent(IResourceReader reader)
+ {
+ IDictionaryEnumerator en = reader.GetEnumerator();
+ while (en.MoveNext()) {
+ this.data.Add((string)en.Key, en.Value);
+ }
+ }
+
+ ///
+ /// Loads the content of the file into the cache.
+ ///
+ protected virtual void LoadContent()
+ {
+ using(IResourceReader reader = this.GetResourceReader()) {
+ if (reader != null) {
+ this.LoadContent(reader);
+ reader.Close();
+ }
+ }
+ }
+
+ ///
+ /// Gets a resource reader for the resource file represented by this instance.
+ ///
+ /// A resource reader for the resource file represented by this instance.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ protected virtual IResourceReader GetResourceReader()
+ {
+ return new ResourceReader(this.FileName);
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Save changes done to the resource content to disk.
+ ///
+ protected void Save()
+ {
+ this.SaveContent();
+ this.lastWriteTimeUtc = File.GetLastWriteTimeUtc(this.FileName);
+ }
+
+ ///
+ /// Save changes done to the resource content to disk.
+ ///
+ protected virtual void SaveContent()
+ {
+ using(IResourceWriter writer = this.GetResourceWriter()) {
+ if (writer != null) {
+ this.SaveContent(writer);
+ writer.Close();
+ }
+ }
+ }
+
+ ///
+ /// Save changes done to the resource content to disk.
+ ///
+ /// The to be used to save the resource content.
+ protected virtual void SaveContent(IResourceWriter writer)
+ {
+ foreach (KeyValuePair entry in this.data) {
+ writer.AddResource(entry.Key, entry.Value);
+ }
+ }
+
+ ///
+ /// Gets a resource writer for the resource file represented by this instance.
+ ///
+ /// A resource writer for the resource file represented by this instance.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ protected virtual IResourceWriter GetResourceWriter()
+ {
+ return new ResourceWriter(this.FileName);
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Gets an iterator that can be used to iterate over all key/value pairs in this resource.
+ ///
+ public IEnumerable> Data {
+ get {
+ this.EnsureLoaded();
+ return this.data;
+ }
+ }
+
+ ///
+ /// Determines if the resource file this instance represents contains the specified key.
+ ///
+ public bool ContainsKey(string key)
+ {
+ this.EnsureLoaded();
+ return this.data.ContainsKey(key);
+ }
+
+ ///
+ /// Tries to get the value of the resource with the specified key.
+ ///
+ /// true, if the key exists, otherwise false.
+ public bool TryGetValue(string key, out object value)
+ {
+ this.EnsureLoaded();
+ return this.data.TryGetValue(key, out value);
+ }
+
+ ///
+ /// Adds a new key to the resource file.
+ ///
+ /// A key with the same name already exists.
+ 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();
+ }
+
+ ///
+ /// Modify the value of an existing entry.
+ ///
+ /// The specified key does not exist.
+ 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();
+ }
+
+ ///
+ /// Renames a resource key.
+ ///
+ /// The old name of the resource key to rename.
+ /// The new name of the resource key.
+ /// The specified key does not exist or the new key does already exist.
+ 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();
+ }
+
+ ///
+ /// Removes the specified resource key permanently.
+ ///
+ /// The resource key to remove.
+ /// The specified key does not exist.
+ 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();
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceResolverService.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceResolverService.cs
new file mode 100644
index 0000000000..0c0eed19b3
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ResourceResolverService.cs
@@ -0,0 +1,136 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Provides facilities to find and resolve expressions referencing resources.
+ ///
+ public static class ResourceResolverService
+ {
+
+ ///
+ /// The AddIn tree path where the resource resolvers are registered.
+ ///
+ public const string ResourceResolversAddInTreePath = "/AddIns/ResourceToolkit/Resolvers";
+
+ // ********************************************************************************************************************************
+
+ static List resolvers;
+
+ ///
+ /// Gets a list of all registered resource resolvers.
+ ///
+ public static IEnumerable Resolvers {
+ get {
+ if (resolvers == null) {
+ resolvers = AddInTree.BuildItems(ResourceResolversAddInTreePath, null, false);
+ }
+ return resolvers;
+ }
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Attempts to resolve a reference to a resource using all registered resolvers.
+ ///
+ /// The text editor for which a resource resolution attempt should be performed.
+ /// A that describes which resource is referenced by the expression at the caret in the specified editor, or null if all registered resolvers return null.
+ public static ResourceResolveResult Resolve(TextEditorControl editor)
+ {
+ ResourceResolveResult result;
+ foreach (IResourceResolver resolver in Resolvers) {
+ if ((result = resolver.Resolve(editor)) != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Attempts to resolve a reference to a resource using all registered resolvers.
+ ///
+ /// The name of the file that contains the expression to be resolved.
+ /// The document that contains the expression to be resolved.
+ /// The 0-based line in the file that contains the expression to be resolved.
+ /// The 0-based column position of the expression to be resolved.
+ /// A that describes which resource is referenced by the expression at the caret in the specified editor, or null if all registered resolvers return null.
+ 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;
+ }
+
+ // ********************************************************************************************************************************
+
+ ///
+ /// Builds the formatted description string for the specified resource.
+ ///
+ 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();
+ }
+
+ }
+}
diff --git a/src/AddIns/Misc/ResourceToolkit/Project/Src/ToolTips/ResourceToolTipProvider.cs b/src/AddIns/Misc/ResourceToolkit/Project/Src/ToolTips/ResourceToolTipProvider.cs
new file mode 100644
index 0000000000..1aaad44e38
--- /dev/null
+++ b/src/AddIns/Misc/ResourceToolkit/Project/Src/ToolTips/ResourceToolTipProvider.cs
@@ -0,0 +1,48 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+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
+{
+ ///
+ /// Provides tooltips for resources.
+ ///
+ 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;
+ }
+
+ }
+}
diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
index de723432e0..bd4e241ebe 100644
--- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
+++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
@@ -744,4 +744,4 @@
-
+
\ No newline at end of file
diff --git a/src/Main/StartUp/Project/Resources/StringResources.resources b/src/Main/StartUp/Project/Resources/StringResources.resources
index 93e8457f9e..386777cb2a 100644
Binary files a/src/Main/StartUp/Project/Resources/StringResources.resources and b/src/Main/StartUp/Project/Resources/StringResources.resources differ
diff --git a/src/SharpDevelop.sln b/src/SharpDevelop.sln
index ab90db3d70..8fb099a6a8 100644
--- a/src/SharpDevelop.sln
+++ b/src/SharpDevelop.sln
@@ -1,5 +1,5 @@
Microsoft Visual Studio Solution File, Format Version 9.00
-# SharpDevelop 2.1.0.1793
+# SharpDevelop 2.1.0.1865
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AddIns", "AddIns", "{14A277EE-7DF1-4529-B639-7D1EF334C1C5}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
@@ -56,6 +56,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{CE5B42B7-6
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceToolkit", "AddIns\Misc\ResourceToolkit\Project\ResourceToolkit.csproj", "{461606BD-E824-4D0A-8CBA-01810B1F5E02}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpDbTools", "AddIns\Misc\SharpServerTools\SharpDbTools\SharpDbTools.csproj", "{F2CC804D-5A9C-4875-A423-4FB9A5D07018}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpServerTools", "AddIns\Misc\SharpServerTools\SharpServerTools.csproj", "{E8922383-B6F5-4107-AB82-49662D98B2FE}"
@@ -376,6 +378,10 @@ Global
{F2CC804D-5A9C-4875-A423-4FB9A5D07018}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F2CC804D-5A9C-4875-A423-4FB9A5D07018}.Release|Any CPU.Build.0 = Release|Any CPU
{F2CC804D-5A9C-4875-A423-4FB9A5D07018}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {461606BD-E824-4D0A-8CBA-01810B1F5E02}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {461606BD-E824-4D0A-8CBA-01810B1F5E02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {461606BD-E824-4D0A-8CBA-01810B1F5E02}.Release|Any CPU.Build.0 = Release|Any CPU
+ {461606BD-E824-4D0A-8CBA-01810B1F5E02}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{CE5B42B7-6E8C-4385-9E97-F4023FC16BF2} = {14A277EE-7DF1-4529-B639-7D1EF334C1C5}
@@ -419,6 +425,7 @@ Global
{BDDDCD01-D2FE-4EAD-9425-4B6B91922C7C} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2}
{E8922383-B6F5-4107-AB82-49662D98B2FE} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2}
{F2CC804D-5A9C-4875-A423-4FB9A5D07018} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2}
+ {461606BD-E824-4D0A-8CBA-01810B1F5E02} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2}
{000E4F64-5D0D-4EB1-B0BF-1A62ADBC6EAD} = {BDDDCD01-D2FE-4EAD-9425-4B6B91922C7C}
{869951D5-A0D6-4DC6-9F1D-E6B9A12AC446} = {BDDDCD01-D2FE-4EAD-9425-4B6B91922C7C}
{E6F4983F-DE41-4AEC-88E7-1FA9AFB4E6FF} = {BDDDCD01-D2FE-4EAD-9425-4B6B91922C7C}