001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/util/tags/sakai_2-4-1/util-util/util/src/java/org/sakaiproject/util/Xml.java $
003: * $Id: Xml.java 29108 2007-04-18 22:07:43Z ajpoland@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.util;
021:
022: import java.io.FileInputStream;
023: import java.io.FileOutputStream;
024: import java.io.InputStream;
025: import java.io.InputStreamReader;
026: import java.io.OutputStream;
027: import java.io.StringReader;
028: import java.io.StringWriter;
029: import java.util.Enumeration;
030: import java.util.Properties;
031: import java.util.Stack;
032:
033: import javax.xml.parsers.DocumentBuilder;
034: import javax.xml.parsers.DocumentBuilderFactory;
035: import javax.xml.parsers.ParserConfigurationException;
036:
037: import org.apache.commons.logging.Log;
038: import org.apache.commons.logging.LogFactory;
039: import org.sakaiproject.util.commonscodec.CommonsCodecBase64;
040: import org.w3c.dom.DOMImplementation;
041: import org.w3c.dom.Document;
042: import org.w3c.dom.Element;
043: import org.w3c.dom.Node;
044: import org.w3c.dom.NodeList;
045: import org.w3c.dom.ls.DOMImplementationLS;
046: import org.w3c.dom.ls.LSOutput;
047: import org.w3c.dom.ls.LSSerializer;
048: import org.xml.sax.InputSource;
049:
050: /**
051: * <p>
052: * Xml is a DOM XML helper object with static functions to help with XML.
053: * </p>
054: */
055: public class Xml {
056: /** Our log (commons). */
057: private static Log M_log = LogFactory.getLog(Xml.class);
058:
059: /**
060: * Create a new DOM Document.
061: *
062: * @return A new DOM document.
063: */
064: public static Document createDocument() {
065: try {
066: DocumentBuilder builder = getDocumentBuilder();
067: Document doc = builder.newDocument();
068:
069: return doc;
070: } catch (Exception any) {
071: M_log.warn("createDocument: " + any.toString());
072: return null;
073: }
074: }
075:
076: /**
077: * Read a DOM Document from xml in a file.
078: *
079: * @param name
080: * The file name for the xml file.
081: * @return A new DOM Document with the xml contents.
082: */
083: public static Document readDocument(String name) {
084: Document doc = null;
085: // first try using whatever character encoding the XML itself specifies
086: try {
087: DocumentBuilder docBuilder = getDocumentBuilder();
088: InputStream fis = new FileInputStream(name);
089: doc = docBuilder.parse(fis);
090: } catch (Exception e) {
091: doc = null;
092: }
093:
094: if (doc != null)
095: return doc;
096:
097: // OK, that didn't work - the document is probably ISO-8859-1
098: try {
099: DocumentBuilder docBuilder = getDocumentBuilder();
100: InputStreamReader in = new InputStreamReader(
101: new FileInputStream(name), "ISO-8859-1");
102: InputSource inputSource = new InputSource(in);
103: doc = docBuilder.parse(inputSource);
104: } catch (Exception any) {
105: doc = null;
106: }
107:
108: if (doc != null)
109: return doc;
110:
111: // try forcing UTF-8
112: try {
113: DocumentBuilder docBuilder = getDocumentBuilder();
114: InputStreamReader in = new InputStreamReader(
115: new FileInputStream(name), "UTF-8");
116: InputSource inputSource = new InputSource(in);
117: doc = docBuilder.parse(inputSource);
118: } catch (Exception any) {
119: M_log.warn("readDocument failed on file: " + name
120: + " with exception: " + any.toString());
121: doc = null;
122: }
123:
124: return doc;
125: }
126:
127: /**
128: * Read a DOM Document from xml in a string.
129: *
130: * @param in
131: * The string containing the XML
132: * @return A new DOM Document with the xml contents.
133: */
134: public static Document readDocumentFromString(String in) {
135: try {
136: DocumentBuilder docBuilder = getDocumentBuilder();
137: InputSource inputSource = new InputSource(new StringReader(
138: in));
139: Document doc = docBuilder.parse(inputSource);
140: return doc;
141: } catch (Exception any) {
142: M_log.warn("readDocumentFromString: " + any.toString());
143: return null;
144: }
145: }
146:
147: /**
148: * Read a DOM Document from xml in a stream.
149: *
150: * @param in
151: * The stream containing the XML
152: * @return A new DOM Document with the xml contents.
153: */
154: public static Document readDocumentFromStream(InputStream in) {
155: try {
156: DocumentBuilder docBuilder = getDocumentBuilder();
157: InputSource inputSource = new InputSource(in);
158: Document doc = docBuilder.parse(inputSource);
159: return doc;
160: } catch (Exception any) {
161: M_log.warn("readDocumentFromStream: " + any.toString());
162: return null;
163: }
164: }
165:
166: /**
167: * Write a DOM Document to an xml file.
168: *
169: * @param doc
170: * The DOM Document to write.
171: * @param fileName
172: * The complete file name path.
173: */
174: public static void writeDocument(Document doc, String fileName) {
175: try {
176: OutputStream out = new FileOutputStream(fileName);
177: // get an instance of the DOMImplementation registry
178: DocumentBuilderFactory factory = DocumentBuilderFactory
179: .newInstance();
180: DocumentBuilder builder = factory.newDocumentBuilder();
181: DOMImplementation impl = builder.getDOMImplementation();
182:
183: DOMImplementationLS feature = (DOMImplementationLS) impl
184: .getFeature("LS", "3.0");
185: LSSerializer serializer = feature.createLSSerializer();
186: LSOutput output = feature.createLSOutput();
187: output.setByteStream(out);
188: output.setEncoding("UTF-8");
189: serializer.write(doc, output);
190:
191: out.close();
192: } catch (Exception any) {
193: M_log.warn("writeDocument: " + any.toString());
194: }
195: }
196:
197: /**
198: * Write a DOM Document to an output stream.
199: *
200: * @param doc
201: * The DOM Document to write.
202: * @param out
203: * The output stream.
204: */
205: public static String writeDocumentToString(Document doc) {
206: try {
207:
208: StringWriter sw = new StringWriter();
209:
210: DocumentBuilderFactory factory = DocumentBuilderFactory
211: .newInstance();
212: DocumentBuilder builder = factory.newDocumentBuilder();
213: DOMImplementation impl = builder.getDOMImplementation();
214:
215: DOMImplementationLS feature = (DOMImplementationLS) impl
216: .getFeature("LS", "3.0");
217: LSSerializer serializer = feature.createLSSerializer();
218: LSOutput output = feature.createLSOutput();
219: output.setCharacterStream(sw);
220: output.setEncoding("UTF-8");
221: serializer.write(doc, output);
222:
223: sw.flush();
224: return sw.toString();
225: } catch (Exception any) {
226: M_log.warn("writeDocumentToString: " + any.toString());
227: return null;
228: }
229: }
230:
231: /**
232: * Place a string into the attribute <tag>of the element <el>, encoded so special characters can be used.
233: *
234: * @param el
235: * The element.
236: * @param tag
237: * The attribute name.
238: * @param value
239: * The string.
240: */
241: public static void encodeAttribute(Element el, String tag,
242: String value) {
243: // encode the message body base64, and make it an attribute
244: try {
245: String encoded = new String(CommonsCodecBase64
246: .encodeBase64(value.getBytes("UTF-8")), "UTF-8");
247: el.setAttribute(tag, encoded);
248: } catch (Exception e) {
249: M_log.warn("encodeAttribute: " + e);
250: }
251: }
252:
253: /**
254: * Decode a string from the attribute <tag>of the element <el>, that was made using encodeAttribute().
255: *
256: * @param el
257: * The element.
258: * @param tag
259: * The attribute name.
260: * @return The string; may be empty, won't be null.
261: */
262: public static String decodeAttribute(Element el, String tag) {
263: String charset = StringUtil.trimToNull(el
264: .getAttribute("charset"));
265: if (charset == null)
266: charset = "UTF-8";
267:
268: String body = StringUtil.trimToNull(el.getAttribute(tag));
269: if (body != null) {
270: try {
271: byte[] decoded = CommonsCodecBase64.decodeBase64(body
272: .getBytes("UTF-8"));
273: body = new String(decoded, charset);
274: } catch (Exception e) {
275: M_log.warn("decodeAttribute: " + e);
276: }
277: }
278:
279: if (body == null)
280: body = "";
281:
282: return body;
283: }
284:
285: /**
286: * @return a DocumentBuilder object for XML parsing.
287: */
288: protected static DocumentBuilder getDocumentBuilder()
289: throws ParserConfigurationException {
290: DocumentBuilderFactory dbf = DocumentBuilderFactory
291: .newInstance();
292:
293: return dbf.newDocumentBuilder();
294: }
295:
296: /**
297: * Serialize the properties into XML, adding an element to the doc under the top of the stack element.
298: *
299: * @param propsToSerialize
300: * The properties to serialize.
301: * @param doc
302: * The DOM doc to contain the XML (or null for a string return).
303: * @param stack
304: * The DOM elements, the top of which is the containing element of the new "resource" element.
305: * @return The newly added element.
306: */
307: public static Element propertiesToXml(Properties propsToSerialize,
308: Document doc, Stack stack) {
309: Element properties = doc.createElement("properties");
310: ((Element) stack.peek()).appendChild(properties);
311: Enumeration props = propsToSerialize.propertyNames();
312: while (props.hasMoreElements()) {
313: String name = (String) props.nextElement();
314: String value = propsToSerialize.getProperty(name);
315: Element propElement = doc.createElement("property");
316: properties.appendChild(propElement);
317: propElement.setAttribute("name", name);
318:
319: // encode to allow special characters in the value
320: Xml.encodeAttribute(propElement, "value", (String) value);
321: propElement.setAttribute("enc", "BASE64");
322: }
323:
324: return properties;
325: }
326:
327: /**
328: * Fill in a properties from XML.
329: *
330: * @param properties
331: * The properties to fill in.
332: * @param el
333: * The XML DOM element.
334: */
335: public static void xmlToProperties(Properties properties, Element el) {
336: // the children (property)
337: NodeList children = el.getChildNodes();
338: final int length = children.getLength();
339: for (int i = 0; i < length; i++) {
340: Node child = children.item(i);
341: if (child.getNodeType() != Node.ELEMENT_NODE)
342: continue;
343: Element element = (Element) child;
344:
345: // look for property
346: if (element.getTagName().equals("property")) {
347: String name = element.getAttribute("name");
348: String enc = StringUtil.trimToNull(element
349: .getAttribute("enc"));
350: String value = null;
351: if ("BASE64".equalsIgnoreCase(enc)) {
352: value = decodeAttribute(element, "value");
353: } else {
354: value = element.getAttribute("value");
355: }
356:
357: properties.put(name, value);
358: }
359: }
360: }
361: }
|