private async Task <Document> LoadDocumentAsync(DocumentSettings settings, Uri uri, DocumentCategory category, DocumentContextCallback contextCallback)
            {
                var cachedDocument = GetCachedDocument(uri);

                if (cachedDocument != null)
                {
                    return(cachedDocument);
                }

                string contents;
                var    flags = settings.AccessFlags;

                if (uri.IsFile)
                {
                    if (!flags.HasFlag(DocumentAccessFlags.EnableFileLoading))
                    {
                        throw new UnauthorizedAccessException("This script engine is not configured for loading documents from the file system");
                    }

                    using (var reader = new StreamReader(uri.LocalPath))
                    {
                        contents = await reader.ReadToEndAsync().ConfigureAwait(false);
                    }
                }
                else
                {
                    if (!flags.HasFlag(DocumentAccessFlags.EnableWebLoading))
                    {
                        throw new UnauthorizedAccessException("This script engine is not configured for downloading documents from the Web");
                    }

                    using (var client = new WebClient())
                    {
                        contents = await client.DownloadStringTaskAsync(uri).ConfigureAwait(false);
                    }
                }

                var documentInfo = new DocumentInfo(uri)
                {
                    Category = category, ContextCallback = contextCallback
                };

                var callback = settings.LoadCallback;

                if (callback != null)
                {
                    callback(ref documentInfo);
                }

                return(CacheDocument(new StringDocument(documentInfo, contents)));
            }
            private static IEnumerable <Uri> GetRawUris(DocumentSettings settings, DocumentInfo?sourceInfo, string specifier)
            {
                Uri    baseUri;
                Uri    uri;
                string path;

                if (sourceInfo.HasValue && SpecifierMayBeRelative(settings, specifier))
                {
                    baseUri = GetBaseUri(sourceInfo.Value);
                    if ((baseUri != null) && Uri.TryCreate(baseUri, specifier, out uri))
                    {
                        yield return(uri);
                    }
                }

                var searchPath = settings.SearchPath;

                if (!string.IsNullOrWhiteSpace(searchPath))
                {
                    foreach (var url in searchPath.SplitSearchPath())
                    {
                        if (Uri.TryCreate(url, UriKind.Absolute, out baseUri) && TryCombineSearchUri(baseUri, specifier, out uri))
                        {
                            yield return(uri);
                        }
                    }
                }

                if (MiscHelpers.Try(out path, () => Path.Combine(Directory.GetCurrentDirectory(), specifier)) && Uri.TryCreate(path, UriKind.Absolute, out uri))
                {
                    yield return(uri);
                }

                if (MiscHelpers.Try(out path, () => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, specifier)) && Uri.TryCreate(path, UriKind.Absolute, out uri))
                {
                    yield return(uri);
                }

                using (var process = Process.GetCurrentProcess())
                {
                    var module = process.MainModule;
                    if ((module != null) && Uri.TryCreate(module.FileName, UriKind.Absolute, out baseUri) && Uri.TryCreate(baseUri, specifier, out uri))
                    {
                        yield return(uri);
                    }
                }
            }
        /// <summary>
        /// Loads a document.
        /// </summary>
        /// <param name="settings">Document access settings for the operation.</param>
        /// <param name="sourceInfo">An optional structure containing meta-information for the requesting document.</param>
        /// <param name="specifier">A string specifying the document to be loaded.</param>
        /// <param name="category">An optional category for the requested document.</param>
        /// <param name="contextCallback">An optional context callback for the requested document.</param>
        /// <returns>A <see cref="Document"/> instance that represents the loaded document.</returns>
        /// <remarks>
        /// A loaded document must have an absolute <see cref="DocumentInfo.Uri">URI</see>. Once a
        /// load operation has completed successfully, subsequent requests that resolve to the same
        /// URI are expected to return the same <see cref="Document"/> reference, although loaders
        /// are not required to manage document caches of unlimited size.
        /// </remarks>
        public virtual Document LoadDocument(DocumentSettings settings, DocumentInfo?sourceInfo, string specifier, DocumentCategory category, DocumentContextCallback contextCallback)
        {
            MiscHelpers.VerifyNonBlankArgument(specifier, "specifier", "Invalid document specifier");

            try
            {
                return(LoadDocumentAsync(settings, sourceInfo, specifier, category, contextCallback).Result);
            }
            catch (AggregateException exception)
            {
                exception = exception.Flatten();
                if (exception.InnerExceptions.Count == 1)
                {
                    throw new FileLoadException(null, specifier, exception.InnerExceptions[0]);
                }

                throw new FileLoadException(null, specifier, exception);
            }
        }
            private static async Task <List <Uri> > GetCandidateUrisAsync(DocumentSettings settings, DocumentInfo?sourceInfo, string specifier)
            {
                var candidateUris = new List <Uri>();

                var rawUris = GetRawUris(settings, sourceInfo, specifier).Distinct();

                if (!string.IsNullOrWhiteSpace(settings.FileNameExtensions))
                {
                    rawUris = rawUris.SelectMany(uri => ApplyFileNameExtensions(sourceInfo, uri, settings.FileNameExtensions));
                }

                foreach (var testUri in rawUris)
                {
                    if (await IsCandidateUriAsync(settings, testUri).ConfigureAwait(false))
                    {
                        candidateUris.Add(testUri);
                    }
                }

                return(candidateUris);
            }
            private static async Task <List <Uri> > GetCandidateUrisAsync(DocumentSettings settings, DocumentInfo?sourceInfo, Uri uri)
            {
                var candidateUris = new List <Uri>();

                if (string.IsNullOrWhiteSpace(settings.FileNameExtensions))
                {
                    candidateUris.Add(uri);
                }
                else
                {
                    foreach (var testUri in ApplyFileNameExtensions(sourceInfo, uri, settings.FileNameExtensions))
                    {
                        if (await IsCandidateUriAsync(settings, testUri).ConfigureAwait(false))
                        {
                            candidateUris.Add(testUri);
                        }
                    }
                }

                return(candidateUris);
            }
 /// <summary>
 /// Loads a document asynchronously.
 /// </summary>
 /// <param name="settings">Document access settings for the operation.</param>
 /// <param name="sourceInfo">An optional structure containing meta-information for the requesting document.</param>
 /// <param name="specifier">A string specifying the document to be loaded.</param>
 /// <param name="category">An optional category for the requested document.</param>
 /// <param name="contextCallback">An optional context callback for the requested document.</param>
 /// <returns>A task that represents the asynchronous operation. Upon completion, the task's result is a <see cref="Document"/> instance that represents the loaded document.</returns>
 /// <remarks>
 /// A loaded document must have an absolute <see cref="DocumentInfo.Uri">URI</see>. Once a
 /// load operation has completed successfully, subsequent requests that resolve to the same
 /// URI are expected to return the same <see cref="Document"/> reference, although loaders
 /// are not required to manage document caches of unlimited size.
 /// </remarks>
 public abstract Task <Document> LoadDocumentAsync(DocumentSettings settings, DocumentInfo?sourceInfo, string specifier, DocumentCategory category, DocumentContextCallback contextCallback);
            public override async Task <Document> LoadDocumentAsync(DocumentSettings settings, DocumentInfo?sourceInfo, string specifier, DocumentCategory category, DocumentContextCallback contextCallback)
            {
                MiscHelpers.VerifyNonNullArgument(settings, "settings");
                MiscHelpers.VerifyNonBlankArgument(specifier, "specifier", "Invalid document specifier");

                if ((settings.AccessFlags & DocumentAccessFlags.EnableAllLoading) == DocumentAccessFlags.None)
                {
                    throw new UnauthorizedAccessException("This script engine is not configured for loading documents");
                }

                if (category == null)
                {
                    category = sourceInfo.HasValue ? sourceInfo.Value.Category : DocumentCategory.Script;
                }

                List <Uri> candidateUris;

                Uri uri;

                if (Uri.TryCreate(specifier, UriKind.RelativeOrAbsolute, out uri) && uri.IsAbsoluteUri)
                {
                    candidateUris = await GetCandidateUrisAsync(settings, sourceInfo, uri).ConfigureAwait(false);
                }
                else
                {
                    candidateUris = await GetCandidateUrisAsync(settings, sourceInfo, specifier).ConfigureAwait(false);
                }

                if (candidateUris.Count < 1)
                {
                    throw new FileNotFoundException(null, specifier);
                }

                if (candidateUris.Count == 1)
                {
                    return(await LoadDocumentAsync(settings, candidateUris[0], category, contextCallback).ConfigureAwait(false));
                }

                var exceptions = new List <Exception>(candidateUris.Count);

                foreach (var candidateUri in candidateUris)
                {
                    var task = LoadDocumentAsync(settings, candidateUri, category, contextCallback);
                    try
                    {
                        return(await task.ConfigureAwait(false));
                    }
                    catch (Exception exception)
                    {
                        if ((task.Exception != null) && task.Exception.InnerExceptions.Count == 1)
                        {
                            Debug.Assert(ReferenceEquals(task.Exception.InnerExceptions[0], exception));
                            exceptions.Add(exception);
                        }
                        else
                        {
                            exceptions.Add(task.Exception);
                        }
                    }
                }

                if (exceptions.Count < 1)
                {
                    MiscHelpers.AssertUnreachable();
                    throw new FileNotFoundException(null, specifier);
                }

                if (exceptions.Count == 1)
                {
                    MiscHelpers.AssertUnreachable();
                    throw new FileLoadException(exceptions[0].Message, specifier, exceptions[0]);
                }

                throw new AggregateException(exceptions).Flatten();
            }
 private static async Task <bool> IsCandidateUriAsync(DocumentSettings settings, Uri uri)
 {
     return(uri.IsFile ?
            settings.AccessFlags.HasFlag(DocumentAccessFlags.EnableFileLoading) && File.Exists(uri.LocalPath) :
            settings.AccessFlags.HasFlag(DocumentAccessFlags.EnableWebLoading) && await WebDocumentExistsAsync(uri).ConfigureAwait(false));
 }
 private static bool SpecifierMayBeRelative(DocumentSettings settings, string specifier)
 {
     return(!settings.AccessFlags.HasFlag(DocumentAccessFlags.EnforceRelativePrefix) || relativePrefixes.Any(specifier.StartsWith));
 }