All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.css.compiler.commandline.DefaultCommandLineCompiler Maven / Gradle / Ivy

Go to download

Closure Stylesheets is an extension to CSS that adds variables, functions, conditionals, and mixins to standard CSS. The tool also supports minification, linting, RTL flipping, and CSS class renaming.

There is a newer version: 20160212
Show newest version
/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.css.compiler.commandline;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.css.AbstractCommandLineCompiler;
import com.google.common.css.ExitCodeHandler;
import com.google.common.css.JobDescription;
import com.google.common.css.JobDescription.OutputFormat;
import com.google.common.css.RecordingSubstitutionMap;
import com.google.common.css.SourceCode;
import com.google.common.css.compiler.ast.BasicErrorManager;
import com.google.common.css.compiler.ast.CssTree;
import com.google.common.css.compiler.ast.ErrorManager;
import com.google.common.css.compiler.ast.GssError;
import com.google.common.css.compiler.ast.GssParser;
import com.google.common.css.compiler.ast.GssParserException;
import com.google.common.css.compiler.passes.CompactPrinter;
import com.google.common.css.compiler.passes.DefaultGssSourceMapGenerator;
import com.google.common.css.compiler.passes.GssSourceMapGenerator;
import com.google.common.css.compiler.passes.NullGssSourceMapGenerator;
import com.google.common.css.compiler.passes.PassRunner;
import com.google.common.css.compiler.passes.PrettyPrinter;
import com.google.common.io.Files;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

import javax.annotation.Nullable;

/**
 * {@link DefaultCommandLineCompiler} provides the CSS parser from command line interface to users.
 *
 * @author [email protected] (Oana Florescu)
 */
public class DefaultCommandLineCompiler extends AbstractCommandLineCompiler {

  /**
   * The compiler will limit the number of error messages it outputs to this
   * number.
   */
  protected static final int MAXIMUM_ERRORS_TO_OUTPUT = 100;

  private CssTree cssTree;
  private final ErrorManager errorManager;
  private final PassRunner passRunner;
  private final GssSourceMapGenerator gssSourceMapGenerator;

  /**
   * Constructs a {@code DefaultCommandLineCompiler}.
   *
   * @param job The inputs the compiler should process and the options to use.
   * @param errorManager The error manager to use for error reporting.
   */
  protected DefaultCommandLineCompiler(JobDescription job,
      ExitCodeHandler exitCodeHandler, ErrorManager errorManager) {
    super(job, exitCodeHandler);
    this.errorManager = errorManager;
    this.passRunner = new PassRunner(job, errorManager);
    this.gssSourceMapGenerator = createSourceMapGenerator(job);
  }

  private GssSourceMapGenerator createSourceMapGenerator(JobDescription job) {
    if (!job.createSourceMap) {
      return new NullGssSourceMapGenerator();
    }
    return new DefaultGssSourceMapGenerator(job.sourceMapLevel);
  }

  /**
   * Parses all the inputs, reports error messages and combines the parsed
   * inputs into one stylesheet.
   *
   * @return the resulting stylesheet in string format
   */
  public String compile() throws GssParserException {
    Preconditions.checkState(!compilerWasUsed);
    compilerWasUsed = true;

    // Reserving the input length might not be enough for pretty printed output,
    // but it will certainly save resizing the buffers for compressed output.
    // The length of the copyright notice is also included in the total length.
    int copyrightNoticeSize = job.copyrightNotice != null
        ? job.copyrightNotice.length() : 0;
    StringBuilder result = new StringBuilder(job.getAllInputsLength()
        + copyrightNoticeSize);
    if (job.copyrightNotice != null) {
      result.append(job.copyrightNotice);
    }

    if (job.allowDefPropagation) {
      GssParser parser = new GssParser(job.inputs);
      parseAndPrint(result, parser);
    } else {
      for (SourceCode source : job.inputs) {
        GssParser parser = new GssParser(source);
        parseAndPrint(result, parser);
      }
    }

    return result.toString();
  }

  /**
   * Helper method for parsing and outputting the result.
   */
  private void parseAndPrint(StringBuilder result, GssParser parser)
      throws GssParserException {
    cssTree = parser.parse();
    if (job.outputFormat != OutputFormat.DEBUG) {
      passRunner.runPasses(cssTree);
    }

    if (job.outputFormat == OutputFormat.COMPRESSED) {
      CompactPrinter compactPrinterPass = new CompactPrinter(cssTree, gssSourceMapGenerator);
      compactPrinterPass.runPass();
      result.append(compactPrinterPass.getCompactPrintedString());
    } else {
      PrettyPrinter prettyPrinterPass = new PrettyPrinter(cssTree
          .getVisitController(),
          null /* use external buffer */,
          gssSourceMapGenerator);
      prettyPrinterPass
          .setPreserveComments(job.preserveComments)
          .runPass();
      result.append(prettyPrinterPass.getPrettyPrintedString());
    }
  }

  /**
   * Executes the job associated with this compiler and returns the compiled CSS
   * as a string. If {@code renameFile} is specified along with a
   * {@link RecordingSubstitutionMap}, then the renaming file will be written,
   * as well.
   */
  protected String execute(@Nullable File renameFile, @Nullable File sourcemapFile) {
    try {
      String compilerOutput = compile();

      // Print any errors or warnings.
      errorManager.generateReport();

      // If there were errors, fail.
      if (errorManager.hasErrors()) {
        exitCodeHandler.processExitCode(
            AbstractCommandLineCompiler.ERROR_MESSAGE_EXIT_CODE);
      }

      // Write the class substitution map to file, using same format as
      // VariableMap in jscomp.
      RecordingSubstitutionMap recordingSubstitutionMap = passRunner
          .getRecordingSubstitutionMap();
      if (recordingSubstitutionMap != null && renameFile != null) {
        PrintWriter renamingMapWriter = new PrintWriter(
            Files.newWriter(renameFile, UTF_8));
        Map renamingMap = recordingSubstitutionMap
            .getMappings();
        writeRenamingMap(renamingMap, renamingMapWriter);
        renamingMapWriter.close();
      }

      if (job.createSourceMap
          && sourcemapFile != null && !Strings.isNullOrEmpty(sourcemapFile.getName())) {
        PrintWriter sourceMapWriter = new PrintWriter(
            Files.newWriter(sourcemapFile, UTF_8));
        gssSourceMapGenerator.appendOutputTo(sourceMapWriter, sourcemapFile.getName());
        sourceMapWriter.close();
      }

      return compilerOutput;
    } catch (IOException e) {
      AbstractCommandLineCompiler.exitOnUnhandledException(e, exitCodeHandler);
    } catch (GssParserException e) {
      System.err.println("Compiler parsing error: " + e.getMessage());
      e.printStackTrace();
      exitCodeHandler.processExitCode(
          AbstractCommandLineCompiler.ERROR_MESSAGE_EXIT_CODE);
    } catch (RuntimeException e) {
      System.err.println("Compiler internal error: " + e.getMessage());
      e.printStackTrace();
      exitCodeHandler.processExitCode(
          AbstractCommandLineCompiler.INTERNAL_ERROR_EXIT_CODE);
    }

    // This line is unreachable because all paths through the above code block
    // result in calling System.exit().
    return null;
  }

  /**
   * Writes the mappings to the specified writer. By default, mappings are
   * written (one per line) as:
   * 
   * key:value
   * 
*

Subclasses may override this method to provide alternate output formats. * Subclasses must not close the writer. */ protected void writeRenamingMap(Map renamingMap, PrintWriter renamingMapWriter) { job.outputRenamingMapFormat.writeRenamingMap(renamingMap, renamingMapWriter); } /** * An error message handler. */ protected static final class CompilerErrorManager extends BasicErrorManager { private boolean warningsAsErrors = false; @Override public void print(String msg) { System.err.println(msg); } @Override public void reportWarning(GssError warning) { if (warningsAsErrors) { report(warning); } else { super.reportWarning(warning); } } public void setWarningsAsErrors(boolean state) { warningsAsErrors = state; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy