📄 xmpserializerrdf.java
字号:
// =================================================================================================// ADOBE SYSTEMS INCORPORATED// Copyright 2006-2007 Adobe Systems Incorporated// All Rights Reserved//// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms// of the Adobe license agreement accompanying it.// =================================================================================================package com.adobe.xmp.impl;import java.io.IOException;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.util.Arrays;import java.util.HashSet;import java.util.Iterator;import java.util.Set;import com.adobe.xmp.XMPConst;import com.adobe.xmp.XMPError;import com.adobe.xmp.XMPException;import com.adobe.xmp.XMPMeta;import com.adobe.xmp.XMPMetaFactory;import com.adobe.xmp.options.SerializeOptions;/** * Serializes the <code>XMPMeta</code>-object using the standard RDF serialization format. * The output is written to an <code>OutputStream</code> * according to the <code>SerializeOptions</code>. * * @since 11.07.2006 */public class XMPSerializerRDF{ /** default padding */ private static final int DEFAULT_PAD = 2048; /** */ private static final String PACKET_HEADER = "<?xpacket begin=\"\uFEFF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>"; /** The w/r is missing inbetween */ private static final String PACKET_TRAILER = "<?xpacket end=\""; /** */ private static final String PACKET_TRAILER2 = "\"?>"; /** */ private static final String RDF_XMPMETA_START = "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\""; /** */ private static final String RDF_XMPMETA_END = "</x:xmpmeta>"; /** */ private static final String RDF_RDF_START = "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">"; /** */ private static final String RDF_RDF_END = "</rdf:RDF>"; /** */ private static final String RDF_SCHEMA_START = "<rdf:Description rdf:about="; /** */ private static final String RDF_SCHEMA_END = "</rdf:Description>"; /** */ private static final String RDF_STRUCT_START = "<rdf:Description"; /** */ private static final String RDF_STRUCT_END = "</rdf:Description>"; /** a set of all rdf attribute qualifier */ static final Set RDF_ATTR_QUALIFIER = new HashSet(Arrays.asList(new String[] { XMPConst.XML_LANG, "rdf:resource", "rdf:ID", "rdf:bagID", "rdf:nodeID" })); /** the metadata object to be serialized. */ private XMPMetaImpl xmp; /** the output stream to serialize to */ private CountOutputStream outputStream; /** this writer is used to do the actual serialisation */ private OutputStreamWriter writer; /** the stored serialisation options */ private SerializeOptions options; /** the size of one unicode char, for UTF-8 set to 1 * (Note: only valid for ASCII chars lower than 0x80), * set to 2 in case of UTF-16 */ private int unicodeSize = 1; // UTF-8 /** the padding in the XMP Packet, or the length of the complete packet in * case of option <em>exactPacketLength</em>. */ private int padding; /** * The actual serialisation. * * @param xmp the metadata object to be serialized * @param out outputStream the output stream to serialize to * @param options the serialization options * * @throws XMPException If case of wrong options or any other serialisaton error. */ public void serialize(XMPMeta xmp, OutputStream out, SerializeOptions options) throws XMPException { try { outputStream = new CountOutputStream(out); writer = new OutputStreamWriter(outputStream, options.getEncoding()); this.xmp = (XMPMetaImpl) xmp; this.options = options; this.padding = options.getPadding(); writer = new OutputStreamWriter(outputStream, options.getEncoding()); checkOptionsConsistence(); // serializes the whole packet, but don't write the tail yet // and flush to make sure that the written bytes are calculated correctly String tailStr = serializeAsRDF(); writer.flush(); // adds padding addPadding(tailStr.length()); // writes the tail write(tailStr); writer.flush(); outputStream.close(); } catch (IOException e) { throw new XMPException("Error writing to the OutputStream", XMPError.UNKNOWN); } } /** * Calulates the padding according to the options and write it to the stream. * @param tailLength the length of the tail string * @throws XMPException thrown if packet size is to small to fit the padding * @throws IOException forwards writer errors */ private void addPadding(int tailLength) throws XMPException, IOException { if (options.getExactPacketLength()) { // the string length is equal to the length of the UTF-8 encoding int minSize = outputStream.getBytesWritten() + tailLength * unicodeSize; if (minSize > padding) { throw new XMPException("Can't fit into specified packet size", XMPError.BADSERIALIZE); } padding -= minSize; // Now the actual amount of padding to add. } // fix rest of the padding according to Unicode unit size. padding /= unicodeSize; int newlineLen = options.getNewline().length(); if (padding >= newlineLen) { padding -= newlineLen; // Write this newline last. while (padding >= (100 + newlineLen)) { writeChars(100, ' '); writeNewline(); padding -= (100 + newlineLen); } writeChars(padding, ' '); writeNewline(); } else { writeChars(padding, ' '); } } /** * Checks if the supplied options are consistent. * @throws XMPException Thrown if options are conflicting */ protected void checkOptionsConsistence() throws XMPException { if (options.getEncodeUTF16BE() | options.getEncodeUTF16LE()) { unicodeSize = 2; } if (options.getExactPacketLength()) { if (options.getOmitPacketWrapper() | options.getIncludeThumbnailPad()) { throw new XMPException("Inconsistent options for exact size serialize", XMPError.BADOPTIONS); } if ((options.getPadding() & (unicodeSize - 1)) != 0) { throw new XMPException("Exact size must be a multiple of the Unicode element", XMPError.BADOPTIONS); } } else if (options.getReadOnlyPacket()) { if (options.getOmitPacketWrapper() | options.getIncludeThumbnailPad()) { throw new XMPException("Inconsistent options for read-only packet", XMPError.BADOPTIONS); } padding = 0; } else if (options.getOmitPacketWrapper()) { if (options.getIncludeThumbnailPad()) { throw new XMPException("Inconsistent options for non-packet serialize", XMPError.BADOPTIONS); } padding = 0; } else { if (padding == 0) { padding = DEFAULT_PAD * unicodeSize; } if (options.getIncludeThumbnailPad()) { if (!xmp.doesPropertyExist(XMPConst.NS_XMP, "Thumbnails")) { padding += 10000 * unicodeSize; } } } } /** * Writes the (optional) packet header and the outer rdf-tags. * @return Returns the packet end processing instraction to be written after the padding. * @throws IOException Forwarded writer exceptions. * @throws XMPException */ private String serializeAsRDF() throws IOException, XMPException { // Write the packet header PI. if (!options.getOmitPacketWrapper()) { writeIndent(0); write(PACKET_HEADER); writeNewline(); } // Write the xmpmeta element's start tag. writeIndent(0); write(RDF_XMPMETA_START); // Note: this flag can only be set by unit tests if (!options.getOmitVersionAttribute()) { write(XMPMetaFactory.getVersionInfo().getMessage()); } write("\">"); writeNewline(); // Write the rdf:RDF start tag. writeIndent(1); write(RDF_RDF_START); writeNewline(); // Write all of the properties. if (options.getUseCompactFormat()) { serializeCompactRDFSchemas(); } else { serializePrettyRDFSchemas(); } // Write the rdf:RDF end tag. writeIndent(1); write(RDF_RDF_END); writeNewline(); // Write the xmpmeta end tag. writeIndent(0); write(RDF_XMPMETA_END); writeNewline(); // Write the packet trailer PI into the tail string as UTF-8. String tailStr = ""; if (!options.getOmitPacketWrapper()) { for (int level = options.getBaseIndent(); level > 0; level--) { tailStr += options.getIndent(); } tailStr += PACKET_TRAILER; tailStr += options.getReadOnlyPacket() ? 'r' : 'w'; tailStr += PACKET_TRAILER2; } return tailStr; } /** * Serializes the metadata in pretty-printed manner. * @throws IOException Forwarded writer exceptions * @throws XMPException */ private void serializePrettyRDFSchemas() throws IOException, XMPException { if (xmp.getRoot().getChildrenLength() > 0) { for (Iterator it = xmp.getRoot().iterateChildren(); it.hasNext(); ) { XMPNode currSchema = (XMPNode) it.next(); serializePrettyRDFSchema(currSchema); } } else { writeIndent(2); write(RDF_SCHEMA_START); // Special case an empty XMP object. writeTreeName(); write("/>"); writeNewline(); } } /** * @throws IOException */ private void writeTreeName() throws IOException { write('"'); String name = xmp.getRoot().getName(); if (name != null) { appendNodeValue(name, true); } write('"'); } /** * Serializes the metadata in compact manner. * @throws IOException Forwarded writer exceptions * @throws XMPException */ private void serializeCompactRDFSchemas() throws IOException, XMPException { // Begin the rdf:Description start tag. writeIndent(2); write(RDF_SCHEMA_START); writeTreeName(); // Write all necessary xmlns attributes. Set usedPrefixes = new HashSet(); usedPrefixes.add("xml"); usedPrefixes.add("rdf"); for (Iterator it = xmp.getRoot().iterateChildren(); it.hasNext();) { XMPNode schema = (XMPNode) it.next(); declareUsedNamespaces(schema, usedPrefixes, 4); } // Write the top level "attrProps" and close the rdf:Description start tag. boolean allAreAttrs = true; for (Iterator it = xmp.getRoot().iterateChildren(); it.hasNext();) { XMPNode schema = (XMPNode) it.next(); allAreAttrs &= serializeCompactRDFAttrProps (schema, 3); } if (!allAreAttrs) { write('>'); writeNewline(); } else { write("/>"); writeNewline(); return; // ! Done if all properties in all schema are written as attributes. } // Write the remaining properties for each schema. for (Iterator it = xmp.getRoot().iterateChildren(); it.hasNext();) { XMPNode schema = (XMPNode) it.next(); serializeCompactRDFElementProps (schema, 3); } // Write the rdf:Description end tag. writeIndent(2); write(RDF_SCHEMA_END); writeNewline(); } /** * Write each of the parent's simple unqualified properties as an attribute. Returns true if all * of the properties are written as attributes. * * @param parentNode the parent property node * @param indent the current indent level * @return Returns true if all properties can be rendered as RDF attribute. * @throws IOException */ private boolean serializeCompactRDFAttrProps(XMPNode parentNode, int indent) throws IOException { boolean allAreAttrs = true; for (Iterator it = parentNode.iterateChildren(); it.hasNext();) { XMPNode prop = (XMPNode) it.next(); if (canBeRDFAttrProp(prop)) { writeNewline(); writeIndent(indent); write(prop.getName()); write("=\""); appendNodeValue(prop.getValue(), true); write('"'); } else { allAreAttrs = false;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -