/*******************************************************************************
* Copyright (c) 2011 SAP AG and others.
* 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:
* SAP AG - initial API and implementation
******************************************************************************/
package com.sap.furcas.prettyprinter;
import static com.sap.furcas.prettyprinter.TextBlocksFactory.getLengthOf;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import com.sap.furcas.metamodel.FURCAS.TCS.ClassTemplate;
import com.sap.furcas.metamodel.FURCAS.TCS.ContextTemplate;
import com.sap.furcas.metamodel.FURCAS.TCS.EnumLiteralMapping;
import com.sap.furcas.metamodel.FURCAS.TCS.EnumerationTemplate;
import com.sap.furcas.metamodel.FURCAS.TCS.Literal;
import com.sap.furcas.metamodel.FURCAS.TCS.Operator;
import com.sap.furcas.metamodel.FURCAS.TCS.OperatorTemplate;
import com.sap.furcas.metamodel.FURCAS.TCS.PrimitiveTemplate;
import com.sap.furcas.metamodel.FURCAS.TCS.Priority;
import com.sap.furcas.metamodel.FURCAS.TCS.Property;
import com.sap.furcas.metamodel.FURCAS.TCS.Sequence;
import com.sap.furcas.metamodel.FURCAS.TCS.SequenceElement;
import com.sap.furcas.metamodel.FURCAS.TCS.TCSPackage;
import com.sap.furcas.metamodel.FURCAS.TCS.Template;
import com.sap.furcas.metamodel.FURCAS.textblocks.DocumentNode;
import com.sap.furcas.metamodel.FURCAS.textblocks.TextBlock;
import com.sap.furcas.prettyprinter.Formatter.FormatRequest;
import com.sap.furcas.prettyprinter.Formatter.Type;
import com.sap.furcas.prettyprinter.context.PrintContext;
import com.sap.furcas.prettyprinter.context.PrintResult;
import com.sap.furcas.prettyprinter.context.PrintResult.LeafResult;
import com.sap.furcas.prettyprinter.context.PrintResult.NullResult;
import com.sap.furcas.prettyprinter.context.PrintResult.ResultContainer;
import com.sap.furcas.prettyprinter.context.TemplatePrintContext;
import com.sap.furcas.prettyprinter.exceptions.SyntaxMismatchException;
import com.sap.furcas.prettyprinter.policy.PrintPolicy;
import com.sap.furcas.prettyprinter.utils.EMFModelInspector;
import com.sap.furcas.runtime.tcs.SyntaxLookup;
import com.sap.furcas.runtime.tcs.TcsUtil;
import com.sap.furcas.runtime.textblocks.shortprettyprint.PrettyPrinterUtil;
/**
* Serializes a model element according to a given {@link Template}. Most of the actual
* serialization work is performed by a {@link SequenceHandler}.
*
* @author Stephan Erb
*
*/
public class TemplateHandler {
private static DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
static {
decimalFormatSymbols.setDecimalSeparator('.');
}
private final DecimalFormat decilmalFormat = new DecimalFormat("0.##############", decimalFormatSymbols);
private final TextBlocksFactory tbFactory;
private final Formatter formatter;
private final SyntaxLookup syntaxLookup;
private SequenceHandler sequenceHandler;
public TemplateHandler(TextBlocksFactory factory, Formatter formatter, SyntaxLookup syntaxLookup) {
this.tbFactory = factory;
this.formatter = formatter;
this.syntaxLookup = syntaxLookup;
}
public void setSequenceHandler(SequenceHandler handler) {
sequenceHandler = handler;
}
public LeafResult serializePrimitiveTemplate(Object value, SequenceElement seqElem, PrimitiveTemplate template,
PrintContext context, PrintPolicy policy) {
String content = PrettyPrinterUtil.escapeUsingSerializer(primitiveToString(value), template);
return createFormattedTokenWithContent(content, seqElem, context, policy);
}
private String primitiveToString(Object value) {
if (value instanceof Double) {
return decilmalFormat.format(value);
} else {
return value.toString();
}
}
public PrintResult serializeEnumerationTemplate(EObject modelElement, Property seqElem, Enum<?> value,
EnumerationTemplate template, PrintContext context, PrintPolicy policy) throws SyntaxMismatchException {
String enumLiteral = value.toString();
for (EnumLiteralMapping mapping : template.getMappings()) {
if (mapping.getLiteral().getName().equals(enumLiteral)) {
return sequenceHandler.serializeSequenceElement(modelElement, mapping.getElement(), context, policy);
}
}
// as fallback or if the template is automatic, print as is
return createFormattedTokenWithContent(enumLiteral, seqElem, context, policy);
}
private LeafResult createFormattedTokenWithContent(String content, SequenceElement seqElem, PrintContext context,
PrintPolicy policy) {
List<FormatRequest> formatRequests = new ArrayList<FormatRequest>(context.getPendingFormattingRequest());
formatRequests.add(FormatRequest.create(Type.ADD_OPTIONAL_SPACE));
formatRequests = policy.getOverruledFormattingBetween(formatRequests, context.getLastSequenceElement(), seqElem, content);
List<DocumentNode> formatting = formatter.translateToTokens(formatRequests, context);
formatting.add(tbFactory.createLexedToken(content, seqElem, getLengthOf(formatting, context.getNextOffset())));
return new LeafResult(formatting);
}
public PrintResult serializeContextTemplate(EObject modelElement, ContextTemplate template, SequenceElement seqElem,
PrintContext context, PrintPolicy policy) throws SyntaxMismatchException {
switch (template.eClass().getClassifierID()) {
case TCSPackage.CLASS_TEMPLATE:
return serializeClassTemplate(modelElement, (ClassTemplate) template, seqElem, context, policy);
case TCSPackage.OPERATOR_TEMPLATE:
return serializeOperatorTemplate(modelElement, (OperatorTemplate) template, seqElem, context, policy);
default:
Activator.logger.logError("Unable to serialize unknwon context template of type "
+ EMFModelInspector.getTypeName(template) + ". Skipping.");
return new NullResult();
}
}
private PrintResult serializeClassTemplate(EObject modelElement, ClassTemplate template, SequenceElement seqElem,
PrintContext context, PrintPolicy policy) throws SyntaxMismatchException {
PrintContext templateContext = new TemplatePrintContext(context, modelElement, template);
ResultContainer result = sequenceHandler.serializeSequence(modelElement, template.getTemplateSequence(), templateContext, policy);
TextBlock node = tbFactory.createTextBlock(modelElement, result.getNodes(), template, seqElem, context.getNextOffset());
return new LeafResult(node, result, result.asSubContext(context).getPendingFormattingRequest());
}
private PrintResult serializeOperatorTemplate(EObject modelElement, OperatorTemplate template, SequenceElement seqElem,
PrintContext context, PrintPolicy policy) throws SyntaxMismatchException {
String opPropName = TcsUtil.getPropertyName(template.getStoreOperatorTo());
String rightPropName = TcsUtil.getPropertyName(template.getStoreRightSideTo());
Object r = null;
boolean isPostfix = false; // only valid for unary operators
boolean isUnary = false;
if (rightPropName != null) {
r = EMFModelInspector.get(modelElement, rightPropName);
if (r instanceof Collection<?>) {
isUnary = (((Collection<?>) r).size() == 0);
} else {
isUnary = r == null;
}
} else {
isUnary = true;
}
Operator operator = null;
if (opPropName != null) {
String op = EMFModelInspector.getString(modelElement, opPropName);
if (op == null) {
throw new RuntimeException("Property " + opPropName + " has not been set in " + modelElement);
}
for (Iterator<Operator> i = template.getOperators().iterator(); i.hasNext() && (operator == null);) {
Operator opme = i.next();
Literal literal = opme.getLiteral();
String opmes = null;
if (literal == null) {
opmes = "";
} else {
opmes = literal.getValue();
}
int arity = opme.getArity();
if (op.equals(opmes)) {
if (rightPropName != null) {
if ((isUnary && (arity == 1)) || ((!isUnary) && (arity == 2))) {
operator = opme;
}
} else {
operator = opme;
isPostfix = opme.isPostfix();
}
}
}
if (operator == null) {
throw new RuntimeException();
//throw new OperatorTemplateOperatorMissmatchException(currentContext, op, template);
}
} else {
operator = template.getOperators().iterator().next();
isUnary = operator.getArity() == 1;
if (isUnary) {
isPostfix = operator.isPostfix();
}
}
int curPrio = context.getPriority();
int priority = ((Priority) operator.eContainer()).getValue();
boolean paren = priority > curPrio;
Literal literal = operator.getLiteral();
PrintContext templateContext = new TemplatePrintContext(context, modelElement, template, priority);
ResultContainer result = new ResultContainer(templateContext.getPendingFormattingRequest());
if (paren) {
result.merge(sequenceHandler.serializeLiteral(syntaxLookup.getSymbolByValue("("), seqElem,
result.asSubContext(templateContext), policy));
}
EObject source = (EObject) TcsUtil.getPropertyValue(modelElement, template.getStoreLeftSideTo());
if (isUnary) {
if (isPostfix) {
result.merge(sequenceHandler.serializeEObjectViaTemplateCall(modelElement, seqElem, source, /*mode*/ null,
result.asSubContext(templateContext), policy));
if (literal != null) {
result.merge(sequenceHandler.serializeLiteral(literal, seqElem,
result.asSubContext(templateContext), policy));
}
} else {
if (literal != null) {
result.merge(sequenceHandler.serializeLiteral(literal, seqElem,
result.asSubContext(templateContext), policy));
}
result.merge(sequenceHandler.serializeEObjectViaTemplateCall(modelElement, seqElem, source, /*mode*/ null,
result.asSubContext(templateContext), policy)); }
} else {
result.merge(sequenceHandler.serializeEObjectViaTemplateCall(modelElement, seqElem, source, /*mode*/ null,
result.asSubContext(templateContext), policy));
if (literal != null) {
result.merge(sequenceHandler.serializeLiteral(literal, seqElem,
result.asSubContext(templateContext), policy));
}
}
Sequence seq = template.getTemplateSequence();
if (rightPropName == null) {
PrintContext mainTemplateContext = new TemplatePrintContext(result.asSubContext(templateContext), modelElement, template, Integer.MAX_VALUE);
result.merge(sequenceHandler.serializeSequence(modelElement, seq, mainTemplateContext, policy));
} else {
if (seq != null) {
result.merge(sequenceHandler.serializeSequence(modelElement, seq, result.asSubContext(templateContext), policy));
}
if (r instanceof Collection<?>) {
for (Iterator<?> i = ((Collection<?>) r).iterator(); i.hasNext();) {
result.merge(sequenceHandler.serializeEObjectViaTemplateCall(modelElement, seqElem, ((EObject) i.next()),
/*mode*/ null, result.asSubContext(templateContext), policy));
}
} else {
if (!isUnary) {
result.merge(sequenceHandler.serializeEObjectViaTemplateCall(modelElement, seqElem, ((EObject) r),
/*mode*/ null, result.asSubContext(templateContext), policy));
}
}
}
if (paren) {
result.merge(sequenceHandler.serializeLiteral(syntaxLookup.getSymbolByValue(")"), seqElem, result.asSubContext(templateContext), policy));
}
TextBlock node = tbFactory.createTextBlock(modelElement, result.getNodes(), template, seqElem, context.getNextOffset());
return new LeafResult(node, result, result.asSubContext(templateContext).getPendingFormattingRequest());
}
}