/*
 * Sonargraph Integration Access
 * Copyright (C) 2016-2018 hello2morrow GmbH
 * mailto: support AT hello2morrow DOT com
 *
 * 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 com.hello2morrow.sonargraph.integration.access.persistence;

import java.util.regex.Pattern;

import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

final class XmlCDataStreamWriter implements XMLStreamWriter
{
    private static final String CDATA_END = "]]>";
    private static final String CDATA_START = "<![CDATA[";
    /**
     * The CDATA section end string "]]>" must not be part of text in a CDATA section. Split it into "]]" and ">", and put CDATA_END and CDATA_START
     * in between.
     */
    private static final String CDATA_END_ENCODED = "]]" + CDATA_END + CDATA_START + ">";
    private static final Pattern XML_CHARS = Pattern.compile("[&<>]");
    private final XMLStreamWriter xmlStreamWriter;

    XmlCDataStreamWriter(final XMLStreamWriter writer)
    {
        xmlStreamWriter = writer;
    }

    @Override
    public void writeCharacters(final String text) throws XMLStreamException
    {
        //If there are unescaped characters, the use of a CDATA section is needed.
        final boolean useCData = XML_CHARS.matcher(text).find();
        if (useCData)
        {
            //We need to replace the CDATA end ']]>' if it exists in the text.
            xmlStreamWriter.writeCData(text.replaceAll(CDATA_END, CDATA_END_ENCODED));
        }
        else
        {
            xmlStreamWriter.writeCharacters(text);
        }
    }

    @Override
    public void writeStartElement(final String localName) throws XMLStreamException
    {
        xmlStreamWriter.writeStartElement(localName);
    }

    @Override
    public void writeStartElement(final String namespaceURI, final String localName) throws XMLStreamException
    {
        xmlStreamWriter.writeStartElement(namespaceURI, localName);
    }

    @Override
    public void writeStartElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException
    {
        xmlStreamWriter.writeStartElement(prefix, localName, namespaceURI);
    }

    @Override
    public void writeEmptyElement(final String namespaceURI, final String localName) throws XMLStreamException
    {
        xmlStreamWriter.writeEmptyElement(namespaceURI, localName);
    }

    @Override
    public void writeEmptyElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException
    {
        xmlStreamWriter.writeEmptyElement(prefix, localName, namespaceURI);
    }

    @Override
    public void writeEmptyElement(final String localName) throws XMLStreamException
    {
        xmlStreamWriter.writeEmptyElement(localName);
    }

    @Override
    public void writeEndElement() throws XMLStreamException
    {
        xmlStreamWriter.writeEndElement();
    }

    @Override
    public void writeEndDocument() throws XMLStreamException
    {
        xmlStreamWriter.writeEndDocument();
    }

    @Override
    public void close() throws XMLStreamException
    {
        xmlStreamWriter.close();
    }

    @Override
    public void flush() throws XMLStreamException
    {
        xmlStreamWriter.flush();
    }

    @Override
    public void writeAttribute(final String localName, final String value) throws XMLStreamException
    {
        xmlStreamWriter.writeAttribute(localName, value);
    }

    @Override
    public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String value) throws XMLStreamException
    {
        xmlStreamWriter.writeAttribute(prefix, namespaceURI, localName, value);
    }

    @Override
    public void writeAttribute(final String namespaceURI, final String localName, final String value) throws XMLStreamException
    {
        xmlStreamWriter.writeAttribute(namespaceURI, localName, value);
    }

    @Override
    public void writeNamespace(final String prefix, final String namespaceURI) throws XMLStreamException
    {
        xmlStreamWriter.writeNamespace(prefix, namespaceURI);
    }

    @Override
    public void writeDefaultNamespace(final String namespaceURI) throws XMLStreamException
    {
        xmlStreamWriter.writeDefaultNamespace(namespaceURI);
    }

    @Override
    public void writeComment(final String data) throws XMLStreamException
    {
        xmlStreamWriter.writeComment(data);
    }

    @Override
    public void writeProcessingInstruction(final String target) throws XMLStreamException
    {
        xmlStreamWriter.writeProcessingInstruction(target);
    }

    @Override
    public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException
    {
        xmlStreamWriter.writeProcessingInstruction(target, data);
    }

    @Override
    public void writeCData(final String data) throws XMLStreamException
    {
        xmlStreamWriter.writeCData(data);
    }

    @Override
    public void writeDTD(final String dtd) throws XMLStreamException
    {
        xmlStreamWriter.writeDTD(dtd);
    }

    @Override
    public void writeEntityRef(final String name) throws XMLStreamException
    {
        xmlStreamWriter.writeEntityRef(name);
    }

    @Override
    public void writeStartDocument() throws XMLStreamException
    {
        xmlStreamWriter.writeStartDocument();
    }

    @Override
    public void writeStartDocument(final String version) throws XMLStreamException
    {
        xmlStreamWriter.writeStartDocument(version);
    }

    @Override
    public void writeStartDocument(final String encoding, final String version) throws XMLStreamException
    {
        xmlStreamWriter.writeStartDocument(encoding, version);
    }

    @Override
    public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException
    {
        xmlStreamWriter.writeCharacters(text, start, len);
    }

    @Override
    public String getPrefix(final String uri) throws XMLStreamException
    {
        return xmlStreamWriter.getPrefix(uri);
    }

    @Override
    public void setPrefix(final String prefix, final String uri) throws XMLStreamException
    {
        xmlStreamWriter.setPrefix(prefix, uri);
    }

    @Override
    public void setDefaultNamespace(final String uri) throws XMLStreamException
    {
        xmlStreamWriter.setDefaultNamespace(uri);
    }

    @Override
    public void setNamespaceContext(final NamespaceContext context) throws XMLStreamException
    {
        xmlStreamWriter.setNamespaceContext(context);
    }

    @Override
    public NamespaceContext getNamespaceContext()
    {
        return xmlStreamWriter.getNamespaceContext();
    }

    @Override
    public Object getProperty(final String name)
    {
        return xmlStreamWriter.getProperty(name);
    }
}
@@ -1350,24 +1350,105 @@
 TART + %22%3E%22;%0A
+    private static final Pattern CDATA_END_PATTERN = Pattern.compile(CDATA_END);%0A
     private 
@@ -1477,16 +1477,24 @@
 ML_CHARS
+_PATTERN
  = Patte
@@ -1514,16 +1514,17 @@
 &%3C%3E%5D%22);%0A
+%0A
     priv
@@ -1891,16 +1891,24 @@
 ML_CHARS
+_PATTERN
 .matcher
@@ -1946,32 +1946,134 @@
 Data)%0A        %7B%0A
+            final String escapedText = CDATA_END_PATTERN.matcher(text).replaceAll(CDATA_END_ENCODED);%0A
             //We
@@ -2178,53 +2178,19 @@
 ata(
-text.replaceAll(CDATA_END, CDATA_END_ENCODED)
+escapedText
 );%0A 
@@ -6610,56 +6610,40 @@
     
-xmlStreamWriter.writeCharacters(text, start, len
+writeCharacters(new String(text)
 );%0A 

Enforce generation of CDATA section. Use a static pattern to improve performance.