/**
 * Copyright (c) 2014 Eclipse contributorsand others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * Contributors:
 *   stefan schalomon, msg-systems.com - Initial API and implementation
 */
package org.eclipse.emf.test.xml;

import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;

import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.xmi.impl.XMLString;
import org.junit.Test;

public class XMLStringTest
{
  /**
   * A test that causes {@link ArrayIndexOutOfBoundsException}.
   */
  @Test
  public void testXMLString() throws Exception
  {
    XMLString doc = new XMLString();
    // write XML with default encoding - this will resize
    // doc.outputchars to a length greater default BUFFER_SIZE,
    // doc.outputbytes is not used during this call
    writeDoc( doc, false, 5, 10, 1000 );

    // now write a bigger XML with ASCII encoding - doc.outputbytes
    // will be initialized with default BUFFER_SIZE, that is,
    // doc.outputbytes.length < doc.outputchars.length which will
    // eventually trigger the ArrayIndexOutOfBoundsException
    writeDoc( doc, true, 20, 20, 1000 );
  }

  /**
   * A test with overridden {@link XMLString} that fixes the AIOOBE bug.
   */
  @Test
  public void testFixedXMLString() throws Exception
  {
    XMLString doc = new XMLString()
    {
      private static final long serialVersionUID = 1L;

      @Override
      public void writeAscii( OutputStream os, int flushThreshold ) throws IOException
      {
        int requiredLength = Math.max( BUFFER_SIZE, outputchars.length );
        // ensure the byte and character buffers have the same length
        if( outputbytes == null || outputbytes.length < requiredLength )
        {
          outputbytes = new byte[requiredLength];
        }

        super.writeAscii( os, flushThreshold );
      }
    };

    writeDoc( doc, false, 5, 10, 1000 );
    // with the fix above, the following call will work
    writeDoc( doc, true, 20, 20, 1000 );
  }

  private void writeDoc( XMLString doc,
                         boolean ascii,
                         int elementCount,
                         int attributeCount,
                         int contentSize ) throws Exception
  {
    doc.reset( null, null, Integer.MAX_VALUE, null );
    doc.startElement( "doc" );
    for( int e = 1; e <= elementCount; e++ )
    {
      StringBuilder content = new StringBuilder();
      for( int c = 1; c <= contentSize; c++ )
      {
        content.append( Long.toHexString( 0xBAADF00DDE7EC7EDL ) );
      }

      doc.startElement( "elem" + e );
      for( int a = 1; a <= attributeCount; a++ )
      {
        doc.startAttribute( "attr" + a );
        doc.addAttributeContent( "AttributeValue:" + content.toString() );
        doc.endAttribute();
      }
      doc.endContentElement( "ElementContent:" + content.toString() );
    }
    doc.endElement();

    StringWriter writer = new StringWriter();
    if( ascii )
    {
      doc.writeAscii( new URIConverter.WriteableOutputStream( writer, "ASCII"), Integer.MAX_VALUE );
    }
    else
    {
      doc.write( (Writer) writer, Integer.MAX_VALUE );
    }
  }
}
