Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Metadata / MetadataArtifactLoader.cs
blob215b129f8b7657e3450dcc78546a51fed30cdc35
1 //---------------------------------------------------------------------
2 // <copyright file="MetadataArtifactLoader.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Metadata.Edm
12 using System.Collections.Generic;
13 using System.Data.Entity;
14 using System.Diagnostics;
15 using System.IO;
16 using System.Runtime.Versioning;
17 using System.Xml;
19 /// <summary>
20 /// This is the base class for the resource metadata artifact loader; derived
21 /// classes enacpsulate a single resource as well as collections of resources,
22 /// along the lines of the Composite pattern.
23 /// </summary>
24 internal abstract class MetadataArtifactLoader
26 protected readonly static string resPathPrefix = @"res://";
27 protected readonly static string resPathSeparator = @"/";
28 protected readonly static string altPathSeparator = @"\";
29 protected readonly static string wildcard = @"*";
31 /// <summary>
32 /// Read-only access to the resource/file path
33 /// </summary>
34 public abstract string Path{ get; }
35 public abstract void CollectFilePermissionPaths(List<string> paths, DataSpace spaceToGet);
37 /// <summary>
38 /// This enum is used to indicate the level of extension check to be perfoemed
39 /// on a metadata URI.
40 /// </summary>
41 public enum ExtensionCheck
43 /// <summary>
44 /// Do not perform any extension check
45 /// </summary>
46 None = 0,
48 /// <summary>
49 /// Check the extension against a specific value
50 /// </summary>
51 Specific,
53 /// <summary>
54 /// Check the extension against the set of acceptable extensions
55 /// </summary>
56 All
59 [ResourceExposure(ResourceScope.Machine)] //Exposes the file name which is a Machine resource
60 [ResourceConsumption(ResourceScope.Machine)] //For Create method call. But the path is not created in this method.
61 public static MetadataArtifactLoader Create(string path,
62 ExtensionCheck extensionCheck,
63 string validExtension,
64 ICollection<string> uriRegistry)
66 return Create(path, extensionCheck, validExtension, uriRegistry, new DefaultAssemblyResolver());
68 /// <summary>
69 /// Factory method to create an artifact loader. This is where an appropriate
70 /// subclass of MetadataArtifactLoader is created, depending on the kind of
71 /// resource it will encapsulate.
72 /// </summary>
73 /// <param name="path">The path to the resource(s) to be loaded</param>
74 /// <param name="extensionCheck">Any URI extension checks to perform</param>
75 /// <param name="validExtension">A specific extension for an artifact resource</param>
76 /// <param name="uriRegistry">The global registry of URIs</param>
77 /// <param name="resolveAssembly"></param>
78 /// <returns>A concrete instance of an artifact loader.</returns>
79 [ResourceExposure(ResourceScope.Machine)] //Exposes the file name which is a Machine resource
80 [ResourceConsumption(ResourceScope.Machine)] //For CheckArtifactExtension method call. But the path is not created in this method.
81 internal static MetadataArtifactLoader Create(string path,
82 ExtensionCheck extensionCheck,
83 string validExtension,
84 ICollection<string> uriRegistry,
85 MetadataArtifactAssemblyResolver resolver)
87 Debug.Assert(path != null);
88 Debug.Assert(resolver != null);
90 // res:// -based artifacts
92 if (MetadataArtifactLoader.PathStartsWithResPrefix(path))
94 return MetadataArtifactLoaderCompositeResource.CreateResourceLoader(path, extensionCheck, validExtension, uriRegistry, resolver);
97 // Files and Folders
99 string normalizedPath = MetadataArtifactLoader.NormalizeFilePaths(path);
100 if (Directory.Exists(normalizedPath))
102 return new MetadataArtifactLoaderCompositeFile(normalizedPath, uriRegistry);
104 else if (File.Exists(normalizedPath))
106 switch (extensionCheck)
108 case ExtensionCheck.Specific:
109 CheckArtifactExtension(normalizedPath, validExtension);
110 break;
112 case ExtensionCheck.All:
113 if (!MetadataArtifactLoader.IsValidArtifact(normalizedPath))
115 throw EntityUtil.Metadata(Strings.InvalidMetadataPath);
117 break;
120 return new MetadataArtifactLoaderFile(normalizedPath, uriRegistry);
123 throw EntityUtil.Metadata(Strings.InvalidMetadataPath);
126 /// <summary>
127 /// Factory method to create an aggregating artifact loader, one that encapsulates
128 /// multiple collections.
129 /// </summary>
130 /// <param name="allCollections">The list of collections to be aggregated</param>
131 /// <returns>A concrete instance of an artifact loader.</returns>
132 public static MetadataArtifactLoader Create(List<MetadataArtifactLoader> allCollections)
134 return new MetadataArtifactLoaderComposite(allCollections);
137 /// <summary>
138 /// Helper method that wraps a list of file paths in MetadataArtifactLoader instances.
139 /// </summary>
140 /// <param name="filePaths">The list of file paths to wrap</param>
141 /// <param name="validExtension">An acceptable extension for the file</param>
142 /// <returns>An instance of MetadataArtifactLoader</returns>
143 [ResourceExposure(ResourceScope.Machine)] //Exposes the file names which are a Machine resource
144 [ResourceConsumption(ResourceScope.Machine)] //For CreateCompositeFromFilePaths method call. But the path is not created in this method.
145 public static MetadataArtifactLoader CreateCompositeFromFilePaths(IEnumerable<string> filePaths, string validExtension)
147 Debug.Assert(!string.IsNullOrEmpty(validExtension));
149 return CreateCompositeFromFilePaths(filePaths, validExtension, new DefaultAssemblyResolver());
152 [ResourceExposure(ResourceScope.Machine)] //Exposes the file names which are a Machine resource
153 [ResourceConsumption(ResourceScope.Machine)] //For Create method call. But the paths are not created in this method.
154 internal static MetadataArtifactLoader CreateCompositeFromFilePaths(IEnumerable<string> filePaths, string validExtension, MetadataArtifactAssemblyResolver resolver)
156 ExtensionCheck extensionCheck;
157 if (string.IsNullOrEmpty(validExtension))
159 extensionCheck = ExtensionCheck.All;
161 else
163 extensionCheck = ExtensionCheck.Specific;
166 List<MetadataArtifactLoader> loaders = new List<MetadataArtifactLoader>();
168 // The following set is used to remove duplicate paths from the incoming array
169 HashSet<string> uriRegistry = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
171 foreach(string path in filePaths)
173 if (string.IsNullOrEmpty(path))
175 throw EntityUtil.Metadata(System.Data.Entity.Strings.NotValidInputPath,
176 EntityUtil.CollectionParameterElementIsNullOrEmpty("filePaths"));
179 string trimedPath = path.Trim();
180 if (trimedPath.Length > 0)
182 loaders.Add(MetadataArtifactLoader.Create(
183 trimedPath,
184 extensionCheck,
185 validExtension,
186 uriRegistry,
187 resolver)
192 return MetadataArtifactLoader.Create(loaders);
195 /// <summary>
196 /// Helper method that wraps a collection of XmlReader objects in MetadataArtifactLoader
197 /// instances.
198 /// </summary>
199 /// <param name="filePaths">The collection of XmlReader objects to wrap</param>
200 /// <returns>An instance of MetadataArtifactLoader</returns>
201 public static MetadataArtifactLoader CreateCompositeFromXmlReaders(IEnumerable<XmlReader> xmlReaders)
203 List<MetadataArtifactLoader> loaders = new List<MetadataArtifactLoader>();
205 foreach (XmlReader reader in xmlReaders)
207 if (reader == null)
209 throw EntityUtil.CollectionParameterElementIsNull("xmlReaders");
212 loaders.Add(new MetadataArtifactLoaderXmlReaderWrapper(reader));
215 return MetadataArtifactLoader.Create(loaders);
218 /// <summary>
219 /// If the path doesn't have the right extension, throw
220 /// </summary>
221 /// <param name="path">The path to the resource</param>
222 /// <param name="validExtension"></param>
223 internal static void CheckArtifactExtension(string path, string validExtension)
225 Debug.Assert(!string.IsNullOrEmpty(path));
226 Debug.Assert(!string.IsNullOrEmpty(validExtension));
228 string extension = GetExtension(path);
229 if (!extension.Equals(validExtension, StringComparison.OrdinalIgnoreCase))
231 throw EntityUtil.Metadata(Strings.InvalidFileExtension(path, extension, validExtension));
235 /// <summary>
236 /// Get paths to all artifacts, in the original, unexpanded form
237 /// </summary>
238 /// <returns>A List of strings identifying paths to all resources</returns>
239 public virtual List<string> GetOriginalPaths()
241 return new List<string>(new string[] { Path });
244 /// <summary>
245 /// Get paths to artifacts for a specific DataSpace, in the original, unexpanded
246 /// form
247 /// </summary>
248 /// <param name="spaceToGet">The DataSpace for the artifacts of interest</param>
249 /// <returns>A List of strings identifying paths to all artifacts for a specific DataSpace</returns>
250 public virtual List<string> GetOriginalPaths(DataSpace spaceToGet)
252 List<string> list = new List<string>();
253 if (MetadataArtifactLoader.IsArtifactOfDataSpace(Path, spaceToGet))
255 list.Add(Path);
257 return list;
260 public virtual bool IsComposite
264 return false;
267 /// <summary>
268 /// Get paths to all artifacts
269 /// </summary>
270 /// <returns>A List of strings identifying paths to all resources</returns>
271 public abstract List<string> GetPaths();
273 /// <summary>
274 /// Get paths to artifacts for a specific DataSpace.
275 /// </summary>
276 /// <param name="spaceToGet">The DataSpace for the artifacts of interest</param>
277 /// <returns>A List of strings identifying paths to all artifacts for a specific DataSpace</returns>
278 public abstract List<string> GetPaths(DataSpace spaceToGet);
280 public List<XmlReader> GetReaders()
282 return GetReaders(null);
284 /// <summary>
285 /// Get XmlReaders for all resources
286 /// </summary>
287 /// <returns>A List of XmlReaders for all resources</returns>
288 public abstract List<XmlReader> GetReaders(Dictionary<MetadataArtifactLoader, XmlReader> sourceDictionary);
290 /// <summary>
291 /// Get XmlReaders for a specific DataSpace.
292 /// </summary>
293 /// <param name="spaceToGet">The DataSpace for the artifacts of interest</param>
294 /// <returns>A List of XmlReader object</returns>
295 public abstract List<XmlReader> CreateReaders(DataSpace spaceToGet);
297 /// <summary>
298 /// Helper method to determine whether a given path to a resource
299 /// starts with the "res://" prefix.
300 /// </summary>
301 /// <param name="path">The resource path to test.</param>
302 /// <returns>true if the path represents a resource location</returns>
303 internal static bool PathStartsWithResPrefix(string path)
305 return path.StartsWith(MetadataArtifactLoader.resPathPrefix, StringComparison.OrdinalIgnoreCase);
308 /// <summary>
309 /// Helper method to determine whether a resource identifies a C-Space
310 /// artifact.
311 /// </summary>
312 /// <param name="resource">The resource path</param>
313 /// <returns>true if the resource identifies a C-Space artifact</returns>
314 protected static bool IsCSpaceArtifact(string resource)
316 Debug.Assert(!string.IsNullOrEmpty(resource));
318 string extn = GetExtension(resource);
319 if (!string.IsNullOrEmpty(extn))
321 return string.Compare(extn, XmlConstants.CSpaceSchemaExtension, StringComparison.OrdinalIgnoreCase) == 0;
323 return false;
326 /// <summary>
327 /// Helper method to determine whether a resource identifies an S-Space
328 /// artifact.
329 /// </summary>
330 /// <param name="resource">The resource path</param>
331 /// <returns>true if the resource identifies an S-Space artifact</returns>
332 protected static bool IsSSpaceArtifact(string resource)
334 Debug.Assert(!string.IsNullOrEmpty(resource));
336 string extn = GetExtension(resource);
337 if (!string.IsNullOrEmpty(extn))
339 return string.Compare(extn, XmlConstants.SSpaceSchemaExtension, StringComparison.OrdinalIgnoreCase) == 0;
341 return false;
344 /// <summary>
345 /// Helper method to determine whether a resource identifies a CS-Space
346 /// artifact.
347 /// </summary>
348 /// <param name="resource">The resource path</param>
349 /// <returns>true if the resource identifies a CS-Space artifact</returns>
350 protected static bool IsCSSpaceArtifact(string resource)
352 Debug.Assert(!string.IsNullOrEmpty(resource));
354 string extn = GetExtension(resource);
355 if (!string.IsNullOrEmpty(extn))
357 return string.Compare(extn, XmlConstants.CSSpaceSchemaExtension, StringComparison.OrdinalIgnoreCase) == 0;
359 return false;
362 // don't use Path.GetExtension because it is ok for the resource
363 // name to have characters in it that would be illegal in a path (ie '<' is illegal in a path)
364 // and when they do, Path.GetExtension throws and ArgumentException
365 private static string GetExtension(string resource)
367 if(String.IsNullOrEmpty(resource))
368 return string.Empty;
370 int pos = resource.LastIndexOf('.');
371 if (pos < 0)
372 return string.Empty;
374 return resource.Substring(pos);
378 /// <summary>
379 /// Helper method to determine whether a resource identifies a valid artifact.
380 /// </summary>
381 /// <param name="resource">The resource path</param>
382 /// <returns>true if the resource identifies a valid artifact</returns>
383 internal static bool IsValidArtifact(string resource)
385 Debug.Assert(!string.IsNullOrEmpty(resource));
387 string extn = GetExtension(resource);
388 if (!string.IsNullOrEmpty(extn))
390 return (
391 string.Compare(extn, XmlConstants.CSpaceSchemaExtension, StringComparison.OrdinalIgnoreCase) == 0 ||
392 string.Compare(extn, XmlConstants.SSpaceSchemaExtension, StringComparison.OrdinalIgnoreCase) == 0 ||
393 string.Compare(extn, XmlConstants.CSSpaceSchemaExtension, StringComparison.OrdinalIgnoreCase) == 0
396 return false;
399 /// <summary>
400 /// This helper method accepts a resource URI and a value from the DataSpace enum
401 /// and determines whether the resource identifies an artifact of that DataSpace.
402 /// </summary>
403 /// <param name="resource">A URI to an artifact resource</param>
404 /// <param name="dataSpace">A DataSpace enum value</param>
405 /// <returns>true if the resource identifies an artifact of the specified DataSpace</returns>
406 protected static bool IsArtifactOfDataSpace(string resource, DataSpace dataSpace)
408 if (dataSpace == DataSpace.CSpace)
409 return MetadataArtifactLoader.IsCSpaceArtifact(resource);
411 if (dataSpace == DataSpace.SSpace)
412 return MetadataArtifactLoader.IsSSpaceArtifact(resource);
414 if (dataSpace == DataSpace.CSSpace)
415 return MetadataArtifactLoader.IsCSSpaceArtifact(resource);
417 Debug.Assert(false, "Invalid DataSpace specified.");
418 return false;
421 /// <summary>
422 /// Normalize a file path:
423 /// 1. Add backslashes if given a drive letter.
424 /// 2. Resolve the '~' macro in a Web/ASP.NET environment.
425 /// 3. Expand the |DataDirectory| macro, if found in the argument.
426 /// 4. Convert relative paths into absolute paths.
427 /// </summary>
428 /// <param name="path">the path to normalize</param>
429 /// <returns>The normalized file path</returns>
430 [ResourceExposure(ResourceScope.Machine)] //Exposes the file name which is a Machine resource
431 [ResourceConsumption(ResourceScope.Machine)] //For Path.GetFullPath method call. But the path is not created in this method.
432 static internal string NormalizeFilePaths(string path)
434 bool getFullPath = true; // used to determine whether we need to invoke GetFullPath()
436 if (!String.IsNullOrEmpty(path))
438 path = path.Trim();
440 // If the path starts with a '~' character, try to resolve it as a Web/ASP.NET
441 // application path.
443 if (path.StartsWith(EdmConstants.WebHomeSymbol, StringComparison.Ordinal))
445 AspProxy aspProxy = new AspProxy();
446 path = aspProxy.MapWebPath(path);
447 getFullPath = false;
450 if (path.Length == 2 && path[1] == System.IO.Path.VolumeSeparatorChar)
452 path = path + System.IO.Path.DirectorySeparatorChar;
454 else
456 // See if the path contains the |DataDirectory| macro that we need to
457 // expand. Note that ExpandDataDirectory() won't process the path unless
458 // it begins with the macro.
460 string fullPath = System.Data.EntityClient.DbConnectionOptions.ExpandDataDirectory(
461 System.Data.EntityClient.EntityConnectionStringBuilder.MetadataParameterName, // keyword ("Metadata")
462 path // value
465 // ExpandDataDirectory() returns null if it doesn't find the macro in its
466 // argument.
468 if (fullPath != null)
470 path = fullPath;
471 getFullPath = false;
477 if (getFullPath)
479 path = System.IO.Path.GetFullPath(path);
482 catch (ArgumentException e)
484 throw EntityUtil.Metadata(System.Data.Entity.Strings.NotValidInputPath, e);
486 catch (NotSupportedException e)
488 throw EntityUtil.Metadata(System.Data.Entity.Strings.NotValidInputPath, e);
490 catch (PathTooLongException)
492 throw EntityUtil.Metadata(System.Data.Entity.Strings.NotValidInputPath);
495 return path;