OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.Collections; | |
5 using System.Diagnostics; | |
6 using System.Diagnostics.CodeAnalysis; | |
7 using System.Globalization; | |
8 using System.IO; | |
9 using System.Text; | |
10 | |
11 namespace Microsoft.VisualStudio.Project | |
12 { | |
13 /// <summary> | |
14 /// Replacement type | |
15 /// </summary> | |
16 public enum TokenReplaceType | |
17 { | |
18 ReplaceString, | |
19 ReplaceNumber, | |
20 ReplaceCode | |
21 } | |
22 | |
23 /// <summary> | |
24 /// Contain a number of functions that handle token replacement | |
25 /// </summary> | |
26 [CLSCompliant(false)] | |
27 public class TokenProcessor | |
28 { | |
29 #region fields | |
30 // Internal fields | |
31 private ArrayList tokenlist; | |
32 | |
33 | |
34 #endregion | |
35 | |
36 #region Initialization | |
37 /// <summary> | |
38 /// Constructor | |
39 /// </summary> | |
40 public TokenProcessor() | |
41 { | |
42 tokenlist = new ArrayList(); | |
43 } | |
44 | |
45 /// <summary> | |
46 /// Reset list of TokenReplacer entries | |
47 /// </summary> | |
48 public virtual void Reset() | |
49 { | |
50 tokenlist.Clear(); | |
51 } | |
52 | |
53 | |
54 /// <summary> | |
55 /// Add a replacement type entry | |
56 /// </summary> | |
57 /// <param name="token">token to replace</param> | |
58 /// <param name="replacement">replacement string</param> | |
59 public virtual void AddReplace(string token, string replacement) | |
60 { | |
61 tokenlist.Add(new ReplacePairToken(token, replacement)); | |
62 } | |
63 | |
64 /// <summary> | |
65 /// Add replace between entry | |
66 /// </summary> | |
67 /// <param name="tokenStart">Start token</param> | |
68 /// <param name="tokenEnd">End token</param> | |
69 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBe
SpelledCorrectly", MessageId = "tokenid")] | |
70 public virtual void AddReplaceBetween(string tokenid, string tok
enStart, string tokenEnd, string replacement) | |
71 { | |
72 tokenlist.Add(new ReplaceBetweenPairToken(tokenid, token
Start, tokenEnd, replacement)); | |
73 } | |
74 | |
75 /// <summary> | |
76 /// Add a deletion entry | |
77 /// </summary> | |
78 /// <param name="tokenToDelete">Token to delete</param> | |
79 public virtual void AddDelete(string tokenToDelete) | |
80 { | |
81 tokenlist.Add(new DeleteToken(tokenToDelete)); | |
82 } | |
83 #endregion | |
84 | |
85 #region TokenProcessing | |
86 /// <summary> | |
87 /// For all known token, replace token with correct value | |
88 /// </summary> | |
89 /// <param name="source">File of the source file</param> | |
90 /// <param name="destination">File of the destination file</para
m> | |
91 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Perf
ormance", "CA1800:DoNotCastUnnecessarily"), SuppressMessage("Microsoft.Naming",
"CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Untoken")] | |
92 public virtual void UntokenFile(string source, string destinatio
n) | |
93 { | |
94 if(string.IsNullOrEmpty(source)) | |
95 throw new ArgumentNullException("source"); | |
96 | |
97 if(string.IsNullOrEmpty(destination)) | |
98 throw new ArgumentNullException("destination"); | |
99 | |
100 // Make sure that the destination folder exists. | |
101 string destinationFolder = Path.GetDirectoryName(destina
tion); | |
102 if(!Directory.Exists(destinationFolder)) | |
103 { | |
104 Directory.CreateDirectory(destinationFolder); | |
105 } | |
106 | |
107 //Open the file. Check to see if the File is binary or t
ext. | |
108 // NOTE: This is not correct because GetBinaryType will
return true | |
109 // only if the file is executable, not if it is a dll, a
library or | |
110 // any other type of binary file. | |
111 | |
112 uint binaryType; | |
113 if(!NativeMethods.GetBinaryType(source, out binaryType)) | |
114 { | |
115 Encoding encoding = Encoding.Default; | |
116 string buffer = null; | |
117 // Create the reader to get the text. Note that
we will default to ASCII as | |
118 // encoding if the file does not contains a diff
erent signature. | |
119 using(StreamReader reader = new StreamReader(sou
rce, Encoding.ASCII, true)) | |
120 { | |
121 // Get the content of the file. | |
122 buffer = reader.ReadToEnd(); | |
123 // Detect the encoding of the source fil
e. Note that we | |
124 // can get the encoding only after a rea
d operation is | |
125 // performed on the file. | |
126 encoding = reader.CurrentEncoding; | |
127 } | |
128 foreach(object pair in tokenlist) | |
129 { | |
130 if(pair is DeleteToken) | |
131 DeleteTokens(ref buffer, (Delete
Token)pair); | |
132 if(pair is ReplaceBetweenPairToken) | |
133 ReplaceBetweenTokens(ref buffer,
(ReplaceBetweenPairToken)pair); | |
134 if(pair is ReplacePairToken) | |
135 ReplaceTokens(ref buffer, (Repla
cePairToken)pair); | |
136 } | |
137 File.WriteAllText(destination, buffer, encoding)
; | |
138 } | |
139 else | |
140 File.Copy(source, destination); | |
141 | |
142 } | |
143 | |
144 /// <summary> | |
145 /// Replaces the tokens in a buffer with the replacement string | |
146 /// </summary> | |
147 /// <param name="buffer">Buffer to update</param> | |
148 /// <param name="tokenToReplace">replacement data</param> | |
149 public virtual void ReplaceTokens(ref string buffer, ReplacePair
Token tokenToReplace) | |
150 { | |
151 buffer = buffer.Replace(tokenToReplace.Token, tokenToRep
lace.Replacement); | |
152 } | |
153 | |
154 /// <summary> | |
155 /// Deletes the token from the buffer | |
156 /// </summary> | |
157 /// <param name="buffer">Buffer to update</param> | |
158 /// <param name="tokenToDelete">token to delete</param> | |
159 public virtual void DeleteTokens(ref string buffer, DeleteToken
tokenToDelete) | |
160 { | |
161 buffer = buffer.Replace(tokenToDelete.StringToDelete, st
ring.Empty); | |
162 } | |
163 | |
164 /// <summary> | |
165 /// Replaces the token from the buffer between the provided toke
ns | |
166 /// </summary> | |
167 /// <param name="buffer">Buffer to update</param> | |
168 /// <param name="rpBetweenToken">replacement token</param> | |
169 public virtual void ReplaceBetweenTokens(ref string buffer, Repl
aceBetweenPairToken rpBetweenToken) | |
170 { | |
171 string regularExp = rpBetweenToken.TokenStart + "[^" + r
pBetweenToken.TokenIdentifier + "]*" + rpBetweenToken.TokenEnd; | |
172 buffer = System.Text.RegularExpressions.Regex.Replace(bu
ffer, regularExp, rpBetweenToken.TokenReplacement); | |
173 } | |
174 | |
175 #endregion | |
176 | |
177 #region Guid generators | |
178 /// <summary> | |
179 /// Generates a string representation of a guid with the followi
ng format: | |
180 /// 0x01020304, 0x0506, 0x0708, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x
0E, 0x0F, 0x10 | |
181 /// </summary> | |
182 /// <param name="value">Guid to be generated</param> | |
183 /// <returns>The guid as string</returns> | |
184 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Perf
ormance", "CA1822:MarkMembersAsStatic")] | |
185 public string GuidToForm1(Guid value) | |
186 { | |
187 byte[] GuidBytes = value.ToByteArray(); | |
188 StringBuilder ResultingStr = new StringBuilder(80); | |
189 | |
190 // First 4 bytes | |
191 int i = 0; | |
192 int Number = 0; | |
193 for(i = 0; i < 4; ++i) | |
194 { | |
195 int CurrentByte = GuidBytes[i]; | |
196 Number += CurrentByte << (8 * i); | |
197 } | |
198 UInt32 FourBytes = (UInt32)Number; | |
199 ResultingStr.AppendFormat(CultureInfo.InvariantCulture,
"0x{0}", FourBytes.ToString("X", CultureInfo.InvariantCulture)); | |
200 | |
201 // 2 chunks of 2 bytes | |
202 for(int j = 0; j < 2; ++j) | |
203 { | |
204 Number = 0; | |
205 for(int k = 0; k < 2; ++k) | |
206 { | |
207 int CurrentByte = GuidBytes[i++]; | |
208 Number += CurrentByte << (8 * k); | |
209 } | |
210 UInt16 TwoBytes = (UInt16)Number; | |
211 ResultingStr.AppendFormat(CultureInfo.InvariantC
ulture, ", 0x{0}", TwoBytes.ToString("X", CultureInfo.InvariantCulture)); | |
212 } | |
213 | |
214 // 8 chunks of 1 bytes | |
215 for(int j = 0; j < 8; ++j) | |
216 { | |
217 ResultingStr.AppendFormat(CultureInfo.InvariantC
ulture, ", 0x{0}", GuidBytes[i++].ToString("X", CultureInfo.InvariantCulture)); | |
218 } | |
219 | |
220 return ResultingStr.ToString(); | |
221 } | |
222 #endregion | |
223 | |
224 #region Helper Methods | |
225 /// <summary> | |
226 /// This function will accept a subset of the characters that ca
n create an | |
227 /// identifier name: there are other unicode char that can be in
side the name, but | |
228 /// this function will not allow. By now it can work this way, b
ut when and if the | |
229 /// VSIP package will handle also languages different from engli
sh, this function | |
230 /// must be changed. | |
231 /// </summary> | |
232 /// <param name="c">Character to validate</param> | |
233 /// <returns>true if successful false otherwise</returns> | |
234 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBe
SpelledCorrectly", MessageId = "c")] | |
235 protected static bool IsValidIdentifierChar(char c) | |
236 { | |
237 if((c >= 'a') && (c <= 'z')) | |
238 { | |
239 return true; | |
240 } | |
241 if((c >= 'A') && (c <= 'Z')) | |
242 { | |
243 return true; | |
244 } | |
245 if(c == '_') | |
246 { | |
247 return true; | |
248 } | |
249 if((c >= '0') && (c <= '9')) | |
250 { | |
251 return true; | |
252 } | |
253 | |
254 return false; | |
255 } | |
256 | |
257 /// <summary> | |
258 /// Verifies if the start character is valid | |
259 /// </summary> | |
260 /// <param name="c">Start character</param> | |
261 /// <returns>true if successful false otherwise</returns> | |
262 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBe
SpelledCorrectly", MessageId = "c")] | |
263 protected static bool IsValidIdentifierStartChar(char c) | |
264 { | |
265 if(!IsValidIdentifierChar(c)) | |
266 { | |
267 return false; | |
268 } | |
269 if((c >= '0') && (c <= '9')) | |
270 { | |
271 return false; | |
272 } | |
273 | |
274 return true; | |
275 } | |
276 | |
277 /// <summary> | |
278 /// The goal here is to reduce the risk of name conflict between
2 classes | |
279 /// added in different directories. This code does NOT garanty u
niqueness. | |
280 /// To garanty uniqueness, you should change this function to wo
rk with | |
281 /// the language service to verify that the namespace+class gene
rated does | |
282 /// not conflict. | |
283 /// </summary> | |
284 /// <param name="fileFullPath">Full path to the new file</param> | |
285 /// <returns>Namespace to use for the new file</returns> | |
286 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Perf
ormance", "CA1822:MarkMembersAsStatic")] | |
287 public string GetFileNamespace(string fileFullPath, ProjectNode
node) | |
288 { | |
289 // Get base namespace from the project | |
290 string namespce = node.GetProjectProperty("RootNamespace
"); | |
291 if(String.IsNullOrEmpty(namespce)) | |
292 namespce = Path.GetFileNameWithoutExtension(file
FullPath); ; | |
293 | |
294 // If the item is added to a subfolder, the name space s
hould reflect this. | |
295 // This is done so that class names from 2 files with th
e same name but different | |
296 // directories don't conflict. | |
297 string relativePath = Path.GetDirectoryName(fileFullPath
); | |
298 string projectPath = Path.GetDirectoryName(node.GetMkDoc
ument()); | |
299 // Our project system only support adding files that are
sibling of the project file or that are in subdirectories. | |
300 if(String.Compare(projectPath, 0, relativePath, 0, proje
ctPath.Length, true, CultureInfo.CurrentCulture) == 0) | |
301 { | |
302 relativePath = relativePath.Substring(projectPat
h.Length); | |
303 } | |
304 else | |
305 { | |
306 Debug.Fail("Adding an item to the project that i
s NOT under the project folder."); | |
307 // We are going to use the full file path for ge
nerating the namespace | |
308 } | |
309 | |
310 // Get the list of parts | |
311 int index = 0; | |
312 string[] pathParts; | |
313 pathParts = relativePath.Split(Path.DirectorySeparatorCh
ar); | |
314 | |
315 // Use a string builder with default size being the expe
cted size | |
316 StringBuilder result = new StringBuilder(namespce, names
pce.Length + relativePath.Length + 1); | |
317 // For each path part | |
318 while(index < pathParts.Length) | |
319 { | |
320 string part = pathParts[index]; | |
321 ++index; | |
322 | |
323 // This could happen if the path had leading/tra
iling slash, we want to ignore empty pieces | |
324 if(String.IsNullOrEmpty(part)) | |
325 continue; | |
326 | |
327 // If we reach here, we will be adding something
, so add a namespace separator '.' | |
328 result.Append('.'); | |
329 | |
330 // Make sure it starts with a letter | |
331 if(!char.IsLetter(part, 0)) | |
332 result.Append('N'); | |
333 | |
334 // Filter invalid namespace characters | |
335 foreach(char c in part) | |
336 { | |
337 if(char.IsLetterOrDigit(c)) | |
338 result.Append(c); | |
339 } | |
340 } | |
341 return result.ToString(); | |
342 } | |
343 #endregion | |
344 | |
345 } | |
346 | |
347 /// <summary> | |
348 /// Storage classes for replacement tokens | |
349 /// </summary> | |
350 public class ReplacePairToken | |
351 { | |
352 /// <summary> | |
353 /// token string | |
354 /// </summary> | |
355 private string token; | |
356 | |
357 /// <summary> | |
358 /// Replacement string | |
359 /// </summary> | |
360 private string replacement; | |
361 | |
362 /// <summary> | |
363 /// Constructor | |
364 /// </summary> | |
365 /// <param name="token">replaceable token</param> | |
366 /// <param name="replacement">replacement string</param> | |
367 public ReplacePairToken(string token, string replacement) | |
368 { | |
369 this.token = token; | |
370 this.replacement = replacement; | |
371 } | |
372 | |
373 /// <summary> | |
374 /// Token that needs to be replaced | |
375 /// </summary> | |
376 public string Token | |
377 { | |
378 get { return token; } | |
379 } | |
380 /// <summary> | |
381 /// String to replace the token with | |
382 /// </summary> | |
383 public string Replacement | |
384 { | |
385 get { return replacement; } | |
386 } | |
387 } | |
388 | |
389 /// <summary> | |
390 /// Storage classes for token to be deleted | |
391 /// </summary> | |
392 public class DeleteToken | |
393 { | |
394 /// <summary> | |
395 /// String to delete | |
396 /// </summary> | |
397 private string token; | |
398 | |
399 /// <summary> | |
400 /// Constructor | |
401 /// </summary> | |
402 /// <param name="token">Deletable token.</param> | |
403 public DeleteToken(string token) | |
404 { | |
405 this.token = token; | |
406 } | |
407 | |
408 /// <summary> | |
409 /// Token marking the end of the block to delete | |
410 /// </summary> | |
411 public string StringToDelete | |
412 { | |
413 get { return token; } | |
414 } | |
415 } | |
416 | |
417 /// <summary> | |
418 /// Storage classes for string to be deleted between tokens to be delete
d | |
419 /// </summary> | |
420 public class ReplaceBetweenPairToken | |
421 { | |
422 /// <summary> | |
423 /// Token start | |
424 /// </summary> | |
425 private string tokenStart; | |
426 | |
427 /// <summary> | |
428 /// End token | |
429 /// </summary> | |
430 private string tokenEnd; | |
431 | |
432 /// <summary> | |
433 /// Replacement string | |
434 /// </summary> | |
435 private string replacement; | |
436 | |
437 /// <summary> | |
438 /// Token identifier string | |
439 /// </summary> | |
440 private string tokenidentifier; | |
441 | |
442 /// <summary> | |
443 /// Constructor | |
444 /// </summary> | |
445 /// <param name="blockStart">Start token</param> | |
446 /// <param name="blockEnd">End Token</param> | |
447 /// <param name="replacement">Replacement string.</param> | |
448 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBe
SpelledCorrectly", MessageId = "tokenid")] | |
449 public ReplaceBetweenPairToken(string tokenid, string blockStart
, string blockEnd, string replacement) | |
450 { | |
451 tokenStart = blockStart; | |
452 tokenEnd = blockEnd; | |
453 this.replacement = replacement; | |
454 tokenidentifier = tokenid; | |
455 } | |
456 | |
457 /// <summary> | |
458 /// Token marking the begining of the block to delete | |
459 /// </summary> | |
460 public string TokenStart | |
461 { | |
462 get { return tokenStart; } | |
463 } | |
464 | |
465 /// <summary> | |
466 /// Token marking the end of the block to delete | |
467 /// </summary> | |
468 public string TokenEnd | |
469 { | |
470 get { return tokenEnd; } | |
471 } | |
472 | |
473 /// <summary> | |
474 /// Token marking the end of the block to delete | |
475 /// </summary> | |
476 public string TokenReplacement | |
477 { | |
478 get { return replacement; } | |
479 } | |
480 | |
481 /// <summary> | |
482 /// Token Identifier | |
483 /// </summary> | |
484 public string TokenIdentifier | |
485 { | |
486 get { return tokenidentifier; } | |
487 } | |
488 } | |
489 } | |
OLD | NEW |