blob: 60582a3f519b50967c6a638e183b13966d797736 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.lib.meta;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.transform.Result;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.Files;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.xml.Commentable;
import org.apache.openjpa.lib.xml.XMLWriter;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
/**
* Abstract base type for serlializers that transfer groups of objects
* to XML. Includes a way of serializing objects back to the XML files
* they were parsed from. Serializers are not thread safe.
*
* @author Abe White
*/
public abstract class XMLMetaDataSerializer implements MetaDataSerializer {
private static final Localizer _loc = Localizer.forPackage
(XMLMetaDataSerializer.class);
private static final SAXTransformerFactory _factory =
(SAXTransformerFactory) TransformerFactory.newInstance();
private Log _log = null;
// current serialization state
private final AttributesImpl _attrs = new AttributesImpl();
private ContentHandler _handler = null;
private int _flags = 0;
private File _backup = null;
/**
* The log to write to.
*/
public Log getLog() {
return _log;
}
/**
* The log to write to.
*/
public void setLog(Log log) {
_log = log;
}
@Override
public void serialize(int flags) throws IOException {
serialize((Map) null, flags);
}
@Override
public void serialize(Map output, int flags)
throws IOException {
Map<File, Collection<Object>> files = getFileMap();
if (files == null)
return;
// for each file, serialize objects
for (Entry<File, Collection<Object>> entry : files.entrySet()) {
File file = entry.getKey();
Collection<Object> fileObjs = entry.getValue();
if (_log != null && _log.isInfoEnabled())
_log.info(_loc.get("ser-file", file));
try {
TransformerHandler trans = _factory.newTransformerHandler();
Writer writer;
if (output == null) {
_backup = prepareWrite(file);
writer = new FileWriter(file);
} else
writer = new StringWriter();
Writer xml = writer;
if ((flags & PRETTY) > 0)
xml = new XMLWriter(writer);
trans.setResult(new StreamResult(xml));
serialize(fileObjs, trans, flags);
if (output != null)
output.put(file, ((StringWriter) writer).toString());
} catch (SAXException se) {
throw new IOException(se.toString());
} catch (TransformerConfigurationException tce) {
throw new IOException(tce.toString());
}
}
}
/**
* Prepare to write to the given file. Back up the file and make sure the
* path to it is created.
*/
protected File prepareWrite(File file) throws IOException {
File backup = Files.backup(file, false);
if (backup == null) {
File parent = file.getParentFile();
if (parent != null && !AccessController.doPrivileged(
J2DoPrivHelper.existsAction(parent)))
AccessController.doPrivileged(
J2DoPrivHelper.mkdirsAction(parent));
}
return backup;
}
/**
* Returns a {@link Map} with keys of the {@link File} to be
* written to, and values of a {@link Collection} of
* {@link SourceTracker} instances.
*/
protected Map<File, Collection<Object>> getFileMap() {
Collection<Object> objs = getObjects();
if (objs == null || objs.isEmpty())
return null;
// create a map of files to lists of objects
Map<File, Collection<Object>> files =
new HashMap<>();
File file;
Collection<Object> fileObjs;
for(Object obj : objs) {
file = getSourceFile(obj);
if (file == null) {
if (_log != null && _log.isTraceEnabled())
_log.trace(_loc.get("no-file", obj));
continue;
}
fileObjs = (Collection<Object>) files.get(file);
if (fileObjs == null) {
fileObjs = new LinkedList<>();
files.put(file, fileObjs);
}
fileObjs.add(obj);
}
return files;
}
/**
* Return the source file for the given instance. By default, checks
* to see if the instance implements {@link SourceTracker}.
*/
protected File getSourceFile(Object obj) {
if (obj instanceof SourceTracker)
return ((SourceTracker) obj).getSourceFile();
return null;
}
@Override
public void serialize(File file, int flags) throws IOException {
if (_log != null)
_log.info(_loc.get("ser-file", file));
_backup = prepareWrite(file);
try {
FileWriter out = new FileWriter(
AccessController.doPrivileged(
J2DoPrivHelper.getCanonicalPathAction(file)),
(flags & APPEND) > 0);
serialize(out, flags);
out.close();
} catch (PrivilegedActionException pae) {
throw (IOException) pae.getException();
}
}
@Override
public void serialize(Writer out, int flags) throws IOException {
try {
if ((flags & PRETTY) > 0)
serialize(new StreamResult(new XMLWriter(out)), flags);
else
serialize(new StreamResult(out), flags);
} catch (SAXException se) {
throw new IOException(se.toString());
}
}
/**
* Serialize the current set of objects to the given result.
*/
public void serialize(Result result, int flags) throws SAXException {
try {
TransformerHandler trans = _factory.newTransformerHandler();
trans.setResult(result);
serialize(trans, flags);
} catch (TransformerConfigurationException tce) {
throw new SAXException(tce);
}
}
/**
* Serilize the current set of objects to a series of SAX events on the
* given handler.
*/
public void serialize(ContentHandler handler, int flags)
throws SAXException {
serialize(getObjects(), handler, flags);
}
/**
* Serialize the given collection of objects to the given handler.
*/
private void serialize(Collection<Object> objs, ContentHandler handler,
int flags) throws SAXException {
if (_log != null && _log.isTraceEnabled())
_log.trace(_loc.get("ser-objs", objs));
_handler = handler;
_flags = flags;
try {
if (!objs.isEmpty()) {
handler.startDocument();
serialize(objs);
handler.endDocument();
}
}
finally {
reset();
}
}
/**
* Whether this serialization is in verbose mode.
*/
protected boolean isVerbose() {
return (_flags & VERBOSE) > 0;
}
/**
* The backup file made for the current file being parsed.
*/
protected File currentBackupFile() {
return _backup;
}
/**
* Start an element with the current attribute settings. Clears the
* attributes as well.
*/
protected void startElement(String name) throws SAXException {
_handler.startElement("", name, name, _attrs);
_attrs.clear();
}
/**
* End the current element.
*/
protected void endElement(String name) throws SAXException {
_handler.endElement("", name, name);
}
/**
* Add text to the current element.
*/
protected void addText(String text) throws SAXException {
_handler.characters(text.toCharArray(), 0, text.length());
}
/**
* Add an attribute to the current group.
*/
protected void addAttribute(String name, String value) {
_attrs.addAttribute("", name, name, "CDATA", value);
}
/**
* The current attributes.
*/
protected Attributes getAttributes() {
return _attrs;
}
/**
* Add a comment to the stream.
*/
protected void addComments(String[] comments) throws SAXException {
if (comments == null || comments.length == 0
|| !(_handler instanceof LexicalHandler))
return;
LexicalHandler lh = (LexicalHandler) _handler;
char[] chars;
for (int i = 0; i < comments.length; i++) {
chars = comments[i].toCharArray();
lh.comment(chars, 0, chars.length);
}
}
/**
* Write the given entity's comments. By default, tests if entity is
* {@link Commentable}.
*/
protected void addComments(Object obj) throws SAXException {
if (obj instanceof Commentable)
addComments(((Commentable) obj).getComments());
}
/**
* Reset serialization state for the next document.
*/
protected void reset() {
_attrs.clear();
_handler = null;
_flags = 0;
_backup = null;
}
/**
* Serialize the given set of objects.
*/
protected abstract void serialize(Collection<Object> objs) throws
SAXException;
/**
* Return the current set of objects for serialization.
*/
protected abstract Collection<Object> getObjects();
}