/*******************************************************************************
* 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 java.util.Collections;
import java.util.List;
import org.antlr.runtime.Lexer;
import org.eclipse.emf.ecore.EObject;
import com.sap.furcas.metamodel.FURCAS.TCS.ClassTemplate;
import com.sap.furcas.metamodel.FURCAS.TCS.ConcreteSyntax;
import com.sap.furcas.metamodel.FURCAS.TCS.ContextTemplate;
import com.sap.furcas.metamodel.FURCAS.TCS.Template;
import com.sap.furcas.metamodel.FURCAS.textblocks.AbstractToken;
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.context.InitialPrintContext;
import com.sap.furcas.prettyprinter.context.PrintContext;
import com.sap.furcas.prettyprinter.context.PrintResult;
import com.sap.furcas.prettyprinter.context.PrintResult.ResultContainer;
import com.sap.furcas.prettyprinter.exceptions.SyntaxMismatchException;
import com.sap.furcas.prettyprinter.incremental.TextBlockBasedPrintPolicy;
import com.sap.furcas.prettyprinter.incremental.TextBlockIndex;
import com.sap.furcas.prettyprinter.policy.DefaultPrintPolicy;
import com.sap.furcas.prettyprinter.policy.PrintPolicy;
import com.sap.furcas.runtime.common.interfaces.IMetaModelLookup;
import com.sap.furcas.runtime.common.util.TCSSpecificOCLEvaluator;
import com.sap.furcas.runtime.parser.ParserFactory;
import com.sap.furcas.runtime.parser.impl.ObservableInjectingParser;
import com.sap.furcas.runtime.tcs.MetaModelElementResolutionHelper;
import com.sap.furcas.runtime.tcs.SyntaxLookup;
import com.sap.furcas.runtime.tcs.TcsUtil;
import com.sap.furcas.runtime.textblocks.TbDebugUtil;
import com.sap.furcas.runtime.textblocks.TbNavigationUtil;
import com.sap.furcas.runtime.textblocks.validation.TbValidationUtil;
/**
* A {@link PrettyPrinter} translates a domain model into a textual representation.
* The type and style of such a textual representation is given in form of a {@link ConcreteSyntax}.
*
* The {@link PrettyPrinter} resembles a backtracking, recursive descent parser.
* In contrast to matching parse rules against text fragments, the {@link PrettyPrinter} has to
* pattern match {@link Template}s against domain model elements. It does this by simultaneously
* inspecting and walking both the domain model and the syntax definition.
*
* During recursive descend (realized via recursive method calls), the {@link PrettyPrinter} tries
* to determine and apply the best matching template for a model element.
* A successfull application of a complete {@link Template} then leads to the instantiation of a {@link TextBlock}.
* {@link TextBlock}s are a structured representation of text and serve as the input for FURCAS text editors
* and the incremental parser infrastructure.
*
* This implementation is based on earlier work by Frédéric Jouault and Mikaël Barbero, with contributions
* by Philipp Meier and Andreas Landerer.
*
* @author Stephan Erb
*/
public class PrettyPrinter {
private final TemplateHandler templateHandler;
private final ConcreteSyntax syntax;
private final Formatter formatter;
private final TextBlocksFactory tbfactory;
public PrettyPrinter(ConcreteSyntax syntax, IMetaModelLookup<EObject> metamodelLookup,
TCSSpecificOCLEvaluator oclEvaluator, ParserFactory<? extends ObservableInjectingParser, ? extends Lexer> parserFactory) {
this.tbfactory = new TextBlocksFactory(parserFactory);
this.syntax = syntax;
this.formatter = new Formatter(tbfactory);
SequenceElementValidator validator = new SequenceElementValidator(oclEvaluator);
MetaModelElementResolutionHelper<EObject> resolutionHelper = new MetaModelElementResolutionHelper<EObject>(metamodelLookup);
SyntaxLookup syntaxLookup = new SyntaxLookup(syntax, resolutionHelper);
this.templateHandler = new TemplateHandler(tbfactory, formatter, syntaxLookup);
TemplateFinder templateFinder = new TemplateFinder(syntaxLookup, metamodelLookup);
SequenceHandler sequenceHandler = new SequenceHandler(tbfactory, templateFinder, templateHandler,
oclEvaluator, validator, formatter);
this.templateHandler.setSequenceHandler(sequenceHandler);
}
/**
* Print a model element from scratch.
*/
public TextBlock prettyPrint(EObject modelElement) throws SyntaxMismatchException {
PrintPolicy policy = new DefaultPrintPolicy();
ClassTemplate template = TcsUtil.getMainClassTemplate(syntax);
return prettyPrintInternal(modelElement, template, policy);
}
/**
* Print a model element from scratch, but re-use information from an
* existing/old {@link TextBlock} of this element.
*/
public TextBlock prettyPrint(EObject modelElement, TextBlock oldBlock) throws SyntaxMismatchException {
return prettyPrint(modelElement, TcsUtil.getMainClassTemplate(syntax), oldBlock);
}
/**
* Print a model element from scratch, but re-use information from an
* existing/old {@link TextBlock} of this element.
*/
public TextBlock prettyPrint(EObject modelElement, ContextTemplate template, TextBlock oldBlock) throws SyntaxMismatchException {
assert oldBlock.getCorrespondingModelElements().contains(modelElement);
TextBlockIndex index = new TextBlockIndex();
index.index(oldBlock, syntax);
PrintPolicy policy = new TextBlockBasedPrintPolicy(oldBlock, template, index);
return prettyPrintInternal(modelElement, template, policy);
}
private TextBlock prettyPrintInternal(EObject modelElement, ContextTemplate template, PrintPolicy policy) throws SyntaxMismatchException {
PrintContext context = new InitialPrintContext();
// Print the template
PrintResult result = templateHandler.serializeContextTemplate(modelElement, template, /*seqElem*/ null,
context, policy);
assert result.getNodes().size() == 1;
TextBlock resultBlock = (TextBlock) result.getNodes().get(0);
addPendingFormatting(policy, context, result, resultBlock);
resultBlock.setCachedString(TbDebugUtil.getDocumentNodeAsPlainString(resultBlock));
addBosEosTokensIfNeeded(template, resultBlock);
TbValidationUtil.assertTextBlockConsistencyRecursive(resultBlock);
return resultBlock;
}
private void addPendingFormatting(PrintPolicy policy, PrintContext context, PrintResult result, TextBlock resultBlock) {
ResultContainer tmpResult = new ResultContainer(Collections.<FormatRequest>emptyList());
tmpResult.merge(result);
context = tmpResult.asSubContext(context); // state of the world after the model has ben printed entirely
List<FormatRequest> formatRequests = policy.getOverruledFormattingBetween(context.getPendingFormattingRequest(),
context.getLastSequenceElement(), /*seqElem*/ null, /* next token content */ "");
List<DocumentNode> formatting = formatter.translateToTokens(formatRequests, context);
resultBlock.getSubNodes().addAll(formatting);
resultBlock.setLength(TextBlocksFactory.getLengthOf(formatting, resultBlock.getLength()));
}
private void addBosEosTokensIfNeeded(Template template, TextBlock resultBlock) {
if (template instanceof ClassTemplate && ((ClassTemplate) template).isIsMain()) {
AbstractToken bosToken = tbfactory.createBOSToken();
AbstractToken eosToken = tbfactory.createEOSToken(resultBlock.getLength());
resultBlock.getSubNodes().add(0, bosToken);
resultBlock.getSubNodes().add(resultBlock.getSubNodes().size(), eosToken);
fixLookBack(TbNavigationUtil.nextToken(bosToken));
}
}
/**
* The pretty printer uses hardcoded lookbacks.
* We have to prevent that we look beyond the first token.
*/
private void fixLookBack(AbstractToken firstContentToken) {
if (firstContentToken == null) {
return;
} else {
firstContentToken.setLookback(0);
}
}
}