/*
 * $Id$
 *
 * SARL is an general-purpose agent programming language.
 * More details on http://www.sarl.io
 *
 * Copyright (C) 2014-2026 SARL.io, the original authors and main authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.sarl.lang.mwe2.codebuilder.fragments;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;

import com.google.inject.Inject;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Group;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.UntilToken;
import org.eclipse.xtext.documentation.IEObjectDocumentationProvider;
import org.eclipse.xtext.documentation.IEObjectDocumentationProviderExtension;
import org.eclipse.xtext.documentation.impl.MultiLineCommentDocumentationProvider;
import org.eclipse.xtext.formatting2.ITextReplacerContext;
import org.eclipse.xtext.formatting2.regionaccess.IComment;
import org.eclipse.xtext.formatting2.regionaccess.ILineRegion;
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess;
import org.eclipse.xtext.formatting2.regionaccess.ITextSegment;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.serializer.ISerializationContext;
import org.eclipse.xtext.serializer.acceptor.ISequenceAcceptor;
import org.eclipse.xtext.serializer.acceptor.ISyntacticSequenceAcceptor;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider.ISynState;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider.ISynTransition;
import org.eclipse.xtext.serializer.analysis.ISyntacticSequencerPDAProvider.SynStateType;
import org.eclipse.xtext.serializer.diagnostic.ISerializationDiagnostic.Acceptor;
import org.eclipse.xtext.serializer.sequencer.HiddenTokenSequencer;
import org.eclipse.xtext.serializer.sequencer.ISyntacticSequencer;
import org.eclipse.xtext.serializer.sequencer.RuleCallStack;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.xbase.XBlockExpression;
import org.eclipse.xtext.xbase.compiler.DocumentationAdapter;
import org.eclipse.xtext.xbase.compiler.IAppendable;
import org.eclipse.xtext.xbase.compiler.StringBuilderBasedAppendable;
import org.eclipse.xtext.xbase.compiler.output.FakeTreeAppendable;
import org.eclipse.xtext.xbase.lib.Pure;
import org.eclipse.xtext.xtext.generator.grammarAccess.GrammarAccessExtensions;
import org.eclipse.xtext.xtext.generator.model.GuiceModuleAccess.BindingFactory;
import org.eclipse.xtext.xtext.generator.model.TypeReference;

/** Generate the documentation adapters and related types.
 *
 * @author $Author: sgalland$
 * @version $FullVersion$
 * @mavengroupid $GroupId$
 * @mavenartifactid $ArtifactId$
 */
public class DocumentationBuilderFragment extends AbstractSubCodeBuilderFragment {

	@Inject
	private GrammarAccessExtensions grammarAccessExtensions;

	/** Replies the interface for the documentation builder.
	 *
	 * @return the documentation builder interface.
	 */
	@Pure
	public TypeReference getIEcoreDocumentationBuilder() {
		return new TypeReference(getCodeElementExtractor().getDocumentationPackage()
				+ ".IEcoreDocumentationBuilder"); //$NON-NLS-1$
	}

	/** Replies the implementation for the documentation builder.
	 *
	 * @return the documentation builder implementation.
	 */
	@Pure
	public TypeReference getEcoreDocumentationBuilderImpl() {
		return new TypeReference(getCodeElementExtractor().getDocumentationPackage()
				+ ".EcoreDocumentationBuilder"); //$NON-NLS-1$
	}

	/** Replies the interface for the documentation formatter.
	 *
	 * @return the documentation formatter interface.
	 */
	@Pure
	public TypeReference getIDocumentationFormatter() {
		return new TypeReference(getCodeElementExtractor().getDocumentationPackage()
				+ ".IDocumentationFormatter"); //$NON-NLS-1$
	}

	/** Replies the implementation for the documentation formatter.
	 *
	 * @return the documentation formatter implementation.
	 */
	@Pure
	public TypeReference getDocumentationFormatterImpl() {
		return new TypeReference(getCodeElementExtractor().getDocumentationPackage()
				+ ".DocumentationFormatter"); //$NON-NLS-1$
	}

	/** Replies the implementation for the syntactic sequencer supporting Ecore documentation.
	 *
	 * @return the syntactic sequencer implementation.
	 */
	@Pure
	public TypeReference getEcoreDocumentationSyntacticSequencer() {
		return new TypeReference(getCodeElementExtractor().getSerializerPackage() + "." //$NON-NLS-1$
				+ getLanguageName().toUpperCase() + "EcoreDocumentationSyntacticSequencer"); //$NON-NLS-1$
	}

	/** Replies the implementation for the custom syntactic sequencer supporting Ecore documentation.
	 *
	 * @return the syntactic sequencer implementation.
	 */
	@Pure
	public TypeReference getEcoreDocumentationSyntacticSequencerCustom() {
		return new TypeReference(getCodeElementExtractor().getSerializerPackage() + "." //$NON-NLS-1$
				+ getLanguageName().toUpperCase() + "EcoreDocumentationSyntacticSequencerCustom"); //$NON-NLS-1$
	}

	/** Replies the implementation for the syntactic sequencer.
	 *
	 * @return the syntactic sequencer implementation.
	 */
	@Pure
	public TypeReference getSyntacticSequencer() {
		return new TypeReference(getCodeElementExtractor().getSerializerPackage() + "." //$NON-NLS-1$
				+ getLanguageName().toUpperCase() + "SyntacticSequencer"); //$NON-NLS-1$
	}

	/** Replies the implementation for the documentation provider.
	 *
	 * @return the documentation provider implementation.
	 */
	@Pure
	public TypeReference getDocumentationProviderImpl() {
		return new TypeReference(getCodeElementExtractor().getDocumentationPackage() + "." //$NON-NLS-1$
				+ getLanguageName() + "DocumentationProvider"); //$NON-NLS-1$
	}

	/** Replies the custom implementation for the documentation provider.
	 *
	 * @return the custom documentation provider implementation.
	 */
	@Pure
	public TypeReference getDocumentationProviderImplCustom() {
		return new TypeReference(getCodeElementExtractor().getDocumentationPackage() + "." //$NON-NLS-1$
				+ getLanguageName().toUpperCase() + "DocumentationProviderCustom"); //$NON-NLS-1$
	}

	@Override
	public void generate() {
		super.generate();
		generateInnerDocumentationAdapter();
		generateIDocumentationFormatter();
		generateIEcoreDocumentationBuilder();
		generateDocumentationFormatterImpl();
		generateEcoreDocumentationBuilderImpl();
		generateDocumentationProviderImpl();
		generateEcoreDocumentationSyntacticSequencer();
	}

	@Override
	public void getExportedPackages(Set<String> exportedPackages) {
		if (exportedPackages != null) {
			super.getExportedPackages(exportedPackages);
			exportedPackages.add(getCodeElementExtractor().getSerializerPackage());
			exportedPackages.add(getCodeElementExtractor().getDocumentationPackage());
		}
	}

	@Override
	public void generateRuntimeBindings(BindingFactory factory) {
		super.generateRuntimeBindings(factory);

		factory.addfinalTypeToType(getIDocumentationFormatter(), getDocumentationFormatterImpl());
		factory.addfinalTypeToTypeSingleton(getIEcoreDocumentationBuilder(), getEcoreDocumentationBuilderImpl());

		bindTypeReferences(factory,
				new TypeReference(IEObjectDocumentationProvider.class),
				getDocumentationProviderImpl(),
				getDocumentationProviderImplCustom());
		bindTypeReferences(factory,
				new TypeReference(IEObjectDocumentationProviderExtension.class),
				getDocumentationProviderImpl(),
				getDocumentationProviderImplCustom());
		bindTypeReferences(factory,
				new TypeReference(ISyntacticSequencer.class),
				getEcoreDocumentationSyntacticSequencer(),
				getEcoreDocumentationSyntacticSequencerCustom());
	}

	/** Generate the adapter that supports the inner documentation.
	 */
	protected void generateInnerDocumentationAdapter() {
		final var adapter = getCodeElementExtractor().getInnerBlockDocumentationAdapter();
		final var content = new StringConcatenationClient() {
			@Override
			protected void appendTo(TargetStringConcatenation it) {
				appendEmptyComment(it);
				it.append("public class "); //$NON-NLS-1$
				it.append(adapter.getSimpleName());
				it.append(" extends "); //$NON-NLS-1$
				it.append(AdapterImpl.class);
				it.append(" {"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\tprivate String documentation;"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tpublic String getDocumentation() {"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t\treturn this.documentation;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\tpublic void setDocumentation(String documentation) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tthis.documentation = documentation;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tpublic boolean isAdapterForType(Object type) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\treturn type == "); //$NON-NLS-1$
				it.append(adapter.getSimpleName());
				it.append(".class;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
			}

		};
		final var createJavaFile = getFileAccessFactory().createJavaFile(adapter, content);
		createJavaFile.writeTo(getSrcGen());
	}

	/** Generate the interface for the documentation formatter.
	 */
	protected void generateIDocumentationFormatter() {
		final var formatter = getIDocumentationFormatter();
		final var content = new StringConcatenationClient() {
			@Override
			protected void appendTo(TargetStringConcatenation it) {
				it.append("/** Format a documentation string."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append(" */"); //$NON-NLS-1$
				it.newLine();
				it.append("public interface "); //$NON-NLS-1$
				it.append(formatter.getSimpleName());
				it.append(" {"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the characters that must be used as prefix of each line in a multiline comment."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the prefix."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tString getMultilineCommentLinePrefix();"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the characters that must be used to start a comment."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the symbols."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tString getMultilineCommentStartSymbols();"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the characters that must be used to end a comment."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the symbols."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tString getMultilineCommentEndSymbols();"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the string that must be used as prefix of a singleline comment."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the prefix."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tString getSinglelineCommentPrefix();"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the formatted string that corresponds to the given documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param doc the documentation text. It may be on multiple lines."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the formatted comment."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tString formatMultilineComment(String doc);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the formatted string that corresponds to the given documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param doc the documentation text. It may be on multiple lines."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param indentation the string to put for indenting the comment."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the formatted comment."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tString formatMultilineComment(String doc, String indentation);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the formatted string that corresponds to the given documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param doc the documentation text. It may be on multiple lines."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param appendable the receiver of the formatted string."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tvoid formatMultilineComment(String doc, "); //$NON-NLS-1$
				it.append(IAppendable.class);
				it.append(" appendable);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the formatted string that corresponds to the given documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param doc the documentation text. It may be on multiple lines."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param indentation the string to put for indenting the comment."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param appendable the receiver of the formatted string."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tvoid formatMultilineComment(String doc, String indentation, "); //$NON-NLS-1$
				it.append(IAppendable.class);
				it.append(" appendable);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Format the given multiline documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t *"); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param context the formatting context."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param comment the comment to format out."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tpublic void formatMultilineComment("); //$NON-NLS-1$
				it.append(ITextReplacerContext.class);
				it.append(" context, "); //$NON-NLS-1$
				it.append(IComment.class);
				it.append(" comment);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the formatted string that corresponds to the given documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param doc the documentation text. It may be on multiple lines."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the formatted comment."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tString formatSinglelineComment(String doc);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the formatted string that corresponds to the given documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param doc the documentation text. It may be on multiple lines."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param indentation the string to put for indenting the comment."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the formatted comment."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tString formatSinglelineComment(String doc, String indentation);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the formatted string that corresponds to the given documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param doc the documentation text. It may be on multiple lines."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param appendable the receiver of the formatted string."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tvoid formatSinglelineComment(String doc, "); //$NON-NLS-1$
				it.append(IAppendable.class);
				it.append(" appendable);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the formatted string that corresponds to the given documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param doc the documentation text. It may be on multiple lines."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param indentation the string to put for indenting the comment."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param appendable the receiver of the formatted string."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tvoid formatSinglelineComment(String doc, String indentation, "); //$NON-NLS-1$
				it.append(IAppendable.class);
				it.append(" appendable);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Format the given singleline documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t *"); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param context the formatting context."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param comment the comment to format out."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tvoid formatSinglelineComment("); //$NON-NLS-1$
				it.append(ITextReplacerContext.class);
				it.append(" context, "); //$NON-NLS-1$
				it.append(IComment.class);
				it.append(" comment);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
			}

		};
		final var createJavaFile = getFileAccessFactory().createJavaFile(formatter, content);
		createJavaFile.writeTo(getSrcGen());
	}

	/** Generate the interface for the documentation builder.
	 */
	protected void generateIEcoreDocumentationBuilder() {
		final var builder = getIEcoreDocumentationBuilder();
		final var content = new StringConcatenationClient() {
			@Override
			protected void appendTo(TargetStringConcatenation it) {
				it.append("/** Build a documentation string for specific objects."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append(" */"); //$NON-NLS-1$
				it.newLine();
				it.append("public interface "); //$NON-NLS-1$
				it.append(builder.getSimpleName());
				it.append(" {"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the grammar rule that corresponds to multiline comments."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the ML grammar rule."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\t"); //$NON-NLS-1$
				it.append(AbstractRule.class);
				it.append(" getMLCommentRule();"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the grammar rule that corresponds to singleline comments."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the SL grammar rule."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\t"); //$NON-NLS-1$
				it.append(AbstractRule.class);
				it.append(" getSLCommentRule();"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the documentation formatter used by this builder."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return documentation formatter."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\t"); //$NON-NLS-1$
				it.append(getIDocumentationFormatter());
				it.append(" getDocumentationFormatter();"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies the formatted string that corresponds to the given documentation."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param doc the documentation text. It may be on multiple lines."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param objectType the type of the object for which the document must be built."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return the formatted comment."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tString build(String doc, Class<?> objectType);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("\t/** Replies if multiline comments are the default for the given type of objects."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @param type the type of objects."); //$NON-NLS-1$
				it.newLine();
				it.append("\t * @return {@code true} if multiline comment is the default."); //$NON-NLS-1$
				it.newLine();
				it.append("\t *      Otherwise singleline comment is the default."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append("\t */"); //$NON-NLS-1$
				it.newLine();
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tboolean isMultilineCommentFor(Class<?> type);"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				it.append("}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
			}

		};
		final var createJavaFile = getFileAccessFactory().createJavaFile(builder, content);
		createJavaFile.writeTo(getSrcGen());
	}

	/** Generate the implementation for the documentation formatter.
	 */
	protected void generateDocumentationFormatterImpl() {
		final var formatter = getDocumentationFormatterImpl();
		final var content = new StringConcatenationClient() {
			@SuppressWarnings("synthetic-access")
			@Override
			protected void appendTo(TargetStringConcatenation it) {
				it.append("/** Formatter a documentation string."); //$NON-NLS-1$
				it.newLine();
				appendFileLineComment(it);
				it.append(" */"); //$NON-NLS-1$
				it.newLine();
				it.append("public class DocumentationFormatter implements "); //$NON-NLS-1$
				it.append(getIDocumentationFormatter());
				it.append(" {"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\tprivate static final String SPACE_CHAR = \" \";"); //$NON-NLS-1$
				it.newLine();
				appendEmptyComment(it);
				it.append("\tprivate static final String NL_CHAR = \"\\n\";"); //$NON-NLS-1$
				it.newLine();
				appendEmptyComment(it);
				it.append("\tprivate static final String EMPTY_STR = \"\";"); //$NON-NLS-1$
				it.newLine();
				appendEmptyComment(it);
				it.append("\tprivate String mlLinePrefix;"); //$NON-NLS-1$
				it.newLine();
				appendEmptyComment(it);
				it.append("\tprivate String mlStart;"); //$NON-NLS-1$
				it.newLine();
				appendEmptyComment(it);
				it.append("\tprivate String mlEnd;"); //$NON-NLS-1$
				it.newLine();
				appendEmptyComment(it);
				it.append("\tprivate String slPrefix;"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\tprotected static boolean isNewLine(char character) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tif (character == '\\n' || character == '\\r' || character == '\\f') {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\treturn true;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t}"); //$NON-NLS-1$
				it.newLine();
				it.append("\treturn ((((1 << Character.LINE_SEPARATOR)"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\t| (1 << Character.PARAGRAPH_SEPARATOR)) >> Character.getType((int) character)) & 1) != 0;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tpublic String getMultilineCommentStartSymbols() {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\treturn this.mlStart;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\tpublic void setMultilineCommentStartSymbols(String symbols) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tthis.mlStart = symbols;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tpublic String getMultilineCommentEndSymbols() {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\treturn this.mlEnd;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\tpublic void setMultilineCommentEndSymbols(String symbols) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tthis.mlEnd = symbols;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tpublic String getMultilineCommentLinePrefix() {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\treturn this.mlLinePrefix;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\tpublic void setMultilineCommentLinePrefix(String prefix) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tthis.mlLinePrefix = prefix;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tpublic String getSinglelineCommentPrefix() {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\treturn this.slPrefix;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\t@"); //$NON-NLS-1$
				it.append(Pure.class);
				it.newLine();
				it.append("\tprotected "); //$NON-NLS-1$
				it.append(Set.class);
				it.append("<Character> getSinglelineCommentSpecialChars() {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tfinal "); //$NON-NLS-1$
				it.append(Set.class);
				it.append("<Character> set = new "); //$NON-NLS-1$
				it.append(TreeSet.class);
				it.append("<>();"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tset.add('*');"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tset.add('+');"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tset.add('-');"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tset.add('=');"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\treturn set;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\tpublic void setSinglelineCommentPrefix(String prefix) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tthis.slPrefix = prefix;"); //$NON-NLS-1$
				it.newLine();
				it.append("\t}"); //$NON-NLS-1$
				it.newLineIfNotEmpty();
				it.newLine();
				appendEmptyComment(it);
				it.append("\t@"); //$NON-NLS-1$
				it.append(getInjectType());
				it.newLine();
				it.append("\tpublic void setGrammarAccess("); //$NON-NLS-1$
				it.append(DocumentationBuilderFragment.this.grammarAccessExtensions.getGrammarAccess(getGrammar()));
				it.append(" access) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tif (this.mlStart == null || this.mlEnd == null) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\t"); //$NON-NLS-1$
				it.append(AbstractRule.class);
				it.append(" mlRule = access.getML_COMMENTRule();"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\tfor ("); //$NON-NLS-1$
				it.append(AbstractElement.class);
				it.append(" element : (("); //$NON-NLS-1$
				it.append(Group.class);
				it.append(") mlRule.getAlternatives()).getElements()) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\t\tif (element instanceof "); //$NON-NLS-1$
				it.append(Keyword.class);
				it.append(" $c$value && this.mlStart == null) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\t\t\tthis.mlStart = $c$value.getValue();"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\t\t} else if (element instanceof "); //$NON-NLS-1$
				it.append(UntilToken.class);
				it.append(" $c$value && this.mlEnd == null) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\t\t\tthis.mlEnd = (("); //$NON-NLS-1$
				it.append(Keyword.class);
				it.append(") $c$value.getTerminal()).getValue();"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\t\t}"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\t}"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t}"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\tif (this.mlLinePrefix == null) {"); //$NON-NLS-1$
				it.newLine();
				it.append("\t\t\tthis.mlLinePrefix = this.mlStart.substring(this.mlStart.length() - 1);"); //$NON-NLS-1$
