001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.util.xml;
011:
012: import java.io.*;
013: import java.util.ResourceBundle;
014: import java.util.MissingResourceException;
015:
016: import org.xml.sax.InputSource;
017:
018: import org.w3c.dom.*;
019: import javax.xml.transform.*;
020: import javax.xml.transform.stream.StreamResult;
021: import javax.xml.transform.dom.DOMSource;
022:
023: import org.mmbase.util.logging.*;
024:
025: /**
026: * Abstract class for creating xml documents.
027: * Use this class as the base class for writers that construct and export DOM documents.
028: * The document can then be used internally or serialized using a number of
029: * utility methods.
030: *
031: * @since MMBase-1.6
032: * @author Pierre van Rooden
033: * @version $Id: DocumentWriter.java,v 1.11 2007/02/24 21:57:50 nklasens Exp $
034: */
035: abstract public class DocumentWriter extends DocumentReader {
036:
037: // logger
038: private static final Logger log = Logging
039: .getLoggerInstance(DocumentWriter.class);
040:
041: /**
042: * True if the document has been generated
043: */
044: private boolean documentGenerated = false;
045:
046: /**
047: * If true, comments are included
048: */
049: private boolean includeComments = false;
050:
051: /**
052: * Resource bundle with builder comments
053: */
054: private ResourceBundle messageRB;
055:
056: // keep public and system id
057: String publicId = "";
058: String systemId = "";
059:
060: /**
061: * Constructs the document writer.
062: * The constructor creates a basic document with a root element based on the specified document type parameters.
063: * The document is empty after construction.
064: * It is actually filled with a call to {@link #generateDocument()}, which is in turn called when
065: * the document is first accessed through {@link #getDocument()}.
066: * @param qualifiedName the qualified name of the document's root element
067: * @param publicId the PUBLIC id of the document type
068: * @param systemId the SYSTEm id of the document type
069: */
070: public DocumentWriter(String qualifiedName, String publicId,
071: String systemId) throws DOMException {
072: DOMImplementation domImpl = DocumentReader.getDocumentBuilder()
073: .getDOMImplementation();
074: this .publicId = publicId;
075: this .systemId = systemId;
076: DocumentType doctype = domImpl.createDocumentType(
077: qualifiedName, this .publicId, this .systemId);
078: document = domImpl.createDocument(null, qualifiedName, doctype);
079: }
080:
081: /**
082: * Constructs the document by reading it from a source.
083: * @param source the input source from which to read the document
084: * @since MMBase-1.7
085: */
086: public DocumentWriter(InputSource source) {
087: super (source);
088: documentGenerated = true;
089: }
090:
091: /**
092: * Constructs the document by reading it from a source.
093: * You can pass a resolve class to this constructor, allowing you to indicate the package in which the dtd
094: * of the document read is to be found. The dtd sould be in the resources package under the package of the class passed.
095: * @param source the input source from which to read the document
096: * @param validating whether to validate the document
097: * @param resolveBase the base class whose package is used to resolve dtds, set to null if unknown
098: * @since MMBase-1.7
099: */
100: public DocumentWriter(InputSource source, boolean validating,
101: Class<?> resolveBase) {
102: super (source, validating, resolveBase);
103: documentGenerated = true;
104: }
105:
106: /**
107: * Initialize the ResourceBundle with the given resource.
108: * You need a respource to use the addfCOmment() and getMessage() methods.
109: * @param resourcelocation Resource.
110: */
111: protected void getMessageRetriever(String resourcelocation) {
112: try {
113: messageRB = ResourceBundle.getBundle(resourcelocation);
114: } catch (MissingResourceException e) {
115: log.error("Resource for DocumentWriter is missing: "
116: + resourcelocation);
117: }
118: }
119:
120: /**
121: * Retrieves a message from the resource bundle.
122: * @param key the key of the message
123: */
124: protected String getMessage(String key) {
125: return getMessage(key, "");
126: }
127:
128: /**
129: * Retrieves a message from the resource bundle.
130: * @param key the key of the message
131: * @param a1 the first parameter to substitute in the message
132: */
133: protected String getMessage(String key, String a1) {
134: return getMessage(key, a1, "", "");
135: }
136:
137: /**
138: * Retrieves a message from the resource bundle.
139: * @param key the key of the message
140: * @param a1 the first parameter to substitute in the message
141: * @param a2 the second parameter to substitute in the message
142: */
143: protected String getMessage(String key, String a1, String a2) {
144: return getMessage(key, a1, a2, "");
145: }
146:
147: /**
148: * Retrieves a message from the resource bundle.
149: * @param key the key of the message
150: * @param a1 the first parameter to substitute in the message
151: * @param a2 the second parameter to substitute in the message
152: * @param a3 the third parameter to substitute in the message
153: */
154: protected String getMessage(String key, String a1, String a2,
155: String a3) {
156: if (messageRB != null)
157: try {
158: String message = messageRB.getString(key);
159: Object[] args = new String[3];
160: args[0] = a1;
161: args[1] = a2;
162: args[2] = a3;
163: return java.text.MessageFormat.format(message, args);
164: } catch (MissingResourceException e) {
165: log
166: .error("Resource for DocumentWriter is broken. There is no "
167: + key + " key in resource.");
168: }
169: return null;
170: }
171:
172: /**
173: * Creates a DOM element which contains a Text Node, and adds it to the
174: * specified node as a child.
175: * @param tagname name of the new element
176: * @param content content of the new element as a string
177: * @param out the element to which to add the new Element.
178: * @return the newly created element
179: */
180: protected Element addContentElement(String tagname, String content,
181: Element out) {
182: Element el = document.createElement(tagname);
183: if (content == null)
184: content = "";
185: Text tel = document.createTextNode(content);
186: el.appendChild(tel);
187: out.appendChild(el);
188: return el;
189: }
190:
191: /**
192: * Creates a Comment (provided comments should be included), and adds it to the
193: * specified node as a child.
194: * The comment is retrieved from a resource bundle - if no resource was specified,
195: * no comments are added.
196: * @param key the key of the comment to add as a string
197: * @param out the element to which to add the new Comment.
198: * @return the newly created comment or null if nothing was added
199: * @see #setIncludeComments
200: */
201: protected Comment addComment(String key, Element out) {
202: return addComment(key, "", "", out);
203: }
204:
205: /**
206: * Creates a Comment (provided comments should be included), and adds it to the
207: * specified node as a child.
208: * The comment is retrieved from a resource bundle - if no resource was specified,
209: * no comments are added.
210: * @param key the key of the comment to add as a string
211: * @param a1 the first parameter to substitute in the comment
212: * @param out the element to which to add the new Comment.
213: * @return the newly created comment or null if nothing was added
214: * @see #setIncludeComments
215: */
216: protected Comment addComment(String key, String a1, Element out) {
217: return addComment(key, a1, "", out);
218: }
219:
220: /**
221: * Creates a Comment (provided comments should be included), and adds it to the
222: * specified node as a child.
223: * The comment is retrieved from a resource bundle - if no resource was specified,
224: * no comments are added.
225: * @param key the comment to add as a string
226: * @param a1 the first parameter to substitute in the comment
227: * @param a2 the second parameter to substitute in the comment
228: * @param out the element to which to add the new Comment.
229: * @return the newly created comment or null if nothing was added
230: * @see #setIncludeComments
231: */
232: protected Comment addComment(String key, String a1, String a2,
233: Element out) {
234: Comment comm = null;
235: if (includeComments) {
236: String message = getMessage(key, a1, a2);
237: if (message != null) {
238: comm = document.createComment(" " + message + " ");
239: out.appendChild(comm);
240: }
241: }
242: return comm;
243: }
244:
245: /**
246: * Generates the document.
247: * You need to override this class with the code that constructs your document.
248: * @throws DOMException when an error occurred during generation
249: */
250: abstract protected void generate() throws DOMException;
251:
252: /**
253: * Generates the document if it hadn't be done so already.
254: * If not, an exception is thrown.
255: * Use getDocument() to safely retrive a generated coeumnt.
256: * @throws DOMException when an error occurred during generation
257: * @throws DOMException when the document was already constructed
258: */
259: public final Document generateDocument() throws DOMException {
260: if (!documentGenerated) {
261: generate();
262: documentGenerated = true;
263: return document;
264: } else {
265: throw new IllegalStateException(
266: "Document already constructed");
267: }
268: }
269:
270: /**
271: * Returns the completed document representation;
272: * If the document was not yet generated, it is generated by calling generateDocument().
273: * @return the generated document
274: * @throws DOMException when an error occurred during generation
275: */
276: public Document getDocument() throws DOMException {
277: if (!documentGenerated) {
278: generateDocument();
279: }
280: return document;
281: }
282:
283: /**
284: * Sets whether the document will include comments
285: * @param value if true, the document will include comments
286: */
287: public void setIncludeComments(boolean value) {
288: includeComments = value;
289: }
290:
291: /**
292: * Gets whether the document will include comments
293: * @return if true, the document will include comments
294: */
295: public boolean includeComments() {
296: return includeComments;
297: }
298:
299: /**
300: * Generates the document and returns it as a string.
301: * @throws TransformerException if the document is malformed
302: */
303: public String writeToString() throws TransformerException {
304: StringWriter strw = new StringWriter(500);
305: write(new StreamResult(strw));
306: return strw.toString();
307: }
308:
309: /**
310: * Generates the document and store it as a file in the given path.
311: * @param filename the filepath where the configuration is to be stored
312: * @throws TransformerException if the document is malformed
313: * @throws IOException if the file cannot be written
314: */
315: public void writeToFile(String filename) throws IOException,
316: TransformerException {
317: writeToStream(new FileOutputStream(filename));
318: }
319:
320: /**
321: * Generates the document and store it in the given stream.
322: * @param out the output stream where the configuration is to be stored
323: */
324: public void writeToStream(OutputStream out)
325: throws TransformerException {
326: write(new StreamResult(out));
327: }
328:
329: /**
330: * Generates the document and writes it to the result object.
331: * @param result the StreamResult object where to store the configuration'
332: */
333: public void write(StreamResult result) throws TransformerException {
334: Document doc = getDocument();
335: TransformerFactory tfactory = TransformerFactory.newInstance();
336: tfactory.setURIResolver(new org.mmbase.util.xml.URIResolver(
337: new java.io.File("")));
338: // This creates a transformer that does a simple identity transform,
339: // and thus can be used for all intents and purposes as a serializer.
340: Transformer serializer = tfactory.newTransformer();
341: // sets indent amount for xalan
342: // should be done elsewhere, but where?
343: serializer.setOutputProperty(
344: "{http://xml.apache.org/xslt}indent-amount", "2");
345: // xml output configuration
346: serializer.setOutputProperty(OutputKeys.INDENT, "yes");
347: serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
348: "no");
349: serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,
350: publicId);
351: serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
352: systemId);
353: serializer.transform(new DOMSource(doc), result);
354: }
355: }
|