Package com.puppetlabs.xtext.dommodel.formatter

Source Code of com.puppetlabs.xtext.dommodel.formatter.AbstractLayoutManager

/**
* Copyright (c) 2013 Puppet Labs, Inc. and other contributors, as listed below.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*   Puppet Labs
*/
package com.puppetlabs.xtext.dommodel.formatter;

import com.puppetlabs.xtext.dommodel.DomModelUtils;
import com.puppetlabs.xtext.dommodel.IDomNode;
import com.puppetlabs.xtext.dommodel.IDomNode.NodeClassifier;
import com.puppetlabs.xtext.dommodel.IDomNode.NodeType;
import com.puppetlabs.xtext.dommodel.formatter.css.LineBreaks;
import com.puppetlabs.xtext.dommodel.formatter.css.Spacing;
import com.puppetlabs.xtext.dommodel.formatter.css.StyleFactory.DedentStyle;
import com.puppetlabs.xtext.dommodel.formatter.css.StyleFactory.IndentStyle;
import com.puppetlabs.xtext.dommodel.formatter.css.StyleFactory.LayoutManagerStyle;
import com.puppetlabs.xtext.dommodel.formatter.css.StyleSet;
import com.puppetlabs.xtext.textflow.CharSequences;
import com.puppetlabs.xtext.textflow.ITextFlow;
import org.eclipse.xtext.util.Strings;

/**
* Abstract implementation of ILayoutManaager
*
*/
public abstract class AbstractLayoutManager extends AbstractLayout implements ILayoutManager {

  private final static Integer DEFAULT_0 = Integer.valueOf(0);

  /**
   * Reverses any indentation set before the composite.
   */
  @Override
  public void afterComposite(StyleSet styleSet, IDomNode node, ITextFlow output, ILayoutContext context) {
    // Process indentation for all types of composites.
    // This looks a bit odd, but protects against the pathological case where a style
    // has both indents and dedents. If both indent and dedent are 0, indentation is unchanged.
    output.changeIndentation(styleSet.getStyleValue(DedentStyle.class, node, DEFAULT_0) -
        styleSet.getStyleValue(IndentStyle.class, node, DEFAULT_0));
  }

  /**
   * Outputs the result of applying the given {@link Spacing} and {@link LineBreaks} specifications to the given
   * text to the given output {@link ITextFlow}.
   * <p>
   * Called when it has been decided that a whitespace should be processed (it is included in the region to format).
   * </p>
   * <p>
   * If the given {@link LineBreaks} has a <i>normal</i> {@link LineBreaks#getNormal()} or <i>max</i> {@link LineBreaks#getMax()} greater than 0 the
   * line break specification wins, and no spaces are produced.
   * </p>
   * <p>
   * A missing quantity will produce the <i>normal</i> quantity, a quantity less than <i>min</i> will produce a <i>min</i> quantity, and a quantity
   * greater than <i>max</i> will produce a <i>max</i> quantity.
   * </p>
   *
   * @param context
   *            - provides line separator information
   * @param text
   *            - the text applied to spacing and line break specifications
   * @param spacing
   *            - the spacing specification
   * @param linebreaks
   *            - the line break specification
   * @param output
   *            - where output is produced
   */
  protected void applySpacingAndLinebreaks(ILayoutContext context, IDomNode node, String text, Spacing spacing,
      LineBreaks linebreaks, ITextFlow output) {
    text = text == null
        ? ""
        : text;
    final String lineSep = context.getLineSeparatorInformation().getLineSeparator();
    final int existingLinebreaks = Strings.countLines(text, lineSep.toCharArray());
    // if line break is wanted, it wins
    if(linebreaks.getNormal() > 0 || (linebreaks.getMax() > 0 && existingLinebreaks > 0)) {
      // output a conforming number of line breaks
      // possibly accept a comment as a linebreak if this whitespace did not already contain
      // a break
      int emittedLinebreaks = 0;
      if(existingLinebreaks == 0 && linebreaks.isCommentEndingWithBreakAcceptable()) {
        IDomNode n = DomModelUtils.nextLeaf(node);
        if(n != null && n.getNodeType() == NodeType.COMMENT &&
            n.getStyleClassifiers().contains(NodeClassifier.LINESEPARATOR_TERMINATED)) {
          // comment breaks the line
          output.appendSpaces(1); // separate the comment with a space

          // format the comment with layout manager given by rules (or this layout by default)
          final StyleSet styleSet = context.getCSS().collectStyles(n);
          tracer.recordEffectiveStyle(node, styleSet);

          final ILayoutManager layout = styleSet.getStyleValue(LayoutManagerStyle.class, n, this);
          layout.format(styleSet, n, output, context);
          // do not process this comment again
          context.markConsumed(n);

          // comment counts as one linebreak
          emittedLinebreaks++;
        }
      }
      if(linebreaks.isExistingAcceptable())
        output.ensureBreaks(linebreaks.apply(existingLinebreaks));
      else
        output.appendBreaks(linebreaks.apply(existingLinebreaks) - emittedLinebreaks);
    }
    else {
      // remove all line breaks by replacing them with spaces
      text = text.replace(lineSep, " ");
      // output a conforming number of spaces, and make it nonBreakableSpace if required.
      if(spacing.isBreakable())
        output.appendSpaces(spacing.apply(text.length()));
      else
        output.appendText(CharSequences.spaces(spacing.apply(text.length())));
    }
  }

