/// <summary>The outermost call is special.</summary> /// <remarks> /// The outermost call is special. The names almost certainly differ. The /// qualifiers (and hence options) will differ for an alias to the x-default /// item of a langAlt array. /// </remarks> /// <param name="aliasNode">the alias node</param> /// <param name="baseNode">the base node of the alias</param> /// <param name="outerCall">marks the outer call of the recursion</param> /// <exception cref="XmpException">Forwards XMP errors</exception> private static void CompareAliasedSubtrees(XmpNode aliasNode, XmpNode baseNode, bool outerCall) { if (baseNode.Value != aliasNode.Value || aliasNode.GetChildrenLength() != baseNode.GetChildrenLength()) { throw new XmpException("Mismatch between alias and base nodes", XmpErrorCode.BadXmp); } if (!outerCall && (baseNode.Name != aliasNode.Name || !aliasNode.Options.Equals(baseNode.Options) || aliasNode.GetQualifierLength() != baseNode.GetQualifierLength())) { throw new XmpException("Mismatch between alias and base nodes", XmpErrorCode.BadXmp); } for (IIterator an = aliasNode.IterateChildren(), bn = baseNode.IterateChildren(); an.HasNext() && bn.HasNext();) { var aliasChild = (XmpNode)an.Next(); var baseChild = (XmpNode)bn.Next(); CompareAliasedSubtrees(aliasChild, baseChild, false); } for (IIterator an = aliasNode.IterateQualifier(), bn1 = baseNode.IterateQualifier(); an.HasNext() && bn1.HasNext();) { var aliasQual = (XmpNode)an.Next(); var baseQual = (XmpNode)bn1.Next(); CompareAliasedSubtrees(aliasQual, baseQual, false); } }
/// <summary>Prepares the next node to return if not already done.</summary> public virtual bool HasNext() { if (_returnProperty != null) { // hasNext has been called before return(true); } // find next node switch (_state) { case IterateNode: return(ReportNode()); case IterateChildren: if (_childrenIterator == null) { _childrenIterator = _visitedNode.IterateChildren(); } var hasNext = IterateChildrenMethod(_childrenIterator); if (!hasNext && _visitedNode.HasQualifier && !_enclosing.Options.IsOmitQualifiers) { _state = IterateQualifier; _childrenIterator = null; hasNext = HasNext(); } return(hasNext); } if (_childrenIterator == null) { _childrenIterator = _visitedNode.IterateQualifier(); } return(IterateChildrenMethod(_childrenIterator)); }
/// <summary>Writes all used namespaces of the subtree in node to the output.</summary> /// <remarks> /// Writes all used namespaces of the subtree in node to the output. /// The subtree is recursively traversed. /// </remarks> /// <param name="node">the root node of the subtree</param> /// <param name="usedPrefixes">a set containing currently used prefixes</param> /// <param name="indent">the current indent level</param> /// <exception cref="System.IO.IOException">Forwards all writer exceptions.</exception> private void DeclareUsedNamespaces(XmpNode node, ICollection <object> usedPrefixes, int indent) { if (node.Options.IsSchemaNode) { // The schema node name is the URI, the value is the prefix. var prefix = node.Value.Substring(0, node.Value.Length - 1 - 0); DeclareNamespace(prefix, node.Name, usedPrefixes, indent); } else if (node.Options.IsStruct) { for (var it = node.IterateChildren(); it.HasNext();) { var field = (XmpNode)it.Next(); DeclareNamespace(field.Name, null, usedPrefixes, indent); } } for (var it = node.IterateChildren(); it.HasNext();) { var child = (XmpNode)it.Next(); DeclareUsedNamespaces(child, usedPrefixes, indent); } for (var it = node.IterateQualifier(); it.HasNext();) { var qualifier = (XmpNode)it.Next(); DeclareNamespace(qualifier.Name, null, usedPrefixes, indent); DeclareUsedNamespaces(qualifier, usedPrefixes, indent); } }
/// <summary>Serializes the general qualifier.</summary> /// <param name="node">the root node of the subtree</param> /// <param name="indent">the current indent level</param> /// <exception cref="System.IO.IOException">Forwards all writer exceptions.</exception> /// <exception cref="XmpException">If qualifier and element fields are mixed.</exception> private void SerializeCompactRdfGeneralQualifier(int indent, XmpNode node) { // The node has general qualifiers, ones that can't be // attributes on a property element. // Emit using the qualified property pseudo-struct form. The // value is output by a call // to SerializePrettyRDFProperty with emitAsRDFValue set. // *** We're losing compactness in the calls to SerializePrettyRDFProperty. // *** Should refactor to have SerializeCompactRDFProperty that does one node. Write(" rdf:parseType=\"Resource\">"); WriteNewline(); SerializeCanonicalRdfProperty(node, false, true, indent + 1); for (var iq = node.IterateQualifier(); iq.HasNext();) { var qualifier = (XmpNode)iq.Next(); SerializeCanonicalRdfProperty(qualifier, false, 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(); } }