<commit_before>using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using SquishIt.Framework.Minifiers;
using SquishIt.Framework.Resolvers;
using SquishIt.Framework.Renderers;
using SquishIt.Framework.Files;
using SquishIt.Framework.Utilities;

namespace SquishIt.Framework.Base
{
    public abstract class BundleBase<T> where T : BundleBase<T>
    {
		private static Dictionary<string, string> renderPathCache = new Dictionary<string, string>();
		
		private const string DEFAULT_GROUP = "default";
        protected IFileWriterFactory fileWriterFactory;
        protected IFileReaderFactory fileReaderFactory;
        protected IDebugStatusReader debugStatusReader;
        protected ICurrentDirectoryWrapper currentDirectoryWrapper;
        protected IHasher hasher;
		protected abstract IMinifier<T> DefaultMinifier { get; }
    	private IMinifier<T> minifier;
    	protected IMinifier<T> Minifier
    	{
    		get
    		{
    			if (minifier == null)
    				minifier = DefaultMinifier;
				return minifier;
    		}
    		set { minifier = value; }
    	}

    	protected string HashKeyName { get; set; }
        private bool ShouldRenderOnlyIfOutputFileIsMissing { get; set; }
        protected List<string> DependentFiles = new List<string>();
        internal Dictionary<string, GroupBundle> GroupBundles = new Dictionary<string, GroupBundle>
        {
            { DEFAULT_GROUP, new GroupBundle() }
        };

    	private IBundleCache bundleCache;

        protected BundleBase(IFileWriterFactory fileWriterFactory, IFileReaderFactory fileReaderFactory, IDebugStatusReader debugStatusReader, ICurrentDirectoryWrapper currentDirectoryWrapper, IHasher hasher, IBundleCache bundleCache)
        {
            this.fileWriterFactory = fileWriterFactory;
            this.fileReaderFactory = fileReaderFactory;
            this.debugStatusReader = debugStatusReader;
            this.currentDirectoryWrapper = currentDirectoryWrapper;
            this.hasher = hasher;
            ShouldRenderOnlyIfOutputFileIsMissing = false;
            HashKeyName = "r";
        	this.bundleCache = bundleCache;
        }

        private List<string> GetFiles(List<Asset> assets)
        {
            var inputFiles = GetInputFiles(assets);
            var resolvedFilePaths = new List<string>();
 
            foreach (InputFile file in inputFiles)
            {
                resolvedFilePaths.AddRange(file.Resolver.TryResolve(file.FilePath));
            }

            return resolvedFilePaths;
        }

        private List<InputFile> GetInputFiles(List<Asset> assets)
        {
            var inputFiles = new List<InputFile>();
            foreach (var asset in assets)
            {
                if (asset.RemotePath == null)
                {
                    inputFiles.Add(GetFileSystemPath(asset.LocalPath));
                }
                else if (asset.IsEmbeddedResource)
                {
                    inputFiles.Add(GetEmbeddedResourcePath(asset.RemotePath));
                }
            }
            return inputFiles;
        }

        private InputFile GetFileSystemPath(string localPath)
        {
            string mappedPath = FileSystem.ResolveAppRelativePathToFileSystem(localPath);
            return new InputFile(mappedPath, ResolverFactory.Get<FileResolver>());
        }
        
        private InputFile GetEmbeddedResourcePath(string resourcePath)
        {
            return new InputFile(resourcePath, ResolverFactory.Get<EmbeddedResourceResolver>());
        }

        private string ExpandAppRelativePath(string file)
        {            
            if (file.StartsWith("~/"))
            {
                string appRelativePath = HttpRuntime.AppDomainAppVirtualPath;
                if (appRelativePath != null && !appRelativePath.EndsWith("/"))
                    appRelativePath += "/";
                return file.Replace("~/", appRelativePath);    
            }
            return file;
        }

        protected string ReadFile(string file)
        {
            using (var sr = fileReaderFactory.GetFileReader(file))
            {
                return sr.ReadToEnd();
            }
        }

        protected bool FileExists(string file)
        {
            return fileReaderFactory.FileExists(file);
        }

        private string GetAdditionalAttributes(GroupBundle groupBundle)
        {
            var result = new StringBuilder();
            foreach (var attribute in groupBundle.Attributes)
            {
                result.Append(attribute.Key);
                result.Append("=\"");
                result.Append(attribute.Value);
                result.Append("\" ");
            }
            return result.ToString();
        }

        private string GetFilesForRemote(List<string> remoteAssetPaths, GroupBundle groupBundle)
        {
            var sb = new StringBuilder();
            foreach (var uri in remoteAssetPaths)
            {
                sb.Append(FillTemplate(groupBundle, uri));
            }

            return sb.ToString();
        }

        private void AddAsset(Asset asset, string group = DEFAULT_GROUP)
        {
            GroupBundle groupBundle;
            if (GroupBundles.TryGetValue(group, out groupBundle))
            {
                groupBundle.Assets.Add(asset);
            }
            else
            {
                groupBundle = new GroupBundle();
                groupBundle.Assets.Add(asset);
                GroupBundles[group] = groupBundle;
            }
        }

        public T Add(string filePath)
        {
            AddAsset(new Asset(filePath));
            return (T)this;
        }

        public T Add(string filePath, string group)
        {
            AddAsset(new Asset(filePath), group);
            return (T)this;
        }

        public T AddRemote(string localPath, string remotePath)
        {
            AddAsset(new Asset(localPath, remotePath));
            return (T)this;
        }

        public T AddEmbeddedResource(string localPath, string embeddedResourcePath)
        {
            AddAsset(new Asset(localPath, embeddedResourcePath, 0, true));
            return (T)this;
        }

		public T RenderOnlyIfOutputFileMissing()
		{
			ShouldRenderOnlyIfOutputFileIsMissing = true;<commit_msg>Add method accepts multiple filePaths. This feature allows to write cleaner code!!<commit_after>using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using SquishIt.Framework.Minifiers;
using SquishIt.Framework.Resolvers;
using SquishIt.Framework.Renderers;
using SquishIt.Framework.Files;
using SquishIt.Framework.Utilities;

namespace SquishIt.Framework.Base
{
    public abstract class BundleBase<T> where T : BundleBase<T>
    {
		private static Dictionary<string, string> renderPathCache = new Dictionary<string, string>();
		
		private const string DEFAULT_GROUP = "default";
        protected IFileWriterFactory fileWriterFactory;
        protected IFileReaderFactory fileReaderFactory;
        protected IDebugStatusReader debugStatusReader;
        protected ICurrentDirectoryWrapper currentDirectoryWrapper;
        protected IHasher hasher;
		protected abstract IMinifier<T> DefaultMinifier { get; }
    	private IMinifier<T> minifier;
    	protected IMinifier<T> Minifier
    	{
    		get
    		{
    			if (minifier == null)
    				minifier = DefaultMinifier;
				return minifier;
    		}
    		set { minifier = value; }
    	}

    	protected string HashKeyName { get; set; }
        private bool ShouldRenderOnlyIfOutputFileIsMissing { get; set; }
        protected List<string> DependentFiles = new List<string>();
        internal Dictionary<string, GroupBundle> GroupBundles = new Dictionary<string, GroupBundle>
        {
            { DEFAULT_GROUP, new GroupBundle() }
        };

    	private IBundleCache bundleCache;

        protected BundleBase(IFileWriterFactory fileWriterFactory, IFileReaderFactory fileReaderFactory, IDebugStatusReader debugStatusReader, ICurrentDirectoryWrapper currentDirectoryWrapper, IHasher hasher, IBundleCache bundleCache)
        {
            this.fileWriterFactory = fileWriterFactory;
            this.fileReaderFactory = fileReaderFactory;
            this.debugStatusReader = debugStatusReader;
            this.currentDirectoryWrapper = currentDirectoryWrapper;
            this.hasher = hasher;
            ShouldRenderOnlyIfOutputFileIsMissing = false;
            HashKeyName = "r";
        	this.bundleCache = bundleCache;
        }

        private List<string> GetFiles(List<Asset> assets)
        {
            var inputFiles = GetInputFiles(assets);
            var resolvedFilePaths = new List<string>();
 
            foreach (InputFile file in inputFiles)
            {
                resolvedFilePaths.AddRange(file.Resolver.TryResolve(file.FilePath));
            }

            return resolvedFilePaths;
        }

        private List<InputFile> GetInputFiles(List<Asset> assets)
        {
            var inputFiles = new List<InputFile>();
            foreach (var asset in assets)
            {
                if (asset.RemotePath == null)
                {
                    inputFiles.Add(GetFileSystemPath(asset.LocalPath));
                }
                else if (asset.IsEmbeddedResource)
                {
                    inputFiles.Add(GetEmbeddedResourcePath(asset.RemotePath));
                }
            }
            return inputFiles;
        }

        private InputFile GetFileSystemPath(string localPath)
        {
            string mappedPath = FileSystem.ResolveAppRelativePathToFileSystem(localPath);
            return new InputFile(mappedPath, ResolverFactory.Get<FileResolver>());
        }
        
        private InputFile GetEmbeddedResourcePath(string resourcePath)
        {
            return new InputFile(resourcePath, ResolverFactory.Get<EmbeddedResourceResolver>());
        }

        private string ExpandAppRelativePath(string file)
        {            
            if (file.StartsWith("~/"))
            {
                string appRelativePath = HttpRuntime.AppDomainAppVirtualPath;
                if (appRelativePath != null && !appRelativePath.EndsWith("/"))
                    appRelativePath += "/";
                return file.Replace("~/", appRelativePath);    
            }
            return file;
        }

        protected string ReadFile(string file)
        {
            using (var sr = fileReaderFactory.GetFileReader(file))
            {
                return sr.ReadToEnd();
            }
        }

        protected bool FileExists(string file)
        {
            return fileReaderFactory.FileExists(file);
        }

        private string GetAdditionalAttributes(GroupBundle groupBundle)
        {
            var result = new StringBuilder();
            foreach (var attribute in groupBundle.Attributes)
            {
                result.Append(attribute.Key);
                result.Append("=\"");
                result.Append(attribute.Value);
                result.Append("\" ");
            }
            return result.ToString();
        }

        private string GetFilesForRemote(List<string> remoteAssetPaths, GroupBundle groupBundle)
        {
            var sb = new StringBuilder();
            foreach (var uri in remoteAssetPaths)
            {
                sb.Append(FillTemplate(groupBundle, uri));
            }

            return sb.ToString();
        }

        private void AddAsset(Asset asset, string group = DEFAULT_GROUP)
        {
            GroupBundle groupBundle;
            if (GroupBundles.TryGetValue(group, out groupBundle))
            {
                groupBundle.Assets.Add(asset);
            }
            else
            {
                groupBundle = new GroupBundle();
                groupBundle.Assets.Add(asset);
                GroupBundles[group] = groupBundle;
            }
        }

        public T Add(params string[] filesPath)
        {
            foreach (var filePath in filesPath)
                Add(filePath);
            
            return (T)this;
        }

        public T Add(string filePath)
        {
            AddAsset(new Asset(filePath));
            return (T)this;
        }

        public T AddInGroup(string group, params string[] filesPath)
        {
            foreach (var filePath in filesPath)
                AddInGroup(group, filePath);

            return (T)this;
        }

        public T AddInGroup( string group, string filePath)
        {
            AddAsset(new Asset(filePath), group);
            return (T)this;
        }

        public T AddRemote(string localPath, string remotePath)
        {
            AddAsset(new Asset(localPath, remotePath));
            return (T)this;
        }

        public T AddEmbeddedResource(string localPath, string embeddedResourcePath)
        {
            AddAsset(new Asset(localPath, embeddedResourcePath, 0, true));
            return (T)this;
        }

		public T RenderOnlyIfOutputFileMissing()
		{
			ShouldRenderOnlyIfOutputFileIsMissing = true;
{'fraction_non_alphanumeric': 0.076999238385377, 'fraction_numerical': 0.0001523229246001523, 'mean_word_length': 1.9435104236718224, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '232828', 'n_tokens_mistral': 3332, 'n_tokens_neox': 3174, 'n_words': 714}