| /* |
| * 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(); |
| } |