1f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// ================================================================================================= 2f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// ADOBE SYSTEMS INCORPORATED 3f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// Copyright 2006 Adobe Systems Incorporated 4f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// All Rights Reserved 5f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// 6f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms 7f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// of the Adobe license agreement accompanying it. 8f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// ================================================================================================= 9f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 10f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingpackage com.adobe.xmp.impl; 11f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 12f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.io.IOException; 13f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.io.OutputStream; 14f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.io.OutputStreamWriter; 15f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.Arrays; 16f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.HashSet; 17f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.Iterator; 18f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.Set; 19f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 20f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPConst; 21f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPError; 22f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPException; 23f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPMeta; 24f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPMetaFactory; 25f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.options.SerializeOptions; 26f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 27f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 28f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling/** 29f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Serializes the <code>XMPMeta</code>-object using the standard RDF serialization format. 30f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The output is written to an <code>OutputStream</code> 31f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * according to the <code>SerializeOptions</code>. 32f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 33f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @since 11.07.2006 34f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 35f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingpublic class XMPSerializerRDF 36f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling{ 37f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** default padding */ 38f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final int DEFAULT_PAD = 2048; 39f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 40f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String PACKET_HEADER = 41f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling "<?xpacket begin=\"\uFEFF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>"; 42f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** The w/r is missing inbetween */ 43f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String PACKET_TRAILER = "<?xpacket end=\""; 44f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 45f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String PACKET_TRAILER2 = "\"?>"; 46f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 47f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String RDF_XMPMETA_START = 48f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\""; 49f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 50f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String RDF_XMPMETA_END = "</x:xmpmeta>"; 51f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 52f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String RDF_RDF_START = 53f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">"; 54f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 55f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String RDF_RDF_END = "</rdf:RDF>"; 56f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 57f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 58f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String RDF_SCHEMA_START = "<rdf:Description rdf:about="; 59f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 60f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String RDF_SCHEMA_END = "</rdf:Description>"; 61f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 62f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String RDF_STRUCT_START = "<rdf:Description"; 63f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 64f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String RDF_STRUCT_END = "</rdf:Description>"; 65f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** a set of all rdf attribute qualifier */ 66f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling static final Set RDF_ATTR_QUALIFIER = new HashSet(Arrays.asList(new String[] { 67f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPConst.XML_LANG, "rdf:resource", "rdf:ID", "rdf:bagID", "rdf:nodeID" })); 68f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 69f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** the metadata object to be serialized. */ 70f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private XMPMetaImpl xmp; 71f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** the output stream to serialize to */ 72f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private CountOutputStream outputStream; 73f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** this writer is used to do the actual serialisation */ 74f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private OutputStreamWriter writer; 75f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** the stored serialisation options */ 76f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private SerializeOptions options; 77f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** the size of one unicode char, for UTF-8 set to 1 78f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * (Note: only valid for ASCII chars lower than 0x80), 79f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * set to 2 in case of UTF-16 */ 80f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private int unicodeSize = 1; // UTF-8 81f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** the padding in the XMP Packet, or the length of the complete packet in 82f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * case of option <em>exactPacketLength</em>. */ 83f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private int padding; 84f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 85f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 86f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 87f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The actual serialisation. 88f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 89f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param xmp the metadata object to be serialized 90f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param out outputStream the output stream to serialize to 91f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param options the serialization options 92f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 93f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException If case of wrong options or any other serialisaton error. 94f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 95f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling public void serialize(XMPMeta xmp, OutputStream out, 96f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling SerializeOptions options) throws XMPException 97f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 98f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling try 99f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 100f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling outputStream = new CountOutputStream(out); 101f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writer = new OutputStreamWriter(outputStream, options.getEncoding()); 102f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 103f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling this.xmp = (XMPMetaImpl) xmp; 104f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling this.options = options; 105f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling this.padding = options.getPadding(); 106f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 107f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writer = new OutputStreamWriter(outputStream, options.getEncoding()); 108f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 109f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling checkOptionsConsistence(); 110f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 111f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // serializes the whole packet, but don't write the tail yet 112f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // and flush to make sure that the written bytes are calculated correctly 113f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling String tailStr = serializeAsRDF(); 114f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writer.flush(); 115f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 116f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // adds padding 117f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling addPadding(tailStr.length()); 118f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 119f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // writes the tail 120f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(tailStr); 121f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writer.flush(); 122f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 123f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling outputStream.close(); 124f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 125f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling catch (IOException e) 126f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 127f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Error writing to the OutputStream", XMPError.UNKNOWN); 128f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 129f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 130f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 131f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 132f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 133f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Calulates the padding according to the options and write it to the stream. 134f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param tailLength the length of the tail string 135f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException thrown if packet size is to small to fit the padding 136f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException forwards writer errors 137f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 138f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void addPadding(int tailLength) throws XMPException, IOException 139f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 140f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (options.getExactPacketLength()) 141f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 142f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // the string length is equal to the length of the UTF-8 encoding 143f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int minSize = outputStream.getBytesWritten() + tailLength * unicodeSize; 144f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (minSize > padding) 145f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 146f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Can't fit into specified packet size", 147f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADSERIALIZE); 148f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 149f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling padding -= minSize; // Now the actual amount of padding to add. 150f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 151f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 152f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // fix rest of the padding according to Unicode unit size. 153f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling padding /= unicodeSize; 154f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 155f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int newlineLen = options.getNewline().length(); 156f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (padding >= newlineLen) 157f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 158f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling padding -= newlineLen; // Write this newline last. 159f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling while (padding >= (100 + newlineLen)) 160f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 161f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeChars(100, ' '); 162f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 163f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling padding -= (100 + newlineLen); 164f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 165f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeChars(padding, ' '); 166f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 167f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 168f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 169f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 170f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeChars(padding, ' '); 171f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 172f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 173f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 174f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 175f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 176f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Checks if the supplied options are consistent. 177f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException Thrown if options are conflicting 178f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 179f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling protected void checkOptionsConsistence() throws XMPException 180f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 181f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (options.getEncodeUTF16BE() | options.getEncodeUTF16LE()) 182f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 183f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling unicodeSize = 2; 184f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 185f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 186f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (options.getExactPacketLength()) 187f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 188f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (options.getOmitPacketWrapper() | options.getIncludeThumbnailPad()) 189f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 190f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Inconsistent options for exact size serialize", 191f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADOPTIONS); 192f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 193f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if ((options.getPadding() & (unicodeSize - 1)) != 0) 194f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 195f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Exact size must be a multiple of the Unicode element", 196f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADOPTIONS); 197f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 198f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 199f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (options.getReadOnlyPacket()) 200f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 201f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (options.getOmitPacketWrapper() | options.getIncludeThumbnailPad()) 202f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 203f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Inconsistent options for read-only packet", 204f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADOPTIONS); 205f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 206f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling padding = 0; 207f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 208f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (options.getOmitPacketWrapper()) 209f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 210f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (options.getIncludeThumbnailPad()) 211f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 212f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Inconsistent options for non-packet serialize", 213f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADOPTIONS); 214f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 215f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling padding = 0; 216f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 217f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 218f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 219f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (padding == 0) 220f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 221f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling padding = DEFAULT_PAD * unicodeSize; 222f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 223f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 224f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (options.getIncludeThumbnailPad()) 225f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 226f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!xmp.doesPropertyExist(XMPConst.NS_XMP, "Thumbnails")) 227f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 228f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling padding += 10000 * unicodeSize; 229f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 230f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 231f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 232f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 233f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 234f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 235f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 236f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Writes the (optional) packet header and the outer rdf-tags. 237f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns the packet end processing instraction to be written after the padding. 238f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwarded writer exceptions. 239f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException 240f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 241f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private String serializeAsRDF() throws IOException, XMPException 242f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 243f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write the packet header PI. 244f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!options.getOmitPacketWrapper()) 245f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 246f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(0); 247f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(PACKET_HEADER); 248f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 249f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 250f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 251f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write the xmpmeta element's start tag. 252f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(0); 253f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_XMPMETA_START); 254f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Note: this flag can only be set by unit tests 255f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!options.getOmitVersionAttribute()) 256f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 257f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(XMPMetaFactory.getVersionInfo().getMessage()); 258f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 259f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("\">"); 260f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 261f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 262f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write the rdf:RDF start tag. 263f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(1); 264f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_RDF_START); 265f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 266f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 267f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write all of the properties. 268f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (options.getUseCompactFormat()) 269f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 270f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializeCompactRDFSchemas(); 271f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 272f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 273f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 274f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializePrettyRDFSchemas(); 275f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 276f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 277f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write the rdf:RDF end tag. 278f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(1); 279f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_RDF_END); 280f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 281f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 282f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write the xmpmeta end tag. 283f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(0); 284f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_XMPMETA_END); 285f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 286f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 287f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write the packet trailer PI into the tail string as UTF-8. 288f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling String tailStr = ""; 289f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!options.getOmitPacketWrapper()) 290f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 291f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (int level = options.getBaseIndent(); level > 0; level--) 292f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 293f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling tailStr += options.getIndent(); 294f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 295f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 296f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling tailStr += PACKET_TRAILER; 297f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling tailStr += options.getReadOnlyPacket() ? 'r' : 'w'; 298f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling tailStr += PACKET_TRAILER2; 299f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 300f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 301f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return tailStr; 302f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 303f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 304f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 305f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 306f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Serializes the metadata in pretty-printed manner. 307f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwarded writer exceptions 308f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException 309f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 310f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void serializePrettyRDFSchemas() throws IOException, XMPException 311f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 312f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (xmp.getRoot().getChildrenLength() > 0) 313f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 314f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = xmp.getRoot().iterateChildren(); it.hasNext(); ) 315f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 316f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode currSchema = (XMPNode) it.next(); 317f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializePrettyRDFSchema(currSchema); 318f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 319f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 320f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 321f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 322f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(2); 323f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_SCHEMA_START); // Special case an empty XMP object. 324f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeTreeName(); 325f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("/>"); 326f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 327f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 328f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 329f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 330f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 331f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 332f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException 333f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 334f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void writeTreeName() throws IOException 335f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 336f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('"'); 337f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling String name = xmp.getRoot().getName(); 338f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (name != null) 339f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 340f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendNodeValue(name, true); 341f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 342f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('"'); 343f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 344f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 345f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 346f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 347f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Serializes the metadata in compact manner. 348f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwarded writer exceptions 349f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException 350f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 351f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void serializeCompactRDFSchemas() throws IOException, XMPException 352f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 353f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Begin the rdf:Description start tag. 354f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(2); 355f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_SCHEMA_START); 356f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeTreeName(); 357f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 358f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write all necessary xmlns attributes. 359f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling Set usedPrefixes = new HashSet(); 360f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling usedPrefixes.add("xml"); 361f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling usedPrefixes.add("rdf"); 362f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 363f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = xmp.getRoot().iterateChildren(); it.hasNext();) 364f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 365f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode schema = (XMPNode) it.next(); 366f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling declareUsedNamespaces(schema, usedPrefixes, 4); 367f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 368f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 369f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write the top level "attrProps" and close the rdf:Description start tag. 370f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean allAreAttrs = true; 371f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = xmp.getRoot().iterateChildren(); it.hasNext();) 372f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 373f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode schema = (XMPNode) it.next(); 374f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling allAreAttrs &= serializeCompactRDFAttrProps (schema, 3); 375f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 376f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 377f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!allAreAttrs) 378f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 379f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('>'); 380f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 381f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 382f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 383f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 384f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("/>"); 385f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 386f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return; // ! Done if all properties in all schema are written as attributes. 387f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 388f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 389f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write the remaining properties for each schema. 390f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = xmp.getRoot().iterateChildren(); it.hasNext();) 391f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 392f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode schema = (XMPNode) it.next(); 393f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializeCompactRDFElementProps (schema, 3); 394f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 395f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 396f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write the rdf:Description end tag. 397f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(2); 398f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_SCHEMA_END); 399f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 400f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 401f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 402f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 403f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 404f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 405f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Write each of the parent's simple unqualified properties as an attribute. Returns true if all 406f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * of the properties are written as attributes. 407f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 408f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param parentNode the parent property node 409f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param indent the current indent level 410f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns true if all properties can be rendered as RDF attribute. 411f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException 412f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 413f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private boolean serializeCompactRDFAttrProps(XMPNode parentNode, int indent) throws IOException 414f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 415f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean allAreAttrs = true; 416f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 417f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = parentNode.iterateChildren(); it.hasNext();) 418f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 419f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode prop = (XMPNode) it.next(); 420f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 421f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (canBeRDFAttrProp(prop)) 422f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 423f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 424f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(indent); 425f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(prop.getName()); 426f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("=\""); 427f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendNodeValue(prop.getValue(), true); 428f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('"'); 429f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 430f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 431f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 432f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling allAreAttrs = false; 433f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 434f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 435f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return allAreAttrs; 436f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 437f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 438f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 439f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 440f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Recursively handles the "value" for a node that must be written as an RDF 441f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * property element. It does not matter if it is a top level property, a 442f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * field of a struct, or an item of an array. The indent is that for the 443f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * property element. The patterns bwlow ignore attribute qualifiers such as 444f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * xml:lang, they don't affect the output form. 445f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 446f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <blockquote> 447f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 448f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <pre> 449f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ns:UnqualifiedStructProperty-1 450f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... The fields as attributes, if all are simple and unqualified 451f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * /> 452f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 453f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ns:UnqualifiedStructProperty-2 rdf:parseType="Resource"> 454f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... The fields as elements, if none are simple and unqualified 455f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </ns:UnqualifiedStructProperty-2> 456f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 457f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ns:UnqualifiedStructProperty-3> 458f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <rdf:Description 459f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... The simple and unqualified fields as attributes 460f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * > 461f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... The compound or qualified fields as elements 462f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </rdf:Description> 463f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </ns:UnqualifiedStructProperty-3> 464f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 465f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ns:UnqualifiedArrayProperty> 466f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <rdf:Bag> or Seq or Alt 467f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... Array items as rdf:li elements, same forms as top level properties 468f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </rdf:Bag> 469f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </ns:UnqualifiedArrayProperty> 470f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 471f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ns:QualifiedProperty rdf:parseType="Resource"> 472f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <rdf:value> ... Property "value" 473f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * following the unqualified forms ... </rdf:value> 474f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... Qualifiers looking like named struct fields 475f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </ns:QualifiedProperty> 476f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </pre> 477f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 478f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </blockquote> 479f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 480f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * *** Consider numbered array items, but has compatibility problems. *** 481f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Consider qualified form with rdf:Description and attributes. 482f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 483f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param parentNode the parent node 484f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param indent the current indent level 485f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwards writer exceptions 486f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException If qualifier and element fields are mixed. 487f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 488f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void serializeCompactRDFElementProps(XMPNode parentNode, int indent) 489f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throws IOException, XMPException 490f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 491f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = parentNode.iterateChildren(); it.hasNext();) 492f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 493f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode node = (XMPNode) it.next(); 494f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (canBeRDFAttrProp (node)) 495f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 496f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling continue; 497f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 498f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 499f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean emitEndTag = true; 500f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean indentEndTag = true; 501f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 502f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Determine the XML element name, write the name part of the start tag. Look over the 503f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // qualifiers to decide on "normal" versus "rdf:value" form. Emit the attribute 504f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // qualifiers at the same time. 505f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling String elemName = node.getName(); 506f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (XMPConst.ARRAY_ITEM_NAME.equals(elemName)) 507f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 508f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling elemName = "rdf:li"; 509f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 510f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 511f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(indent); 512f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('<'); 513f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(elemName); 514f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 515f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean hasGeneralQualifiers = false; 516f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean hasRDFResourceQual = false; 517f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 518f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator iq = node.iterateQualifier(); iq.hasNext();) 519f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 520f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode qualifier = (XMPNode) iq.next(); 521f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!RDF_ATTR_QUALIFIER.contains(qualifier.getName())) 522f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 523f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling hasGeneralQualifiers = true; 524f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 525f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 526f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 527f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling hasRDFResourceQual = "rdf:resource".equals(qualifier.getName()); 528f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(' '); 529f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(qualifier.getName()); 530f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("=\""); 531f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendNodeValue(qualifier.getValue(), true); 532f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('"'); 533f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 534f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 535f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 536f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 537f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Process the property according to the standard patterns. 538f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (hasGeneralQualifiers) 539f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 540f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializeCompactRDFGeneralQualifier(indent, node); 541f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 542f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 543f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 544f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This node has only attribute qualifiers. Emit as a property element. 545f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!node.getOptions().isCompositeProperty()) 546f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 547f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling Object[] result = serializeCompactRDFSimpleProp(node); 548f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitEndTag = ((Boolean) result[0]).booleanValue(); 549f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling indentEndTag = ((Boolean) result[1]).booleanValue(); 550f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 551f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (node.getOptions().isArray()) 552f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 553f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializeCompactRDFArrayProp(node, indent); 554f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 555f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 556f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 557f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitEndTag = serializeCompactRDFStructProp( 558f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling node, indent, hasRDFResourceQual); 559f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 560f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 561f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 562f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 563f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Emit the property element end tag. 564f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (emitEndTag) 565f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 566f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (indentEndTag) 567f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 568f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(indent); 569f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 570f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("</"); 571f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(elemName); 572f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('>'); 573f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 574f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 575f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 576f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 577f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 578f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 579f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 580f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 581f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Serializes a simple property. 582f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 583f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param node an XMPNode 584f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns an array containing the flags emitEndTag and indentEndTag. 585f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwards the writer exceptions. 586f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 587f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private Object[] serializeCompactRDFSimpleProp(XMPNode node) throws IOException 588f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 589f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is a simple property. 590f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling Boolean emitEndTag = Boolean.TRUE; 591f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling Boolean indentEndTag = Boolean.TRUE; 592f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 593f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (node.getOptions().isURI()) 594f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 595f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(" rdf:resource=\""); 596f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendNodeValue(node.getValue(), true); 597f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("\"/>"); 598f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 599f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitEndTag = Boolean.FALSE; 600f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 601f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (node.getValue() == null || node.getValue().length() == 0) 602f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 603f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("/>"); 604f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 605f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitEndTag = Boolean.FALSE; 606f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 607f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 608f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 609f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('>'); 610f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendNodeValue (node.getValue(), false); 611f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling indentEndTag = Boolean.FALSE; 612f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 613f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 614f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return new Object[] {emitEndTag, indentEndTag}; 615f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 616f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 617f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 618f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 619f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Serializes an array property. 620f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 621f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param node an XMPNode 622f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param indent the current indent level 623f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwards the writer exceptions. 624f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException If qualifier and element fields are mixed. 625f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 626f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void serializeCompactRDFArrayProp(XMPNode node, int indent) throws IOException, 627f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPException 628f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 629f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is an array. 630f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('>'); 631f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 632f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitRDFArrayTag (node, true, indent + 1); 633f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 634f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (node.getOptions().isArrayAltText()) 635f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 636f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNodeUtils.normalizeLangArray (node); 637f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 638f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 639f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializeCompactRDFElementProps(node, indent + 2); 640f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 641f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitRDFArrayTag(node, false, indent + 1); 642f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 643f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 644f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 645f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 646f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Serializes a struct property. 647f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 648f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param node an XMPNode 649f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param indent the current indent level 650f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param hasRDFResourceQual Flag if the element has resource qualifier 651f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns true if an end flag shall be emitted. 652f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwards the writer exceptions. 653f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException If qualifier and element fields are mixed. 654f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 655f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private boolean serializeCompactRDFStructProp(XMPNode node, int indent, 656f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean hasRDFResourceQual) throws XMPException, IOException 657f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 658f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This must be a struct. 659f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean hasAttrFields = false; 660f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean hasElemFields = false; 661f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean emitEndTag = true; 662f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 663f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator ic = node.iterateChildren(); ic.hasNext(); ) 664f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 665f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode field = (XMPNode) ic.next(); 666f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (canBeRDFAttrProp(field)) 667f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 668f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling hasAttrFields = true; 669f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 670f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 671f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 672f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling hasElemFields = true; 673f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 674f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 675f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (hasAttrFields && hasElemFields) 676f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 677f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; // No sense looking further. 678f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 679f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 680f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 681f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (hasRDFResourceQual && hasElemFields) 682f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 683f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException( 684f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling "Can't mix rdf:resource qualifier and element fields", 685f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADRDF); 686f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 687f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 688f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!node.hasChildren()) 689f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 690f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Catch an empty struct as a special case. The case 691f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // below would emit an empty 692f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // XML element, which gets reparsed as a simple property 693f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // with an empty value. 694f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(" rdf:parseType=\"Resource\"/>"); 695f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 696f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitEndTag = false; 697f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 698f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 699f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (!hasElemFields) 700f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 701f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // All fields can be attributes, use the 702f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // emptyPropertyElt form. 703f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializeCompactRDFAttrProps(node, indent + 1); 704f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("/>"); 705f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 706f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitEndTag = false; 707f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 708f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 709f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (!hasAttrFields) 710f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 711f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // All fields must be elements, use the 712f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // parseTypeResourcePropertyElt form. 713f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(" rdf:parseType=\"Resource\">"); 714f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 715f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializeCompactRDFElementProps(node, indent + 1); 716f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 717f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 718f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 719f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 720f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Have a mix of attributes and elements, use an inner rdf:Description. 721f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('>'); 722f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 723f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(indent + 1); 724f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_STRUCT_START); 725f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializeCompactRDFAttrProps(node, indent + 2); 726f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(">"); 727f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 728f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializeCompactRDFElementProps(node, indent + 1); 729f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(indent + 1); 730f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_STRUCT_END); 731f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 732f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 733f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return emitEndTag; 734f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 735f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 736f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 737f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 738f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Serializes the general qualifier. 739f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param node the root node of the subtree 740f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param indent the current indent level 741f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwards all writer exceptions. 742f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException If qualifier and element fields are mixed. 743f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 744f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void serializeCompactRDFGeneralQualifier(int indent, XMPNode node) 745f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throws IOException, XMPException 746f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 747f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // The node has general qualifiers, ones that can't be 748f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // attributes on a property element. 749f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Emit using the qualified property pseudo-struct form. The 750f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // value is output by a call 751f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // to SerializePrettyRDFProperty with emitAsRDFValue set. 752f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(" rdf:parseType=\"Resource\">"); 753f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 754f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 755f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializePrettyRDFProperty(node, true, indent + 1); 756f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 757f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator iq = node.iterateQualifier(); iq.hasNext();) 758f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 759f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode qualifier = (XMPNode) iq.next(); 760f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializePrettyRDFProperty(qualifier, false, indent + 1); 761f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 762f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 763f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 764f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 765f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 766f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Serializes one schema with all contained properties in pretty-printed 767f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * manner.<br> 768f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Each schema's properties are written in a separate 769f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * rdf:Description element. All of the necessary namespaces are declared in 770f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * the rdf:Description element. The baseIndent is the base level for the 771f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * entire serialization, that of the x:xmpmeta element. An xml:lang 772f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * qualifier is written as an attribute of the property start tag, not by 773f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * itself forcing the qualified property form. 774f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 775f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <blockquote> 776f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 777f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <pre> 778f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <rdf:Description rdf:about="TreeName" xmlns:ns="URI" ... > 779f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 780f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... The actual properties of the schema, see SerializePrettyRDFProperty 781f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 782f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <!-- ns1:Alias is aliased to ns2:Actual --> ... If alias comments are wanted 783f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 784f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </rdf:Description> 785f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </pre> 786f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 787f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </blockquote> 788f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 789f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param schemaNode a schema node 790f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwarded writer exceptions 791f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException 792f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 793f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void serializePrettyRDFSchema(XMPNode schemaNode) throws IOException, XMPException 794f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 795f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(2); 796f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_SCHEMA_START); 797f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeTreeName(); 798f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 799f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling Set usedPrefixes = new HashSet(); 800f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling usedPrefixes.add("xml"); 801f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling usedPrefixes.add("rdf"); 802f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 803f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling declareUsedNamespaces(schemaNode, usedPrefixes, 4); 804f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 805f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('>'); 806f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 807f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 808f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write each of the schema's actual properties. 809f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = schemaNode.iterateChildren(); it.hasNext();) 810f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 811f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode propNode = (XMPNode) it.next(); 812f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializePrettyRDFProperty(propNode, false, 3); 813f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 814f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 815f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Write the rdf:Description end tag. 816f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(2); 817f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(RDF_SCHEMA_END); 818f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 819f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 820f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 821f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 822f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 823f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Writes all used namespaces of the subtree in node to the output. 824f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The subtree is recursivly traversed. 825f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param node the root node of the subtree 826f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param usedPrefixes a set containing currently used prefixes 827f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param indent the current indent level 828f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwards all writer exceptions. 829f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 830f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void declareUsedNamespaces(XMPNode node, Set usedPrefixes, int indent) 831f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throws IOException 832f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 833f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (node.getOptions().isSchemaNode()) 834f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 835f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // The schema node name is the URI, the value is the prefix. 836f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling String prefix = node.getValue().substring(0, node.getValue().length() - 1); 837f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling declareNamespace(prefix, node.getName(), usedPrefixes, indent); 838f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 839f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (node.getOptions().isStruct()) 840f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 841f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = node.iterateChildren(); it.hasNext();) 842f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 843f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode field = (XMPNode) it.next(); 844f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling declareNamespace(field.getName(), null, usedPrefixes, indent); 845f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 846f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 847f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 848f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = node.iterateChildren(); it.hasNext();) 849f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 850f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode child = (XMPNode) it.next(); 851f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling declareUsedNamespaces(child, usedPrefixes, indent); 852f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 853f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 854f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = node.iterateQualifier(); it.hasNext();) 855f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 856f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode qualifier = (XMPNode) it.next(); 857f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling declareNamespace(qualifier.getName(), null, usedPrefixes, indent); 858f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling declareUsedNamespaces(qualifier, usedPrefixes, indent); 859f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 860f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 861f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 862f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 863f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 864f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Writes one namespace declaration to the output. 865f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param prefix a namespace prefix (without colon) or a complete qname (when namespace == null) 866f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param namespace the a namespace 867f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param usedPrefixes a set containing currently used prefixes 868f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param indent the current indent level 869f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwards all writer exceptions. 870f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 871f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void declareNamespace(String prefix, String namespace, Set usedPrefixes, int indent) 872f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throws IOException 873f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 874f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (namespace == null) 875f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 876f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // prefix contains qname, extract prefix and lookup namespace with prefix 877f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling QName qname = new QName(prefix); 878f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (qname.hasPrefix()) 879f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 880f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling prefix = qname.getPrefix(); 881f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // add colon for lookup 882f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling namespace = XMPMetaFactory.getSchemaRegistry().getNamespaceURI(prefix + ":"); 883f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // prefix w/o colon 884f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling declareNamespace(prefix, namespace, usedPrefixes, indent); 885f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 886f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 887f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 888f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return; 889f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 890f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 891f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 892f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!usedPrefixes.contains(prefix)) 893f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 894f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 895f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(indent); 896f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("xmlns:"); 897f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(prefix); 898f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("=\""); 899f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(namespace); 900f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('"'); 901f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling usedPrefixes.add(prefix); 902f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 903f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 904f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 905f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 906f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 907f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Recursively handles the "value" for a node. It does not matter if it is a 908f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * top level property, a field of a struct, or an item of an array. The 909f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * indent is that for the property element. An xml:lang qualifier is written 910f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * as an attribute of the property start tag, not by itself forcing the 911f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * qualified property form. The patterns below mostly ignore attribute 912f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * qualifiers like xml:lang. Except for the one struct case, attribute 913f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * qualifiers don't affect the output form. 914f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 915f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <blockquote> 916f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 917f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <pre> 918f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ns:UnqualifiedSimpleProperty>value</ns:UnqualifiedSimpleProperty> 919f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 920f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ns:UnqualifiedStructProperty rdf:parseType="Resource"> 921f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * (If no rdf:resource qualifier) 922f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... Fields, same forms as top level properties 923f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </ns:UnqualifiedStructProperty> 924f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 925f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ns:ResourceStructProperty rdf:resource="URI" 926f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... Fields as attributes 927f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * > 928f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 929f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ns:UnqualifiedArrayProperty> 930f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <rdf:Bag> or Seq or Alt 931f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... Array items as rdf:li elements, same forms as top level properties 932f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </rdf:Bag> 933f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </ns:UnqualifiedArrayProperty> 934f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 935f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ns:QualifiedProperty rdf:parseType="Resource"> 936f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <rdf:value> ... Property "value" following the unqualified 937f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * forms ... </rdf:value> 938f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ... Qualifiers looking like named struct fields 939f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </ns:QualifiedProperty> 940f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </pre> 941f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 942f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </blockquote> 943f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 944f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param node the property node 945f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param emitAsRDFValue property shall be renderes as attribute rather than tag 946f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param indent the current indent level 947f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwards all writer exceptions. 948f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException If "rdf:resource" and general qualifiers are mixed. 949f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 950f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void serializePrettyRDFProperty(XMPNode node, boolean emitAsRDFValue, int indent) 951f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throws IOException, XMPException 952f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 953f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean emitEndTag = true; 954f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean indentEndTag = true; 955f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 956f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Determine the XML element name. Open the start tag with the name and 957f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // attribute qualifiers. 958f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 959f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling String elemName = node.getName(); 960f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (emitAsRDFValue) 961f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 962f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling elemName = "rdf:value"; 963f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 964f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (XMPConst.ARRAY_ITEM_NAME.equals(elemName)) 965f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 966f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling elemName = "rdf:li"; 967f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 968f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 969f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(indent); 970f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('<'); 971f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(elemName); 972f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 973f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean hasGeneralQualifiers = false; 974f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean hasRDFResourceQual = false; 975f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 976f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = node.iterateQualifier(); it.hasNext();) 977f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 978f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode qualifier = (XMPNode) it.next(); 979f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!RDF_ATTR_QUALIFIER.contains(qualifier.getName())) 980f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 981f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling hasGeneralQualifiers = true; 982f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 983f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 984f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 985f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling hasRDFResourceQual = "rdf:resource".equals(qualifier.getName()); 986f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!emitAsRDFValue) 987f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 988f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(' '); 989f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(qualifier.getName()); 990f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("=\""); 991f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendNodeValue(qualifier.getValue(), true); 992f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('"'); 993f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 994f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 995f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 996f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 997f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Process the property according to the standard patterns. 998f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 999f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (hasGeneralQualifiers && !emitAsRDFValue) 1000f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1001f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This node has general, non-attribute, qualifiers. Emit using the 1002f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // qualified property form. 1003f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // ! The value is output by a recursive call ON THE SAME NODE with 1004f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // emitAsRDFValue set. 1005f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1006f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (hasRDFResourceQual) 1007f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1008f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Can't mix rdf:resource and general qualifiers", 1009f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADRDF); 1010f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1011f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1012f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(" rdf:parseType=\"Resource\">"); 1013f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 1014f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1015f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializePrettyRDFProperty(node, true, indent + 1); 1016f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1017f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = node.iterateQualifier(); it.hasNext();) 1018f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1019f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode qualifier = (XMPNode) it.next(); 1020f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!RDF_ATTR_QUALIFIER.contains(qualifier.getName())) 1021f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1022f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializePrettyRDFProperty(qualifier, false, indent + 1); 1023f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1024f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1025f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1026f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 1027f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1028f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This node has no general qualifiers. Emit using an unqualified form. 1029f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1030f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!node.getOptions().isCompositeProperty()) 1031f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1032f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is a simple property. 1033f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1034f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (node.getOptions().isURI()) 1035f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1036f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(" rdf:resource=\""); 1037f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendNodeValue(node.getValue(), true); 1038f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("\"/>"); 1039f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 1040f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitEndTag = false; 1041f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1042f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (node.getValue() == null || "".equals(node.getValue())) 1043f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1044f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("/>"); 1045f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 1046f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitEndTag = false; 1047f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1048f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 1049f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1050f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('>'); 1051f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendNodeValue(node.getValue(), false); 1052f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling indentEndTag = false; 1053f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1054f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1055f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (node.getOptions().isArray()) 1056f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1057f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is an array. 1058f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('>'); 1059f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 1060f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitRDFArrayTag(node, true, indent + 1); 1061f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (node.getOptions().isArrayAltText()) 1062f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1063f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNodeUtils.normalizeLangArray(node); 1064f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1065f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = node.iterateChildren(); it.hasNext();) 1066f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1067f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode child = (XMPNode) it.next(); 1068f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializePrettyRDFProperty(child, false, indent + 2); 1069f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1070f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitRDFArrayTag(node, false, indent + 1); 1071f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1072f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1073f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1074f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (!hasRDFResourceQual) 1075f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1076f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is a "normal" struct, use the rdf:parseType="Resource" form. 1077f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!node.hasChildren()) 1078f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1079f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(" rdf:parseType=\"Resource\"/>"); 1080f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 1081f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitEndTag = false; 1082f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1083f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 1084f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1085f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(" rdf:parseType=\"Resource\">"); 1086f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 1087f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = node.iterateChildren(); it.hasNext();) 1088f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1089f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode child = (XMPNode) it.next(); 1090f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling serializePrettyRDFProperty(child, false, indent + 1); 1091f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1092f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1093f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1094f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 1095f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1096f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is a struct with an rdf:resource attribute, use the 1097f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // "empty property element" form. 1098f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = node.iterateChildren(); it.hasNext();) 1099f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1100f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode child = (XMPNode) it.next(); 1101f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!canBeRDFAttrProp(child)) 1102f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1103f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Can't mix rdf:resource and complex fields", 1104f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADRDF); 1105f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1106f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 1107f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(indent + 1); 1108f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(' '); 1109f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(child.getName()); 1110f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("=\""); 1111f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendNodeValue(child.getValue(), true); 1112f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('"'); 1113f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1114f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("/>"); 1115f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 1116f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling emitEndTag = false; 1117f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1118f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1119f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1120f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Emit the property element end tag. 1121f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (emitEndTag) 1122f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1123f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (indentEndTag) 1124f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1125f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(indent); 1126f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1127f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("</"); 1128f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(elemName); 1129f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write('>'); 1130f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 1131f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1132f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1133f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1134f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1135f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1136f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Writes the array start and end tags. 1137f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 1138f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param arrayNode an array node 1139f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param isStartTag flag if its the start or end tag 1140f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param indent the current indent level 1141f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException forwards writer exceptions 1142f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1143f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void emitRDFArrayTag(XMPNode arrayNode, boolean isStartTag, int indent) 1144f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throws IOException 1145f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1146f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (isStartTag || arrayNode.hasChildren()) 1147f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1148f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeIndent(indent); 1149f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(isStartTag ? "<rdf:" : "</rdf:"); 1150f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1151f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (arrayNode.getOptions().isArrayAlternate()) 1152f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1153f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("Alt"); 1154f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1155f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (arrayNode.getOptions().isArrayOrdered()) 1156f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1157f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("Seq"); 1158f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1159f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 1160f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1161f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("Bag"); 1162f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1163f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1164f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (isStartTag && !arrayNode.hasChildren()) 1165f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1166f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write("/>"); 1167f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1168f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 1169f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1170f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write(">"); 1171f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1172f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1173f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writeNewline(); 1174f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1175f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1176f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1177f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1178f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1179f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Serializes the node value in XML encoding. Its used for tag bodies and 1180f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * attributes. <em>Note:</em> The attribute is always limited by quotes, 1181f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * thats why <code>&apos;</code> is never serialized. <em>Note:</em> 1182f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Control chars are written unescaped, but if the user uses others than tab, LF 1183f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * and CR the resulting XML will become invalid. 1184f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 1185f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param value the value of the node 1186f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param forAttribute flag if value is an attribute value 1187f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException 1188f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1189f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void appendNodeValue(String value, boolean forAttribute) throws IOException 1190f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1191f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling write (Utils.escapeXML(value, forAttribute, true)); 1192f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1193f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1194f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1195f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1196f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * A node can be serialized as RDF-Attribute, if it meets the following conditions: 1197f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <ul> 1198f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <li>is not array item 1199f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <li>don't has qualifier 1200f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <li>is no URI 1201f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <li>is no composite property 1202f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * </ul> 1203f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 1204f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param node an XMPNode 1205f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns true if the node serialized as RDF-Attribute 1206f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1207f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private boolean canBeRDFAttrProp(XMPNode node) 1208f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1209f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 1210f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling !node.hasQualifier() && 1211f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling !node.getOptions().isURI() && 1212f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling !node.getOptions().isCompositeProperty() && 1213f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling !XMPConst.ARRAY_ITEM_NAME.equals(node.getName()); 1214f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1215f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1216f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1217f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1218f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Writes indents and automatically includes the baseindend from the options. 1219f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param times number of indents to write 1220f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException forwards exception 1221f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1222f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void writeIndent(int times) throws IOException 1223f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1224f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (int i = options.getBaseIndent() + times; i > 0; i--) 1225f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1226f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writer.write(options.getIndent()); 1227f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1228f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1229f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1230f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1231f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1232f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Writes a char to the output. 1233f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param c a char 1234f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException forwards writer exceptions 1235f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1236f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void write(int c) throws IOException 1237f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1238f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writer.write(c); 1239f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1240f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1241f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1242f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1243f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Writes a String to the output. 1244f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param str a String 1245f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException forwards writer exceptions 1246f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1247f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void write(String str) throws IOException 1248f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1249f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writer.write(str); 1250f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1251f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1252f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1253f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1254f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Writes an amount of chars, mostly spaces 1255f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param number number of chars 1256f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param c a char 1257f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException 1258f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1259f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void writeChars(int number, char c) throws IOException 1260f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1261f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (; number > 0; number--) 1262f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1263f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writer.write(c); 1264f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1265f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1266f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1267f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1268f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1269f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Writes a newline according to the options. 1270f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws IOException Forwards exception 1271f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1272f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private void writeNewline() throws IOException 1273f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1274f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling writer.write(options.getNewline()); 1275f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1276f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling}