/**
* 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 java.util.Collection;
import com.puppetlabs.xtext.dommodel.IDomNode;
import com.puppetlabs.xtext.dommodel.formatter.ILayoutManager.ILayoutContext;
import com.puppetlabs.xtext.dommodel.formatter.css.StyleFactory.LayoutManagerStyle;
import com.puppetlabs.xtext.dommodel.formatter.css.StyleSet;
import com.puppetlabs.xtext.dommodel.formatter.css.debug.FormattingTracer;
import com.puppetlabs.xtext.textflow.ITextFlow;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.inject.Inject;
import com.google.inject.name.Named;
/**
* <p>
* A DomNodeLayoutFeeder feeds {@link IDomNode} instances to an {@link ILayoutManager}.
* </p>
* <p>
* This feeder obtains the layout manager to use by looking up the style of the respective node. If a node does not specify how to be formatted, a
* {@link FlowLayout} will be used.
* </p>
* <p>
* This class is useful in specialized layout managers that want to control certain aspects of formatting, but allows children to be formatted as per
* their specified formatting styles. Simply calling {@link #sequence(IDomNode, ITextFlow, ILayoutContext)} on a node will invoke an
* {@link ILayoutManager#format(StyleSet, IDomNode, ITextFlow, ILayoutContext)} for the given node (and all sub nodes).
* </p>
* <p>
* The feeder prunes children by returning if the layout manager returns true from one of its format methods.
* </p>
*
*/
public class DomNodeLayoutFeeder {
@Inject
@Named("Default")
protected ILayoutManager defaultLayout;
@Inject
protected FormattingTracer tracer;
/**
* Sequences a collection of IDomNode in depth first order. Each node is passed to an
* {@link ILayoutManager#format(StyleSet, IDomNode, ITextFlow, ILayoutContext)} where the layout manager is obtained via style collection.
*
* @param dom
* @param output
* @param context
*/
public void sequence(Collection<IDomNode> nodes, ITextFlow output, ILayoutContext context) {
sequence(nodes, output, context, Predicates.<IDomNode> alwaysTrue(), Predicates.<IDomNode> alwaysFalse());
}
public IDomNode sequence(Collection<IDomNode> nodes, ITextFlow output, ILayoutContext context,
Predicate<IDomNode> include, Predicate<IDomNode> until) {
IDomNode last = null;
for(IDomNode n : nodes) {
if(until.apply(n))
return n;
last = sequence(n, output, context, include, until);
}
return last;
}
/**
* Sequences the IDomNode in depth first order. Each node is passed to an
* {@link ILayoutManager#format(StyleSet, IDomNode, ITextFlow, ILayoutContext)} where the layout manager is obtained via style collection.
*
* @param dom
* @param output
* @param context
*/
public void sequence(IDomNode node, ITextFlow output, ILayoutContext context) {
sequence(node, output, context, Predicates.<IDomNode> alwaysTrue(), Predicates.<IDomNode> alwaysFalse());
}
public IDomNode sequence(IDomNode node, ITextFlow output, ILayoutContext context, Predicate<IDomNode> until) {
return sequence(node, output, context, Predicates.<IDomNode> alwaysTrue(), until);
}
public IDomNode sequence(IDomNode node, ITextFlow output, ILayoutContext context, Predicate<IDomNode> include,
Predicate<IDomNode> until) {
if(until.apply(node))
return node;
if(node.isLeaf()) {
if(include.apply(node))
sequenceLeaf(node, output, context);
}
else
return sequenceComposite(node, output, context, include, until);
return null;
}
protected IDomNode sequenceComposite(IDomNode node, ITextFlow output, ILayoutContext context,
Predicate<IDomNode> include, Predicate<IDomNode> until) {
final StyleSet styleSet = context.getCSS().collectStyles(node);
tracer.recordEffectiveStyle(node, styleSet);
final ILayoutManager layout = styleSet.getStyleValue(LayoutManagerStyle.class, node, defaultLayout);
layout.beforeComposite(styleSet, node, output, context);
// if layout of composite by layout manager returns true, children are already processed
IDomNode last = null;
if(!layout.format(styleSet, node, output, context))
for(IDomNode n : node.getChildren()) {
if(until.apply(n))
return n;
last = sequence(n, output, context, include, until);
}
layout.afterComposite(styleSet, node, output, context);
return last;
}
protected void sequenceLeaf(IDomNode node, ITextFlow output, ILayoutContext context) {
final StyleSet styleSet = context.getCSS().collectStyles(node);
tracer.recordEffectiveStyle(node, styleSet);
final ILayoutManager layout = styleSet.getStyleValue(LayoutManagerStyle.class, node, defaultLayout);
layout.format(styleSet, node, output, context);
}
}