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
26package com.sun.xml.internal.stream.writers;
27
28import java.io.FileWriter;
29import java.io.IOException;
30import java.io.OutputStreamWriter;
31import java.io.Writer;
32import java.nio.charset.Charset;
33import java.nio.charset.CharsetEncoder;
34import com.sun.org.apache.xerces.internal.util.XMLChar;
35import 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
43public 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}
255