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.Listeners;
  42 import org.testng.annotations.Test;
  43 import org.testng.annotations.DataProvider;
  44 
  45 /*
  46  * @test
  47  * @bug 8145974
  48  * @modules javax.xml
  49  * @summary Check that XMLStreamWriter generates valid xml with surrogate pair
  50  *  used within element text
  51  */
  52 
  53 @Listeners({jaxp.library.BasePolicy.class})
  54 public class SurrogatesTest {
  55 
  56     // Test that valid surrogate characters can be written/readen by xml stream
  57     // reader/writer
  58     @Test(dataProvider = "validData")
  59     public void xmlWithValidSurrogatesTest(String content)
  60             throws Exception {
  61         generateAndReadXml(content);
  62     }
  63 
  64     // Test that unbalanced surrogate character will
  65     @Test(dataProvider = "invalidData",
  66             expectedExceptions = XMLStreamException.class)
  67     public void xmlWithUnbalancedSurrogatesTest(String content)
  68             throws Exception {
  69         generateAndReadXml(content);
  70     }
  71 
  72     // Generates xml content with XMLStreamWriter and read it to check
  73     // for correctness of xml and generated data
  74     void generateAndReadXml(String content) throws Exception {
  75         ByteArrayOutputStream stream = new ByteArrayOutputStream();
  76         XMLOutputFactory factory = XMLOutputFactory.newInstance();
  77         OutputStreamWriter streamWriter = new OutputStreamWriter(stream);
  78         XMLStreamWriter writer = factory.createXMLStreamWriter(streamWriter);
  79 
  80         // Generate xml with selected stream writer type
  81         generateXML(writer, content);
  82         String output = stream.toString();
  83         System.out.println("Generated xml: " + output);
  84         // Read generated xml with StAX parser
  85         readXML(output.getBytes(), content);
  86     }
  87 
  88     // Generates XML with provided xml stream writer. Provided string
  89     // is inserted into xml twice: with usage of writeCharacters( String )
  90     // and writeCharacters( char [], int , int )
  91     private void generateXML(XMLStreamWriter writer, String sequence)
  92             throws XMLStreamException {
  93         char[] seqArr = sequence.toCharArray();
  94         writer.writeStartDocument();
  95         writer.writeStartElement("root");
  96 
  97         // Use writeCharacters( String ) to write characters
  98         writer.writeStartElement("writeCharactersWithString");
  99         writer.writeCharacters(sequence);
 100         writer.writeEndElement();
 101 
 102         // Use writeCharacters( char [], int , int ) to write characters
 103         writer.writeStartElement("writeCharactersWithArray");
 104         writer.writeCharacters(seqArr, 0, seqArr.length);
 105         writer.writeEndElement();
 106 
 107         // Close root element and document
 108         writer.writeEndElement();
 109         writer.writeEndDocument();
 110         writer.flush();
 111         writer.close();
 112     }
 113 
 114     // Reads generated XML data and check if it contains expected
 115     // text in writeCharactersWithString and writeCharactersWithArray
 116     // elements
 117     private void readXML(byte[] xmlData, String expectedContent)
 118             throws Exception {
 119         InputStream stream = new ByteArrayInputStream(xmlData);
 120         XMLInputFactory factory = XMLInputFactory.newInstance();
 121         XMLStreamReader xmlReader
 122                 = factory.createXMLStreamReader(stream);
 123         boolean inTestElement = false;
 124         StringBuilder sb = new StringBuilder();
 125         while (xmlReader.hasNext()) {
 126             String ename;
 127             switch (xmlReader.getEventType()) {
 128                 case XMLStreamConstants.START_ELEMENT:
 129                     ename = xmlReader.getLocalName();
 130                     if (ename.equals("writeCharactersWithString")
 131                             || ename.equals("writeCharactersWithArray")) {
 132                         inTestElement = true;
 133                     }
 134                     break;
 135                 case XMLStreamConstants.END_ELEMENT:
 136                     ename = xmlReader.getLocalName();
 137                     if (ename.equals("writeCharactersWithString")
 138                             || ename.equals("writeCharactersWithArray")) {
 139                         inTestElement = false;
 140                         String content = sb.toString();
 141                         System.out.println(ename + " text:'" + content + "' expected:'" + expectedContent+"'");
 142                         Assert.assertEquals(content, expectedContent);
 143                         sb.setLength(0);
 144                     }
 145                     break;
 146                 case XMLStreamConstants.CHARACTERS:
 147                     if (inTestElement) {
 148                         sb.append(xmlReader.getText());
 149                     }
 150                     break;
 151             }
 152             xmlReader.next();
 153         }
 154     }
 155 
 156     @DataProvider(name = "validData")
 157     Object[][] getValidData() {
 158         return new Object[][] {
 159             {"Don't Worry Be \uD83D\uDE0A"},
 160             {"BMP characters \uE000\uFFFD"},
 161             {"Simple text"},
 162         };
 163     }
 164 
 165     @DataProvider(name = "invalidData")
 166     Object[][] getInvalidData() {
 167         return new Object[][] {
 168             {"Unbalanced surrogate \uD83D"},
 169             {"Unbalanced surrogate \uD83Dis here"},
 170             {"Surrogate with followup BMP\uD83D\uFFF9"},
 171         };
 172     }
 173 }