~halega/+junk/sharpdevelop

« back to all changes in this revision

Viewing changes to src/AddIns/Misc/ResourceToolkit/Project/Src/Resolver/ICSharpCodeCoreResourceResolver.cs

  • Committer: sk
  • Date: 2011-09-10 05:17:57 UTC
  • Revision ID: halega@halega.com-20110910051757-qfouz1llya9m6boy
4.1.0.7915 Release Candidate 1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
 
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
 
3
 
 
4
using System;
 
5
using System.Collections.Generic;
 
6
using System.IO;
 
7
using System.Text;
 
8
 
 
9
using ICSharpCode.Core;
 
10
using ICSharpCode.SharpDevelop.Editor;
 
11
using ICSharpCode.SharpDevelop.Project;
 
12
 
 
13
namespace Hornung.ResourceToolkit.Resolver
 
14
{
 
15
        /// <summary>
 
16
        /// Resolves references to resources that are accessed using ICSharpCode.Core
 
17
        /// ("${res: ... }").
 
18
        /// </summary>
 
19
        public class ICSharpCodeCoreResourceResolver : AbstractResourceResolver
 
20
        {
 
21
                
 
22
                /// <summary>
 
23
                /// Initializes a new instance of the <see cref="ICSharpCodeCoreResourceResolver"/> class.
 
24
                /// </summary>
 
25
                public ICSharpCodeCoreResourceResolver() : base()
 
26
                {
 
27
                }
 
28
                
 
29
                // ********************************************************************************************************************************
 
30
                
 
31
                /// <summary>
 
32
                /// Determines whether this resolver supports resolving resources in the given file.
 
33
                /// </summary>
 
34
                /// <param name="fileName">The name of the file to examine.</param>
 
35
                /// <returns><c>true</c>, if this resolver supports resolving resources in the given file, <c>false</c> otherwise.</returns>
 
36
                public override bool SupportsFile(string fileName)
 
37
                {
 
38
                        // Any parseable source code file may contain references
 
39
                        if (ResourceResolverService.GetParser(fileName) != null) {
 
40
                                return true;
 
41
                        }
 
42
                        
 
43
                        // Support additional files by extension
 
44
                        switch(Path.GetExtension(fileName).ToLowerInvariant()) {
 
45
                                case ".addin":
 
46
                                case ".xfrm":
 
47
                                case ".xml":
 
48
                                        return true;
 
49
                                default:
 
50
                                        break;
 
51
                        }
 
52
                        
 
53
                        return false;
 
54
                }
 
55
                
 
56
                static readonly string[] possiblePatterns = new string[] {
 
57
                        "${res:"
 
58
                };
 
59
                
 
60
                /// <summary>
 
61
                /// Gets a list of patterns that can be searched for in the specified file
 
62
                /// to find possible resource references that are supported by this
 
63
                /// resolver.
 
64
                /// </summary>
 
65
                /// <param name="fileName">The name of the file to get a list of possible patterns for.</param>
 
66
                public override IEnumerable<string> GetPossiblePatternsForFile(string fileName)
 
67
                {
 
68
                        if (this.SupportsFile(fileName)) {
 
69
                                return possiblePatterns;
 
70
                        }
 
71
                        return new string[0];
 
72
                }
 
73
                
 
74
                // ********************************************************************************************************************************
 
75
                
 
76
                /// <summary>
 
77
                /// The token that indicates a reference to an ICSharpCode.Core resource.
 
78
                /// </summary>
 
79
                public const string ResourceReferenceToken = @"${res:";
 
80
                
 
81
                /// <summary>
 
82
                /// Attempts to resolve a reference to a resource.
 
83
                /// </summary>
 
84
                /// <param name="fileName">The name of the file that contains the expression to be resolved.</param>
 
85
                /// <param name="document">The document that contains the expression to be resolved.</param>
 
86
                /// <param name="caretLine">The 0-based line in the file that contains the expression to be resolved.</param>
 
87
                /// <param name="caretColumn">The 0-based column position of the expression to be resolved.</param>
 
88
                /// <param name="caretOffset">The offset of the position of the expression to be resolved.</param>
 
89
                /// <param name="charTyped">The character that has been typed at the caret position but is not yet in the buffer (this is used when invoked from code completion), or <c>null</c>.</param>
 
90
                /// <returns>A <see cref="ResourceResolveResult"/> that describes which resource is referenced by the expression at the specified position in the specified file, or <c>null</c> if that expression does not reference a (known) resource.</returns>
 
91
                protected override ResourceResolveResult Resolve(string fileName, IDocument document, int caretLine, int caretColumn, int caretOffset, char? charTyped)
 
92
                {
 
93
                        // If Resolve is invoked from code completion,
 
94
                        // we are only interested in the ':' character of '${res:'.
 
95
                        if (charTyped != null && charTyped != ':') {
 
96
                                return null;
 
97
                        }
 
98
                        
 
99
                        // Find $ character to the left of the caret.
 
100
                        char ch = document.GetCharAt(caretOffset);
 
101
                        if (ch == '}' && caretOffset > 0) {
 
102
                                ch = document.GetCharAt(--caretOffset);
 
103
                        }
 
104
                        while (!Char.IsWhiteSpace(ch) && ch != '$' && ch != '}' && caretOffset > 0) {
 
105
                                ch = document.GetCharAt(--caretOffset);
 
106
                        }
 
107
                        
 
108
                        if (caretOffset + 6 >= document.TextLength || document.GetText(caretOffset, 6) != ResourceReferenceToken) {
 
109
                                return null;
 
110
                        }
 
111
                        caretOffset += 6;
 
112
                        
 
113
                        // Read resource key.
 
114
                        StringBuilder key = new StringBuilder();
 
115
                        while (caretOffset < document.TextLength && !Char.IsWhiteSpace(ch = document.GetCharAt(caretOffset++)) && ch != '}') {
 
116
                                key.Append(ch);
 
117
                        }
 
118
                        if (ch != '}') {
 
119
                                key = null;
 
120
                                #if DEBUG
 
121
                        } else {
 
122
                                LoggingService.Debug("ResourceToolkit: ICSharpCodeCoreResourceResolver found resource key: "+key.ToString());
 
123
                                #endif
 
124
                        }
 
125
                        
 
126
                        ResourceSetReference resource = ResolveICSharpCodeCoreResourceSet(key == null ? null : key.ToString(), fileName);
 
127
                        
 
128
                        #if DEBUG
 
129
                        if (resource.FileName == null) {
 
130
                                LoggingService.Info("ResourceToolkit: ICSharpCodeCoreResourceResolver: Could not find the ICSharpCode.Core resource file name for the source file '"+fileName+"', key '"+(key == null ? "<null>" : key.ToString())+"'.");
 
131
                        }
 
132
                        #endif
 
133
                        
 
134
                        // TODO: Add information about callingClass, callingMember, returnType
 
135
                        return new ResourceResolveResult(null, null, null, resource, key == null ? null : key.ToString());
 
136
                }
 
137
                
 
138
                // ********************************************************************************************************************************
 
139
                
 
140
                /// <summary>
 
141
                /// Tries to find the resource set which serves as source for
 
142
                /// the resources accessed by the ICSharpCode.Core.
 
143
                /// </summary>
 
144
                /// <param name="key">The resource key to look for. May be null.</param>
 
145
                /// <param name="sourceFileName">The name of the source code file that contains the reference to the resource.</param>
 
146
                public static ResourceSetReference ResolveICSharpCodeCoreResourceSet(string key, string sourceFileName)
 
147
                {
 
148
                        
 
149
                        // As there is no easy way to find out the actual location of the resources
 
150
                        // based on the source code, we just look in some standard directories.
 
151
                        
 
152
                        // Local set (SD addin or standalone application with standard directory structure)
 
153
                        ResourceSetReference local = GetICSharpCodeCoreLocalResourceSet(sourceFileName);
 
154
                        
 
155
                        // Prefer local set, especially if the key is there.
 
156
                        if (local.ResourceFileContent != null) {
 
157
                                if (key != null) {
 
158
                                        if (local.ResourceFileContent.ContainsKey(key)) {
 
159
                                                return local;
 
160
                                        }
 
161
                                } else {
 
162
                                        return local;
 
163
                                }
 
164
                        }
 
165
                        
 
166
                        // Resource set of the host application
 
167
                        ResourceSetReference host = GetICSharpCodeCoreHostResourceSet(sourceFileName);
 
168
                        if (key != null) {
 
169
                                if (host.ResourceFileContent != null) {
 
170
                                        if (host.ResourceFileContent.ContainsKey(key)) {
 
171
                                                return host;
 
172
                                        }
 
173
                                }
 
174
                        }
 
175
                        
 
176
                        // Use local file also if the key is not there
 
177
                        // (allows adding of a new key)
 
178
                        return local.ResourceFileContent == null ? host : local;
 
179
                }
 
180
                
 
181
                /// <summary>
 
182
                /// Tries to find an ICSharpCode.Core resource file in the given path
 
183
                /// according to the file names defined in the AddIn tree.
 
184
                /// </summary>
 
185
                static string FindICSharpCodeCoreResourceFile(string path)
 
186
                {
 
187
                        string file;
 
188
                        foreach (string fileName in AddInTree.BuildItems<string>("/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/ResourceFileNames", null, false)) {
 
189
                                if ((file = FindResourceFileName(Path.Combine(path, fileName))) != null) {
 
190
                                        return file;
 
191
                                }
 
192
                        }
 
193
                        return null;
 
194
                }
 
195
                
 
196
                /// <summary>
 
197
                /// Gets a dummy resource set name used to represent the ICSharpCode.Core
 
198
                /// resource set of the local application.
 
199
                /// </summary>
 
200
                public const string ICSharpCodeCoreLocalResourceSetName = "[ICSharpCodeCoreLocalResourceSet]";
 
201
                
 
202
                /// <summary>
 
203
                /// Gets a dummy resource set name used to represent the ICSharpCode.Core
 
204
                /// resource set of the host application.
 
205
                /// </summary>
 
206
                public const string ICSharpCodeCoreHostResourceSetName = "[ICSharpCodeCoreHostResourceSet]";
 
207
                
 
208
                static readonly ResourceSetReference EmptyLocalResourceSetReference = new ResourceSetReference(ICSharpCodeCoreLocalResourceSetName, null);
 
209
                static readonly ResourceSetReference EmptyHostResourceSetReference = new ResourceSetReference(ICSharpCodeCoreHostResourceSetName, null);
 
210
                
 
211
                /// <summary>
 
212
                /// Tries to find the local string resource set used for ICSharpCode.Core resource access.
 
213
                /// </summary>
 
214
                /// <param name="sourceFileName">The name of the source code file which to find the ICSharpCode.Core resource set for.</param>
 
215
                /// <returns>A <see cref="ResourceSetReference"/> that describes the referenced resource set. The contained file name may be <c>null</c> if the file cannot be determined.</returns>
 
216
                public static ResourceSetReference GetICSharpCodeCoreLocalResourceSet(string sourceFileName)
 
217
                {
 
218
                        IProject project = ProjectFileDictionaryService.GetProjectForFile(sourceFileName);
 
219
                        if (project == null || String.IsNullOrEmpty(project.Directory)) {
 
220
                                return EmptyLocalResourceSetReference;
 
221
                        }
 
222
                        
 
223
                        string localFile;
 
224
                        ResourceSetReference local = null;
 
225
                        
 
226
                        if (!NRefactoryAstCacheService.CacheEnabled || !cachedLocalResourceSets.TryGetValue(project, out local)) {
 
227
                                foreach (string relativePath in AddInTree.BuildItems<string>("/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/LocalResourcesLocations", null, false)) {
 
228
                                        if ((localFile = FindICSharpCodeCoreResourceFile(Path.GetFullPath(Path.Combine(project.Directory, relativePath)))) != null) {
 
229
                                                local = new ResourceSetReference(ICSharpCodeCoreLocalResourceSetName, localFile);
 
230
                                                if (NRefactoryAstCacheService.CacheEnabled) {
 
231
                                                        cachedLocalResourceSets.Add(project, local);
 
232
                                                }
 
233
                                                break;
 
234
                                        }
 
235
                                }
 
236
                        }
 
237
                        
 
238
                        return local ?? EmptyLocalResourceSetReference;
 
239
                }
 
240
                
 
241
                /// <summary>
 
242
                /// Tries to find the string resource set of the host application for ICSharpCode.Core resource access.
 
243
                /// </summary>
 
244
                /// <param name="sourceFileName">The name of the source code file which to find the ICSharpCode.Core resource set for.</param>
 
245
                /// <returns>A <see cref="ResourceSetReference"/> that describes the referenced resource set. The contained file name may be <c>null</c> if the file cannot be determined.</returns>
 
246
                public static ResourceSetReference GetICSharpCodeCoreHostResourceSet(string sourceFileName)
 
247
                {
 
248
                        IProject project = ProjectFileDictionaryService.GetProjectForFile(sourceFileName);
 
249
                        ResourceSetReference host = null;
 
250
                        string hostFile;
 
251
                        
 
252
                        if (project == null ||
 
253
                            !NRefactoryAstCacheService.CacheEnabled || !cachedHostResourceSets.TryGetValue(project, out host)) {
 
254
                                
 
255
                                // Get SD directory using the reference to ICSharpCode.Core
 
256
                                string coreAssemblyFullPath = GetICSharpCodeCoreFullPath(project);
 
257
                                
 
258
                                if (coreAssemblyFullPath == null) {
 
259
                                        // Look for the ICSharpCode.Core project using all available projects.
 
260
                                        if (ProjectService.OpenSolution != null) {
 
261
                                                foreach (IProject p in ProjectService.OpenSolution.Projects) {
 
262
                                                        if ((coreAssemblyFullPath = GetICSharpCodeCoreFullPath(p)) != null) {
 
263
                                                                break;
 
264
                                                        }
 
265
                                                }
 
266
                                        }
 
267
                                }
 
268
                                
 
269
                                if (coreAssemblyFullPath == null) {
 
270
                                        return EmptyHostResourceSetReference;
 
271
                                }
 
272
                                
 
273
                                #if DEBUG
 
274
                                LoggingService.Debug("ResourceToolkit: ICSharpCodeCoreResourceResolver coreAssemblyFullPath = "+coreAssemblyFullPath);
 
275
                                #endif
 
276
                                
 
277
                                foreach (string relativePath in AddInTree.BuildItems<string>("/AddIns/ResourceToolkit/ICSharpCodeCoreResourceResolver/HostResourcesLocations", null, false)) {
 
278
                                        if ((hostFile = FindICSharpCodeCoreResourceFile(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(coreAssemblyFullPath), relativePath)))) != null) {
 
279
                                                host = new ResourceSetReference(ICSharpCodeCoreHostResourceSetName, hostFile);
 
280
                                                if (NRefactoryAstCacheService.CacheEnabled && project != null) {
 
281
                                                        cachedHostResourceSets.Add(project, host);
 
282
                                                }
 
283
                                                break;
 
284
                                        }
 
285
                                }
 
286
                                
 
287
                        }
 
288
                        
 
289
                        return host ?? EmptyHostResourceSetReference;
 
290
                }
 
