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