1 /*
   2  * Copyright (c) 2005, 2013, 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 com.sun.xml.internal.stream.writers;
  27 
  28 import java.io.FileWriter;
  29 import java.io.IOException;
  30 import java.io.OutputStreamWriter;
  31 import java.io.Writer;
  32 import java.nio.charset.Charset;
  33 import java.nio.charset.CharsetEncoder;
  34 import com.sun.org.apache.xerces.internal.util.XMLChar;
  35 import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
  36 
  37 /**
  38  * Implements common xml writer functions.
  39  *
  40  * @author Neeraj Bajaj,K.Venugopal Sun Microsystems.
  41  */
  42 
  43 public class WriterUtility {
  44 
  45 
  46     public static final String START_COMMENT = "<!--";
  47     public static final String END_COMMENT = "-->";
  48     public static final String DEFAULT_ENCODING = " encoding=\"utf-8\"";
  49     public static final String DEFAULT_XMLDECL ="<?xml version=\"1.0\" ?>";
  50     public static final String DEFAULT_XML_VERSION ="1.0";
  51     public static final char CLOSE_START_TAG = '>';
  52     public static final char OPEN_START_TAG = '<';
  53     public static final String OPEN_END_TAG ="</";
  54     public static final char CLOSE_END_TAG = '>';
  55     public static final String START_CDATA = "<![CDATA[";
  56     public static final String END_CDATA = "]]>";
  57     public static final String CLOSE_EMPTY_ELEMENT = "/>";
  58     public static final String SPACE = " ";
  59     public static final String UTF_8 = "utf-8";
  60 
  61     static final boolean DEBUG_XML_CONTENT = false;
  62 
  63     /**XXX: This feature is only used when writing element content values.
  64      * default value is 'true' however, if the feature is set to false
  65      * characters wont be escaped.
  66      * This feature has no effect when writing Attribute values, character would still be escaped.
  67      * I can't think of any reason why this would be useful when writing attribute values.
  68      * However, this can be reconsidered if there is any usecase.
  69      */
  70     boolean fEscapeCharacters = true ;
  71 
  72     /** Writer object*/
  73     Writer fWriter = null;
  74 
  75     //CharsetEncoder
  76     CharsetEncoder fEncoder ;
  77 
  78     public WriterUtility(){
  79         fEncoder = getDefaultEncoder();
  80     }
  81 
  82 
  83     /** Creates a new instance of WriterUtility */
  84     public WriterUtility(Writer writer) {
  85         fWriter = writer;
  86         if(writer instanceof OutputStreamWriter){
  87             String charset = ((OutputStreamWriter)writer).getEncoding();
  88             if(charset != null){
  89                 fEncoder = Charset.forName(charset).newEncoder();
  90             }
  91         }else if(writer instanceof FileWriter){
  92             String charset = ((FileWriter)writer).getEncoding();
  93             if(charset != null){
  94                 fEncoder = Charset.forName(charset).newEncoder();
  95             }
  96         }
  97         else{
  98             //attempt to retreive default fEncoderoder
  99             fEncoder = getDefaultEncoder();
 100         }
 101     }
 102 
 103     /**
 104      * sets the writer object
 105      * @param writer file to write into
 106      */
 107     public void  setWriter(Writer writer){
 108         fWriter = writer;
 109     }
 110 
 111     public void setEscapeCharacters(boolean escape){
 112         fEscapeCharacters = escape ;
 113     }
 114 
 115     public boolean getEscapeCharacters(){
 116         return fEscapeCharacters;
 117     }
 118 
 119     /**
 120      * writes xml content (characters and element content
 121      * @param content
 122      */
 123     public void writeXMLContent(char[] content, int start, int length) throws IOException{
 124         writeXMLContent(content, start, length, getEscapeCharacters());
 125     }
 126 
 127     /**
 128      * writes xml content (characters and element content
 129      * @param content
 130      */
 131     private void writeXMLContent(char[] content, int start, int length, boolean escapeCharacter) throws IOException{
 132         if(DEBUG_XML_CONTENT){
 133             System.out.println("content to write is " + new String(content, start, length));
 134         }
 135         int index;
 136         char ch;
 137         int sc;
 138         final int end = start + length ;
 139         //define startWritePos to track the position from where the character array data needs to be written
 140         //initialize this variable to start pos. indicating that no data has been written
 141         int startWritePos = start;
 142 
 143         for ( index = start ; index < end ; index++ ) {
 144             ch = content[ index ];
 145 
 146             if(fEncoder != null && !fEncoder.canEncode(ch)){
 147                 //- write the data to the point we get this character
 148                 fWriter.write(content, startWritePos, index - startWritePos );
 149 
 150                 //escape this character
 151                 fWriter.write( "&#x" );
 152                 fWriter.write(Integer.toHexString(ch));
 153                 fWriter.write( ';' );
 154                 //increase the startWritePos by 1 indicating that next write should start from
 155                 //one position ahead
 156                 startWritePos = index + 1;
 157 
 158             }
 159             if(DEBUG_XML_CONTENT){
 160                 System.out.println("startWritePos = " + startWritePos);
 161                 System.out.println("index = " + index);
 162                 System.out.println("start = " + start);
 163                 System.out.println("end = " + end);
 164             }
 165 
 166             switch(ch){
 167                 case '<' :{
 168                     if(escapeCharacter){
 169                         //this character needs to be escaped, write the data from the last write pos
 170                         fWriter.write(content, startWritePos, index - startWritePos);
 171                         fWriter.write("&lt;");
 172                         if(DEBUG_XML_CONTENT){
 173                             System.out.print(new String(content, startWritePos, index - startWritePos));
 174                             System.out.println("&lt;");
 175                         }
 176                         //increase the startWritePos by 1 indicating that next write should start from
 177                         //one position ahead
 178                         startWritePos = index + 1;
 179                     }
 180                     break;
 181                 }
 182                 case '&' :{
 183                     if(escapeCharacter){
 184                         //this character needs to be escaped, write the data from the last write pos
 185                         fWriter.write(content, startWritePos, index - startWritePos);
 186                         fWriter.write("&amp;");
 187                         if(DEBUG_XML_CONTENT){
 188                             System.out.print(new String(content,startWritePos, index - startWritePos));
 189                             System.out.println("&amp;");
 190                         }
 191                         //increase the startWritePos by 1 indicating that next write should start from
 192                         //one position ahead
 193                         startWritePos = index + 1;
 194                     }
 195                     break;
 196                 }
 197 
 198                 case '>': {
 199                     if(escapeCharacter){
 200                         //this character needs to be escaped, write the data from the last write pos
 201                         fWriter.write(content, startWritePos, index - startWritePos);
 202                         fWriter.write("&gt;");
 203                         if(DEBUG_XML_CONTENT){
 204                             System.out.print(new String(content,startWritePos, index - startWritePos));
 205                             System.out.println("&gt;");
 206                         }
 207                         //increase the startWritePos by 1 indicating that next write should start from
 208                         //one position ahead
 209                         startWritePos = index + 1;
 210                     }
 211                     break;
 212                 }
 213             }
 214         }
 215         if(DEBUG_XML_CONTENT){
 216             System.out.println("out of the loop, writing " + new String(content, startWritePos, end - startWritePos));
 217         }
 218         //write any pending data
 219         fWriter.write(content, startWritePos, end - startWritePos);
 220     }
 221 
 222     /**
 223      * writes xml content (characters and element content
 224      * @param content
 225      */
 226     public void writeXMLContent(String content) throws IOException{
 227         if(content == null || content.length() == 0) return ;
 228         writeXMLContent(content.toCharArray(), 0, content.length());
 229     }
 230 
 231 
 232     /**
 233      * Write Attribute value to the underlying stream.
 234      *
 235      * @param value
 236      */
 237 
 238     public void  writeXMLAttributeValue(String value)throws IOException{
 239         writeXMLContent(value.toCharArray(), 0, value.length(), true);
 240     }
 241 
 242     private CharsetEncoder getDefaultEncoder(){
 243         try{
 244             String encoding = SecuritySupport.getSystemProperty("file.encoding");
 245             if(encoding != null){
 246                 return Charset.forName(encoding).newEncoder();
 247             }
 248         }
 249         catch(Exception ex){
 250             //for any exception thrown , catch and continue
 251         }
 252         return null;
 253     }
 254 }