/// <summary>Serializes an array property.</summary> /// <param name="node">an XMPNode</param> /// <param name="indent">the current indent level</param> /// <exception cref="System.IO.IOException">Forwards the writer exceptions.</exception> /// <exception cref="XmpException">If qualifier and element fields are mixed.</exception> private void SerializeCompactRdfArrayProp(XmpNode node, int indent) { // This is an array. Write('>'); WriteNewline(); EmitRdfArrayTag(node, true, indent + 1); if (node.Options.IsArrayAltText) { XmpNodeUtils.NormalizeLangArray(node); } SerializeCompactRdfElementProps(node, indent + 2); EmitRdfArrayTag(node, false, indent + 1); }
/// <summary>Recursively handles the "value" for a node.</summary> /// <remarks> /// Recursively handles the "value" for a node. It does not matter if it is a /// top level property, a field of a struct, or an item of an array. The /// indent is that for the property element. An xml:lang qualifier is written /// as an attribute of the property start tag, not by itself forcing the /// qualified property form. The patterns below mostly ignore attribute /// qualifiers like xml:lang. Except for the one struct case, attribute /// qualifiers don't affect the output form. /// <code> /// <ns:UnqualifiedSimpleProperty>value</ns:UnqualifiedSimpleProperty> /// <ns:UnqualifiedStructProperty> (If no rdf:resource qualifier) /// <rdf:Description> /// ... Fields, same forms as top level properties /// </rdf:Description> /// </ns:UnqualifiedStructProperty> /// <ns:ResourceStructProperty rdf:resource="URI" /// ... Fields as attributes /// > /// <ns:UnqualifiedArrayProperty> /// <rdf:Bag> or Seq or Alt /// ... Array items as rdf:li elements, same forms as top level properties /// </rdf:Bag> /// </ns:UnqualifiedArrayProperty> /// <ns:QualifiedProperty> /// <rdf:Description> /// <rdf:value> ... Property "value" following the unqualified /// forms ... </rdf:value> /// ... Qualifiers looking like named struct fields /// </rdf:Description> /// </ns:QualifiedProperty> /// </code> /// </remarks> /// <param name="node">the property node</param> /// <param name="emitAsRdfValue">property shall be rendered as attribute rather than tag</param> /// <param name="useCanonicalRdf"> /// use canonical form with inner description tag or /// the compact form with rdf:ParseType="resource" attribute. /// </param> /// <param name="indent">the current indent level</param> /// <exception cref="System.IO.IOException">Forwards all writer exceptions.</exception> /// <exception cref="XmpException">If "rdf:resource" and general qualifiers are mixed.</exception> private void SerializeCanonicalRdfProperty(XmpNode node, bool useCanonicalRdf, bool emitAsRdfValue, int indent) { var emitEndTag = true; var indentEndTag = true; // Determine the XML element name. Open the start tag with the name and // attribute qualifiers. var elemName = node.Name; if (emitAsRdfValue) { elemName = "rdf:value"; } else if (elemName == XmpConstants.ArrayItemName) { elemName = XmpConstants.RdfLi; } WriteIndent(indent); Write('<'); Write(elemName); var hasGeneralQualifiers = false; var hasRdfResourceQual = false; for (var it = node.IterateQualifier(); it.HasNext();) { var qualifier = (XmpNode)it.Next(); if (!RdfAttrQualifier.Contains(qualifier.Name)) { hasGeneralQualifiers = true; } else { hasRdfResourceQual = qualifier.Name == "rdf:resource"; if (!emitAsRdfValue) { Write(' '); Write(qualifier.Name); Write("=\""); AppendNodeValue(qualifier.Value, true); Write('"'); } } } // Process the property according to the standard patterns. if (hasGeneralQualifiers && !emitAsRdfValue) { // This node has general, non-attribute, qualifiers. Emit using the // qualified property form. // ! The value is output by a recursive call ON THE SAME NODE with // emitAsRDFValue set. if (hasRdfResourceQual) { throw new XmpException("Can't mix rdf:resource and general qualifiers", XmpErrorCode.BadRdf); } // Change serialization to canonical format with inner rdf:Description-tag // depending on option if (useCanonicalRdf) { Write(">"); WriteNewline(); indent++; WriteIndent(indent); Write(RdfStructStart); Write(">"); } else { Write(" rdf:parseType=\"Resource\">"); } WriteNewline(); SerializeCanonicalRdfProperty(node, useCanonicalRdf, true, indent + 1); for (var it = node.IterateQualifier(); it.HasNext();) { var qualifier = (XmpNode)it.Next(); if (!RdfAttrQualifier.Contains(qualifier.Name)) { SerializeCanonicalRdfProperty(qualifier, useCanonicalRdf, false, indent + 1); } } if (useCanonicalRdf) { WriteIndent(indent); Write(RdfStructEnd); WriteNewline(); indent--; } } else { // This node has no general qualifiers. Emit using an unqualified form. if (!node.Options.IsCompositeProperty) { // This is a simple property. if (node.Options.IsUri) { Write(" rdf:resource=\""); AppendNodeValue(node.Value, true); Write("\"/>"); WriteNewline(); emitEndTag = false; } else if (string.IsNullOrEmpty(node.Value)) { Write("/>"); WriteNewline(); emitEndTag = false; } else { Write('>'); AppendNodeValue(node.Value, false); indentEndTag = false; } } else { if (node.Options.IsArray) { // This is an array. Write('>'); WriteNewline(); EmitRdfArrayTag(node, true, indent + 1); if (node.Options.IsArrayAltText) { XmpNodeUtils.NormalizeLangArray(node); } for (var it1 = node.IterateChildren(); it1.HasNext();) { var child = (XmpNode)it1.Next(); SerializeCanonicalRdfProperty(child, useCanonicalRdf, false, indent + 2); } EmitRdfArrayTag(node, false, indent + 1); } else if (!hasRdfResourceQual) { // This is a "normal" struct, use the rdf:parseType="Resource" form. if (!node.HasChildren) { // Change serialization to canonical format with inner rdf:Description-tag // if option is set if (useCanonicalRdf) { Write(">"); WriteNewline(); WriteIndent(indent + 1); Write(RdfEmptyStruct); } else { Write(" rdf:parseType=\"Resource\"/>"); emitEndTag = false; } WriteNewline(); } else { // Change serialization to canonical format with inner rdf:Description-tag // if option is set if (useCanonicalRdf) { Write(">"); WriteNewline(); indent++; WriteIndent(indent); Write(RdfStructStart); Write(">"); } else { Write(" rdf:parseType=\"Resource\">"); } WriteNewline(); for (var it = node.IterateChildren(); it.HasNext();) { var child = (XmpNode)it.Next(); SerializeCanonicalRdfProperty(child, useCanonicalRdf, false, indent + 1); } if (useCanonicalRdf) { WriteIndent(indent); Write(RdfStructEnd); WriteNewline(); indent--; } } } else { // This is a struct with an rdf:resource attribute, use the // "empty property element" form. for (var it1 = node.IterateChildren(); it1.HasNext();) { var child = (XmpNode)it1.Next(); if (!CanBeRdfAttrProp(child)) { throw new XmpException("Can't mix rdf:resource and complex fields", XmpErrorCode.BadRdf); } WriteNewline(); WriteIndent(indent + 1); Write(' '); Write(child.Name); Write("=\""); AppendNodeValue(child.Value, true); Write('"'); } Write("/>"); WriteNewline(); emitEndTag = false; } } } // Emit the property element end tag. if (emitEndTag) { if (indentEndTag) { WriteIndent(indent); } Write("</"); Write(elemName); Write('>'); WriteNewline(); } }