1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package stream.XMLStreamWriterTest;
  27 
  28 import java.io.ByteArrayInputStream;
  29 import java.io.ByteArrayOutputStream;
  30 import java.io.InputStream;
  31 import java.io.OutputStreamWriter;
  32 
  33 import javax.xml.stream.XMLInputFactory;
  34 import javax.xml.stream.XMLOutputFactory;
  35 import javax.xml.stream.XMLStreamConstants;
  36 import javax.xml.stream.XMLStreamException;
  37 import javax.xml.stream.XMLStreamReader;
  38 import javax.xml.stream.XMLStreamWriter;
  39 
  40 import org.testng.Assert;
  41 import org.testng.annotations.Test;
  42 import org.testng.annotations.DataProvider;
  43 
  44 /*
  45  * @test
  46  * @bug 8145974
  47  * @modules javax.xml
  48  * @summary Check that XMLStreamWriter generates valid xml with surrogate pair
  49  *  used within element text
  50  */
  51 
  52 public class SurrogatesTest {
  53 
  54     // Test that valid surrogate characters can be written/readen by xml stream
  55     // reader/writer
  56     @Test(dataProvider = "validData")
  57     public void xmlWithValidSurrogatesTest(String content)
  58             throws Exception {
  59         generateAndReadXml(content);
  60     }
  61 
  62     // Test that unbalanced surrogate character will
  63     @Test(dataProvider = "invalidData",
  64             expectedExceptions = XMLStreamException.class)
  65     public void xmlWithUnbalancedSurrogatesTest(String content)
  66             throws Exception {
  67         generateAndReadXml(content);
  68     }
  69 
  70     // Generates xml content with XMLStreamWriter and read it to check
  71     // for correctness of xml and generated data
  72     void generateAndReadXml(String content) throws Exception {
  73         ByteArrayOutputStream stream = new ByteArrayOutputStream();
  74         XMLOutputFactory factory = XMLOutputFactory.newInstance();
  75         OutputStreamWriter streamWriter = new OutputStreamWriter(stream);
  76         XMLStreamWriter writer = factory.createXMLStreamWriter(streamWriter);
  77 
  78         // Generate xml with selected stream writer type
  79         generateXML(writer, content);
  80         String output = stream.toString();
  81         System.out.println("Generated xml: " + output);
  82         // Read generated xml with StAX parser
  83         readXML(output.getBytes(), content);
  84     }
  85 
  86     // Generates XML with provided xml stream writer. Provided string
  87     // is inserted into xml twice: with usage of writeCharacters( String )
  88     // and writeCharacters( char [], int , int )
  89     private void generateXML(XMLStreamWriter writer, String sequence)
  90             throws XMLStreamException {
  91         char[] seqArr = sequence.toCharArray();
  92         writer.writeStartDocument();
  93         writer.writeStartElement("root");
  94 
  95         // Use writeCharacters( String ) to write characters
  96         writer.writeStartElement("writeCharactersWithString");
  97         writer.writeCharacters(sequence);
  98         writer.writeEndElement();
  99 
 100         // Use writeCharacters( char [], int , int ) to write characters
 101         writer.writeStartElement("writeCharactersWithArray");
 102         writer.writeCharacters(seqArr, 0, seqArr.length);
 103         writer.writeEndElement();
 104 
 105         // Close root element and document
 106         writer.writeEndElement();
 107         writer.writeEndDocument();
 108         writer.flush();
 109         writer.close();
 110     }
 111 
 112     // Reads generated XML data and check if it contains expected
 113     // text in writeCharactersWithString and writeCharactersWithArray
 114     // elements
 115     private void readXML(byte[] xmlData, String expectedContent)
 116             throws Exception {
 117         InputStream stream = new ByteArrayInputStream(xmlData);
 118         XMLInputFactory factory = XMLInputFactory.newInstance();
 119         XMLStreamReader xmlReader
 120                 = factory.createXMLStreamReader(stream);
 121         boolean inTestElement = false;
 122         StringBuilder sb = new StringBuilder();
 123         while (xmlReader.hasNext()) {
 124             String ename;
 125             switch (xmlReader.getEventType()) {
 126                 case XMLStreamConstants.START_ELEMENT:
 127                     ename = xmlReader.getLocalName();
 128                     if (ename.equals("writeCharactersWithString")
 129                             || ename.equals("writeCharactersWithArray")) {
 130                         inTestElement = true;
 131                     }
 132                     break;
 133                 case XMLStreamConstants.END_ELEMENT:
 134                     ename = xmlReader.getLocalName();
 135                     if (ename.equals("writeCharactersWithString")
 136                             || ename.equals("writeCharactersWithArray")) {
 137                         inTestElement = false;
 138                         String content = sb.toString();
 139                         System.out.println(ename + " text:'" + content + "' expected:'" + expectedContent+"'");
 140                         Assert.assertEquals(content, expectedContent);
 141                         sb.setLength(0);
 142                     }
 143                     break;
 144                 case XMLStreamConstants.CHARACTERS:
 145                     if (inTestElement) {
 146                         sb.append(xmlReader.getText());
 147                     }
 148                     break;
 149             }
 150             xmlReader.next();
 151         }
 152     }
 153 
 154     @DataProvider(name = "validData")
 155     Object[][] getValidData() {
 156         return new Object[][] {
 157             {"Don't Worry Be \uD83D\uDE0A"},
 158             {"BMP characters \uE000\uFFFD"},
 159             {"Simple text"},
 160         };
 161     }
 162 
 163     @DataProvider(name = "invalidData")
 164     Object[][] getInvalidData() {
 165         return new Object[][] {
 166             {"Unbalanced surrogate \uD83D"},
 167             {"Unbalanced surrogate \uD83Dis here"},
 168             {"Surrogate with followup BMP\uD83D\uFFF9"},
 169         };
 170     }
 171 }