291
                
 
292
                static string GetICSharpCodeCoreFullPath(IProject sourceProject)
 
293
                {
 
294
                        if (sourceProject == null) {
 
295
                                return null;
 
296
                        }
 
297
                        
 
298
                        string coreAssemblyFullPath = null;
 
299
                        
 
300
                        if (sourceProject.Name.Equals("ICSharpCode.Core", StringComparison.OrdinalIgnoreCase)) {
 
301
                                
 
302
                                // This is the ICSharpCode.Core project itself.
 
303
                                coreAssemblyFullPath = sourceProject.OutputAssemblyFullPath;
 
304
                                
 
305
                        } else {
 
306
                                
 
307
                                // Get the ICSharpCode.Core.dll path by using the project reference.
 
308
                                foreach (ProjectItem item in sourceProject.Items) {
 
309
                                        ProjectReferenceProjectItem prpi = item as ProjectReferenceProjectItem;
 
310
                                        if (prpi != null) {
 
311
                                                if (prpi.ReferencedProject != null) {
 
312
                                                        if (prpi.ReferencedProject.Name.Equals("ICSharpCode.Core", StringComparison.OrdinalIgnoreCase) && prpi.ReferencedProject.OutputAssemblyFullPath != null) {
 
313
                                                                coreAssemblyFullPath = prpi.ReferencedProject.OutputAssemblyFullPath;
 
314
                                                                break;
 
315
                                                        }
 
316
                                                }
 
317
                                        }
 
318
                                        ReferenceProjectItem rpi = item as ReferenceProjectItem;
 
319
                                        if (rpi != null) {
 
320
                                                if (rpi.Name.Equals("ICSharpCode.Core", StringComparison.OrdinalIgnoreCase) && rpi.FileName != null) {
 
321
                                                        coreAssemblyFullPath = rpi.FileName;
 
322
                                                        break;
 
323
                                                }
 
324
                                        }
 
325
                                }
 
326
                                
 
327
                        }
 
328
                        
 
329
                        return coreAssemblyFullPath;
 
330
                }
 
331
                
 
332
                #region ICSharpCode.Core resource set mapping cache
 
333
                
 
334
                static Dictionary<IProject, ResourceSetReference> cachedLocalResourceSets;
 
335
                static Dictionary<IProject, ResourceSetReference> cachedHostResourceSets;
 
336
                
 
337
                static ICSharpCodeCoreResourceResolver()
 
338
                {
 
339
                        cachedLocalResourceSets = new Dictionary<IProject, ResourceSetReference>();
 
340
                        cachedHostResourceSets = new Dictionary<IProject, ResourceSetReference>();
 
341
                        NRefactoryAstCacheService.CacheEnabledChanged += NRefactoryCacheEnabledChanged;
 
342
                }
 
343
                
 
344
                static void NRefactoryCacheEnabledChanged(object sender, EventArgs e)
 
345
                {
 
346
                        if (!NRefactoryAstCacheService.CacheEnabled) {
 
347
                                // Clear cache when disabled.
 
348
                                cachedLocalResourceSets.Clear();
 
349
                                cachedHostResourceSets.Clear();
 
350
                        }
 
351
                }
 
352
                
 
353
                #endregion
 
354
        }
 
355
}