Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
108 lines (94 sloc) 5.04 KB
// Copyright (c) Microsoft Corporation, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Web.Hosting;
using System.Linq;
namespace System.Web.Optimization {
/// <summary>
/// Default <see cref="IBundleOrderer"/> which orders files in a bundled using <see cref="BundleCollection.FileSetOrderList"/>.
/// </summary>
public class DefaultBundleOrderer : IBundleOrderer {
// We only need one of these since everything it does is currently static
internal static DefaultBundleOrderer Instance = new DefaultBundleOrderer();
// build a hash of files by name (Note, we group files that share the same name together)
private static Dictionary<string, HashSet<BundleFile>> BuildFileMap(IEnumerable<BundleFile> files) {
Dictionary<string, HashSet<BundleFile>> fileMap = new Dictionary<string, HashSet<BundleFile>>(StringComparer.OrdinalIgnoreCase);
foreach (BundleFile f in files) {
string key = f.VirtualFile.Name;
if (fileMap.ContainsKey(key)) {
fileMap[key].Add(f);
}
else {
HashSet<BundleFile> l = new HashSet<BundleFile>(BundleFileComparer.Instance);
l.Add(f);
fileMap[key] = l;
}
}
return fileMap;
}
private static void AddOrderingFiles(BundleFileSetOrdering ordering, IEnumerable<BundleFile> files, Dictionary<string, HashSet<BundleFile>> fileMap, HashSet<VirtualFile> foundFiles, List<BundleFile> result) {
foreach (string fileName in ordering.Files) {
// If the file ends in a wildcard we need to do special logic
if (fileName.EndsWith("*", StringComparison.OrdinalIgnoreCase)) {
// Adds all files that match the prefix of the filename (i.e. jquery-*)
string prefix = fileName.Substring(0, fileName.Length - 1);
// iterate thru all the files and add matches
var matchedFiles = files.Where(f => !foundFiles.Contains(f.VirtualFile) && f.VirtualFile.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
foreach (BundleFile f in matchedFiles) {
result.Add(f);
foundFiles.Add(f.VirtualFile);
}
}
else {
if (fileMap.ContainsKey(fileName)) {
// Sort the hashed files to guarantee an ordering
List<BundleFile> sortedFiles = new List<BundleFile>(fileMap[fileName]);
sortedFiles.Sort(BundleFileComparer.Instance);
foreach (BundleFile fi in sortedFiles) {
if (!foundFiles.Contains(fi.VirtualFile)) { // Only add the file to the bundle once
result.Add(fi);
foundFiles.Add(fi.VirtualFile);
}
}
}
}
}
}
/// <summary>
/// Reorder files that match patterns specified in <see cref="BundleCollection.FileSetOrderList"/> so that they are bundled before any
/// files that do not match.
/// </summary>
public virtual IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files) {
if (context == null) {
throw new ArgumentNullException("context");
}
if (files == null) {
throw new ArgumentNullException("files");
}
// No need to do anything if no filesets in order list
if (context.BundleCollection.FileSetOrderList.Count == 0) {
return files;
}
// Goal is to return a list of files in FileSetOrderList where each registered file set is ordered if it exists
List<BundleFile> result = new List<BundleFile>();
List<BundleFile> fileList = new List<BundleFile>(files);
Dictionary<string, HashSet<BundleFile>> fileMap = BuildFileMap(fileList);
if (fileMap.Count == 0) {
return result;
}
HashSet<VirtualFile> foundFiles = new HashSet<VirtualFile>(VirtualFileComparer.Instance);
// For each ordering if we find a file, output all files that match that name
foreach (BundleFileSetOrdering ordering in context.BundleCollection.FileSetOrderList) {
AddOrderingFiles(ordering, fileList, fileMap, foundFiles, result);
}
// Last step, add all unused files to the final list
foreach (BundleFile f in fileList) {
if (!foundFiles.Contains(f.VirtualFile)) {
result.Add(f);
foundFiles.Add(f.VirtualFile);
}
}
return result;
}
}
}