Package com.github.sommeri.less4j.utils

Source Code of com.github.sommeri.less4j.utils.CssPrinter

package com.github.sommeri.less4j.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import com.github.sommeri.less4j.LessCompiler;
import com.github.sommeri.less4j.LessSource;
import com.github.sommeri.less4j.core.NotACssException;
import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.AnonymousExpression;
import com.github.sommeri.less4j.core.ast.BinaryExpression;
import com.github.sommeri.less4j.core.ast.BinaryExpressionOperator;
import com.github.sommeri.less4j.core.ast.Body;
import com.github.sommeri.less4j.core.ast.CharsetDeclaration;
import com.github.sommeri.less4j.core.ast.ColorExpression;
import com.github.sommeri.less4j.core.ast.Comment;
import com.github.sommeri.less4j.core.ast.CssClass;
import com.github.sommeri.less4j.core.ast.CssString;
import com.github.sommeri.less4j.core.ast.Declaration;
import com.github.sommeri.less4j.core.ast.DetachedRuleset;
import com.github.sommeri.less4j.core.ast.Document;
import com.github.sommeri.less4j.core.ast.ElementSubsequent;
import com.github.sommeri.less4j.core.ast.EmbeddedScript;
import com.github.sommeri.less4j.core.ast.EmptyExpression;
import com.github.sommeri.less4j.core.ast.EscapedValue;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.FaultyExpression;
import com.github.sommeri.less4j.core.ast.FaultyNode;
import com.github.sommeri.less4j.core.ast.FixedMediaExpression;
import com.github.sommeri.less4j.core.ast.FontFace;
import com.github.sommeri.less4j.core.ast.FunctionExpression;
import com.github.sommeri.less4j.core.ast.GeneralBody;
import com.github.sommeri.less4j.core.ast.IdSelector;
import com.github.sommeri.less4j.core.ast.IdentifierExpression;
import com.github.sommeri.less4j.core.ast.Import;
import com.github.sommeri.less4j.core.ast.InlineContent;
import com.github.sommeri.less4j.core.ast.InterpolableName;
import com.github.sommeri.less4j.core.ast.InterpolatedMediaExpression;
import com.github.sommeri.less4j.core.ast.Keyframes;
import com.github.sommeri.less4j.core.ast.KeyframesName;
import com.github.sommeri.less4j.core.ast.ListExpression;
import com.github.sommeri.less4j.core.ast.ListExpressionOperator;
import com.github.sommeri.less4j.core.ast.Media;
import com.github.sommeri.less4j.core.ast.MediaExpression;
import com.github.sommeri.less4j.core.ast.MediaExpressionFeature;
import com.github.sommeri.less4j.core.ast.MediaQuery;
import com.github.sommeri.less4j.core.ast.Medium;
import com.github.sommeri.less4j.core.ast.MediumModifier;
import com.github.sommeri.less4j.core.ast.MediumModifier.Modifier;
import com.github.sommeri.less4j.core.ast.MediumType;
import com.github.sommeri.less4j.core.ast.Name;
import com.github.sommeri.less4j.core.ast.NamedColorExpression;
import com.github.sommeri.less4j.core.ast.NamedExpression;
import com.github.sommeri.less4j.core.ast.Nth;
import com.github.sommeri.less4j.core.ast.NumberExpression;
import com.github.sommeri.less4j.core.ast.Page;
import com.github.sommeri.less4j.core.ast.PageMarginBox;
import com.github.sommeri.less4j.core.ast.PseudoClass;
import com.github.sommeri.less4j.core.ast.PseudoElement;
import com.github.sommeri.less4j.core.ast.RuleSet;
import com.github.sommeri.less4j.core.ast.Selector;
import com.github.sommeri.less4j.core.ast.SelectorAttribute;
import com.github.sommeri.less4j.core.ast.SelectorCombinator;
import com.github.sommeri.less4j.core.ast.SelectorOperator;
import com.github.sommeri.less4j.core.ast.SelectorPart;
import com.github.sommeri.less4j.core.ast.SimpleSelector;
import com.github.sommeri.less4j.core.ast.StyleSheet;
import com.github.sommeri.less4j.core.ast.Supports;
import com.github.sommeri.less4j.core.ast.SupportsCondition;
import com.github.sommeri.less4j.core.ast.SupportsConditionInParentheses;
import com.github.sommeri.less4j.core.ast.SupportsConditionNegation;
import com.github.sommeri.less4j.core.ast.SupportsLogicalCondition;
import com.github.sommeri.less4j.core.ast.SupportsLogicalOperator;
import com.github.sommeri.less4j.core.ast.SupportsQuery;
import com.github.sommeri.less4j.core.ast.SyntaxOnlyElement;
import com.github.sommeri.less4j.core.ast.UnicodeRangeExpression;
import com.github.sommeri.less4j.core.ast.UnknownAtRule;
import com.github.sommeri.less4j.core.ast.Viewport;
import com.github.sommeri.less4j.core.output.ExtendedStringBuilder;
import com.github.sommeri.less4j.core.output.SourceMapBuilder;
//SelectorCombinator
//EscapedSelector

