/* * $Id$ * * SARL is an general-purpose agent programming language. * More details on http://www.sarl.io * * Copyright (C) 2014-2017 the original authors or 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.externalspec; import java.nio.channels.UnsupportedAddressTypeException; import java.text.MessageFormat; import com.google.common.escape.Escaper; import com.google.common.xml.XmlEscapers; import org.eclipse.xtext.common.types.JvmType; import org.eclipse.xtext.util.Strings; import org.eclipse.xtext.xbase.compiler.ISourceAppender; import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference; import org.eclipse.xtext.xtext.generator.CodeConfig; /** * An abstract generator of an XML-based external language specification. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public abstract class AbstractXmlHighlightingFragment2 extends AbstractExternalHighlightingFragment2<IXmlStyleAppendable> { @Override protected IXmlStyleAppendable newStyleAppendable() { return new XmlAppendable(getCodeConfig(), getLanguageSimpleName(), getLanguageVersion()); } /** Appendable for xml-based styles. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 0.6 */ protected static class XmlAppendable extends AbstractAppendable implements IXmlStyleAppendable { /** Constructor. * * @param codeConfig the code configuration. * @param languageName the language name. * @param languageVersion the language version. */ protected XmlAppendable(CodeConfig codeConfig, String languageName, String languageVersion) { super(" ", codeConfig, languageName, languageVersion); //$NON-NLS-1$ } @Override public void appendComment(String text, Object... parameters) { appendCommentNoNl(text, parameters); newLine(); } /** Append a comment without newline at the end. * * @param text the comment text. * @param parameters the parameters. */ void appendCommentNoNl(String text, Object... parameters) { final String comment = applyFormat(text, parameters); appendNl("<!-- "); //$NON-NLS-1$ for (final String line : comment.split("[\n\r]")) { //$NON-NLS-1$ appendNl("\t " + line.trim()); //$NON-NLS-1$ } append("-->"); //$NON-NLS-1$ } @Override public void appendHeader() { appendNl(MessageFormat.format("<?xml version=\"1.0\" encoding=\"{0}\"?>", //$NON-NLS-1$ getCodeConfig().getEncoding())); final String[] header = Strings.emptyIfNull(getCodeConfig().getFileHeader()).split("[\n\r]+"); //$NON-NLS-1$ appendNl("<!--"); //$NON-NLS-1$ for (final String headerLine : header) { appendNl(headerLine.replaceFirst("^\\s*[/]?[*][/]?", "\t ") //$NON-NLS-1$//$NON-NLS-2$ .replaceFirst("\\s+$", "")); //$NON-NLS-1$//$NON-NLS-2$ } appendNl("-->"); //$NON-NLS-1$ appendNl("<!-- Style for {0} {1} -->", getLanguageSimpleName(), getLanguageVersion()); //$NON-NLS-1$ newLine(); } /** Open a tag. * * @param tag the tag name. * @param nameValuePairs the properties. */ void internalOpenTag(String tag, String... nameValuePairs) { append("<").append(tag); //$NON-NLS-1$ for (int i = 0; i < nameValuePairs.length; i = i + 2) { append(" "); //$NON-NLS-1$ append(nameValuePairs[i]); append("=\""); //$NON-NLS-1$ append(XmlEscapers.xmlAttributeEscaper().escape(nameValuePairs[i + 1])); append("\""); //$NON-NLS-1$ } } @Override public IXmlStyleCloseable open(String tag, String... nameValuePairs) { internalOpenTag(tag, nameValuePairs); append(">"); //$NON-NLS-1$ increaseIndentation(); return new XmlCloseable(tag, this); } @Override public void appendTag(String tag, String... nameValuePairs) { appendTagNoNl(tag, nameValuePairs); newLine(); } /** Append a tag without newline at the end. * * @param tag the tag name. * @param nameValuePairs the properties of the tag. */ void appendTagNoNl(String tag, String... nameValuePairs) { internalOpenTag(tag, nameValuePairs); append(" />"); //$NON-NLS-1$ } @Override public void appendTagWithValue(String tag, String value, String... nameValuePairs) { appendTagWithValueNoNl(tag, value, nameValuePairs); newLine(); } /** Append a valued tag without newline at the end. * * @param tag the tag name. * @param value the value. * @param nameValuePairs the properties of the tag. */ void appendTagWithValueNoNl(String tag, String value, String... nameValuePairs) { internalOpenTag(tag, nameValuePairs); if (Strings.isEmpty(value)) { append(" />"); //$NON-NLS-1$ } else { append(">"); //$NON-NLS-1$ final boolean block = value.contains("\n") || value.contains("\r"); //$NON-NLS-1$//$NON-NLS-2$ final Escaper escaper = XmlEscapers.xmlContentEscaper(); if (block) { increaseIndentation(); boolean first = true; for (final String line : value.split("[\n\r]+")) { //$NON-NLS-1$ if (first) { first = false; } else { newLine(); } final String escapedValue = escaper.escape(line); append(escapedValue); } decreaseIndentation(); newLine(); } else { final String escapedValue = escaper.escape(value); append(escapedValue.trim()); } append("</").append(tag).append(">"); //$NON-NLS-1$ //$NON-NLS-2$ } } } /** Appendable for xml-based styles. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 0.6 */ protected static class XmlCloseable implements IXmlStyleCloseable { private final String tag; private final XmlAppendable parent; private int nbLines = 1; /** Constructor. * * @param tag the tag name. * @param parent the parent. */ protected XmlCloseable(String tag, XmlAppendable parent) { this.tag = tag; this.parent = parent; } @Override public void appendHeader() { throw new UnsupportedAddressTypeException(); } @Override public void appendComment(String text, Object... parameters) { appendNewLines(); this.parent.appendCommentNoNl(text, parameters); newLine(); } @Override public ISourceAppender append(CharSequence string) { appendNewLines(); this.parent.append(string); return this; } @Override public ISourceAppender append(JvmType type) { appendNewLines(); this.parent.append(type); return this; } @Override public ISourceAppender append(LightweightTypeReference typeRef) { appendNewLines(); this.parent.append(typeRef); return this; } @Override public ISourceAppender increaseIndentation() { appendNewLines(); this.parent.increaseIndentation(); return this; } @Override public ISourceAppender decreaseIndentation() { appendNewLines(); this.parent.decreaseIndentation(); return this; } @Override public boolean isJava() { return this.parent.isJava(); } @Override public void appendTag(String tag, String... nameValuePairs) { appendNewLines(); this.parent.appendTagNoNl(tag, nameValuePairs); newLine(); } @Override public void appendTagWithValue(String tag, String value, String... nameValuePairs) { appendNewLines(); this.parent.appendTagWithValueNoNl(tag, value, nameValuePairs); newLine(); } @Override public IXmlStyleCloseable open(String tag, String... nameValuePairs) { appendNewLines(); return this.parent.open(tag, nameValuePairs); } @Override public ISourceAppender newLine() { ++this.nbLines; return this; } @Override public void close() { this.parent.decreaseIndentation(); this.nbLines = Math.max(1, this.nbLines); appendNewLines(); append("</").append(this.tag).append(">"); //$NON-NLS-1$ //$NON-NLS-2$ this.parent.newLine(); } private void appendNewLines() { final int nb = this.nbLines; this.nbLines = 0; for (int i = 0; i < nb; ++i) { this.parent.newLine(); } } } }