  /**
   * This implementation applies indentation set in a composite.
   */
  @Override
  public void beforeComposite(StyleSet styleSet, IDomNode node, ITextFlow output, ILayoutContext context) {
    // Process indentation for all types of composites.
    // This looks a bit odd, but protects against the pathological case where a style
    // has both indents and dedents. If both indent and dedent are 0, indentation is unchanged.
    output.changeIndentation(styleSet.getStyleValue(IndentStyle.class, node, DEFAULT_0) -
        styleSet.getStyleValue(DedentStyle.class, node, DEFAULT_0));
  }

  @Override
  public boolean format(IDomNode node, ITextFlow flow, ILayoutContext context) {
    StyleSet styleSet = context.getCSS().collectStyles(node);
    tracer.recordEffectiveStyle(node, styleSet);

    return format(styleSet, node, flow, context);
  }

  @Override
  public boolean format(StyleSet styleSet, IDomNode node, ITextFlow flow, ILayoutContext context) {
    // if already consumed by previous formatting - skip it and report as formatted
    if(context.isConsumed(node))
      return true;
    if(!node.isLeaf())
      return formatComposite(styleSet, node, flow, context);
    formatLeaf(styleSet, node, flow, context);
    return true; // it was a leaf, there is nothing to prune
  }

  /**
   * Derived class should implement this method to format a leaf containing a comment.
   *
   * @param styleSet
   * @param node
   * @param output
   * @param context
   */
  protected abstract void formatComment(StyleSet styleSet, IDomNode node, ITextFlow output, ILayoutContext context);

  /**
   * <p>
   * This implementation does nothing, and returns <code>false</code>.
   * </p>
   * <p>
   * A derived class may want to decorate children of the composite node and return <code>false</code>, or process and provide all output for
   * children and return <code>true</code>
   * </p>
   *
   * @param styleSet
   * @param node
   * @param flow
   * @param context
   * @return false - children should be visited
   */
  protected boolean formatComposite(StyleSet styleSet, IDomNode node, ITextFlow flow, ILayoutContext context) {
    return false; // Did not process children
  }

  /**
   * Processes indentation styling, and calls one of the specific methods:
   * <ul>
   * <li>{@link #formatToken(StyleSet, IDomNode, ITextFlow, ILayoutContext)}</li>
   * <li>{@link #formatComment(StyleSet, IDomNode, ITextFlow, ILayoutContext)}</li>
   * <li>{@link #formatWhitespace(StyleSet, IDomNode, ITextFlow, ILayoutContext)}</li>
   * </ul>
   * These should be implemented by a subclass.
   *
   * @param styleSet
   * @param node
   * @param output
   * @param context
   */
  protected void formatLeaf(final StyleSet styleSet, IDomNode node, ITextFlow output, ILayoutContext context) {

    // Process indentation for all types of leafs.
    // This looks a bit odd, but protects against the pathological case where a style
    // has both indents and dedents. If both indent and dedent are 0, indentation is unchanged.
    output.changeIndentation(styleSet.getStyleValue(IndentStyle.class, node, DEFAULT_0) -
        styleSet.getStyleValue(DedentStyle.class, node, DEFAULT_0));

    switch(node.getNodeType()) {
      case WHITESPACE:
        formatWhitespace(styleSet, node, output, context);
        break;
      case COMMENT:
        formatComment(styleSet, node, output, context);
        break;
      default:
        formatToken(styleSet, node, output, context);
    }
  }

  /**
   * Derived class should implement this method and format a token (a leaf that is not whitespace, and not comment).
   *
   * @param styleSet
   * @param node
   * @param output
   * @param context
   */
  protected abstract void formatToken(StyleSet styleSet, IDomNode node, ITextFlow output, ILayoutContext context);

  /**
   * Derived class should implement this method and format whitespace.
   *
   * @param styleSet
   * @param node
   * @param output
   * @param context
   */
  protected abstract void formatWhitespace(StyleSet styleSet, IDomNode node, ITextFlow output, ILayoutContext context);

}
TOP

Related Classes of com.puppetlabs.xtext.dommodel.formatter.AbstractLayoutManager

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.