public class CssPrinter {

  public static final String ERROR = "!#error#!";
  protected ExtendedStringBuilder cssOnly = new ExtendedStringBuilder();
  protected SourceMapBuilder cssAndSM;

  private LessSource lessSource;
  private LessSource cssDestination;
  private LessCompiler.Configuration options;
  private Collection<LessSource> additionalSourceFiles;

  public CssPrinter() {
    super();
  }

  public CssPrinter(LessSource lessSource, LessSource cssDestination, Collection<LessSource> additionalSourceFiles, LessCompiler.Configuration options) {
    this.lessSource = lessSource;
    this.cssDestination = cssDestination;
    this.options = options;
    this.additionalSourceFiles = additionalSourceFiles;
    this.cssAndSM = new SourceMapBuilder(cssOnly, cssDestination, additionalSourceFiles, getSourceMapConfiguration(options));
  }

  public CssPrinter(CssPrinter configureFromPrinter) {
    this.options = configureFromPrinter.options;
    this.lessSource = configureFromPrinter.lessSource;
    this.cssDestination = configureFromPrinter.cssDestination;
    this.additionalSourceFiles = configureFromPrinter.additionalSourceFiles;
    this.cssOnly = new ExtendedStringBuilder(configureFromPrinter.cssOnly);
    this.cssAndSM = new SourceMapBuilder(cssOnly, cssDestination, additionalSourceFiles, getSourceMapConfiguration(options));
  }

  private LessCompiler.SourceMapConfiguration getSourceMapConfiguration(LessCompiler.Configuration options) {
    return options != null ? options.getSourceMapConfiguration() : new LessCompiler.SourceMapConfiguration();
  }

  /**
   * returns whether the output changed as a result of the operation
   *
   * @param node
   * @return
   */
  public boolean append(ASTCssNode node) {
    // opening comments should not be docked directly in front of following
    // thing
    if (node == null || node.isSilent())
      return false;

    appendComments(node.getOpeningComments(), true);
    boolean result = switchOnType(node);
    appendComments(node.getTrailingComments(), false);
    return result;
  }

  public boolean switchOnType(ASTCssNode node) {
    switch (node.getType()) {
    case RULE_SET:
      return appendRuleset((RuleSet) node);

    case CSS_CLASS:
      return appendCssClass((CssClass) node);

    case PSEUDO_CLASS:
      return appendPseudoClass((PseudoClass) node);

    case PSEUDO_ELEMENT:
      return appendPseudoElement((PseudoElement) node);

    case NTH:
      return appendNth((Nth) node); // TODOsm: source map

    case SELECTOR:
      return appendSelector((Selector) node);

    case SIMPLE_SELECTOR:
      return appendSimpleSelector((SimpleSelector) node);

    case SELECTOR_OPERATOR:
      return appendSelectorOperator((SelectorOperator) node); // TODOsm: source map

    case SELECTOR_COMBINATOR:
      return appendSelectorCombinator((SelectorCombinator) node); // TODOsm: source map

    case SELECTOR_ATTRIBUTE:
      return appendSelectorAttribute((SelectorAttribute) node); // TODOsm: source map

    case ID_SELECTOR:
      return appendIdSelector((IdSelector) node);

    case CHARSET_DECLARATION:
      return appendCharsetDeclaration((CharsetDeclaration) node);

    case FONT_FACE:
      return appendFontFace((FontFace) node);

    case NAMED_EXPRESSION:
      return appendNamedExpression((NamedExpression) node); // TODOsm: source map

    case BINARY_EXPRESSION:
      return appendComposedExpression((BinaryExpression) node);

    case BINARY_EXPRESSION_OPERATOR:
      return appendBinaryExpressionOperator((BinaryExpressionOperator) node); // TODOsm: source map

    case LIST_EXPRESSION:
      return appendListExpression((ListExpression) node);

    case LIST_EXPRESSION_OPERATOR:
      return appendListExpressionOperator((ListExpressionOperator) node); // TODOsm: source map

    case STRING_EXPRESSION:
      return appendCssString((CssString) node); // TODOsm: source map

    case EMPTY_EXPRESSION:
      return appendEmptyExpression((EmptyExpression) node);

    case NUMBER:
      return appendNumberExpression((NumberExpression) node); // TODOsm: source map

    case IDENTIFIER_EXPRESSION:
      return appendIdentifierExpression((IdentifierExpression) node); // TODOsm: source map

    case UNICODE_RANGE_EXPRESSION:
      return appendUnicodeRangeExpression((UnicodeRangeExpression) node); // TODOsm: source map

    case COLOR_EXPRESSION:
      return appendColorExpression((ColorExpression) node); // TODOsm: source map

    case FUNCTION:
      return appendFunctionExpression((FunctionExpression) node); // TODOsm: source map

    case DECLARATION:
      return appendDeclaration((Declaration) node); // TODOsm: source map

    case MEDIA:
      return appendMedia((Media) node);

    case MEDIA_QUERY:
      return appendMediaQuery((MediaQuery) node); // TODOsm: source map

    case MEDIUM:
      return appendMedium((Medium) node); // TODOsm: source map

    case MEDIUM_MODIFIER:
      return appendMediumModifier((MediumModifier) node); // TODOsm: source map

    case MEDIUM_TYPE:
      return appendMediumType((MediumType) node); // TODOsm: source map

    case FIXED_MEDIA_EXPRESSION:
      return appendMediaExpression((FixedMediaExpression) node); // TODOsm: source map

    case INTERPOLATED_MEDIA_EXPRESSION:
      return appendInterpolatedMediaExpression((InterpolatedMediaExpression) node); // TODOsm: source map

    case MEDIUM_EX_FEATURE:
      return appendMediaExpressionFeature((MediaExpressionFeature) node); // TODOsm: source map

    case STYLE_SHEET:
      return appendStyleSheet((StyleSheet) node);

    case FAULTY_EXPRESSION:
      return appendFaultyExpression((FaultyExpression) node); // TODOsm: source map

    case FAULTY_NODE:
      return appendFaultyNode((FaultyNode) node); // TODOsm: source map

    case ESCAPED_VALUE:
      return appendEscapedValue((EscapedValue) node); // TODOsm: source map

    case EMBEDDED_SCRIPT:
      return appendEmbeddedScript((EmbeddedScript) node); // TODOsm: source map

    case KEYFRAMES:
      return appendKeyframes((Keyframes) node); // TODOsm: source map

    case KEYFRAMES_NAME:
      return appendKeyframesName((KeyframesName) node); // TODOsm: source map

    case UNKNOWN_AT_RULE:
      return appendUnknownAtRule((UnknownAtRule) node); // TODOsm: source map

    case DOCUMENT:
      return appendDocument((Document) node); // TODOsm: source map

    case VIEWPORT:
      return appendViewport((Viewport) node); // TODOsm: source map

    case GENERAL_BODY:
      return appendBodyOptimizeDuplicates((GeneralBody) node);

    case PAGE:
      return appendPage((Page) node); // TODOsm: source map

    case PAGE_MARGIN_BOX:
      return appendPageMarginBox((PageMarginBox) node); // TODOsm: source map

    case NAME:
      return appendName((Name) node); // TODOsm: source map

    case IMPORT:
      return appendImport((Import) node); // TODOsm: source map

    case ANONYMOUS:
      return appendAnonymous((AnonymousExpression) node); // TODOsm: source map

    case SYNTAX_ONLY_ELEMENT:
      return appendSyntaxOnlyElement((SyntaxOnlyElement) node); // TODOsm: source map

    case SUPPORTS:
      return appendSupports((Supports) node); // TODOsm: source map

    case SUPPORTS_QUERY:
      return appendSupportsQuery((SupportsQuery) node); // TODOsm: source map

    case SUPPORTS_CONDITION_NEGATION:
      return appendSupportsConditionNegation((SupportsConditionNegation) node); // TODOsm: source map

    case SUPPORTS_CONDITION_PARENTHESES:
      return appendSupportsConditionParentheses((SupportsConditionInParentheses) node); // TODOsm: source map

    case SUPPORTS_CONDITION_LOGICAL:
      return appendSupportsConditionLogical((SupportsLogicalCondition) node); // TODOsm: source map

    case SUPPORTS_LOGICAL_OPERATOR:
      return appendSupportsLogicalOperator((SupportsLogicalOperator) node); // TODOsm: source map

    case INLINE_CONTENT:
      return appendInlineContent((InlineContent) node); // TODOsm: source map

    case DETACHED_RULESET:
      return appendDetachedRuleset((DetachedRuleset) node);
     
    case ESCAPED_SELECTOR:
    case PARENTHESES_EXPRESSION:
    case SIGNED_EXPRESSION:
    case VARIABLE:
    case DETACHED_RULESET_REFERENCE:
    case INDIRECT_VARIABLE:
    case VARIABLE_DECLARATION:
      throw new NotACssException(node);

    default:
      throw new IllegalStateException("Unknown: " + node.getType() + " " + node.getSourceLine() + ":" + node.getSourceColumn());
    }
  }

  public boolean appendDetachedRuleset(DetachedRuleset node) {
    throw new NotACssException(node);
  }

  public boolean appendCommaSeparated(List<? extends ASTCssNode> values) {
    boolean result = false;
    Iterator<? extends ASTCssNode> names = values.iterator();
    if (names.hasNext()) {
      result |= append(names.next());
    }
    while (names.hasNext()) {
      cssOnly.append(',');
      if (!isCompressing()) cssOnly.ensureSeparator();
      append(names.next());
      result = true;
    }

    return result;
  }

  //TODO: what about source maps?
  private boolean appendInlineContent(InlineContent node) {
    cssOnly.appendAsIs(node.getValue());
    return false;
  }

  private boolean appendSyntaxOnlyElement(SyntaxOnlyElement node) {
    cssOnly.append(node.getSymbol());
    return true;
  }

  private boolean appendAnonymous(AnonymousExpression node) {
    cssOnly.append(node.getValue());
    return true;
  }

  private boolean appendImport(Import node) {
    cssOnly.append("@import").ensureSeparator();
    append(node.getUrlExpression());
    appendCommaSeparated(node.getMediums());
    cssOnly.append(';');

    return true;
  }

  private boolean appendName(Name node) {
    cssOnly.append(node.getName());
    return true;
  }

  private boolean appendPage(Page node) {
    cssOnly.append("@page").ensureSeparator();
    if (node.hasName()) {
      append(node.getName());
      if (!node.hasDockedPseudopage())
        cssOnly.ensureSeparator();
    }

    if (node.hasPseudopage()) {
      append(node.getPseudopage());
      cssOnly.ensureSeparator();
    }

    appendBodySortDeclarations(node.getBody());
    return true;
  }

  private boolean appendPageMarginBox(PageMarginBox node) {
    append(node.getName());
    appendBodySortDeclarations(node.getBody());
    return true;
  }

  private boolean appendKeyframesName(KeyframesName node) {
    append(node.getName());
    return true;
  }

  private boolean appendKeyframes(Keyframes node) {
    cssOnly.append(node.getDialect()).ensureSeparator();

    appendCommaSeparated(node.getNames());
    append(node.getBody());
    return true;
  }

  private boolean appendUnknownAtRule(UnknownAtRule node) {
    cssOnly.append(node.getName()).ensureSeparator();

    appendCommaSeparated(node.getNames());

    append(node.getBody());
    append(node.getSemicolon());
    return true;
  }

  private boolean appendSupports(Supports node) {
    cssOnly.append(node.getDialect()).ensureSeparator();
    append(node.getCondition());
    cssOnly.ensureSeparator();
    append(node.getBody());
    return true;
  }

  private boolean appendSupportsConditionNegation(SupportsConditionNegation node) {
    append(node.getNegation());
    cssOnly.ensureSeparator();
    append(node.getCondition());
    return true;
  }

  private boolean appendSupportsConditionParentheses(SupportsConditionInParentheses node) {
    append(node.getOpeningParentheses());
    append(node.getCondition());
    append(node.getClosingParentheses());
    return true;
  }

  private boolean appendSupportsConditionLogical(SupportsLogicalCondition node) {
    Iterator<SupportsLogicalOperator> operators = node.getLogicalOperators().iterator();
    Iterator<SupportsCondition> conditions = node.getConditions().iterator();

    append(conditions.next());

    while (operators.hasNext()) {
      cssOnly.ensureSeparator();
      append(operators.next());
      cssOnly.ensureSeparator();
      append(conditions.next());
    }
    return true;
  }

  private boolean appendSupportsLogicalOperator(SupportsLogicalOperator node) {
    if (node.getOperator() == null) {
      cssOnly.append(ERROR);
    }
    cssOnly.append(node.getOperator().getSymbol());
    return true;
  }

  private boolean appendSupportsQuery(SupportsQuery node) {
    append(node.getOpeningParentheses());
    append(node.getDeclaration());
    append(node.getClosingParentheses());
    return true;
  }

  private boolean appendDocument(Document node) {
    cssOnly.append(node.getDialect()).ensureSeparator();

    appendCommaSeparated(node.getUrlMatchFunctions());
    append(node.getBody());
    return true;
  }

  private boolean appendViewport(Viewport node) {
    cssOnly.append(node.getDialect());
    append(node.getBody());
    return true;
  }

  private boolean appendFaultyNode(FaultyNode node) {
    cssOnly.append(ERROR);
    return true;
  }

  private boolean appendFaultyExpression(FaultyExpression node) {
    cssOnly.append(ERROR);
    return true;
  }

  private boolean appendNth(Nth node) {
    switch (node.getForm()) {
    case EVEN:
      cssOnly.append("even");
      return true;

    case ODD:
      cssOnly.append("odd");
      return true;

    case STANDARD:
      if (node.getRepeater() != null)
        append(node.getRepeater());
      if (node.getMod() != null)
        append(node.getMod());

    }

    return true;
  }

  protected void appendComments(List<Comment> comments, boolean ensureSeparator) {
    if (comments == null || comments.isEmpty() || isCompressing())
      return;

    cssOnly.ensureSeparator();

    for (Comment comment : comments) {
      String text = comment.getComment();
      if (text!=null)
        cssOnly.append(text);
      if (comment.hasNewLine())
        cssOnly.ensureNewLine();
    }

    if (ensureSeparator)
      cssOnly.ensureSeparator();
  }

  public boolean appendFontFace(FontFace node) {
    cssAndSM.append("@font-face", node.getUnderlyingStructure()).ensureSeparator();
    append(node.getBody());

    return true;
  }

  public boolean appendCharsetDeclaration(CharsetDeclaration node) {
    cssAndSM.append("@charset", node.getUnderlyingStructure()).ensureSeparator();
    append(node.getCharset());
    cssOnly.append(';');

    return true;
  }

  public boolean appendIdSelector(IdSelector node) {
    cssAndSM.append(node.getFullName(), node.getUnderlyingStructure());
    return true;
  }

  public boolean appendSelectorAttribute(SelectorAttribute node) {
    cssOnly.append('[');
    cssOnly.append(node.getName());
    append(node.getOperator());
    append(node.getValue());
    cssOnly.append(']');

    return true;
  }

  private boolean appendSelectorOperator(SelectorOperator operator) {
    SelectorOperator.Operator realOperator = operator.getOperator();
    switch (realOperator) {
    case NONE:
      break;

    default:
      cssOnly.append(realOperator.getSymbol());
    }
    return true;
  }

  public boolean appendPseudoClass(PseudoClass node) {
    cssAndSM.append(node.getFullName(), node.getUnderlyingStructure());
    if (node.hasParameters()) {
      cssOnly.append('(');
      append(node.getParameter());
      cssOnly.append(')');
    }

    return true;
  }

  public boolean appendPseudoElement(PseudoElement node) {
    cssAndSM.append(node.getFullName(), node.getUnderlyingStructure());
    return true;
  }

  public boolean appendStyleSheet(StyleSheet styleSheet) {
    appendComments(styleSheet.getOrphanComments(), false);
    appendAllChilds(styleSheet);
    return true;
  }

  public boolean appendRuleset(RuleSet ruleSet) {
    if (ruleSet.hasEmptyBody())
      return false;

    appendSelectors(ruleSet.getSelectors());
    append(ruleSet.getBody());

    return true;
  }

  private boolean appendBodyOptimizeDuplicates(Body body) {
    if (body.isEmpty())
      return false;

    if (!isCompressing()) cssOnly.ensureSeparator();
    append(body.getOpeningCurlyBrace());
    if (!isCompressing()) cssOnly.ensureNewLine().increaseIndentationLevel();
    Iterable<CssPrinter> declarationsBuilders = collectUniqueBodyMembersStrings(body);
    for (CssPrinter miniBuilder : declarationsBuilders) {
      append(miniBuilder);
    }

    appendComments(body.getOrphanComments(), false);
    cssOnly.decreaseIndentationLevel();
    append(body.getClosingCurlyBrace());

    return true;
  }

  private void append(CssPrinter miniBuilder) {
    cssAndSM.append(miniBuilder.cssAndSM);
  }

  private Iterable<CssPrinter> collectUniqueBodyMembersStrings(Body body) {
    // the same declaration must be printed only once
    LastOfKindSet<String, CssPrinter> declarationsStrings = new LastOfKindSet<String, CssPrinter>();
    for (ASTCssNode declaration : body.getMembers()) {
      CssPrinter miniPrinter = new CssPrinter(this);

      miniPrinter.append(declaration);
      if (!isCompressing()) miniPrinter.cssOnly.ensureNewLine();

      declarationsStrings.add(miniPrinter.toCss().toString(), miniPrinter);
    }

    return declarationsStrings;
  }

  public boolean appendDeclaration(Declaration declaration) {
    cssOnly.appendIgnoreNull(declaration.getNameAsString());
    cssOnly.append(':');
    if (!isCompressing()) cssOnly.ensureSeparator();
    if (declaration.getExpression() != null)
      append(declaration.getExpression());

    if (declaration.isImportant())
      cssOnly.ensureSeparator().append("!important");

    if (shouldHaveSemicolon(declaration))
      cssOnly.appendIgnoreNull(";");

    return true;
  }

  private boolean shouldHaveSemicolon(Declaration declaration) {
    if (null == declaration.getParent() || declaration.getParent().getType() != ASTCssNodeType.SUPPORTS_QUERY)
      return true;

    return false;
  }

  private boolean appendMedia(Media node) {
    cssOnly.append("@media");
    appendCommaSeparated(node.getMediums());
    appendBodySortDeclarations(node.getBody());

    return true;
  }

  private void appendBodySortDeclarations(Body node) {
    // this is sort of hack, bypass the usual append method
    appendComments(node.getOpeningComments(), true);

    cssOnly.ensureSeparator();
    append(node.getOpeningCurlyBrace());
    cssOnly.ensureNewLine().increaseIndentationLevel();

    Iterator<ASTCssNode> declarations = node.getDeclarations().iterator();
    List<ASTCssNode> notDeclarations = node.getNotDeclarations();
    while (declarations.hasNext()) {
      ASTCssNode declaration = declarations.next();
      append(declaration);
      if (declarations.hasNext() || notDeclarations.isEmpty())
        cssOnly.ensureNewLine();
    }
    for (ASTCssNode body : notDeclarations) {
      boolean changedAnything = append(body);
      if (changedAnything)
        cssOnly.ensureNewLine();
    }

    appendComments(node.getOrphanComments(), false);
    cssOnly.decreaseIndentationLevel();
    append(node.getClosingCurlyBrace());

    // this is sort of hack, bypass the usual append method
    appendComments(node.getTrailingComments(), false);
  }

  public boolean appendMediaQuery(MediaQuery mediaQuery) {
    cssOnly.ensureSeparator();
    append(mediaQuery.getMedium());
    boolean needSeparator = (mediaQuery.getMedium() != null);
    for (MediaExpression mediaExpression : mediaQuery.getExpressions()) {
      if (needSeparator) {
        cssOnly.ensureSeparator().append("and");
      }
      append(mediaExpression);
      needSeparator = true;
    }

    return true;
  }

  public boolean appendMedium(Medium medium) {
    append(medium.getModifier());
    append(medium.getMediumType());

    return true;
  }

  public boolean appendMediumModifier(MediumModifier modifier) {
    Modifier kind = modifier.getModifier();
    switch (kind) {
    case ONLY:
      cssOnly.ensureSeparator().append("only");
      break;

    case NOT:
      cssOnly.ensureSeparator().append("not");
      break;

    case NONE:
      break;

    default:
      throw new IllegalStateException("Unknown modifier type.");
    }

    return true;
  }

  public boolean appendMediumType(MediumType medium) {
    cssOnly.ensureSeparator().append(medium.getName());

    return true;
  }

  public boolean appendMediaExpression(FixedMediaExpression expression) {
    cssOnly.ensureSeparator().append('(');
    append(expression.getFeature());
    if (expression.getExpression() != null) {
      cssOnly.append(':');
      if (!isCompressing()) cssOnly.ensureSeparator();
      append(expression.getExpression());
    }
    cssOnly.append(')');
    return true;
  }

  private boolean appendInterpolatedMediaExpression(InterpolatedMediaExpression expression) {
    cssOnly.ensureSeparator();
    return append(expression.getExpression());
  }

  public boolean appendMediaExpressionFeature(MediaExpressionFeature feature) {
    cssOnly.append(feature.getFeature());
    return true;
  }

  public boolean appendNamedExpression(NamedExpression expression) {
    cssOnly.append(expression.getName());
    cssOnly.append('=');
    append(expression.getExpression());

    return true;
  }

  public boolean appendComposedExpression(BinaryExpression expression) {
    append(expression.getLeft());
    append(expression.getOperator());
    append(expression.getRight());

    return true;
  }

  public boolean appendListExpression(ListExpression expression) {
    Iterator<Expression> iterator = expression.getExpressions().iterator();
    if (!iterator.hasNext())
      return false;

    append(iterator.next());
    while (iterator.hasNext()) {
      append(expression.getOperator());
      append(iterator.next());
    }
    return true;
  }

  public boolean appendExpressionOperator(ListExpressionOperator operator) {
    ListExpressionOperator.Operator realOperator = operator.getOperator();
    switch (realOperator) {
    case COMMA:
      cssOnly.append(realOperator.getSymbol());
      if (!isCompressing()) cssOnly.ensureSeparator();
      break;

    case EMPTY_OPERATOR:
      cssOnly.ensureSeparator();
      break;

    default:
      cssOnly.append(realOperator.getSymbol());
    }

    return true;
  }

  public boolean appendBinaryExpressionOperator(BinaryExpressionOperator operator) {
    BinaryExpressionOperator.Operator realOperator = operator.getOperator();
    switch (realOperator) {
    // TODO this is a huge hack which goes around
    // "we do not parse fonts and less.js does" lack of feature
    // left here intentionally, so we can have correct unit test and can come
    // back to it later
    case MINUS:
      cssOnly.ensureSeparator().append('-');
      break;

    default:
      cssOnly.append(realOperator.getSymbol());
    }

    return true;
  }

  public boolean appendListExpressionOperator(ListExpressionOperator operator) {
    ListExpressionOperator.Operator realOperator = operator.getOperator();
    switch (realOperator) {
    case COMMA:
      cssOnly.append(realOperator.getSymbol());
      if (!isCompressing()) cssOnly.ensureSeparator();
      break;

    case EMPTY_OPERATOR:
      cssOnly.ensureSeparator();
      break;

    default:
      cssOnly.append(realOperator.getSymbol());
    }

    return true;
  }

  public boolean appendCssString(CssString expression) {
    String quoteType = expression.getQuoteType();
    cssOnly.append(quoteType).append(expression.getValue()).append(quoteType);

    return true;
  }

  public boolean appendEmptyExpression(EmptyExpression node) {
    return true;
  }

  public boolean appendEscapedValue(EscapedValue escaped) {
    cssOnly.append(escaped.getValue());

    return true;
  }

  public boolean appendEmbeddedScript(EmbeddedScript escaped) {
    cssOnly.append(escaped.getValue());

    return true;
  }

  public boolean appendIdentifierExpression(IdentifierExpression expression) {
    cssOnly.append(expression.getValue());

    return true;
  }

  public boolean appendUnicodeRangeExpression(UnicodeRangeExpression expression) {
    cssOnly.append(expression.getValue());

    return true;
  }

  protected boolean appendColorExpression(ColorExpression expression) {
    // if it is named color expression, write out the name
    if (expression instanceof NamedColorExpression) {
      NamedColorExpression named = (NamedColorExpression) expression;
      cssOnly.append(named.getColorName());
    } else {
//      cssAndSM.append(expression.getValue(), expression.getUnderlyingStructure());
      cssOnly.append(expression.getValue());
    }

    return true;
  }

