001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.lib.meta;
020:
021: import java.io.File;
022: import java.io.FileWriter;
023: import java.io.IOException;
024: import java.io.StringWriter;
025: import java.io.Writer;
026: import java.security.AccessController;
027: import java.security.PrivilegedActionException;
028: import java.util.Collection;
029: import java.util.HashMap;
030: import java.util.Iterator;
031: import java.util.LinkedList;
032: import java.util.Map;
033: import javax.xml.transform.Result;
034: import javax.xml.transform.TransformerConfigurationException;
035: import javax.xml.transform.TransformerFactory;
036: import javax.xml.transform.sax.SAXTransformerFactory;
037: import javax.xml.transform.sax.TransformerHandler;
038: import javax.xml.transform.stream.StreamResult;
039:
040: import org.xml.sax.Attributes;
041: import org.xml.sax.ContentHandler;
042: import org.xml.sax.SAXException;
043: import org.xml.sax.ext.LexicalHandler;
044: import org.xml.sax.helpers.AttributesImpl;
045: import org.apache.openjpa.lib.log.Log;
046: import org.apache.openjpa.lib.util.Files;
047: import org.apache.openjpa.lib.util.J2DoPrivHelper;
048: import org.apache.openjpa.lib.util.Localizer;
049: import org.apache.openjpa.lib.xml.Commentable;
050: import org.apache.openjpa.lib.xml.XMLWriter;
051:
052: /**
053: * Abstract base type for serlializers that transfer groups of objects
054: * to XML. Includes a way of serializing objects back to the XML files
055: * they were parsed from. Serializers are not thread safe.
056: *
057: * @author Abe White
058: * @nojavadoc
059: */
060: public abstract class XMLMetaDataSerializer implements
061: MetaDataSerializer {
062:
063: private static final Localizer _loc = Localizer
064: .forPackage(XMLMetaDataSerializer.class);
065: private static final SAXTransformerFactory _factory = (SAXTransformerFactory) TransformerFactory
066: .newInstance();
067:
068: private Log _log = null;
069:
070: // current serialization state
071: private final AttributesImpl _attrs = new AttributesImpl();
072: private ContentHandler _handler = null;
073: private int _flags = 0;
074: private File _backup = null;
075:
076: /**
077: * The log to write to.
078: */
079: public Log getLog() {
080: return _log;
081: }
082:
083: /**
084: * The log to write to.
085: */
086: public void setLog(Log log) {
087: _log = log;
088: }
089:
090: public void serialize(int flags) throws IOException {
091: serialize((Map) null, flags);
092: }
093:
094: public void serialize(Map output, int flags) throws IOException {
095: Map files = getFileMap();
096: if (files == null)
097: return;
098:
099: // for each file, serialize objects
100: Map.Entry entry;
101: for (Iterator itr = files.entrySet().iterator(); itr.hasNext();) {
102: entry = (Map.Entry) itr.next();
103: File file = (File) entry.getKey();
104: Collection fileObjs = (Collection) entry.getValue();
105:
106: if (_log != null && _log.isInfoEnabled())
107: _log.info(_loc.get("ser-file", file));
108:
109: try {
110: TransformerHandler trans = _factory
111: .newTransformerHandler();
112: Writer writer;
113: if (output == null) {
114: _backup = prepareWrite(file);
115: writer = new FileWriter(file);
116: } else
117: writer = new StringWriter();
118:
119: Writer xml = writer;
120: if ((flags & PRETTY) > 0)
121: xml = new XMLWriter(writer);
122: trans.setResult(new StreamResult(xml));
123: serialize(fileObjs, trans, flags);
124:
125: if (output != null)
126: output
127: .put(file, ((StringWriter) writer)
128: .toString());
129: } catch (SAXException se) {
130: throw new IOException(se.toString());
131: } catch (TransformerConfigurationException tce) {
132: throw new IOException(tce.toString());
133: }
134: }
135: }
136:
137: /**
138: * Prepare to write to the given file. Back up the file and make sure the
139: * path to it is created.
140: */
141: protected File prepareWrite(File file) throws IOException {
142: File backup = Files.backup(file, false);
143: if (backup == null) {
144: File parent = file.getParentFile();
145: if (parent != null
146: && !((Boolean) AccessController
147: .doPrivileged(J2DoPrivHelper
148: .existsAction(parent)))
149: .booleanValue())
150: AccessController.doPrivileged(J2DoPrivHelper
151: .mkdirsAction(parent));
152: }
153: return backup;
154: }
155:
156: /**
157: * Returns a {@link Map} with keys of the {@link File} to be
158: * written to, and values of a {@link Collection} of
159: * {@link SourceTracker} instances.
160: */
161: protected Map getFileMap() {
162: Collection objs = getObjects();
163: if (objs == null || objs.isEmpty())
164: return null;
165:
166: // create a map of files to lists of objects
167: Map files = new HashMap();
168: File file;
169: Collection fileObjs;
170: Object obj;
171: for (Iterator itr = objs.iterator(); itr.hasNext();) {
172: obj = itr.next();
173: file = getSourceFile(obj);
174: if (file == null) {
175: if (_log != null && _log.isTraceEnabled())
176: _log.trace(_loc.get("no-file", obj));
177: continue;
178: }
179:
180: fileObjs = (Collection) files.get(file);
181: if (fileObjs == null) {
182: fileObjs = new LinkedList();
183: files.put(file, fileObjs);
184: }
185: fileObjs.add(obj);
186: }
187:
188: return files;
189: }
190:
191: /**
192: * Return the source file for the given instance. By default, checks
193: * to see if the instance implements {@link SourceTracker}.
194: */
195: protected File getSourceFile(Object obj) {
196: if (obj instanceof SourceTracker)
197: return ((SourceTracker) obj).getSourceFile();
198: return null;
199: }
200:
201: public void serialize(File file, int flags) throws IOException {
202: if (_log != null)
203: _log.info(_loc.get("ser-file", file));
204:
205: _backup = prepareWrite(file);
206: try {
207: FileWriter out = new FileWriter((String) AccessController
208: .doPrivileged(J2DoPrivHelper
209: .getCanonicalPathAction(file)),
210: (flags & APPEND) > 0);
211: serialize(out, flags);
212: out.close();
213: } catch (PrivilegedActionException pae) {
214: throw (IOException) pae.getException();
215: }
216: }
217:
218: public void serialize(Writer out, int flags) throws IOException {
219: try {
220: if ((flags & PRETTY) > 0)
221: serialize(new StreamResult(new XMLWriter(out)), flags);
222: else
223: serialize(new StreamResult(out), flags);
224: } catch (SAXException se) {
225: throw new IOException(se.toString());
226: }
227: }
228:
229: /**
230: * Serialize the current set of objects to the given result.
231: */
232: public void serialize(Result result, int flags) throws SAXException {
233: try {
234: TransformerHandler trans = _factory.newTransformerHandler();
235: trans.setResult(result);
236: serialize(trans, flags);
237: } catch (TransformerConfigurationException tce) {
238: throw new SAXException(tce);
239: }
240: }
241:
242: /**
243: * Serilize the current set of objects to a series of SAX events on the
244: * given handler.
245: */
246: public void serialize(ContentHandler handler, int flags)
247: throws SAXException {
248: serialize(getObjects(), handler, flags);
249: }
250:
251: /**
252: * Serialize the given collection of objects to the given handler.
253: */
254: private void serialize(Collection objs, ContentHandler handler,
255: int flags) throws SAXException {
256: if (_log != null && _log.isTraceEnabled())
257: _log.trace(_loc.get("ser-objs", objs));
258:
259: _handler = handler;
260: _flags = flags;
261: try {
262: if (!objs.isEmpty()) {
263: handler.startDocument();
264: serialize(objs);
265: handler.endDocument();
266: }
267: } finally {
268: reset();
269: }
270: }
271:
272: /**
273: * Whether this serialization is in verbose mode.
274: */
275: protected boolean isVerbose() {
276: return (_flags & VERBOSE) > 0;
277: }
278:
279: /**
280: * The backup file made for the current file being parsed.
281: */
282: protected File currentBackupFile() {
283: return _backup;
284: }
285:
286: /**
287: * Start an element with the current attribute settings. Clears the
288: * attributes as well.
289: */
290: protected void startElement(String name) throws SAXException {
291: _handler.startElement("", name, name, _attrs);
292: _attrs.clear();
293: }
294:
295: /**
296: * End the current element.
297: */
298: protected void endElement(String name) throws SAXException {
299: _handler.endElement("", name, name);
300: }
301:
302: /**
303: * Add text to the current element.
304: */
305: protected void addText(String text) throws SAXException {
306: _handler.characters(text.toCharArray(), 0, text.length());
307: }
308:
309: /**
310: * Add an attribute to the current group.
311: */
312: protected void addAttribute(String name, String value) {
313: _attrs.addAttribute("", name, name, "CDATA", value);
314: }
315:
316: /**
317: * The current attributes.
318: */
319: protected Attributes getAttributes() {
320: return _attrs;
321: }
322:
323: /**
324: * Add a comment to the stream.
325: */
326: protected void addComments(String[] comments) throws SAXException {
327: if (comments == null || comments.length == 0
328: || !(_handler instanceof LexicalHandler))
329: return;
330:
331: LexicalHandler lh = (LexicalHandler) _handler;
332: char[] chars;
333: for (int i = 0; i < comments.length; i++) {
334: chars = comments[i].toCharArray();
335: lh.comment(chars, 0, chars.length);
336: }
337: }
338:
339: /**
340: * Write the given entity's comments. By default, tests if entity is
341: * {@link Commentable}.
342: */
343: protected void addComments(Object obj) throws SAXException {
344: if (obj instanceof Commentable)
345: addComments(((Commentable) obj).getComments());
346: }
347:
348: /**
349: * Reset serialization state for the next document.
350: */
351: protected void reset() {
352: _attrs.clear();
353: _handler = null;
354: _flags = 0;
355: _backup = null;
356: }
357:
358: /**
359: * Serialize the given set of objects.
360: */
361: protected abstract void serialize(Collection objs)
362: throws SAXException;
363:
364: /**
365: * Return the current set of objects for serialization.
366: */
367: protected abstract Collection getObjects();
368: }
|