001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 2005-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.cnd.api.xml;
043:
044: import java.io.BufferedWriter;
045: import java.io.IOException;
046: import java.io.OutputStream;
047: import java.io.OutputStreamWriter;
048: import java.io.PrintWriter;
049: import java.io.UnsupportedEncodingException;
050: import java.io.Writer;
051:
052: import org.openide.xml.XMLUtil;
053:
054: /**
055: * Provides convenience methods to write out XML elements. To be used from
056: * within {@link XMLEncoder#encode}.
057: * <p>
058: * provides the following:
059: * <ul>
060: * <li> Appropriate indentation of elements.
061: Amount of indentation can be configured via
062: {@link XMLDocWriter#setIndentChars}
063: * <li> Formatting and indentation of attribute's.
064: * <li> Escaping of attribute values and content.
065: * </ul>
066: */
067:
068: public class XMLEncoderStream {
069:
070: private String indentElement = " "; // NOI18N
071: private int indent;
072: private PrintWriter writer;
073: private OutputStream os; // just so we can close it
074:
075: XMLEncoderStream(OutputStream os, int indentChars, String encoding) {
076: this .os = os;
077: try {
078: Writer w = new BufferedWriter(new OutputStreamWriter(os,
079: encoding));
080: writer = new PrintWriter(w);
081: } catch (UnsupportedEncodingException ex) {
082: ex.printStackTrace();
083: writer = new PrintWriter(os);
084: }
085: makeIndentElement(indentChars);
086: }
087:
088: private void makeIndentElement(int indentChars) {
089: indentElement = ""; // NOI18N
090: if (indentChars > 20)
091: indentChars = 20;
092: for (int i = 0; i < indentChars; i++) {
093: indentElement += " "; // NOI18N
094: }
095: }
096:
097: /**
098: * .
099: * Puts out XML of this form:
100: * <br>
101: <pre>
102: <tagName version="version" attr1="attr1" ...>
103: <tagName attr1="attr1" ...>
104: </pre>
105: * @param version If >= 0 will automatically cause the output of the
106: * version attribute. This goes hand-in-hand with
107: * {@link XMLDecoder#checkVersion}
108: */
109:
110: public void elementOpen(String tagName, int version,
111: AttrValuePair[] attrs) {
112: writeIndent();
113: writer.print("<" + tagName); // NOI18N
114: writeAttrs(tagName, version, attrs, true);
115: writer.println(">"); // NOI18N
116: indent++;
117: }
118:
119: /**
120: * .
121: * Puts out XML of this form:
122: * <br>
123: <pre>
124: <tagName attr1="attr1" ...>
125: </pre>
126: */
127:
128: public void elementOpen(String tagName, AttrValuePair[] attrs) {
129: elementOpen(tagName, -1, attrs);
130: }
131:
132: /**
133: * .
134: * Puts out XML of this form:
135: * <br>
136: <pre>
137: <tagName version="version">
138: </pre>
139: * @param version If >= 0 will automatically cause the output of the
140: * version attribute. This goes hand-in-hand with
141: * {@link XMLDecoder#checkVersion}
142: */
143:
144: public void elementOpen(String tagName, int version) {
145: elementOpen(tagName, version, null);
146: }
147:
148: /**
149: * .
150: * Puts out XML of this form:
151: * <br>
152: <pre>
153: <tagName>
154: </pre>
155: */
156:
157: public void elementOpen(String tagName) {
158: elementOpen(tagName, -1, null);
159: }
160:
161: /**
162: * .
163: * Puts out XML of this form:
164: * <br>
165: <pre>
166: </tagName>
167: </pre>
168: */
169:
170: public void elementClose(String tagName) {
171: indent--;
172: if (indent < 0)
173: indent = 0;
174: writeIndent();
175: writer.println("</" + tagName + ">"); // NOI18N
176: }
177:
178: /**
179: * .
180: * Puts out XML of this form:
181: * <br>
182: <pre>
183: <tagName attr1="attr1" .../>
184: </pre>
185: */
186:
187: public void element(String tagName, AttrValuePair[] attrs) {
188: element(tagName, -1, attrs, null);
189: }
190:
191: /**
192: * .
193: * Puts out XML of this form:
194: * <br>
195: <pre>
196: <tagName>content</tagName>
197: </pre>
198: */
199:
200: public void element(String tagName, String content) {
201: element(tagName, -1, null, content);
202: }
203:
204: /**
205: * Basic workhorse.
206: * <p.
207: * Puts out XML of this form:
208: * <br>
209: <pre>
210: <tagName version="version" attr1="attr1" .../>
211: <tagName version="version" attr1="attr1" ...>content</tagName>
212: </pre>
213: * @param content If null the first form above is used.
214: * @param version If >= 0 will automatically cause the output of the
215: * version attribute. This goes hand-in-hand with
216: * {@link XMLDecoder#checkVersion}
217: */
218:
219: public void element(String tagName, int version,
220: AttrValuePair[] attrs, String content) {
221: writeIndent();
222: writer.print("<" + tagName); // NOI18N
223: writeAttrs(tagName, version, attrs, true);
224: if (content == null) {
225: writer.print("/>"); // NOI18N
226: } else {
227: writer.print(">"); // NOI18N
228: }
229:
230: if (content != null) {
231: writer.print(escapeContent(content));
232: writer.print("</" + tagName + ">"); // NOI18N
233: }
234: writer.println();
235: }
236:
237: void close() throws IOException {
238: writer.flush();
239: writer.close();
240: writer = null;
241: os.close(); // SHOULD double-check if it's really our responsibility
242: }
243:
244: void println(String s) {
245: writer.println(s);
246: }
247:
248: private void writeIndent(int additionalIndent) {
249: for (int i = 0; i < indent; i++)
250: writer.print(indentElement);
251: for (int i = 0; i < additionalIndent; i++)
252: writer.print(' ');
253: }
254:
255: private void writeIndent() {
256: writeIndent(0);
257: }
258:
259: /**
260: * Decide whether we output attributes in one line
261: * <tag a1="A1" a2="A2" a3="A3">
262: * </pre>
263: * or "format" them.
264: * <pre>
265: * <tag a1="A1"
266: * a2="A2"
267: * a3="A3">
268: * </pre>
269: *
270: * <p>
271: * This decision is quick-and-dirty and will recommend wrapping if the
272: * length of the unformatted form exceeds 80.
273: * <br>
274: * It doesn't take XML escaping into consideration.
275: */
276: private boolean needFormat(String tagName, int version,
277: AttrValuePair[] attrs, boolean format) {
278:
279: if (!format)
280: return false;
281:
282: int additionalIndent = 1 + tagName.length() + 1;
283: final int max = 80;
284: int width = additionalIndent;
285:
286: if (version >= 0) {
287: width += " version=\"\"".length(); // NOI18N
288: width += ("" + version).length(); // NOI18N
289: }
290:
291: if (width > max)
292: return true;
293:
294: if (attrs != null) {
295: for (int ax = 0; ax < attrs.length; ax++) {
296: AttrValuePair avp = attrs[ax];
297:
298: width += " ".length(); // NOI18N
299: width += avp.getAttr().length();
300: width += "=\"".length(); // NOI18N
301: width += avp.getValue().length();
302: width += "\"".length(); // NOI18N
303: if (width > max)
304: return true;
305: }
306: }
307: return false;
308: }
309:
310: private void writeAttrs(String tagName, int version,
311: AttrValuePair[] attrs, boolean format) {
312:
313: int awritten = 0;
314:
315: // call print on each individual chunk separately because it's
316: // more efficient than concatenating Strings like this:
317: // writer.print(" version=\"" + version + "\"");
318:
319: format = needFormat(tagName, version, attrs, format);
320:
321: if (version != -1) {
322: writer.print(" version=\""); // NOI18N
323: writer.print(version);
324: writer.print("\""); // NOI18N
325: awritten++;
326: }
327:
328: if (attrs != null) {
329: int additionalIndent = 1 + tagName.length() + 1;
330: for (int ax = 0; ax < attrs.length; ax++) {
331: AttrValuePair avp = attrs[ax];
332:
333: if (awritten >= 1 && format) {
334: writer.println();
335: writeIndent(additionalIndent);
336: } else {
337: writer.print(" "); // NOI18N
338: }
339: writer.print(avp.getAttr());
340: writer.print("=\""); // NOI18N
341: writer.print(escapeAttributeValue(avp.getValue()));
342: writer.print("\""); // NOI18N
343: awritten++;
344: }
345: }
346: }
347:
348: /**
349: * For example
350: * "File name is "%s"" becomes "File name is &dq;%s&dq;".
351: */
352:
353: public static String escapeAttributeValue(String value) {
354: if (value == null) {
355: return ""; // NOI18N
356: } else {
357: String escapedValue = value;
358: try {
359: escapedValue = XMLUtil.toAttributeValue(value);
360: } catch (java.io.CharConversionException e) {
361: ;
362: }
363: return escapedValue;
364: }
365: }
366:
367: /**
368: * For example "File name is <%s>" becomes
369: * "File name is &lt;%s&gt;".
370: */
371: public static String escapeContent(String content) {
372: if (content == null) {
373: return ""; // NOI18N
374: } else {
375: String escapedContent = content;
376: try {
377: escapedContent = XMLUtil.toElementContent(content);
378: } catch (java.io.CharConversionException e) {
379: ;
380: }
381: return escapedContent;
382: }
383: }
384: }
|