  private boolean appendFunctionExpression(FunctionExpression node) {
    cssOnly.append(node.getName());
    cssOnly.append('(');
    append(node.getParameter());
    cssOnly.append(')');

    return true;
  }

  private boolean appendNumberExpression(NumberExpression node) {
    if (node.hasOriginalString()) {
      cssOnly.append(node.getOriginalString());
    } else {
      if (node.hasExpliciteSign()) {
        if (0 < node.getValueAsDouble())
          cssOnly.append('+');
        else
          cssOnly.append('-');
      }
      cssOnly.append(PrintUtils.formatNumber(node.getValueAsDouble()) + node.getSuffix());
    }

    return true;
  }

  public void appendSelectors(List<Selector> selectors) {
    selectors = filterSilent(selectors);
    // follow less.js formatting in special case - only one empty selector
    if (selectors.size() == 1 && isEmptySelector(selectors.get(0))) {
      cssOnly.append(' ');
    }

    Iterator<Selector> iterator = selectors.iterator();
    while (iterator.hasNext()) {
      Selector selector = iterator.next();
      append(selector);

      if (iterator.hasNext())
        cssOnly.append(',').newLine();
    }
  }

  private <T extends ASTCssNode> List<T> filterSilent(List<T> nodes) {
    List<T> result = new ArrayList<T>();
    for (T t : nodes) {
      if (!t.isSilent())
        result.add(t);
    }
    return result;
  }

  private boolean isEmptySelector(Selector selector) {
    if (selector.isCombined())
      return false;

    SelectorPart head = selector.getHead();
    if (head.getType() != ASTCssNodeType.SIMPLE_SELECTOR)
      return false;

    SimpleSelector simpleHead = (SimpleSelector) head;
    if (!simpleHead.isEmptyForm() || !simpleHead.isStar()) {
      return false;
    }

    if (simpleHead.hasSubsequent())
      return false;

    return true;
  }

  public boolean appendSelector(Selector selector) {
    for (SelectorPart part : selector.getParts()) {
      append(part);
    }
    return true;
  }

  private boolean appendSimpleSelector(SimpleSelector selector) {
    if (selector.hasLeadingCombinator())
      append(selector.getLeadingCombinator());

    appendSimpleSelectorHead(selector);
    appendSimpleSelectorTail(selector);
    return true;
  }

  private void appendSimpleSelectorTail(SimpleSelector selector) {
    List<ElementSubsequent> allChilds = selector.getSubsequent();
    for (ElementSubsequent astCssNode : allChilds) {
      append(astCssNode);
    }
  }

  private boolean appendCssClass(CssClass cssClass) {
    cssAndSM.append(cssClass.getFullName(), cssClass.getUnderlyingStructure());
    return true;
  }

  private void appendSimpleSelectorHead(SimpleSelector selector) {
    cssOnly.ensureSeparator();
    if (!selector.isStar() || !selector.isEmptyForm()) {
      InterpolableName elementName = selector.getElementName();
      cssAndSM.appendIgnoreNull(elementName.getName(), elementName.getUnderlyingStructure());
    }
  }

  public boolean appendSelectorCombinator(SelectorCombinator combinator) {
    SelectorCombinator.CombinatorType realCombinator = combinator.getCombinatorType();
    switch (realCombinator) {
    case DESCENDANT:
      cssOnly.ensureSeparator();
      break;

    default:
      cssOnly.ensureSeparator().append(combinator.getSymbol());

    }

    return true;
  }

  private void appendAllChilds(ASTCssNode node) {
    List<? extends ASTCssNode> allChilds = node.getChilds();
    appendAll(allChilds);
  }

  private void appendAll(List<? extends ASTCssNode> all) {
    for (ASTCssNode kid : all) {
      if (append(kid) && !isCompressing())
        cssOnly.ensureNewLine();
    }
  }

  public String toString() {
    return cssOnly.toString();
  }

  public StringBuilder toCss() {
    return cssOnly.toStringBuilder();
  }

  public String toSourceMap() {
    return cssAndSM.toSourceMap();
  }

  private boolean isCompressing() {
    return options != null ? options.isCompressing() : false;
  }
}
TOP

Related Classes of com.github.sommeri.less4j.utils.CssPrinter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.