private int ProcessOutputGroup()

in AjaxMin/MainClass.cs [955:1199]


        private int ProcessOutputGroup(OutputGroup outputGroup, SwitchParser switchParser)
        {
            int retVal = 0;

            // if there are no input groups, we'll be reading from STDIN and should output our
            // headers right now.
            WriteProgress();
            if (outputGroup.Inputs.Count == 0)
            {
                // output status message so user knows input coming from stdin
                WriteProgress(AjaxMin.MinifyFromStdIn);
            }
            else if (outputGroup.Inputs.Count == 1)
            {
                // minifying a single input group. Simple message to let user know what's going on
                WriteProgress(AjaxMin.MinifySingleInput.FormatInvariant(outputGroup.Inputs[0].Path));
            }
            else
            {
                // combining and minifying. More complex messaging.
                WriteProgress(AjaxMin.MinifyingMultipleInputs);
                foreach (var input in outputGroup.Inputs)
                {
                    WriteProgress("\t{0}", input.Path);
                }
            }

            // combine all the source files into a single string, delimited with ///#SOURCE comments so we can track
            // back to the original files. Also, add all the raw input to the echo builder.
            var inputGroups = outputGroup.ReadInputGroups(switchParser.EncodingInputName);

            // we are always going to build the combined raw sources so we can run some comparison calcs on them.
            var rawBuilder = new StringBuilder(8192);

            // for calculation purposes, we're going to want to calculate the length of 
            // all the raw sources, and combine them together
            var sourceLength = 0L;
            foreach (var inputGroup in inputGroups)
            {
                // add the raw source length and the raw source
                // to the echo builder
                sourceLength += inputGroup.RawSource.Length;
                rawBuilder.Append(inputGroup.RawSource);
            }

            // if the crunch group has any resource strings objects, we need to add them to the back
            // of the settings list
            var hasCrunchSpecificResources = outputGroup.Resources.Count > 0;

            // create a string builder we'll dump our output into
            var outputBuilder = new StringBuilder(8192);

            // we're just echoing the input -- so if this is a JS output file,
            // we want to output a JS version of all resource dictionaries at the top
            // of the file.
            if (m_echoInput
                && outputGroup.CodeType == CodeType.JavaScript
                && switchParser.JSSettings.ResourceStrings.Count > 0)
            {
                foreach (var resourceStrings in switchParser.JSSettings.ResourceStrings)
                {
                    string resourceObject = CreateJSFromResourceStrings(resourceStrings);
                    outputBuilder.Append(resourceObject);
                }
            }

            switch (outputGroup.CodeType)
            {
                case CodeType.StyleSheet:
                    if (hasCrunchSpecificResources)
                    {
                        // add to the CSS list
                        outputGroup.ProcessResourceStrings(switchParser.CssSettings.ResourceStrings, null);
                    }

                    retVal = ProcessCssFile(inputGroups, switchParser, outputBuilder);
                    break;

                case CodeType.JavaScript:
                    if (hasCrunchSpecificResources)
                    {
                        // add to the JS list
                        outputGroup.ProcessResourceStrings(switchParser.JSSettings.ResourceStrings, c_defaultResourceObjectName);
                    }

                    retVal = ProcessJSFile(inputGroups, switchParser, outputBuilder);
                    break;

                default:
                    throw new NotSupportedException(AjaxMin.UnknownInputType);
            }

            // if we are pretty-printing, add a newline
            if (switchParser.PrettyPrint)
            {
                outputBuilder.AppendLine();
            }

            string outputCode = outputBuilder.ToString();

            // use the output group encoding. If none specified, use the default output encoding.
            var encodingOutput = outputGroup.GetEncoding(switchParser.EncodingOutputName);

            // now write the final output file
            if (outputGroup.Path.IsNullOrWhiteSpace())
            {
                // no output file specified - send to STDOUT
                // if the code is empty, don't bother outputting it to the console
                if (!outputCode.IsNullOrWhiteSpace())
                {
                    // however, for some reason when I set the output encoding it
                    // STILL doesn't call the EncoderFallback to Unicode-escape characters
                    // not supported by the encoding scheme. So instead we need to run the
                    // translation outselves. Still need to set the output encoding, though,
                    // so the translated bytes get displayed properly in the console.
                    byte[] encodedBytes = encodingOutput.GetBytes(outputCode);

                    // only output the size analysis if we aren't echoing the input
                    // and we haven't said we want to skip it.
                    if (!m_echoInput && !s_silentMode && !m_skipSizeComparisons)
                    {
                        // calculate the percentage saved
                        var percentage = Math.Round((1 - ((double)encodedBytes.Length) / sourceLength) * 100, 1);
                        WriteProgress(AjaxMin.SavingsMessage.FormatInvariant(
                                            sourceLength,
                                            encodedBytes.Length,
                                            percentage
                                            ));

                        // calculate how much gzip on the unminified, combined original source might be
                        long gzipLength = CalculateGzipSize(encodingOutput.GetBytes(rawBuilder.ToString()));
                        percentage = Math.Round((1 - ((double)gzipLength) / sourceLength) * 100, 1);
                        WriteProgress(AjaxMin.SavingsGzipSourceMessage.FormatInvariant(gzipLength, percentage));

                        // calculate how much gzip on the minified output might be
                        gzipLength = CalculateGzipSize(encodedBytes);
                        percentage = Math.Round((1 - ((double)gzipLength) / sourceLength) * 100, 1);
                        WriteProgress(AjaxMin.SavingsGzipMessage.FormatInvariant(gzipLength, percentage));

                        // blank line after
                        WriteProgress();
                    }

                    // send to console out -- if we even want any output
                    if (!m_noOutput)
                    {
                        // set the console encoding
                        try
                        {
                            // try setting the appropriate output encoding
                            Console.OutputEncoding = encodingOutput;
                        }
                        catch (IOException e)
                        {
                            // sometimes they will error, in which case we'll just set it to ascii
                            Debug.WriteLine(e.ToString());

                            // see if we know what kind of fallback we need
                            EncoderFallback fallback = null;
                            if (outputGroup.CodeType == CodeType.JavaScript)
                            {
                                fallback = new JSEncoderFallback();
                            }
                            else if (outputGroup.CodeType == CodeType.StyleSheet)
                            {
                                fallback = new CssEncoderFallback();
                            }

                            if (fallback != null)
                            {
                                // try it again -- but this time if it fails, just leave the stream
                                // to its default encoding.
                                try
                                {
                                    var asciiClone = (Encoding)Encoding.ASCII.Clone();
                                    asciiClone.EncoderFallback = fallback;
                                    Console.OutputEncoding = asciiClone;
                                }
                                catch (IOException)
                                {
                                    // ignore
                                }
                            }
                        }

                        Console.Out.Write(Console.OutputEncoding.GetChars(encodedBytes));
                        WriteProgress();
                    }
                }
            }
            else
            {
                retVal = this.ClobberFileAndExecuteOperation(
                    outputGroup.Path, (path) =>
                    {
                        // create the output file using the given encoding
                        using (StreamWriter outputStream = new StreamWriter(
                           path,
                           false,
                           encodingOutput
                           ))
                        {
                            outputStream.Write(outputCode);
                        }

                        // only output the size analysis if there is actually some output to measure
                        // and we're not echoing the input
                        if (File.Exists(path) && !m_echoInput && !s_silentMode && !m_skipSizeComparisons)
                        {
                            // get the size of the resulting file
                            FileInfo crunchedFileInfo = new FileInfo(path);
                            long crunchedLength = crunchedFileInfo.Length;
                            if (crunchedLength > 0)
                            {
                                // calculate the percentage saved by minification
                                var percentage = Math.Round((1 - ((double)crunchedLength) / sourceLength) * 100, 1);
                                WriteProgress(AjaxMin.SavingsMessage.FormatInvariant(
                                    sourceLength,
                                    crunchedLength,
                                    percentage));

                                // calculate how much gzip on the unminified, combined original source might be
                                long gzipLength = CalculateGzipSize(encodingOutput.GetBytes(rawBuilder.ToString()));
                                percentage = Math.Round((1 - ((double)gzipLength) / sourceLength) * 100, 1);
                                WriteProgress(AjaxMin.SavingsGzipSourceMessage.FormatInvariant(gzipLength, percentage));

                                // calculate how much gzip on the minified output might be
                                gzipLength = CalculateGzipSize(File.ReadAllBytes(path));
                                percentage = Math.Round((1 - ((double)gzipLength) / sourceLength) * 100, 1);
                                WriteProgress(AjaxMin.SavingsGzipMessage.FormatInvariant(gzipLength, percentage));

                                // blank line after
                                WriteProgress();
                            }
                        }
                    });
            }

            if (retVal == 0 && m_errorsFound)
            {
                // make sure we report an error
                retVal = 1;
            }
            return retVal;
        }