/**
* Copyright (c) 2011 Cloudsmith 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:
* Cloudsmith
*
*/
package org.cloudsmith.geppetto.pp.dsl.ppformatting;
import java.util.Collections;
import java.util.Iterator;
import org.cloudsmith.geppetto.pp.AndExpression;
import org.cloudsmith.geppetto.pp.AppendExpression;
import org.cloudsmith.geppetto.pp.AssignmentExpression;
import org.cloudsmith.geppetto.pp.AtExpression;
import org.cloudsmith.geppetto.pp.AttributeOperation;
import org.cloudsmith.geppetto.pp.AttributeOperations;
import org.cloudsmith.geppetto.pp.BinaryExpression;
import org.cloudsmith.geppetto.pp.BinaryOpExpression;
import org.cloudsmith.geppetto.pp.Case;
import org.cloudsmith.geppetto.pp.CaseExpression;
import org.cloudsmith.geppetto.pp.CollectExpression;
import org.cloudsmith.geppetto.pp.Definition;
import org.cloudsmith.geppetto.pp.DefinitionArgument;
import org.cloudsmith.geppetto.pp.DefinitionArgumentList;
import org.cloudsmith.geppetto.pp.DoubleQuotedString;
import org.cloudsmith.geppetto.pp.ElseExpression;
import org.cloudsmith.geppetto.pp.ElseIfExpression;
import org.cloudsmith.geppetto.pp.ExportedCollectQuery;
import org.cloudsmith.geppetto.pp.ExprList;
import org.cloudsmith.geppetto.pp.Expression;
import org.cloudsmith.geppetto.pp.ExpressionTE;
import org.cloudsmith.geppetto.pp.HashEntry;
import org.cloudsmith.geppetto.pp.HostClassDefinition;
import org.cloudsmith.geppetto.pp.IQuotedString;
import org.cloudsmith.geppetto.pp.IfExpression;
import org.cloudsmith.geppetto.pp.ImportExpression;
import org.cloudsmith.geppetto.pp.LiteralBoolean;
import org.cloudsmith.geppetto.pp.LiteralClass;
import org.cloudsmith.geppetto.pp.LiteralDefault;
import org.cloudsmith.geppetto.pp.LiteralHash;
import org.cloudsmith.geppetto.pp.LiteralList;
import org.cloudsmith.geppetto.pp.LiteralName;
import org.cloudsmith.geppetto.pp.LiteralNameOrReference;
import org.cloudsmith.geppetto.pp.LiteralRegex;
import org.cloudsmith.geppetto.pp.LiteralUndef;
import org.cloudsmith.geppetto.pp.NodeDefinition;
import org.cloudsmith.geppetto.pp.OrExpression;
import org.cloudsmith.geppetto.pp.ParenthesisedExpression;
import org.cloudsmith.geppetto.pp.PuppetManifest;
import org.cloudsmith.geppetto.pp.ResourceBody;
import org.cloudsmith.geppetto.pp.ResourceExpression;
import org.cloudsmith.geppetto.pp.SelectorEntry;
import org.cloudsmith.geppetto.pp.SelectorExpression;
import org.cloudsmith.geppetto.pp.SingleQuotedString;
import org.cloudsmith.geppetto.pp.TextExpression;
import org.cloudsmith.geppetto.pp.UnaryMinusExpression;
import org.cloudsmith.geppetto.pp.UnaryNotExpression;
import org.cloudsmith.geppetto.pp.UnlessExpression;
import org.cloudsmith.geppetto.pp.UnquotedString;
import org.cloudsmith.geppetto.pp.VariableExpression;
import org.cloudsmith.geppetto.pp.VariableTE;
import org.cloudsmith.geppetto.pp.VerbatimTE;
import org.cloudsmith.geppetto.pp.VirtualCollectQuery;
import org.cloudsmith.geppetto.pp.VirtualNameOrReference;
import org.cloudsmith.geppetto.pp.dsl.ppformatting.FormattingCommentAssociator.CommentAssociations;
import org.cloudsmith.geppetto.pp.dsl.services.PPGrammarAccess;
import org.cloudsmith.xtext.dommodel.formatter.context.IFormattingContext;
import org.cloudsmith.xtext.textflow.ITextFlow;
import org.cloudsmith.xtext.textflow.MeasuredTextFlow;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.serializer.sequencer.IContextFinder;
import org.eclipse.xtext.util.EmfFormatter;
import org.eclipse.xtext.util.PolymorphicDispatcher;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
/**
* @author henrik
*
*/
public class PPExpressionFormatter {
@SuppressWarnings("unused")
@Inject
private PPGrammarAccess grammarAccess;
@Inject
protected IContextFinder contextFinder;
@Inject
IFormattingContext formattingContext;
private PolymorphicDispatcher<Void> formatDispatcher = new PolymorphicDispatcher<Void>(
"_format", 2, 2, Collections.singletonList(this), PolymorphicDispatcher.NullErrorHandler.<Void> get()) {
@Override
protected Void handleNoSuchMethod(Object... params) {
return null;
}
};
@Inject
FormattingCommentAssociator commentAssociator;
private CommentAssociations commentAssociations;
@Inject
public PPExpressionFormatter(IGrammarAccess ga) {
// let it crash - it is a configuration error
grammarAccess = (PPGrammarAccess) ga;
}
protected void _format(AndExpression o, ITextFlow.WithText stream) {
// AndExpressionElements access = grammarAccess.getAndExpressionAccess();
// doFormat(o.getLeftExpr(), stream, access.getAndExpressionLeftExprAction_1_0());
// stream.oneSpace();
// stream.text(op);
// stream.oneSpace();
// doFormat(o.getRightExpr(), stream);
internalFormatBinaryExpression(o, "and", stream);
}
protected void _format(AppendExpression o, ITextFlow.WithText stream) {
internalFormatBinaryExpression(o, "+=", stream);
}
protected void _format(AssignmentExpression o, ITextFlow.WithText stream) {
internalFormatBinaryExpression(o, "=", stream);
}
protected void _format(AtExpression o, ITextFlow.WithText stream) {
// TODO: wrap the list if wider than max width
doFormat(o.getLeftExpr(), stream);
stream.appendText("[");
Iterator<Expression> itor = o.getParameters().iterator();
while(itor.hasNext()) {
doFormat(itor.next(), stream);
if(itor.hasNext()) {
stream.appendText(",");
stream.appendSpace();
}
}
stream.appendText("]");
}
protected void _format(BinaryOpExpression o, ITextFlow.WithText stream) {
internalFormatBinaryExpression(o, o.getOpName(), stream);
}
protected void _format(Case o, ITextFlow.WithText stream) {
}
protected void _format(CaseExpression o, ITextFlow.WithText stream) {
stream.appendText("case");
stream.appendSpace();
doFormat(o.getSwitchExpr(), stream);
stream.appendSpace();
stream.appendText("{");
stream.changeIndentation(1);
stream.appendBreaks(1);
// process cases
int width = 0;
for(Case c : o.getCases()) {
MeasuredTextFlow inner = new MeasuredTextFlow(formattingContext);
Iterator<Expression> itor = c.getValues().iterator();
while(itor.hasNext()) {
doFormat(itor.next(), inner);
if(itor.hasNext()) {
inner.appendText(",");
inner.appendSpace();
}
}
width = inner.getWidth();
}
for(Case c : o.getCases()) {
int before = stream.size();
Iterator<Expression> itor = c.getValues().iterator();
while(itor.hasNext()) {
doFormat(itor.next(), stream);
if(itor.hasNext()) {
stream.appendText(",");
stream.appendSpace();
}
}
int after = stream.size();
stream.appendSpaces(width - (before - after));
stream.appendSpace();
stream.appendText(":");
stream.appendSpace();
stream.appendText("{");
stream.changeIndentation(1);
;
formatStatementList(c.getStatements(), stream);
stream.changeIndentation(-1);
stream.appendText("}");
stream.appendBreaks(1);
}
stream.changeIndentation(-1);
stream.appendBreaks(1);
stream.appendText("}");
}
protected void _format(CollectExpression o, ITextFlow.WithText stream) {
doFormat(o.getClassReference(), stream);
stream.appendSpace();
doFormat(o.getQuery(), stream);
stream.appendSpace();
stream.appendText("{");
stream.changeIndentation(1);
stream.appendBreaks(1);
internalFormat(o.getAttributes(), true, stream);
stream.changeIndentation(-1);
stream.appendBreaks(1);
stream.appendText("}");
}
protected void _format(Definition o, ITextFlow.WithText stream) {
stream.appendText("define");
stream.appendSpace();
stream.appendText(o.getClassName());
stream.appendSpace();
internalFormatArguments(o.getArguments(), stream);
if(o.getArguments() != null)
stream.appendSpace();
stream.appendText("{");
stream.changeIndentation(1);
stream.appendBreaks(1);
formatStatementList(o.getStatements(), stream);
stream.changeIndentation(-1);
stream.appendBreaks(1);
stream.appendText("}");
}
protected void _format(DoubleQuotedString o, ITextFlow.WithText stream) {
stream.appendText("\"");
for(TextExpression te : o.getStringPart()) {
doFormat(te, stream);
}
stream.appendText("\"");
}
protected void _format(ElseExpression o, ITextFlow.WithText stream) {
stream.appendText("else");
stream.appendSpace();
stream.appendText("{");
stream.changeIndentation(1);
stream.appendBreaks(1);
formatStatementList(o.getStatements(), stream);
stream.changeIndentation(-1);
stream.appendText("}");
}
protected void _format(ElseIfExpression o, ITextFlow.WithText stream) {
stream.appendText("elsif");
stream.appendSpace();
doFormat(o.getCondExpr(), stream);
stream.appendSpace();
stream.appendText("{");
stream.changeIndentation(1);
stream.appendBreaks(1);
formatStatementList(o.getThenStatements(), stream);
stream.changeIndentation(-1);
stream.appendText("}");
if(o.getElseStatement() != null) {
stream.appendSpace();
doFormat(o.getElseStatement(), stream);
}
}
protected void _format(ExportedCollectQuery o, ITextFlow.WithText stream) {
stream.appendText("<<|");
stream.appendSpace();
doFormat(o.getExpr(), stream);
stream.appendSpace();
stream.appendText("|>>");
}
protected void _format(ExpressionTE o, ITextFlow.WithText stream) {
stream.appendText("${");
doFormat(((ParenthesisedExpression) o.getExpression()).getExpr(), stream);
stream.appendText("}");
}
protected void _format(ExprList o, ITextFlow.WithText stream) {
int size = o.getExpressions().size();
for(int i = 0; i < size; i++) {
Expression e = o.getExpressions().get(i);
doFormat(e, stream);
if(i + 1 < size) {
stream.appendText(",");
stream.appendSpace();
}
}
}
protected void _format(HashEntry o, ITextFlow.WithText stream) {
doFormat(o.getKey(), stream);
stream.appendSpace();
stream.appendText("=>");
stream.appendSpace();
doFormat(o.getValue(), stream);
}
protected void _format(HostClassDefinition o, ITextFlow.WithText stream) {
stream.appendText("class");
stream.appendSpace();
stream.appendText(o.getClassName());
stream.appendSpace();
internalFormatArguments(o.getArguments(), stream);
if(o.getArguments() != null)
stream.appendSpace();
if(o.getParent() != null) {
stream.appendText("inherits");
stream.appendSpace();
doFormat(o.getParent(), stream);
stream.appendSpace();
}
stream.appendText("{");
if(o.getStatements().size() > 0) {
stream.changeIndentation(1);
;
stream.appendBreaks(1);
formatStatementList(o.getStatements(), stream);
stream.changeIndentation(-1);
}
stream.appendBreaks(1);
stream.appendText("}");
}
protected void _format(IfExpression o, ITextFlow.WithText stream) {
stream.appendText("if");
stream.appendSpace();
doFormat(o.getCondExpr(), stream);
stream.appendSpace();
stream.appendText("{");
stream.changeIndentation(1);
stream.appendBreaks(1);
formatStatementList(o.getThenStatements(), stream);
stream.changeIndentation(-1);
stream.appendText("}");
if(o.getElseStatement() != null) {
stream.appendSpace();
doFormat(o.getElseStatement(), stream);
}
}
protected void _format(ImportExpression o, ITextFlow.WithText stream) {
stream.appendText("import");
stream.appendSpace();
Iterator<IQuotedString> itor = o.getValues().iterator();
while(itor.hasNext()) {
doFormat(itor.next(), stream);
if(itor.hasNext()) {
stream.appendText(",");
stream.appendSpace();
}
}
}
protected void _format(LiteralBoolean o, ITextFlow.WithText stream) {
stream.appendText(String.valueOf(o.isValue()));
}
protected void _format(LiteralClass o, ITextFlow.WithText stream) {
stream.appendText("class");
}
protected void _format(LiteralDefault o, ITextFlow.WithText stream) {
stream.appendText("default");
}
protected void _format(LiteralHash o, ITextFlow.WithText stream) {
stream.appendText("[");
internalCommaSeparatedList(o.getElements(), stream);
stream.appendText("]");
}
protected void _format(LiteralList o, ITextFlow.WithText stream) {
stream.appendText("[");
internalCommaSeparatedList(o.getElements(), stream);
stream.appendText("]");
}
protected void _format(LiteralName o, ITextFlow.WithText stream) {
stream.appendText(o.getValue());
}
protected void _format(LiteralNameOrReference o, ITextFlow.WithText stream) {
stream.appendText(o.getValue());
}
protected void _format(LiteralRegex o, ITextFlow.WithText stream) {
stream.appendText(o.getValue());
}
protected void _format(LiteralUndef o, ITextFlow.WithText stream) {
stream.appendText("undef");
}
protected void _format(NodeDefinition o, ITextFlow.WithText stream) {
stream.appendText("node");
stream.appendSpace();
internalCommaSeparatedList(o.getHostNames(), stream);
stream.appendSpace();
if(o.getParentName() != null) {
doFormat(o.getParentName(), stream);
stream.appendSpace();
}
stream.appendText("{");
stream.changeIndentation(1);
stream.appendBreaks(1);
formatStatementList(o.getStatements(), stream);
stream.changeIndentation(-1);
stream.appendText("}");
}
protected void _format(OrExpression o, ITextFlow.WithText stream) {
internalFormatBinaryExpression(o, "or", stream);
}
protected void _format(ParenthesisedExpression o, ITextFlow.WithText stream) {
stream.appendText("(");
doFormat(o.getExpr(), stream);
stream.appendText(")");
}
protected void _format(PuppetManifest o, ITextFlow.WithText stream) {
formatStatementList(o.getStatements(), stream);
}
protected void _format(ResourceBody o, ITextFlow.WithText stream) {
throw new UnsupportedOperationException("Should not be called - use internalFormat");
}
protected void _format(ResourceExpression o, ITextFlow.WithText stream) {
doFormat(o.getResourceExpr(), stream);
stream.appendSpace();
stream.appendText("{");
int bodyCount = o.getResourceData().size();
switch(bodyCount) {
case 0:
stream.appendBreaks(1);
break;
case 1:
ResourceBody body = o.getResourceData().get(0);
// no space after brace if first body has no title
if(body.getNameExpr() != null)
stream.appendSpace();
internalFormatResourceBody(body, true, stream);
stream.appendBreaks(1);
break;
default:
stream.changeIndentation(1);
;
stream.appendBreaks(1);
Iterator<ResourceBody> itor = o.getResourceData().iterator();
while(itor.hasNext()) {
internalFormatResourceBody(itor.next(), false, stream);
stream.appendText(";");
stream.appendBreaks(1);
// add extra blank line between resources, but not between last and closing brace
if(itor.hasNext())
stream.appendBreaks(1);
}
stream.changeIndentation(-1);
break;
}
stream.appendText("}");
}
/**
* TODO: Column collections
*
* @param o
* @param stream
*/
protected void _format(SelectorEntry o, ITextFlow.WithText stream) {
internalFormatBinaryExpression(o, "=>", stream);
}
protected void _format(SelectorExpression o, ITextFlow.WithText stream) {
doFormat(o.getLeftExpr(), stream);
stream.appendSpace();
stream.appendText("?");
stream.appendSpace();
// always surround with {} even if they are optional when there is only one entry
stream.appendText("{");
stream.changeIndentation(1);
stream.appendBreaks(1);
// calculate column width
// Simply skip entries that can be very wide/unknown - format the rest
// TODO: Set "folding" in the stream to allow nested List/hash to fold
//
int width = 0;
for(Expression e : o.getParameters()) {
MeasuredTextFlow inner = new MeasuredTextFlow(formattingContext);
// if e is not a SelectorEntry, it is really a syntax error, but do something reasonable
doFormat(e instanceof SelectorEntry
? ((SelectorEntry) e).getLeftExpr()
: e, inner);
width = inner.getWidth();
}
for(Expression e : o.getParameters()) {
if(e instanceof SelectorEntry == false) {
doFormat(e, stream);
stream.appendBreaks(1);
}
else {
SelectorEntry se = (SelectorEntry) e;
int before = stream.size();
doFormat(se.getLeftExpr(), stream);
int after = stream.size();
// pad to width
stream.appendSpaces(width - (after - before));
stream.appendSpace();
stream.appendText("=>");
stream.appendSpace();
doFormat(se.getRightExpr(), stream);
stream.appendText(",");
stream.appendBreaks(1);
}
}
stream.changeIndentation(-1);
stream.appendText("}");
}
protected void _format(SingleQuotedString o, ITextFlow.WithText stream) {
stream.appendText("'");
stream.appendText(o.getText());
stream.appendText("'");
}
protected void _format(UnaryMinusExpression o, ITextFlow.WithText stream) {
stream.appendText("-");
doFormat(o.getExpr(), stream);
}
protected void _format(UnaryNotExpression o, ITextFlow.WithText stream) {
stream.appendText("!");
doFormat(o.getExpr(), stream);
}
protected void _format(UnlessExpression o, ITextFlow.WithText stream) {
stream.appendText("if");
stream.appendSpace();
doFormat(o.getCondExpr(), stream);
stream.appendSpace();
stream.appendText("{");
stream.changeIndentation(1);
stream.appendBreaks(1);
formatStatementList(o.getThenStatements(), stream);
stream.changeIndentation(-1);
stream.appendText("}");
}
protected void _format(UnquotedString o, ITextFlow.WithText stream) {
stream.appendText("${");
doFormat(o.getExpression(), stream);
stream.appendText("}");
}
protected void _format(VariableExpression o, ITextFlow.WithText stream) {
stream.appendText(o.getVarName());
}
protected void _format(VariableTE o, ITextFlow.WithText stream) {
stream.appendText(o.getVarName());
}
protected void _format(VerbatimTE o, ITextFlow.WithText stream) {
stream.appendText(o.getText());
}
protected void _format(VirtualCollectQuery o, ITextFlow.WithText stream) {
stream.appendText("<|");
stream.appendSpace();
doFormat(o.getExpr(), stream);
stream.appendSpace();
stream.appendText("|>");
}
protected void _format(VirtualNameOrReference o, ITextFlow.WithText stream) {
stream.appendText("@");
if(o.isExported()) {
stream.appendText("@");
}
stream.appendText(o.getValue());
}
private void doFormat(EObject o, ITextFlow stream) {
// EObject context = getContext(o);
Iterator<INode> itor = commentAssociations.before(o);
while(itor.hasNext()) {
INode n = itor.next();
stream.appendText(n.getText());
}
formatDispatcher.invoke(o, stream);
itor = commentAssociations.after(o);
while(itor.hasNext()) {
INode n = itor.next();
stream.appendText(n.getText());
}
}
public void format(EObject o, ITextFlow.WithText stream) {
NodeModelUtils.findActualNodeFor(o);
commentAssociations = commentAssociator.associateCommentsWithSemanticEObjects(
o, Sets.newHashSet(NodeModelUtils.findActualNodeFor(o).getRootNode()));
doFormat(o, stream);
// trailing comments
Iterator<INode> itor = commentAssociations.before(null);
while(itor.hasNext())
stream.appendText(itor.next().getText());
}
protected void formatStatementList(EList<Expression> statements, ITextFlow.WithText stream) {
int size = statements.size();
for(int i = 0; i < size; i++) {
Expression s = statements.get(i);
if(s instanceof LiteralNameOrReference) {
_format((LiteralNameOrReference) s, stream);
if(i + 1 < size) {
stream.appendSpace();
doFormat(statements.get(i + 1), stream);
i++;
}
}
else
doFormat(s, stream);
stream.appendBreaks(1);
}
}
protected EObject getContext(EObject semanticObject) {
Iterator<EObject> contexts = contextFinder.findContextsByContentsAndContainer(semanticObject, null).iterator();
if(!contexts.hasNext())
throw new RuntimeException("No Context for " + EmfFormatter.objPath(semanticObject) + " could be found");
return contexts.next();
}
/**
* @param hostNames
* @param stream
*/
private void internalCommaSeparatedList(Iterable<? extends EObject> elements, ITextFlow.WithText stream) {
Iterator<? extends EObject> itor = elements.iterator();
while(itor.hasNext()) {
doFormat(itor.next(), stream);
if(itor.hasNext()) {
stream.appendText(",");
stream.appendSpace();
}
}
}
protected void internalFormat(AttributeOperations o, boolean commaLast, ITextFlow.WithText stream) {
if(o == null)
return;
int maxWidth = 0;
if(o != null) {
final int size = o.getAttributes().size();
for(AttributeOperation ao : o.getAttributes()) {
String key = ao.getKey();
if(key != null)
maxWidth = Math.max(maxWidth, key.length());
}
int counter = 0;
for(AttributeOperation ao : o.getAttributes()) {
String key = ao.getKey();
if(key == null)
key = "";
stream.appendText(key);
stream.appendSpaces(maxWidth - key.length() + 1);
stream.appendText(ao.getOp());
stream.appendSpace();
doFormat(ao.getValue(), stream);
if(commaLast || counter + 1 < size)
stream.appendText(",");
if(counter + 1 < size)
stream.appendBreaks(1);
counter++;
}
}
}
/**
* @param arguments
* @param stream
*/
private void internalFormatArguments(DefinitionArgumentList arguments, ITextFlow.WithText stream) {
if(arguments == null)
return;
stream.appendText("(");
Iterator<DefinitionArgument> itor = arguments.getArguments().iterator();
while(itor.hasNext()) {
doFormat(itor.next(), stream);
if(itor.hasNext()) {
stream.appendText(",");
stream.appendSpace();
}
}
stream.appendText(")");
}
protected void internalFormatBinaryExpression(BinaryExpression o, String op, ITextFlow.WithText stream) {
doFormat(o.getLeftExpr(), stream);
stream.appendSpace();
stream.appendText(op);
stream.appendSpace();
doFormat(o.getRightExpr(), stream);
}
protected void internalFormatResourceBody(ResourceBody o, boolean commaLast, ITextFlow.WithText stream) {
if(o.getNameExpr() != null) {
doFormat(o.getNameExpr(), stream);
stream.appendText(":");
}
if(o.getAttributes() != null && o.getAttributes().getAttributes().size() > 0) {
stream.changeIndentation(1);
stream.appendBreaks(1);
internalFormat(o.getAttributes(), commaLast, stream);
stream.changeIndentation(-1);
}
}
}